"""
.. _tut-evoked-class:

===============================================
The Evoked data structure: evoked/averaged data
===============================================

This tutorial covers the basics of creating and working with :term:`evoked`
data. It introduces the :class:`~mne.Evoked` data structure in detail,
including how to load, query, subset, export, and plot data from an
:class:`~mne.Evoked` object. For details on creating an :class:`~mne.Evoked`
object from (possibly simulated) data in a :class:`NumPy array
<numpy.ndarray>`, see :ref:`tut-creating-data-structures`.

As usual, we start by importing the modules we need:
"""

# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

# %%

import mne

# %%
# Creating ``Evoked`` objects from ``Epochs``
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# :class:`~mne.Evoked` objects typically store EEG or MEG signals that have
# been *averaged* over multiple :term:`epochs`, which is a common technique for
# estimating stimulus-evoked activity. The data in an :class:`~mne.Evoked`
# object are stored in an :class:`array <numpy.ndarray>` of shape
# ``(n_channels, n_times)`` (in contrast to an :class:`~mne.Epochs` object,
# which stores data of shape ``(n_epochs, n_channels, n_times)``). Thus, to
# create an :class:`~mne.Evoked` object, we'll start by epoching some raw data,
# and then averaging together all the epochs from one condition:

root = mne.datasets.sample.data_path() / "MEG" / "sample"
raw_file = root / "sample_audvis_raw.fif"
raw = mne.io.read_raw_fif(raw_file, verbose=False)

events = mne.find_events(raw, stim_channel="STI 014")
# we'll skip the "face" and "buttonpress" conditions to save memory
event_dict = {
    "auditory/left": 1,
    "auditory/right": 2,
    "visual/left": 3,
    "visual/right": 4,
}
epochs = mne.Epochs(raw, events, tmin=-0.3, tmax=0.7, event_id=event_dict, preload=True)
evoked = epochs["auditory/left"].average()

del raw  # reduce memory usage

# %%
# You may have noticed that MNE informed us that "baseline correction" has been
# applied. This happened automatically during creation of the
# :class:`~mne.Epochs` object, but may also be initiated (or disabled)
# manually. We will discuss this in more detail later.
#
# The information about the baseline period of :class:`~mne.Epochs` is
# transferred to derived :class:`~mne.Evoked` objects to maintain provenance as
# you process your data:

print(f"Epochs baseline: {epochs.baseline}")
print(f"Evoked baseline: {evoked.baseline}")

# %%
# Basic visualization of ``Evoked`` objects
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# We can visualize the average evoked response for left-auditory stimuli using
# the :meth:`~mne.Evoked.plot` method, which yields a butterfly plot of each
# channel type:

evoked.plot()

# %%
# Like the ``plot()`` methods for :meth:`Raw <mne.io.Raw.plot>` and
# :meth:`Epochs <mne.Epochs.plot>` objects,
# :meth:`evoked.plot() <mne.Evoked.plot>` has many parameters for customizing
# the plot output, such as color-coding channel traces by scalp location, or
# plotting the :term:`global field power` alongside the channel traces.
# See :ref:`tut-visualize-evoked` for more information on visualizing
# :class:`~mne.Evoked` objects.
#
#
# Subsetting ``Evoked`` data
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# .. admonition:: Evokeds are not memory-mapped
#   :class: sidebar note
#
#   :class:`~mne.Evoked` objects use a :attr:`~mne.Evoked.data` *attribute*
#   rather than a :meth:`~mne.Epochs.get_data` *method*; this reflects the fact
#   that the data in :class:`~mne.Evoked` objects are always loaded into
#   memory and never `memory-mapped`_ from their location on disk (because they
#   are typically *much* smaller than :class:`~mne.io.Raw` or
#   :class:`~mne.Epochs` objects).
#
#
# Unlike :class:`~mne.io.Raw` and :class:`~mne.Epochs` objects,
# :class:`~mne.Evoked` objects do not support selection by square-bracket
# indexing. Instead, data can be subsetted by indexing the
# :attr:`~mne.Evoked.data` attribute:

