Skip to main content

Variables

Irregular • File: Variables.csv • Writer: LaboVariableDataWriter.cs

At a glance

Variables captures every variable write in the experience — one row per assignment, arithmetic modification, or list update. Each row describes a single write: the target variable, its data type and scope, value(s) before and after, and which variable / modifier produced the change (if any). Stamped at trigger time via Stamp(), so FrameNumber / MonotonicExecutionTime reflect the moment of the write.

Use Variables for:

  • Reconstructing experiment state at any point in time.
  • Debugging — "when did variable X change, and what did it change to?"
  • Condition-dependent analyses — filter other streams based on variable state.

When it writes

Whenever variable infrastructure calls LaboDataCapture.Instance.VariableEntry(observation) — after a variable update in VariableManager or related code. Grep for VariableEntry call sites in the LABO source to see exact triggers.

File & location

  • Default: Variables.csv in the run directory.
  • Writer: LaboVariableDataWriter.cs.
  • Observation: Runtime/DataCapture/Observations/VariableObservation.cs.

Configuration

VariablesDataCapture SO (subclass of DataCaptureConfig). Fields:

FieldWhat it does
enabledLoggingMaster enable/disable.
fileNameOverride the default name.
flushEveryNRowsBatched flush cadence. Default 30.
writeHeaderWrite the header row on first emission. Default true.

Auto-created on experience open. WindowUtility.OpenExperience() populates this slot if missing.

Columns

Shared prefix

See Column conventions — The shared prefix.

Domain columns

ColumnTypeDescription
Variable_NamestringTarget variable name — what was written.
Variable_DataTypestringInt, Float, Bool, String, IntList, FloatList, StringList, etc.
Variable_ScopestringGlobal, Participant, Session, Epoch, etc.
Variable_SingleValuestringPost-write value for non-list variables. NaN for list updates.
Variable_ListValuesstringPost-write list contents (semicolon-separated), for list variables. NaN for non-list.
Variable_ModifyingVariablestringIf the write was driven by another variable (e.g. total = total + delta), the name of the driving variable. NaN if not.
Variable_ModifierstringThe operation — Assign, Increment, Decrement, Multiply, Append, Remove, etc.
Variable_UpdateValuestringThe delta / operand — e.g. 1 for Increment, the appended item for Append.
Variable_IndexintFor list updates, the index affected. 0 for non-list writes.

Sample rows

...shared...,Variable_Name,Variable_DataType,Variable_Scope,Variable_SingleValue,Variable_ListValues,Variable_ModifyingVariable,Variable_Modifier,Variable_UpdateValue,Variable_Index
...,trialIndex,Int,Global,3,NaN,NaN,Increment,1,0
...,responseTimes,FloatList,Participant,NaN,320;445;512,trialTimer,Append,512,2

Note the commas-in-list-values convention — [320, 445, 512] is serialised as 320;445;512 so the CSV parses correctly. See Column conventions — Value encoding.

Join with other streams

Standard FrameNumber join to attach per-frame context to each variable write:

import pandas as pd
exp = pd.read_csv("ExperienceState.csv")
vars_ = pd.read_csv("Variables.csv")

enriched = vars_.merge(exp, on="FrameNumber", how="left", suffixes=("_var", "_frame"))

# All writes to trialIndex during a specific epoch:
enriched[(enriched["Variable_Name"] == "trialIndex") & (enriched["EpochName_frame"] == "Trial")]

Gotchas

  • One row per write, not per state change. If trialIndex is incremented three times on the same frame, you get three rows. State reconstruction = play the rows back in order.
  • Variable_SingleValue is post-write. To reconstruct the pre-write value, either look at the previous row for the same Variable_Name, or apply the inverse of Variable_Modifier with Variable_UpdateValue.
  • Lists are serialised with semicolons. If a list element itself contains a semicolon (unusual but possible for string lists), there will be ambiguity. Treat Variable_ListValues as a best-effort representation; the canonical state reconstruction is replaying the operations, not parsing the list column.
  • Scope matters for reconstruction. A Participant-scoped variable is per-participant; a Session variable is per-session. When replaying, filter by scope + identifying key before applying writes.
  • Stamped at trigger time. Same rule as Events — the MonotonicExecutionTime reflects the real write moment, accurate to microseconds.

Analysis recipes

For condition-dependent filtering of other streams, see the stimulus-locked averaging recipe — the same joining pattern applies for using variable state to segment another stream.