Expressions
Irregular • File:
Expressions.csv• Writer:LaboExpressionsDataWriter.cs
At a glance
Expressions captures discrete detection events for configured facial / hand expressions — one row per detection, stamped at trigger time. Each row records the expression name, left and right similarity scores, the threshold in effect, and which sides met the threshold. Children of a single trigger are flattened — if one detection produces multiple child observations, each gets its own row.
For continuous per-frame facial pose (jaw-open over time, blendshape time series), use the regular Face stream instead. Expressions is for "when did a detectable expression cross the threshold?" questions.
When it writes
Whenever the expression detector calls LaboDataCapture.Instance.ExpressionEntry(observation). Detection logic is driven by configured expression definitions and thresholds; whenever the detector observes that a configured expression has crossed its threshold, it fires.
File & location
- Default:
Expressions.csvin the run directory. - Writer:
LaboExpressionsDataWriter.cs. - Observation:
Runtime/DataCapture/Observations/ExpressionObservation.cs.
Configuration
ExpressionsDataCapture SO (subclass of DataCaptureConfig). Fields:
| Field | What it does |
|---|---|
enabledLogging | Master enable/disable. |
fileName | Override the default name. |
flushEveryNRows | Batched flush cadence. Default 30. |
writeHeader | Write the header row on first emission. Default true. |
Expression definitions and thresholds (which expressions to detect, similarity thresholds) live on the experience's expression-detection components — this SO only controls how the writer emits rows.
Not auto-created on experience open — assign manually if missing.
Columns
Shared prefix
See Column conventions — The shared prefix.
Domain columns
| Column | Type | Units / values | Description |
|---|---|---|---|
Expression_Name | string | — | Name of the detected expression (e.g. Smile, BrowRaise, Frown). |
Expression_State | string | Detected / Partial / Ended | Detection state at this moment. |
Expression_Threshold | float | [0, 1] | Similarity threshold that was active. |
Expression_Left_Similarity | float | [0, 1] | Similarity score on the left side. |
Expression_Right_Similarity | float | [0, 1] | Similarity score on the right side. |
Expression_Left_Met | bool | — | Whether the left-side score met the threshold. |
Expression_Right_Met | bool | — | Whether the right-side score met the threshold. |
Expression_Both_Met | bool | — | Whether both sides met the threshold (full bilateral match). |
Sample rows
...shared...,Expression_Name,Expression_State,Expression_Threshold,Expression_Left_Similarity,Expression_Right_Similarity,Expression_Left_Met,Expression_Right_Met,Expression_Both_Met
...,Smile,Detected,0.75,0.82,0.79,True,True,True
...,BrowRaise,Partial,0.80,0.84,0.62,True,False,False
Join with other streams
Typical pattern — attach per-frame context to each detection, then filter:
import pandas as pd
exp = pd.read_csv("ExperienceState.csv")
expr = pd.read_csv("Expressions.csv")
enriched = expr.merge(exp, on="FrameNumber", how="left", suffixes=("_expr", "_frame"))
# Every full-smile detection during the Stimulus epoch:
smiles = enriched[(enriched["Expression_Name"] == "Smile")
& (enriched["Expression_Both_Met"] == True)
& (enriched["EpochName_frame"] == "Stimulus")]
Or pair with stimulus-onset events from Events to do a "smile latency after stimulus" analysis:
events = pd.read_csv("EventPoints.csv")
stim_onsets = events[events["Event_Description"] == "Stimulus flash"]
# For each onset, find the first Smile detection after it:
for _, onset in stim_onsets.iterrows():
after = smiles[smiles["MonotonicExecutionTime_frame"] > onset["MonotonicExecutionTime"]]
if not after.empty:
latency = after.iloc[0]["MonotonicExecutionTime_frame"] - onset["MonotonicExecutionTime"]
# ... use latency
Gotchas
- Children are flattened. Same pattern as Events — a single trigger may produce multiple child observations, each as its own row.
Partialvs.Detectedvs.Ended. A single expression can fire multiple rows with different states as it ramps up and decays. For "peak detection" analyses, filter onExpression_State == "Detected"; for duration, pairDetectedwith the matchingEnded.- Similarity is not blendshape weight. These scores come from the expression-definition matcher, not directly from raw tracking blendshapes. If you want blendshape weights as a continuous series, use Face.
- Unilateral expressions can be asymmetric.
Expression_Left_Met = True, Expression_Right_Met = Falseis a real state — e.g. a raised eyebrow on one side. UseExpression_Both_Metonly when you want the symmetric-only case. - Threshold is per-row, not per-experience. If you change the threshold mid-run, different rows may have different
Expression_Thresholdvalues. Filter on this column if you need threshold-homogeneous analysis.
Analysis recipes
- Reaction time — for expression-based responses (e.g. "time from stimulus to smile"), pair an Events onset with the first matching Expressions detection.
- Stimulus-locked averaging — bin detections relative to stimulus-onset events.