print(evoked.data[:2, :3])  # first 2 channels, first 3 timepoints

# %%
# To select based on time in seconds, the :meth:`~mne.Evoked.time_as_index`
# method can be useful, although beware that depending on the sampling
# frequency, the number of samples in a span of given duration may not always
# be the same (see the :ref:`time-as-index` section of the tutorial on
# :ref:`Raw data <tut-raw-class>` for details).
#
#
# Selecting, dropping, and reordering channels
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# By default, when creating :class:`~mne.Evoked` data from an
# :class:`~mne.Epochs` object, only the primary data channels will be retained:
# ``eog``, ``ecg``, ``stim``, and ``misc`` channel types will be dropped. You
# can control which channel types are retained via the ``picks`` parameter of
# :meth:`epochs.average() <mne.Epochs.average>`, by passing ``'all'`` to
# retain all channels, or by passing a list of integers, channel names, or
# channel types. See the documentation of :meth:`~mne.Epochs.average` for
# details.
#
# If you've already created the :class:`~mne.Evoked` object, you can use the
# :meth:`~mne.Evoked.pick`, :meth:`~mne.Evoked.pick_channels`,
# :meth:`~mne.Evoked.pick_types`, and :meth:`~mne.Evoked.drop_channels` methods
# to modify which channels are included in an :class:`~mne.Evoked` object.
# You can also use :meth:`~mne.Evoked.reorder_channels` for this purpose; any
# channel names not provided to :meth:`~mne.Evoked.reorder_channels` will be
# dropped. Note that *channel* selection methods modify the object in-place, so
# in interactive/exploratory sessions you may want to create a
# :meth:`~mne.Evoked.copy` first.

evoked_eeg = evoked.copy().pick(picks="eeg")
print(evoked_eeg.ch_names)

new_order = ["EEG 002", "MEG 2521", "EEG 003"]
evoked_subset = evoked.copy().reorder_channels(new_order)
print(evoked_subset.ch_names)

# %%
# Similarities among the core data structures
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# :class:`~mne.Evoked` objects have many similarities with :class:`~mne.io.Raw`
# and :class:`~mne.Epochs` objects, including:
#
# - They can be loaded from and saved to disk in ``.fif`` format, and their
#   data can be exported to a :class:`NumPy array <numpy.ndarray>` (but through
#   the :attr:`~mne.Evoked.data` attribute instead of a ``get_data()``
#   method). :class:`Pandas DataFrame <pandas.DataFrame>` export is also
#   available through the :meth:`~mne.Evoked.to_data_frame` method.
#
# - You can change the name or type of a channel using
#   :meth:`evoked.rename_channels() <mne.Evoked.rename_channels>` or
#   :meth:`evoked.set_channel_types() <mne.Evoked.set_channel_types>`.
#   Both methods take :class:`dictionaries <dict>` where the keys are existing
#   channel names, and the values are the new name (or type) for that channel.
#   Existing channels that are not in the dictionary will be unchanged.
#
# - :term:`SSP projector <projector>` manipulation is possible through
#   :meth:`~mne.Evoked.add_proj`, :meth:`~mne.Evoked.del_proj`, and
#   :meth:`~mne.Evoked.plot_projs_topomap` methods, and the
#   :attr:`~mne.Evoked.proj` attribute. See :ref:`tut-artifact-ssp` for more
#   information on SSP.
#
# - Like :class:`~mne.io.Raw` and :class:`~mne.Epochs` objects,
#   :class:`~mne.Evoked` objects have :meth:`~mne.Evoked.copy`,
#   :meth:`~mne.Evoked.crop`, :meth:`~mne.Evoked.time_as_index`,
#   :meth:`~mne.Evoked.filter`, and :meth:`~mne.Evoked.resample` methods.
#
# - Like :class:`~mne.io.Raw` and :class:`~mne.Epochs` objects,
#   :class:`~mne.Evoked` objects have ``evoked.times``,
#   :attr:`evoked.ch_names <mne.Evoked.ch_names>`, and :class:`info <mne.Info>`
#   attributes.
#
#
# .. _tut-section-load-evk:
#
# Loading and saving ``Evoked`` data
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# Single :class:`~mne.Evoked` objects can be saved to disk with the
# :meth:`evoked.save() <mne.Evoked.save>` method. One difference between
# :class:`~mne.Evoked` objects and the other data structures is that multiple
# :class:`~mne.Evoked` objects can be saved into a single ``.fif`` file, using
# :func:`mne.write_evokeds`. The :ref:`example data <sample-dataset>`
# includes such a ``.fif`` file: the data have already been epoched and
# averaged, and the file contains separate :class:`~mne.Evoked` objects for
# each experimental condition:

