"""
.. _tut-event-arrays:
===================
Working with events
===================
This tutorial describes event representation and how event arrays are used to subselect
data.
As usual we'll start by importing the modules we need, loading some
:ref:`example data <sample-dataset>`, and cropping the :class:`~mne.io.Raw`
object to just 60 seconds before loading it into RAM to save memory:
"""
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
# %%
import os
import numpy as np
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(
sample_data_folder, "MEG", "sample", "sample_audvis_raw.fif"
)
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
raw.crop(tmax=60).load_data()
# %%
# The tutorial :ref:`tut-events-vs-annotations` describes in detail the
# different ways of obtaining an :term:`Events array <events>` from a
# :class:`~mne.io.Raw` object (see the section
# :ref:`overview-tut-events-section` for details). Since the :ref:`sample
# dataset <sample-dataset>` includes experimental events recorded on
# :term:`stim channel` ``STI 014``, we'll start this tutorial by parsing the
# events from that channel using :func:`mne.find_events`:
events = mne.find_events(raw, stim_channel="STI 014")
# %%
# .. _tut-section-events-io:
#
# Reading and writing events from/to a file
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# Event arrays are :class:`NumPy array <numpy.ndarray>` objects, so they could
# be saved to disk as binary :file:`.npy` files using :func:`numpy.save`.
# However, MNE-Python provides convenience functions :func:`mne.read_events`
# and :func:`mne.write_events` for reading and writing event arrays as either
# text files (common file extensions are :file:`.eve`, :file:`.lst`, and
# :file:`.txt`) or binary :file:`.fif` files. The example dataset includes the
# results of ``mne.find_events(raw)`` in a :file:`.fif` file. Since we've
# truncated our :class:`~mne.io.Raw` object, it will have fewer events than the
# events file loaded from disk (which contains events for the entire
# recording), but the events should match for the first 60 seconds anyway:
sample_data_events_file = os.path.join(
sample_data_folder, "MEG", "sample", "sample_audvis_raw-eve.fif"
)
events_from_file = mne.read_events(sample_data_events_file)
assert np.array_equal(events, events_from_file[: len(events)])
# %%
# When writing event arrays to disk, the format will be inferred from the file
# extension you provide. By convention, MNE-Python expects events files to
# either have an :file:`.eve` extension or to have a file basename ending in
# ``-eve`` or ``_eve`` (e.g., :file:`{my_experiment}_eve.fif`), and will issue
# a warning if this convention is not respected.
#
#
# Subselecting and combining events
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# The output of :func:`~mne.find_events` above (repeated here) told us the
# number of events that were found, and the unique integer event IDs present:
mne.find_events(raw, stim_channel="STI 014")
# %%
# .. admonition:: Including/excluding events
# :class: sidebar hint
#
# Just like `~mne.pick_events`, `~mne.read_events` also has ``include``
# and ``exclude`` parameters.
#
# If some of those events are not of interest, you can easily subselect events
# using :func:`mne.pick_events`, which has parameters ``include`` and
# ``exclude``. For example, in the sample data Event ID 32 corresponds to a
# subject button press, which could be excluded as:
events_no_button = mne.pick_events(events, exclude=32)
# %%
# It is also possible to combine two Event IDs using :func:`mne.merge_events`;
# the following example will combine Event IDs 1, 2 and 3 into a single event
# labelled ``1``:
merged_events = mne.merge_events(events, [1, 2, 3], 1)
print(np.unique(merged_events[:, -1]))
# %%
# Note, however, that merging events is not necessary if you simply want to
# pool trial types for analysis; the next section describes how MNE-Python uses
# *event dictionaries* to map integer Event IDs to more descriptive label
# strings.
#
#
# Mapping Event IDs to trial descriptors
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# So far in this tutorial we've only been dealing with integer Event IDs, which
# were assigned based on DC voltage pulse magnitude (which is ultimately
# determined by the experimenter's choices about what signals to send to the
# STIM channels). Keeping track of which Event ID corresponds to which
# experimental condition can be cumbersome, and it is often desirable to pool
# experimental conditions during analysis. You may recall that the mapping of
# integer Event IDs to meaningful descriptions for the :ref:`sample dataset
# <sample-dataset>` is given in :ref:`this table
# <sample-data-event-dict-table>` in the :ref:`introductory tutorial
# <tut-overview>`. Here we simply reproduce that mapping as an
# *event dictionary*:
event_dict = {
"auditory/left": 1,
"auditory/right": 2,
"visual/left": 3,
"visual/right": 4,
"smiley": 5,
"buttonpress": 32,
}
# %%
# Event dictionaries like this one are used when extracting epochs from
# continuous data, and the resulting :class:`~mne.Epochs` object allows pooling
# by requesting partial trial descriptors. For example, if we wanted to pool
# all auditory trials, instead of merging Event IDs 1 and 2 using the
# :func:`~mne.merge_events` function, we can make use of the fact that the keys
# of ``event_dict`` contain multiple trial descriptors separated by ``/``
# characters: requesting ``'auditory'`` trials will select all epochs with
# Event IDs 1 and 2; requesting ``'left'`` trials will select all epochs with
# Event IDs 1 and 3. An example of this is shown later, in the
# :ref:`tut-section-subselect-epochs` section of the tutorial
# :ref:`tut-epochs-class`.
#
#
# Plotting events
# ^^^^^^^^^^^^^^^
#
# Another use of event dictionaries is when plotting events, which can serve as
# a useful check that your event signals were properly sent to the STIM
# channel(s) and that MNE-Python has successfully found them. The function
# :func:`mne.viz.plot_events` will plot each event versus its sample number
# (or, if you provide the sampling frequency, it will plot them versus time in
# seconds). It can also account for the offset between sample number and sample
# index in Neuromag systems, with the ``first_samp`` parameter.
# If an event dictionary is provided, it will be used to generate a legend:
fig = mne.viz.plot_events(
events, sfreq=raw.info["sfreq"], first_samp=raw.first_samp, event_id=event_dict
)
# %%
# Plotting events and raw data together
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Events can also be plotted alongside the :class:`~mne.io.Raw` object they
# were extracted from, by passing the Event array as the ``events`` parameter
# of :meth:`raw.plot <mne.io.Raw.plot>`:
raw.plot(
events=events,
start=5,
duration=10,
color="gray",
event_color={1: "r", 2: "g", 3: "b", 4: "m", 5: "y", 32: "k"},
)
# %%
# .. _`fixed-length-events`:
#
# Making equally-spaced Events arrays
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# For some experiments (such as those intending to analyze resting-state
# activity) there may not be any experimental events included in the raw
# recording. In such cases, an Events array of equally-spaced events can be
# generated using :func:`mne.make_fixed_length_events`:
new_events = mne.make_fixed_length_events(raw, start=5, stop=50, duration=2.0)
# %%
# By default, the events will all be given the integer Event ID of ``1``, but
# you can change that with the ``id`` parameter. It is also possible to specify
# an ``overlap`` duration — i.e., if you ultimately want :term:`epochs` that
# are 2.5 seconds long, but you want them to overlap by 0.5 seconds, you can
# specify ``duration=2.5, overlap=0.5`` in the call to
# :func:`~mne.make_fixed_length_events` (this will yield the same spacing of
# events as ``duration=2, overlap=0)``.