import mne

"""
Explanation: 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
&lt;numpy.ndarray&gt;, see tut-creating-data-structures.
As usual, we'll start by importing the modules we need:
End of explanation
"""


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

"""
Explanation: 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 &lt;numpy.ndarray&gt; 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:
End of explanation
"""


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

"""
Explanation: 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:
End of explanation
"""


evoked.plot()

"""
Explanation: 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:
End of explanation
"""


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

"""
Explanation: Like the plot() methods for :meth:Raw &lt;mne.io.Raw.plot&gt; and
:meth:Epochs &lt;mne.Epochs.plot&gt; objects,
:meth:evoked.plot() &lt;mne.Evoked.plot&gt; 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 tut-visualize-evoked for more information on visualizing
:class:~mne.Evoked objects.
Subsetting Evoked data
.. sidebar:: Evokeds are not memory-mapped
: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:
End of explanation
"""


evoked_eeg = evoked.copy().pick_types(meg=False, eeg=True)
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)

"""
Explanation: 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 time-as-index section of the tutorial on
Raw data &lt;tut-raw-class&gt; 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() &lt;mne.Epochs.average&gt;, 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.
End of explanation
"""


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

"""
Explanation: 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 &lt;numpy.ndarray&gt; (but through
  the :attr:~mne.Evoked.data attribute instead of a get_data()
  method). :class:Pandas DataFrame &lt;pandas.DataFrame&gt; 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() &lt;mne.Evoked.rename_channels&gt; or
  :meth:evoked.set_channel_types() &lt;mne.Evoked.set_channel_types&gt;.
  Both methods take :class:dictionaries &lt;dict&gt; 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 &lt;projector&gt; 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 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 &lt;mne.Evoked.ch_names&gt;, and :class:info &lt;mne.Info&gt;
  attributes.


Loading and saving Evoked data
Single :class:~mne.Evoked objects can be saved to disk with the
:meth:evoked.save() &lt;mne.Evoked.save&gt; 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 example data &lt;sample-dataset&gt;
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:
End of explanation
"""


for evok in evokeds_list:
    print(evok.comment)

"""
Explanation: 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:
End of explanation
"""


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

"""
Explanation: 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):
End of explanation
"""


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

"""
Explanation: 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:
End of explanation
"""


# 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')

"""
Explanation: 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:
End of explanation
"""


left_right_aud = epochs['auditory'].average()
print(left_right_aud)

"""
Explanation: 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 &lt;projector&gt; 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
tut-section-subselect-epochs for more information):
End of explanation
"""


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

"""
Explanation: 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:
End of explanation
"""


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

"""
Explanation: 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 $\frac{1}{N}$, where $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):
End of explanation
"""


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('Trial {}: peak of {} µV at {} ms in channel {}'
          .format(ix, value, latency, channel))

"""
Explanation: 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 minimum_norm_estimates for more information, especially
the 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() &lt;mne.Epochs.standard_error&gt;
will create an :class:~mne.Evoked object (just like
:meth:epochs.average() &lt;mne.Epochs.average&gt; 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() &lt;mne.Epochs.iter_evoked&gt;
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:
End of explanation
"""