evk_file = root / "sample_audvis-ave.fif"
evokeds_list = mne.read_evokeds(evk_file, verbose=False)
print(evokeds_list)
print(type(evokeds_list))

# %%
# Notice that :func:`mne.read_evokeds` returned a :class:`list` of
# :class:`~mne.Evoked` objects, and each one has an ``evoked.comment``
# attribute describing the experimental condition that was averaged to
# generate the estimate:

for evok in evokeds_list:
    print(evok.comment)

# %%
# If you want to load only some of the conditions present in a ``.fif`` file,
# :func:`~mne.read_evokeds` has a ``condition`` parameter, which takes either a
# string (matched against the comment attribute of the evoked objects on disk),
# or an integer selecting the :class:`~mne.Evoked` object based on the order
# it is stored in the file. Passing lists of integers or strings is also
# possible. If only one object is selected, the :class:`~mne.Evoked` object
# will be returned directly (rather than inside a list of length one):

right_vis = mne.read_evokeds(evk_file, condition="Right visual")
print(right_vis)
print(type(right_vis))

# %%
# Previously, when we created an :class:`~mne.Evoked` object by averaging
# epochs, baseline correction was applied by default when we extracted epochs
# from the `~mne.io.Raw` object (the default baseline period is ``(None, 0)``,
# which ensures zero mean for times before the stimulus event). In contrast, if
# we plot the first :class:`~mne.Evoked` object in the list that was loaded
# from disk, we'll see that the data have not been baseline-corrected:

evokeds_list[0].plot(picks="eeg")

# %%
# This can be remedied by either passing a ``baseline`` parameter to
# :func:`mne.read_evokeds`, or by applying baseline correction after loading,
# as shown here:

# Original baseline (none set)
print(f"Baseline after loading: {evokeds_list[0].baseline}")

# Apply a custom baseline correction
evokeds_list[0].apply_baseline((None, 0))
print(f"Baseline after calling apply_baseline(): {evokeds_list[0].baseline}")

# Visualize the evoked response
evokeds_list[0].plot(picks="eeg")

# %%
# Notice that :meth:`~mne.Evoked.apply_baseline` operated in-place. Similarly,
# :class:`~mne.Evoked` objects may have been saved to disk with or without
# :term:`projectors <projector>` applied; you can pass ``proj=True`` to the
# :func:`~mne.read_evokeds` function, or use the :meth:`~mne.Evoked.apply_proj`
# method after loading.
#
#
# Combining ``Evoked`` objects
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# One way to pool data across multiple conditions when estimating evoked
# responses is to do so *prior to averaging* (recall that MNE-Python can select
# based on partial matching of epoch labels separated by ``/``; see
# :ref:`tut-section-subselect-epochs` for more information):

left_right_aud = epochs["auditory"].average()
left_right_aud

# %%
# This approach will weight each epoch equally and create a single
# :class:`~mne.Evoked` object. Notice that the printed representation includes
# ``(average, N=145)``, indicating that the :class:`~mne.Evoked` object was
# created by averaging across 145 epochs. In this case, the event types were
# fairly close in number:

left_aud = epochs["auditory/left"].average()
right_aud = epochs["auditory/right"].average()
print([evok.nave for evok in (left_aud, right_aud)])

# %%
# However, this may not always be the case. If for statistical reasons it is
# important to average *the same number* of epochs from different conditions,
# you can use :meth:`~mne.Epochs.equalize_event_counts` prior to averaging.
#
# Another approach to pooling across conditions is to create separate
# :class:`~mne.Evoked` objects for each condition, and combine them afterwards.
# This can be accomplished with the function :func:`mne.combine_evoked`, which
# computes a weighted sum of the :class:`~mne.Evoked` objects given to it. The
# weights can be manually specified as a list or array of float values, or can
# be specified using the keyword ``'equal'`` (weight each :class:`~mne.Evoked`
# object by :math:`\frac{1}{N}`, where :math:`N` is the number of
# :class:`~mne.Evoked` objects given) or the keyword ``'nave'`` (weight each
# :class:`~mne.Evoked` object proportional to the number of epochs averaged
# together to create it):

left_right_aud = mne.combine_evoked([left_aud, right_aud], weights="nave")
assert left_right_aud.nave == left_aud.nave + right_aud.nave

# %%
# Note that the ``nave`` attribute of the resulting :class:`~mne.Evoked` object
# will reflect the *effective* number of averages, and depends on both the
# ``nave`` attributes of the contributing :class:`~mne.Evoked` objects and the
# weights with which they are combined. Keeping track of effective ``nave`` is
# important for inverse imaging, because ``nave`` is used to scale the noise
# covariance estimate, which in turn affects the magnitude of estimated source
# activity (see :ref:`minimum_norm_estimates` for more information, especially
# the :ref:`whitening_and_scaling` section). Note that
# :func:`mne.grand_average` does *not* adjust ``nave`` to reflect the effective
# number of averaged epochs; it simply sets ``nave`` to the number of *evokeds*
# that were averaged together. For this reason, it is best to use
# :func:`mne.combine_evoked` rather than :func:`mne.grand_average` if you
# intend to perform inverse imaging on the resulting :class:`~mne.Evoked`
# object.
#
#
# Other uses of ``Evoked`` objects
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# Although the most common use of :class:`~mne.Evoked` objects is to store
# *averages* of epoched data, there are a few other uses worth noting here.
# First, the method :meth:`epochs.standard_error() <mne.Epochs.standard_error>`
# will create an :class:`~mne.Evoked` object (just like
# :meth:`epochs.average() <mne.Epochs.average>` does), but the data in the
# :class:`~mne.Evoked` object will be the standard error across epochs instead
# of the average. To indicate this difference, :class:`~mne.Evoked` objects
# have a :attr:`~mne.Evoked.kind` attribute that takes values ``'average'`` or
# ``'standard error'`` as appropriate.
#
# Another use of :class:`~mne.Evoked` objects is to represent *a single trial
# or epoch* of data, usually when looping through epochs. This can be easily
# accomplished with the :meth:`epochs.iter_evoked() <mne.Epochs.iter_evoked>`
# method, and can be useful for applications where you want to do something
# that is only possible for :class:`~mne.Evoked` objects. For example, here
# we use the :meth:`~mne.Evoked.get_peak` method (which is not available for
# :class:`~mne.Epochs` objects) to get the peak response in each trial:

for ix, trial in enumerate(epochs[:3].iter_evoked()):
    channel, latency, value = trial.get_peak(ch_type="eeg", return_amplitude=True)
    latency = int(round(latency * 1e3))  # convert to milliseconds
    value = int(round(value * 1e6))  # convert to µV
    print(f"Trial {ix}: peak of {value} µV at {latency} ms in channel {channel}")

# %%
# .. REFERENCES
#
# .. _`memory-mapped`: https://en.wikipedia.org/wiki/Memory-mapped_file
