{ "cells": [ { "cell_type": "markdown", "id": "54c6383a", "metadata": {}, "source": [ "# Events\n", "\n", "Events are time points marked during a recording, usually triggers that\n", "the experiment software sends to the eye tracker (e.g., \"stimulus on\",\n", "\"response\"). Each event has an onset time and a label. This tutorial\n", "shows how to look at events, select the ones you need and change them,\n", "using the `Events` class." ] }, { "cell_type": "code", "execution_count": 1, "id": "7e7b5f9a", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:20.829527Z", "iopub.status.busy": "2026-11-10T13:34:20.829154Z", "iopub.status.idle": "2026-11-10T13:34:22.266361Z", "shell.execute_reply": "2026-11-10T13:34:22.264939Z" } }, "outputs": [], "source": [ "import pypillometry as pp\n", "from pypillometry import u\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "pp.logging_set_level(\"WARNING\")\n", "\n", "d = pp.get_example_data(\"rlmw_002_short\")" ] }, { "cell_type": "markdown", "id": "fc338c3a", "metadata": {}, "source": [ "## Getting the events\n", "\n", "`d.events` holds the events of a dataset as an `Events` object. In a\n", "notebook, it is shown as a table:" ] }, { "cell_type": "code", "execution_count": 2, "id": "34c4fdf9", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.270931Z", "iopub.status.busy": "2026-11-10T13:34:22.270590Z", "iopub.status.idle": "2026-11-10T13:34:22.288166Z", "shell.execute_reply": "2026-11-10T13:34:22.286688Z" } }, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \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
812374.0F_3
912574.0S_3_2_1
1013733.0C_3_2_1
1113957.0R_3_18
1215973.0F_4
1316473.0S_4_4_1
1417589.0C_4_1_2
1517822.0R_4_11
1619588.0F_5
1720388.0S_5_3_5
1821156.0C_5_5_2
1921388.0R_5_17
2023187.0F_6
2123987.0S_6_4_5
2224916.0C_6_5_2
2325153.0R_6_20
2426802.0F_7
2527002.0S_7_3_1
2628116.0C_7_1_2
2728352.0R_7_6
2830401.0F_8
2930601.0S_8_6_5
3031508.0C_8_6_1
3131734.0R_8_18
3234000.0F_9
3334799.0S_9_4_6
3435700.0C_9_6_2
3535932.0R_9_19
3637615.0F_10
3738415.0S_10_2_5
3839412.0C_10_5_2
3939648.0R_10_18

Total: 40 events

" ], "text/plain": [ "Events: 40 events | units=ms" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events = d.events\n", "events" ] }, { "cell_type": "markdown", "id": "b2349a29", "metadata": {}, "source": [ "The onsets are in ms. `to_units()` returns the events with the onsets in\n", "other units:" ] }, { "cell_type": "code", "execution_count": 3, "id": "61498db4", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.291714Z", "iopub.status.busy": "2026-11-10T13:34:22.291366Z", "iopub.status.idle": "2026-11-10T13:34:22.299927Z", "shell.execute_reply": "2026-11-10T13:34:22.298713Z" } }, "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", "
Onset (sec)Label
05.177F_1
15.677S_1_1_5
26.773C_1_5_2
37.010R_1_15

Total: 4 events

" ], "text/plain": [ "Events: 4 events | units=sec" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d.events.to_units(\"sec\")[:4]" ] }, { "cell_type": "code", "execution_count": 4, "id": "85fdca11", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.302487Z", "iopub.status.busy": "2026-11-10T13:34:22.302127Z", "iopub.status.idle": "2026-11-10T13:34:22.308742Z", "shell.execute_reply": "2026-11-10T13:34:22.307685Z" } }, "outputs": [ { "data": { "text/plain": [ "array([0.08628333, 0.09461667, 0.11288333, 0.11683333])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events.to_units(\"min\").onsets[:4]" ] }, { "cell_type": "markdown", "id": "49c0d9f3", "metadata": {}, "source": [ "The onsets and labels are stored as numpy arrays. An `Events` object has a\n", "length and can be iterated over and indexed like a list. Indexing returns\n", "a new `Events` object:" ] }, { "cell_type": "code", "execution_count": 5, "id": "b235a79a", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.312150Z", "iopub.status.busy": "2026-11-10T13:34:22.311795Z", "iopub.status.idle": "2026-11-10T13:34:22.317262Z", "shell.execute_reply": "2026-11-10T13:34:22.316262Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "40\n", "F_1 at 5177 ms\n", "S_1_1_5 at 5677 ms\n", "C_1_5_2 at 6773 ms\n" ] } ], "source": [ "print(len(events))\n", "for onset, label in events[:3]:\n", " print(f\"{label:10s} at {onset:.0f} ms\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "c34b2a98", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.319793Z", "iopub.status.busy": "2026-11-10T13:34:22.319485Z", "iopub.status.idle": "2026-11-10T13:34:22.325410Z", "shell.execute_reply": "2026-11-10T13:34:22.324537Z" } }, "outputs": [ { "data": { "text/plain": [ "(array([5177., 5677., 6773., 7010., 8776.]),\n", " array(['F_1', 'S_1_1_5', 'C_1_5_2', 'R_1_15', 'F_2'], dtype='\n", " \n", " \n", " \n", " Onset (ms)\n", " Label\n", " \n", " \n", " \n", " \n", " 0\n", " 5677.0\n", " S_1_1_5\n", " \n", " \n", " 1\n", " 9575.0\n", " S_2_6_2\n", " \n", " \n", " 2\n", " 12574.0\n", " S_3_2_1\n", " \n", " \n", " 3\n", " 16473.0\n", " S_4_4_1\n", " \n", " \n", " 4\n", " 20388.0\n", " S_5_3_5\n", " \n", " \n", " 5\n", " 23987.0\n", " S_6_4_5\n", " \n", " \n", " 6\n", " 27002.0\n", " S_7_3_1\n", " \n", " \n", " 7\n", " 30601.0\n", " S_8_6_5\n", " \n", " \n", " 8\n", " 34799.0\n", " S_9_4_6\n", " \n", " \n", " 9\n", " 38415.0\n", " S_10_2_5\n", " \n", " \n", "

Total: 10 events

" ], "text/plain": [ "Events: 10 events | units=ms" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events.filter(\"S_\")" ] }, { "cell_type": "markdown", "id": "b3cf92c4", "metadata": {}, "source": [ "**A function** is called with each label and selects the events for which\n", "it returns `True`. In this dataset, the labels of the `R` events end with\n", "a number; here we select those where it is larger than 18:" ] }, { "cell_type": "code", "execution_count": 8, "id": "a8d6a63b", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.339491Z", "iopub.status.busy": "2026-11-10T13:34:22.339197Z", "iopub.status.idle": "2026-11-10T13:34:22.347037Z", "shell.execute_reply": "2026-11-10T13:34:22.345896Z" } }, "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", "
Onset (ms)Label
010808.0R_2_20
125153.0R_6_20
235932.0R_9_19

Total: 3 events

" ], "text/plain": [ "Events: 3 events | units=ms" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def high_number(label):\n", " parts = label.split(\"_\")\n", " return parts[0] == \"R\" and int(parts[-1]) > 18\n", "\n", "events.filter(high_number)" ] }, { "cell_type": "markdown", "id": "bf3fffeb", "metadata": {}, "source": [ "**A tuple** `(start, end)` selects events by their onset, in the units of\n", "the `Events` object (here, ms):" ] }, { "cell_type": "code", "execution_count": 9, "id": "60f62d72", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.350043Z", "iopub.status.busy": "2026-11-10T13:34:22.349733Z", "iopub.status.idle": "2026-11-10T13:34:22.357919Z", "shell.execute_reply": "2026-11-10T13:34:22.356695Z" } }, "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", "
Onset (ms)Label
010581.0C_2_6_1
110808.0R_2_20
212374.0F_3
312574.0S_3_2_1
413733.0C_3_2_1
513957.0R_3_18
615973.0F_4
716473.0S_4_4_1
817589.0C_4_1_2
917822.0R_4_11
1019588.0F_5

Total: 11 events

" ], "text/plain": [ "Events: 11 events | units=ms" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events.filter((10_000, 20_000))" ] }, { "cell_type": "markdown", "id": "6d512c26", "metadata": {}, "source": [ "Since `filter()` returns a new `Events` object, filters can be chained,\n", "e.g., the `S` events in the first 20 seconds:" ] }, { "cell_type": "code", "execution_count": 10, "id": "8dd1bd00", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.360938Z", "iopub.status.busy": "2026-11-10T13:34:22.360609Z", "iopub.status.idle": "2026-11-10T13:34:22.367879Z", "shell.execute_reply": "2026-11-10T13:34:22.367056Z" } }, "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", "
Onset (ms)Label
05677.0S_1_1_5
19575.0S_2_6_2
212574.0S_3_2_1
316473.0S_4_4_1

Total: 4 events

" ], "text/plain": [ "Events: 4 events | units=ms" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "events.filter(\"S_\").filter((0, 20_000))" ] }, { "cell_type": "markdown", "id": "286fd0a8", "metadata": {}, "source": [ "`test_selector()` shows which events of a dataset a selector picks out, which\n", "is useful while writing a selection function:" ] }, { "cell_type": "code", "execution_count": 11, "id": "70985d8d", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.371320Z", "iopub.status.busy": "2026-11-10T13:34:22.370965Z", "iopub.status.idle": "2026-11-10T13:34:22.377970Z", "shell.execute_reply": "2026-11-10T13:34:22.376523Z" } }, "outputs": [ { "data": { "text/plain": [ "array(['R_2_20', 'R_6_20', 'R_9_19'], dtype='" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(12, 4))\n", "events.filter(\"S_\").plot(units=\"sec\", show_labels=\"all\");" ] }, { "cell_type": "markdown", "id": "ce1585a1", "metadata": {}, "source": [ "Events are also shown in most plots of the data, e.g., in\n", "`d.plot.plot_pupil()`.\n", "\n", "## Using events for intervals\n", "\n", "Analyses usually look at the data around events. `get_epochs()` takes\n", "the same kinds of selectors as `filter()` (string or function), and also\n", "an `Events` object. This makes it easy to use any selection of events,\n", "e.g., only the first five stimuli:" ] }, { "cell_type": "code", "execution_count": 13, "id": "4362fa78", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.797084Z", "iopub.status.busy": "2026-11-10T13:34:22.796685Z", "iopub.status.idle": "2026-11-10T13:34:22.804188Z", "shell.execute_reply": "2026-11-10T13:34:22.802266Z" } }, "outputs": [ { "data": { "text/plain": [ "Epochs 'events_5' | 5 epochs, window (-200, 800) ms" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "first_stimuli = events.filter(\"S_\")[:5]\n", "d.get_epochs(first_stimuli, (-200, 800) * u.ms)" ] }, { "cell_type": "markdown", "id": "7f315f5c", "metadata": {}, "source": [ "See the [Intervals tutorial](intervals.ipynb) for more on intervals.\n", "\n", "## Creating and changing events\n", "\n", "`Events` objects can also be created directly. Onsets with units are\n", "converted, and the object keeps the units:" ] }, { "cell_type": "code", "execution_count": 14, "id": "1ae58073", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.808976Z", "iopub.status.busy": "2026-11-10T13:34:22.808529Z", "iopub.status.idle": "2026-11-10T13:34:22.817764Z", "shell.execute_reply": "2026-11-10T13:34:22.816564Z" } }, "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", "
Onset (sec)Label
01.0start
12.5probe
24.0end

Total: 3 events

" ], "text/plain": [ "Events: 3 events | units=sec" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_events = pp.Events([1, 2.5, 4] * u.sec, [\"start\", \"probe\", \"end\"])\n", "my_events" ] }, { "cell_type": "markdown", "id": "b9608dbb", "metadata": {}, "source": [ "`Events` cannot be changed in place; `set_events()` replaces the events of\n", "a dataset with new ones. For example, we can\n", "shorten the labels to just the event type (the first letter). The onsets\n", "stay the same:" ] }, { "cell_type": "code", "execution_count": 15, "id": "12164cd4", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.821032Z", "iopub.status.busy": "2026-11-10T13:34:22.820591Z", "iopub.status.idle": "2026-11-10T13:34:22.830141Z", "shell.execute_reply": "2026-11-10T13:34:22.828808Z" } }, "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", "
Onset (ms)Label
05177.0F
15677.0S
26773.0C
37010.0R
48776.0F
59575.0S

Total: 6 events

" ], "text/plain": [ "Events: 6 events | units=ms" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "short_labels = [label.split(\"_\")[0] for label in events.labels]\n", "d.set_events(pp.Events(events.onsets, short_labels))\n", "d.events[:6]" ] }, { "cell_type": "markdown", "id": "f0ed0a02", "metadata": {}, "source": [ "Like the other `set_*` methods, `set_events()` always changes the object\n", "in place; use `d.copy().set_events(...)` to keep the original.\n", "\n", "As another example, we add the onset of each blink as an event. Blinks\n", "are stored as `Intervals` (see the [blinks tutorial](blinks.ipynb)); we\n", "take their start times in ms, combine them with the existing events and\n", "sort everything by time:" ] }, { "cell_type": "code", "execution_count": 16, "id": "e050b227", "metadata": { "execution": { "iopub.execute_input": "2026-11-10T13:34:22.833551Z", "iopub.status.busy": "2026-11-10T13:34:22.833202Z", "iopub.status.idle": "2026-11-10T13:34:23.990397Z", "shell.execute_reply": "2026-11-10T13:34:23.989310Z" } }, "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", "
Onset (ms)Label
05177.0F
15408.0blink
25677.0S
36773.0C
47010.0R
57562.0blink
67864.0blink
78130.0blink
88776.0F
99575.0S
1010484.0blink
1110581.0C

Total: 12 events

" ], "text/plain": [ "Events: 12 events | units=ms" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d.pupil.blinks_detect()\n", "blinks = d.get_blinks(\"left\", \"pupil\", units=\"ms\")[\"left_pupil\"]\n", "blink_onsets = [start for start, end in blinks]\n", "\n", "events = d.events\n", "onsets = np.concatenate([events.onsets, blink_onsets])\n", "labels = np.concatenate([events.labels, [\"blink\"] * len(blink_onsets)])\n", "order = np.argsort(onsets)\n", "d.set_events(pp.Events(onsets[order], labels[order]))\n", "\n", "d.events[:12]" ] }, { "cell_type": "markdown", "id": "1ab62019", "metadata": {}, "source": [ "The blink onsets can now be used like any other event, e.g., to look at\n", "the pupil around blinks with `d.get_epochs(\"blink\", ...)`." ] }, { "cell_type": "markdown", "id": "meta-pointer", "metadata": {}, "source": [ "## Metadata\n", "\n", "Events can also carry more information, such as the trial, the condition or the reaction time, decomposed from the labels or taken from a behaviour log. Events can then be selected by it, e.g., `events.filter(\"S_\", cond=\"easy\")`. See the [event metadata tutorial](event_metadata.ipynb)." ] } ], "metadata": { "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.1" } }, "nbformat": 4, "nbformat_minor": 5 }