{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n\n# Modifying data in-place\n\nMany of MNE-Python's data objects (`~mne.io.Raw`, `~mne.Epochs`, `~mne.Evoked`,\netc) have methods that modify the data in-place (either optionally or\nobligatorily). This can be advantageous when working with large datasets\nbecause it reduces the amount of computer memory needed to perform the\ncomputations. However, it can lead to unexpected results if you're not aware\nthat it's happening. This tutorial provides a few examples of in-place\nprocessing, and how and when to avoid it.\n\nAs usual we'll start by importing the modules we need and loading some\n`example data `:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Authors: The MNE-Python contributors.\n# License: BSD-3-Clause\n# Copyright the MNE-Python contributors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import mne\n\nsample_data_folder = mne.datasets.sample.data_path()\nsample_data_raw_file = sample_data_folder / \"MEG\" / \"sample\" / \"sample_audvis_raw.fif\"\n# the preload flag loads the data into memory now\nraw = mne.io.read_raw_fif(sample_data_raw_file, preload=True)\nraw.crop(tmax=10.0) # raw.crop() always happens in-place" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Signal processing\n\nMost MNE-Python data objects have built-in methods for filtering, including\nhigh-, low-, and band-pass filters (`~mne.io.Raw.filter`), band-stop filters\n(`~mne.io.Raw.notch_filter`),\nHilbert transforms (`~mne.io.Raw.apply_hilbert`),\nand even arbitrary or user-defined functions (`~mne.io.Raw.apply_function`).\nThese typically **always** modify data in-place, so if we want to preserve\nthe unprocessed data for comparison, we must first make a copy of it. For\nexample:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "original_raw = raw.copy()\nraw.apply_hilbert()\nprint(\n f\"original data type was {original_raw.get_data().dtype}, after \"\n f\"apply_hilbert the data type changed to {raw.get_data().dtype}.\"\n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Channel picking\n\nAnother group of methods where data is modified in-place are the\nchannel-picking methods. For example:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(f'original data had {original_raw.info[\"nchan\"]} channels.')\noriginal_raw.pick(\"eeg\") # selects only the EEG channels\nprint(f'after picking, it has {original_raw.info[\"nchan\"]} channels.')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note also that when picking only EEG channels, projectors that affected only\nthe magnetometers were dropped, since there are no longer any magnetometer\nchannels.\n\n\n## The ``copy`` parameter\n\nAbove we saw an example of using the `~mne.io.Raw.copy` method to facilitate\ncomparing data before and after processing. This is not needed when using\ncertain MNE-Python *functions*, because they have a *function parameter*\nwhere you can specify ``copy=True`` (return a modified copy of the data) or\n``copy=False`` (operate in-place). For example, `mne.set_eeg_reference` is\none such function; notice that here we plot ``original_raw`` *after* the\nrereferencing has been done, but ``original_raw`` is unaffected because\nwe specified ``copy=True``:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "rereferenced_raw, ref_data = mne.set_eeg_reference(original_raw, [\"EEG 003\"], copy=True)\nfig_orig = original_raw.plot()\nfig_reref = rereferenced_raw.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another example is the picking function `mne.pick_info`, which operates on\n`mne.Info` dictionaries rather than on data objects. See\n`tut-info-class` for details.\n\n\n## Summary\n\nGenerally speaking, you should expect that *methods of data objects* will\noperate in-place, and *functions that take a data object as a parameter* will\noperate on a copy of the data (unless the function has a ``copy`` parameter\nand it defaults to ``False`` or you specify ``copy=False``).\nDuring the exploratory phase of your analysis, where you might want\nto try out the effects of different data cleaning approaches, you should get\nused to patterns like ``raw.copy().filter(...).plot()`` or\n``raw.copy().apply_proj().compute_psd().plot()`` if you want to avoid having\nto re-load data and repeat earlier steps each time you change a computation\n(see the `sect-meth-chain` section for more info on method chaining).\n\n" ] } ], "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.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }