{ "cells": [ { "cell_type": "markdown", "id": "cell-0", "metadata": {}, "source": [ "# Event metadata\n", "\n", "Besides its onset and label, an event often comes with more information: the trial it belongs to, the condition, the choice, the reward or the reaction time. In `pypillometry`, this information is stored as *metadata* of the events: columns with one value per event. The events can then be selected by their metadata, and the metadata are passed on to intervals and segments.\n", "\n", "There are two common sources of metadata:\n", "\n", "1. **The trigger labels.** Many experiments code the information in the triggers that are sent to the eye tracker, e.g., `S_12_3_5` for the stimulus of trial 12 with the symbols 3 and 5. The labels are decomposed into metadata with `parse_labels()`.\n", "2. **A behaviour log.** The experiment software writes a table with one row per trial. `Events.from_table()` creates the events from its time columns and takes the other columns as metadata.\n", "\n", "This tutorial shows both cases, how to select events by their metadata and how to use the metadata in an analysis." ] }, { "cell_type": "code", "execution_count": 1, "id": "cell-1", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:35.411295Z", "iopub.status.busy": "2026-11-02T15:00:35.410544Z", "iopub.status.idle": "2026-11-02T15:00:42.025052Z", "shell.execute_reply": "2026-11-02T15:00:42.023339Z" } }, "outputs": [], "source": [ "import pypillometry as pp\n", "from pypillometry import u\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "pp.logging_set_level(\"WARNING\")\n", "\n", "d = pp.get_example_data(\"rlmw_002_short\")\n", "d = d | (pp.pupil.blinks_detect(vel_onset=-15)\n", " | pp.pupil.blinks_merge_close(distance=100 * u.ms)\n", " | pp.pupil.blinks_interpolate(margin=(50, 150) * u.ms)\n", " | pp.pupil.lowpass_filter(cutoff=5))" ] }, { "cell_type": "markdown", "id": "cell-2", "metadata": {}, "source": [ "## Metadata from trigger labels\n", "\n", "The example data are from a reinforcement-learning task. In each trial, a fixation cross was shown (`F_`), then two symbols (`S___`). The participant chose one of them (`C___`, side 1 is left and 2 is right) and got a reward (`R__`):" ] }, { "cell_type": "code", "execution_count": 2, "id": "cell-3", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.083035Z", "iopub.status.busy": "2026-11-02T15:00:42.082402Z", "iopub.status.idle": "2026-11-02T15:00:42.127045Z", "shell.execute_reply": "2026-11-02T15:00:42.123054Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Label
05177.0F_1
15677.0S_1_1_5
26773.0C_1_5_2
37010.0R_1_15
48776.0F_2
59575.0S_2_6_2
610581.0C_2_6_1
710808.0R_2_20

Total: 8 events

" ], "text/plain": [ "Events: 8 events | units=ms" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events = d.events\n", "events[:8]" ] }, { "cell_type": "markdown", "id": "cell-4", "metadata": {}, "source": [ "`parse_labels()` takes a regular expression with *named groups*, `(?P...)`. Each group becomes a metadata column. For events whose label does not match, the value is missing (NaN for numbers, `\"\"` for text). Columns that contain only numbers are converted to numbers:" ] }, { "cell_type": "code", "execution_count": 3, "id": "cell-5", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.232184Z", "iopub.status.busy": "2026-11-02T15:00:42.228827Z", "iopub.status.idle": "2026-11-02T15:00:42.254554Z", "shell.execute_reply": "2026-11-02T15:00:42.252827Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrial
05177.0F_11
15677.0S_1_1_51
26773.0C_1_5_21
37010.0R_1_151
48776.0F_22
59575.0S_2_6_22
610581.0C_2_6_12
710808.0R_2_202

Total: 8 events

" ], "text/plain": [ "Events: 8 events | units=ms | meta=[trial]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev = events.parse_labels(r\"^[FSCR]_(?P\\d+)\")\n", "ev[:8]" ] }, { "cell_type": "markdown", "id": "cell-6", "metadata": {}, "source": [ "Each call adds columns, so the different kinds of events can be parsed one after the other:" ] }, { "cell_type": "code", "execution_count": 4, "id": "cell-7", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.265130Z", "iopub.status.busy": "2026-11-02T15:00:42.262127Z", "iopub.status.idle": "2026-11-02T15:00:42.298589Z", "shell.execute_reply": "2026-11-02T15:00:42.296785Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensidereward
05177.0F_11NaNNaNNaNNaNNaN
15677.0S_1_1_511.05.0NaNNaNNaN
26773.0C_1_5_21NaNNaN5.02.0NaN
37010.0R_1_151NaNNaNNaNNaN15.0
48776.0F_22NaNNaNNaNNaNNaN
59575.0S_2_6_226.02.0NaNNaNNaN
610581.0C_2_6_12NaNNaN6.01.0NaN
710808.0R_2_202NaNNaNNaNNaN20.0

Total: 8 events

" ], "text/plain": [ "Events: 8 events | units=ms | meta=[trial, left, right, chosen, side, reward]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev = (ev.parse_labels(r\"^S_\\d+_(?P\\d+)_(?P\\d+)\")\n", " .parse_labels(r\"^C_\\d+_(?P\\d+)_(?P\\d+)\")\n", " .parse_labels(r\"^R_\\d+_(?P\\d+)\"))\n", "ev[:8]" ] }, { "cell_type": "markdown", "id": "cell-8", "metadata": {}, "source": [ "Instead of a regular expression, `parse_labels()` also takes a function. It gets a label and returns a dict with the metadata of this event (or `None` for no metadata). This is often easier for more complex codes. The following function gives the same result as the four regular expressions above:" ] }, { "cell_type": "code", "execution_count": 5, "id": "cell-9", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.319789Z", "iopub.status.busy": "2026-11-02T15:00:42.306629Z", "iopub.status.idle": "2026-11-02T15:00:42.341234Z", "shell.execute_reply": "2026-11-02T15:00:42.338498Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensidereward
05177.0F_11NaNNaNNaNNaNNaN
15677.0S_1_1_511.05.0NaNNaNNaN
26773.0C_1_5_21NaNNaN5.02.0NaN
37010.0R_1_151NaNNaNNaNNaN15.0
48776.0F_22NaNNaNNaNNaNNaN
59575.0S_2_6_226.02.0NaNNaNNaN
610581.0C_2_6_12NaNNaN6.01.0NaN
710808.0R_2_202NaNNaNNaNNaN20.0

Total: 8 events

" ], "text/plain": [ "Events: 8 events | units=ms | meta=[trial, left, right, chosen, side, reward]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def parse(label):\n", " kind, trial, *numbers = label.split(\"_\")\n", " names = {\"F\": [], \"S\": [\"left\", \"right\"], \"C\": [\"chosen\", \"side\"], \"R\": [\"reward\"]}\n", " return {\"trial\": int(trial), **dict(zip(names[kind], map(int, numbers)))}\n", "\n", "ev = events.parse_labels(parse)\n", "ev[:8]" ] }, { "cell_type": "markdown", "id": "cell-10", "metadata": {}, "source": [ "The metadata are stored in `meta`, a dict with one numpy array per column. `row()` returns all information about one event:" ] }, { "cell_type": "code", "execution_count": 6, "id": "cell-11", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.345291Z", "iopub.status.busy": "2026-11-02T15:00:42.344880Z", "iopub.status.idle": "2026-11-02T15:00:42.359849Z", "shell.execute_reply": "2026-11-02T15:00:42.357834Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['trial', 'left', 'right', 'chosen', 'side', 'reward']\n", "[nan nan nan 15. nan nan nan 20.]\n" ] }, { "data": { "text/plain": [ "{'onset': 5677.0,\n", " 'label': 'S_1_1_5',\n", " 'trial': 1,\n", " 'left': 1.0,\n", " 'right': 5.0,\n", " 'chosen': nan,\n", " 'side': nan,\n", " 'reward': nan}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(list(ev.meta))\n", "print(ev.meta[\"reward\"][:8])\n", "ev.row(1)" ] }, { "cell_type": "markdown", "id": "cell-12", "metadata": {}, "source": [ "## Information from other events of the trial\n", "\n", "Each column is filled only for the events whose label contains the information. To select the stimuli by the choice or the reward that followed, these values are needed at the stimulus events. `fill_meta()` copies them to all events with the same value in another column, here the trial number:" ] }, { "cell_type": "code", "execution_count": 7, "id": "cell-13", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.364283Z", "iopub.status.busy": "2026-11-02T15:00:42.363421Z", "iopub.status.idle": "2026-11-02T15:00:42.386093Z", "shell.execute_reply": "2026-11-02T15:00:42.383690Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensidereward
05177.0F_11NaNNaN5.02.015.0
15677.0S_1_1_511.05.05.02.015.0
26773.0C_1_5_21NaNNaN5.02.015.0
37010.0R_1_151NaNNaN5.02.015.0
48776.0F_22NaNNaN6.01.020.0
59575.0S_2_6_226.02.06.01.020.0
610581.0C_2_6_12NaNNaN6.01.020.0
710808.0R_2_202NaNNaN6.01.020.0

Total: 8 events

" ], "text/plain": [ "Events: 8 events | units=ms | meta=[trial, left, right, chosen, side, reward]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev = ev.fill_meta([\"chosen\", \"side\", \"reward\"], by=\"trial\")\n", "ev[:8]" ] }, { "cell_type": "markdown", "id": "cell-14", "metadata": {}, "source": [ "Times between events can be added with `add_latency()`. For example, the reaction time is the time from each stimulus to the choice of the same trial. It is stored at the stimulus events (in the units of the events, here ms):" ] }, { "cell_type": "code", "execution_count": 8, "id": "cell-15", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.390297Z", "iopub.status.busy": "2026-11-02T15:00:42.389873Z", "iopub.status.idle": "2026-11-02T15:00:42.412159Z", "shell.execute_reply": "2026-11-02T15:00:42.409862Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrt
05677.0S_1_1_511.05.05.02.015.01096.0
19575.0S_2_6_226.02.06.01.020.01006.0
212574.0S_3_2_132.01.02.01.018.01159.0
316473.0S_4_4_144.01.01.02.011.01116.0
420388.0S_5_3_553.05.05.02.017.0768.0
523987.0S_6_4_564.05.05.02.020.0929.0
627002.0S_7_3_173.01.01.02.06.01114.0
730601.0S_8_6_586.05.06.01.018.0907.0
834799.0S_9_4_694.06.06.02.019.0901.0
938415.0S_10_2_5102.05.05.02.018.0997.0

Total: 10 events

" ], "text/plain": [ "Events: 10 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev = ev.add_latency(\"S_\", \"C_\", \"rt\", by=\"trial\")\n", "ev.filter(\"S_\")" ] }, { "cell_type": "markdown", "id": "cell-16", "metadata": {}, "source": [ "Without `by`, each first event is paired with the next second event after it, as in `get_intervals()`. First events without a second event (e.g., no response) get NaN.\n", "\n", "## Selecting events by metadata\n", "\n", "`filter()` takes criteria for metadata columns as keyword arguments, in addition to the selectors for the labels (see the [events tutorial](events.ipynb)). All criteria must be met. A value selects the events where the column is equal to it:" ] }, { "cell_type": "code", "execution_count": 9, "id": "cell-17", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.419023Z", "iopub.status.busy": "2026-11-02T15:00:42.417321Z", "iopub.status.idle": "2026-11-02T15:00:42.437993Z", "shell.execute_reply": "2026-11-02T15:00:42.435455Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrt
09575.0S_2_6_226.02.06.01.020.01006.0
112574.0S_3_2_132.01.02.01.018.01159.0
230601.0S_8_6_586.05.06.01.018.0907.0

Total: 3 events

" ], "text/plain": [ "Events: 3 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev.filter(\"S_\", side=1)" ] }, { "cell_type": "markdown", "id": "cell-18", "metadata": {}, "source": [ "A list selects the events where the column has one of the values, and a function gets the whole column and returns `True` for the events to keep:" ] }, { "cell_type": "code", "execution_count": 10, "id": "cell-19", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.442954Z", "iopub.status.busy": "2026-11-02T15:00:42.442428Z", "iopub.status.idle": "2026-11-02T15:00:42.465762Z", "shell.execute_reply": "2026-11-02T15:00:42.462938Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrt
05677.0S_1_1_511.05.05.02.015.01096.0
19575.0S_2_6_226.02.06.01.020.01006.0
212574.0S_3_2_132.01.02.01.018.01159.0

Total: 3 events

" ], "text/plain": [ "Events: 3 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev.filter(\"S_\", trial=[1, 2, 3])" ] }, { "cell_type": "code", "execution_count": 11, "id": "cell-20", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.470418Z", "iopub.status.busy": "2026-11-02T15:00:42.469932Z", "iopub.status.idle": "2026-11-02T15:00:42.493390Z", "shell.execute_reply": "2026-11-02T15:00:42.490570Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrt
020388.0S_5_3_553.05.05.02.017.0768.0
123987.0S_6_4_564.05.05.02.020.0929.0
230601.0S_8_6_586.05.06.01.018.0907.0
334799.0S_9_4_694.06.06.02.019.0901.0
438415.0S_10_2_5102.05.05.02.018.0997.0

Total: 5 events

" ], "text/plain": [ "Events: 5 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev.filter(\"S_\", rt=lambda rt: rt < 1000)" ] }, { "cell_type": "markdown", "id": "cell-21", "metadata": {}, "source": [ "A boolean array with one value per event works as well, which is useful for criteria that combine several columns:" ] }, { "cell_type": "code", "execution_count": 12, "id": "cell-22", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.497951Z", "iopub.status.busy": "2026-11-02T15:00:42.497482Z", "iopub.status.idle": "2026-11-02T15:00:42.517874Z", "shell.execute_reply": "2026-11-02T15:00:42.515361Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrt
09575.0S_2_6_226.02.06.01.020.01006.0
112574.0S_3_2_132.01.02.01.018.01159.0
230601.0S_8_6_586.05.06.01.018.0907.0

Total: 3 events

" ], "text/plain": [ "Events: 3 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stim = ev.filter(\"S_\")\n", "stim.filter(stim.meta[\"chosen\"] == stim.meta[\"left\"]) # chose the left symbol" ] }, { "cell_type": "markdown", "id": "cell-23", "metadata": {}, "source": [ "New columns can also be set directly with `set_meta()`, for all events or only for selected ones. Here, we mark the trials with a high reward (above the median):" ] }, { "cell_type": "code", "execution_count": 13, "id": "cell-24", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.521711Z", "iopub.status.busy": "2026-11-02T15:00:42.521285Z", "iopub.status.idle": "2026-11-02T15:00:42.606104Z", "shell.execute_reply": "2026-11-02T15:00:42.605089Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrthigh_reward
07010.0R_1_151NaNNaN5.02.015.0NaNFalse
110808.0R_2_202NaNNaN6.01.020.0NaNTrue
213957.0R_3_183NaNNaN2.01.018.0NaNFalse
317822.0R_4_114NaNNaN1.02.011.0NaNFalse
421388.0R_5_175NaNNaN5.02.017.0NaNFalse
525153.0R_6_206NaNNaN5.02.020.0NaNTrue
628352.0R_7_67NaNNaN1.02.06.0NaNFalse
731734.0R_8_188NaNNaN6.01.018.0NaNFalse
835932.0R_9_199NaNNaN6.02.019.0NaNTrue
939648.0R_10_1810NaNNaN5.02.018.0NaNFalse

Total: 10 events

" ], "text/plain": [ "Events: 10 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt, high_reward]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "high = ev.meta[\"reward\"] > np.nanmedian(ev.meta[\"reward\"])\n", "ev = ev.set_meta(\"high_reward\", high)\n", "ev.filter(\"R_\")" ] }, { "cell_type": "markdown", "id": "store-meta-md", "metadata": {}, "source": [ "## Storing the metadata in the data\n", "\n", "`set_events()` stores the events with their metadata in the dataset. They are kept when the data are cropped and are saved with `write_file()`:" ] }, { "cell_type": "code", "execution_count": 14, "id": "store-meta-code", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (ms)Labeltrialleftrightchosensiderewardrthigh_reward
07010.0R_1_151NaNNaN5.02.015.0NaNFalse
110808.0R_2_202NaNNaN6.01.020.0NaNTrue
213957.0R_3_183NaNNaN2.01.018.0NaNFalse
317822.0R_4_114NaNNaN1.02.011.0NaNFalse
421388.0R_5_175NaNNaN5.02.017.0NaNFalse
525153.0R_6_206NaNNaN5.02.020.0NaNTrue
628352.0R_7_67NaNNaN1.02.06.0NaNFalse
731734.0R_8_188NaNNaN6.01.018.0NaNFalse
835932.0R_9_199NaNNaN6.02.019.0NaNTrue
939648.0R_10_1810NaNNaN5.02.018.0NaNFalse

Total: 10 events

" ], "text/plain": [ "Events: 10 events | units=ms | meta=[trial, left, right, chosen, side, reward, rt, high_reward]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d.set_events(ev)\n", "d.events.filter(\"R_\")" ] }, { "cell_type": "markdown", "id": "cell-27", "metadata": {}, "source": [ "## Metadata in intervals and segments\n", "\n", "`get_epochs()` passes the metadata of the selected events on to the epochs (`meta`). Together with `stat_per_event()`, this gives a table with one row per event, ready for statistical analysis:" ] }, { "cell_type": "code", "execution_count": 15, "id": "cell-28", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:42.792157Z", "iopub.status.busy": "2026-11-02T15:00:42.791526Z", "iopub.status.idle": "2026-11-02T15:00:42.875262Z", "shell.execute_reply": "2026-11-02T15:00:42.870882Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
labeltrialrewardhigh_rewardpupil
0R_1_15115.0False1204.302750
1R_2_20220.0True1188.649806
2R_3_18318.0False1152.004140
3R_4_11411.0False1183.764497
4R_5_17517.0False1115.428883
5R_6_20620.0True1060.981760
6R_7_676.0False1096.171788
7R_8_18818.0False1208.443171
8R_9_19919.0True1075.801028
9R_10_181018.0False1070.730637
\n", "
" ], "text/plain": [ " label trial reward high_reward pupil\n", "0 R_1_15 1 15.0 False 1204.302750\n", "1 R_2_20 2 20.0 True 1188.649806\n", "2 R_3_18 3 18.0 False 1152.004140\n", "3 R_4_11 4 11.0 False 1183.764497\n", "4 R_5_17 5 17.0 False 1115.428883\n", "5 R_6_20 6 20.0 True 1060.981760\n", "6 R_7_6 7 6.0 False 1096.171788\n", "7 R_8_18 8 18.0 False 1208.443171\n", "8 R_9_19 9 19.0 True 1075.801028\n", "9 R_10_18 10 18.0 False 1070.730637" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "epochs = d.get_epochs(\"R_\", window=(0, 1500) * u.ms)\n", "pupil = d.stat_per_event(intervals=epochs, eyes=\"left\", variables=\"pupil\")\n", "\n", "table = epochs.as_pandas()\n", "table[\"pupil\"] = pupil[\"left_pupil\"]\n", "table[[\"label\", \"trial\", \"reward\", \"high_reward\", \"pupil\"]]" ] }, { "cell_type": "markdown", "id": "cell-33", "metadata": {}, "source": [ "## Metadata from a behaviour log\n", "\n", "When the information is in a behaviour log instead, `Events.from_table()` takes the columns with metadata as `meta`. Every event of a row gets the values of that row. Here is a small made-up log with one row per trial (times in seconds):" ] }, { "cell_type": "code", "execution_count": 16, "id": "cell-34", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:43.134751Z", "iopub.status.busy": "2026-11-02T15:00:43.134091Z", "iopub.status.idle": "2026-11-02T15:00:43.160848Z", "shell.execute_reply": "2026-11-02T15:00:43.155628Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Onset (sec)Labeltrialcondrtrow
012.531stim1easy0.4120
112.943response1easy0.4120
215.704stim2hardNaN1
318.877stim3easy0.5382
419.415response3easy0.5382

Total: 5 events | on another clock: map with sync_clocks()

" ], "text/plain": [ "Events: 5 events | units=sec | meta=[trial, cond, rt, row] | needs sync_clocks()" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "behav = pd.DataFrame({\"trial\": [1, 2, 3],\n", " \"cond\": [\"easy\", \"hard\", \"easy\"],\n", " \"stim_on\": [12.531, 15.704, 18.877],\n", " \"rt\": [0.412, np.nan, 0.538]})\n", "\n", "ev_log = pp.Events.from_table(behav, {\"stim\": \"stim_on\",\n", " \"response\": behav.stim_on + behav.rt},\n", " units=\"sec\", meta=[\"trial\", \"cond\", \"rt\"])\n", "ev_log" ] }, { "cell_type": "markdown", "id": "cell-35", "metadata": {}, "source": [ "The column `row` gives the row of the table that each event came from (use `row=None` to leave it out). It links the events of the same row even if the table has no trial number.\n", "\n", "The events are still on the clock of the experiment computer. They are mapped to the eye-tracker clock with `sync_clocks()` and added to the data with `add_events()`, as shown in the tutorial [Events from a behaviour log](importdata_behaviour.ipynb); both keep the metadata. Events that do not have a column (e.g., the triggers that were already in the recording) get missing values in it.\n", "\n", "## Exporting the metadata\n", "\n", "`to_table()` returns the events with their metadata as a dict of columns, which can be turned into a `pandas.DataFrame` (pandas is not needed for the metadata themselves):" ] }, { "cell_type": "code", "execution_count": 17, "id": "cell-36", "metadata": { "execution": { "iopub.execute_input": "2026-11-02T15:00:43.165127Z", "iopub.status.busy": "2026-11-02T15:00:43.164646Z", "iopub.status.idle": "2026-11-02T15:00:43.268345Z", "shell.execute_reply": "2026-11-02T15:00:43.263601Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
onsetlabeltrialleftrightchosensiderewardrthigh_reward
05177.0F_11NaNNaN5.02.015.0NaNFalse
15677.0S_1_1_511.05.05.02.015.01096.0False
26773.0C_1_5_21NaNNaN5.02.015.0NaNFalse
37010.0R_1_151NaNNaN5.02.015.0NaNFalse
48776.0F_22NaNNaN6.01.020.0NaNTrue
\n", "
" ], "text/plain": [ " onset label trial left right chosen side reward rt \\\n", "0 5177.0 F_1 1 NaN NaN 5.0 2.0 15.0 NaN \n", "1 5677.0 S_1_1_5 1 1.0 5.0 5.0 2.0 15.0 1096.0 \n", "2 6773.0 C_1_5_2 1 NaN NaN 5.0 2.0 15.0 NaN \n", "3 7010.0 R_1_15 1 NaN NaN 5.0 2.0 15.0 NaN \n", "4 8776.0 F_2 2 NaN NaN 6.0 1.0 20.0 NaN \n", "\n", " high_reward \n", "0 False \n", "1 False \n", "2 False \n", "3 False \n", "4 True " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.DataFrame(d.events.to_table()).head()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.15.2" } }, "nbformat": 4, "nbformat_minor": 5 }