{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"}],"dockerImageVersionId":30646,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"I have absolutely no expertise in the field of EEG and MNE. This notebook is merely intended to share my understanding of this package through the HMS dataset. I have clearly referred to the official website https://mne.tools/dev/index.html and two videos from the YouTube channel EEG & MEG :: [Brain-Computer Interfaces] (https://www.youtube.com/@mlearnxyz), available at https://www.youtube.com/watch?v=IYuAPisoUeI&t=962sde and https://www.youtube.com/watch?v=wNIaT1UT6rI.\n\nIf experts come across this and wish to provide further insights, they are welcome!","metadata":{}},{"cell_type":"markdown","source":"# Introduction to MNE","metadata":{}},{"cell_type":"markdown","source":"MNE-Python stands as a robust Python library tailored for the intricate tasks of processing, analyzing, and visualizing neurophysiological data, with a specific focus on magnetoencephalography (MEG) and electroencephalography (EEG) data. Engineered with a comprehensive array of tools, this library empowers researchers and scientists to navigate the complexities of time-series neuroimaging data. By leveraging MNE-Python, professionals can conduct diverse analyses, unlocking profound insights into the intricate workings of brain activity.\n\nOur exploration will delve into the practical application of this module using the HMS dataset, providing a hands-on experience to unravel its potential and functionalities in real-world scenarios.\n","metadata":{}},{"cell_type":"markdown","source":"# Import Libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nimport matplotlib.pyplot as plt\nimport mne\n\n\ndata_train_path = '/kaggle/input/hms-harmful-brain-activity-classification/train.csv'","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.611904Z","iopub.execute_input":"2024-03-01T17:05:12.612439Z","iopub.status.idle":"2024-03-01T17:05:12.619005Z","shell.execute_reply.started":"2024-03-01T17:05:12.612399Z","shell.execute_reply":"2024-03-01T17:05:12.617493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read the data (train.csv)","metadata":{}},{"cell_type":"code","source":"# Read the data\ndata = pd.read_csv(data_train_path)\n\n# Copy the Data\ndf = data.copy()\n\n# Observe few lines \nprint(df.head())","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.622372Z","iopub.execute_input":"2024-03-01T17:05:12.622762Z","iopub.status.idle":"2024-03-01T17:05:12.857927Z","shell.execute_reply.started":"2024-03-01T17:05:12.622730Z","shell.execute_reply":"2024-03-01T17:05:12.856181Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Shape of the data\nprint('The shape of df is:', df.shape)","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.860289Z","iopub.execute_input":"2024-03-01T17:05:12.861010Z","iopub.status.idle":"2024-03-01T17:05:12.866504Z","shell.execute_reply.started":"2024-03-01T17:05:12.860969Z","shell.execute_reply":"2024-03-01T17:05:12.865369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creation of the main objects ","metadata":{}},{"cell_type":"markdown","source":"In order to understand the command of MNE, we will take the eeg_ids which has 5 expert consensus (the maximum) 'eeg_id'== 1460778765.","metadata":{}},{"cell_type":"code","source":"# Grouping the DataFrame by 'eeg_id' and counting the number of unique 'expert_consensus' values for each group\ncounts = df.groupby('eeg_id')['expert_consensus'].nunique()\n\n# Select the eeg_id that have more than 4 unique expert consensus\neeg_id_with_multiple_consensus = counts[counts > 4].index.tolist()\nprint('Here is the list of eeg_id with more that 4 unique expert consensus' ,eeg_id_with_multiple_consensus)\n\n# Take the eeg_id which as five expert consensus\ndf_multiple = df[df['eeg_id']==1460778765]\nprint(df_multiple)","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.868353Z","iopub.execute_input":"2024-03-01T17:05:12.868735Z","iopub.status.idle":"2024-03-01T17:05:12.905262Z","shell.execute_reply.started":"2024-03-01T17:05:12.868706Z","shell.execute_reply":"2024-03-01T17:05:12.903985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"First we have to create essential MNE data structures (Raws, Events, Epochs, Evoked) with something called measurement information.","metadata":{}},{"cell_type":"code","source":"# Read the corresponding eeg\ndata_eeg_path = '/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1460778765.parquet'\ndata = pd.read_parquet(data_eeg_path).copy()","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.907636Z","iopub.execute_input":"2024-03-01T17:05:12.908003Z","iopub.status.idle":"2024-03-01T17:05:12.994072Z","shell.execute_reply.started":"2024-03-01T17:05:12.907972Z","shell.execute_reply":"2024-03-01T17:05:12.992844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1. **Create measurement information**\n\nThis data structure behaves like a dictionary. It contains all metadata that is available for a recording. However, its keys are restricted to those provided by the FIF format specification, so new entries should not be manually added. (source MNE website)\n\nIn our case, since we do not have such file we create the info from scratch and we're going to use create_info. It contains the list of columns in the eeg, the type and the frequency which in our case is 200 samples per second.\n\nRemark: the available types are ‘ecg’, ‘bio’, ‘stim’, ‘eog’, ‘misc’, ‘seeg’, ‘dbs’, ‘ecog’, ‘mag’, ‘eeg’, ‘ref_meg’, ‘grad’, ‘emg’, ‘hbr’ ‘eyetrack’ or ‘hbo’.\n\nSince the EKG column does not correspond to cerebral activity but rather reflects heart activity, we will drop it from the dataset.","metadata":{}},{"cell_type":"code","source":"# Drop the EKG column\ndata = data.drop(['EKG'], axis=1)\nprint(data)","metadata":{"execution":{"iopub.status.busy":"2024-03-01T17:05:12.995543Z","iopub.execute_input":"2024-03-01T17:05:12.995877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the channel names\nch_names = data.columns.tolist()\n\n# Define the channel types\nch_types = ['eeg']*len(ch_names) \n\n# Create the measurement information\ninfo = mne.create_info(ch_names, ch_types=ch_types, sfreq=200)\nprint(info)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2. **Create the Raw object**\n\nIn MNE-Python, the Raw object is a fundamental data structure used to represent continuous neurophysiological data, such as EEG (electroencephalography) or MEG (magnetoencephalography) recordings. It is part of the MNE core data structures and is designed to store raw sensor data along with associated metadata.\n\n- Raw Data: It holds the raw sensor data as a 2D NumPy array, where each row corresponds to a sensor (channel), and each column corresponds to a time point.\n\n- Metadata: It's the measurement information which includes information about the recording, such as sensor locations, sampling frequency, channel names, and other relevant information.\n\n- Methods for Data Analysis: The Raw object provides various methods for basic data analysis tasks, such as filtering, resampling, and applying annotations.\n\n- Visualization: MNE-Python includes visualization tools to plot the raw data, allowing users to inspect and analyze the recordings.\n\nTo initialize a Raw object from the ground up, we begin by converting our DataFrame into a NumPy array and then transpose it.","metadata":{}},{"cell_type":"code","source":"# Create the numpy array and transpose it\ndata_values = data.values.T\n\n# Create the Raw object\nraw = mne.io.RawArray(data_values, info)\nprint(raw)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we can plot the raw object. To do this, we define a scaling object and the duration. The rest of the commands is used for visualization convenience.","metadata":{}},{"cell_type":"code","source":"# Define a scaling object\nscalings = {'eeg': 300} \n\n# Define the duration of the eeg in second\nduration = df_multiple['eeg_label_offset_seconds'].max()+50\nprint(duration)\n\n# Plot the raw object\nraw.plot(show_scrollbars=False, show_scalebars=False, duration= duration, scalings=scalings)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Before proceeding, we need to create a montage for the 19 electrodes.","metadata":{}},{"cell_type":"code","source":"# Create the montage\nten_twenty_montage = mne.channels.make_standard_montage('standard_1020')\nraw.set_montage(ten_twenty_montage)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"3. **Create the Epochs object**\n\n\nEpochs objects provide a means of representing continuous data as a collection of time-locked trials, stored in an array of shape (n_events, n_channels, n_times). They are valuable for various statistical methods in neuroscience and facilitate a quick overview of what occurs during a trial.\n\nTo epoch the data, event markers are required, typically stored in the raw object within a channel known as the stimulus channel. In our scenario, we need to create these events, which is essentially an array in the format (time in samples, zero, trigger).","metadata":{}},{"cell_type":"code","source":"# Create Target\nTarget = {'Seizure':0, 'LPD':1, 'GPD':2, 'LRDA':3, 'GRDA':4, 'Other':5}\n\n# Create event ids for this eeg\nevent_ids = {'Seizure':0, 'LPD':1, 'LRDA':3, 'GRDA':4, 'Other':5}\n\n# Create the events\nevents = df_multiple[['eeg_label_offset_seconds', 'expert_consensus']]\nevents.insert(1, 'New', 0)\nprint(events)\n\n# Map 'expert_consensus' values to numerical labels using the 'event_ids' dictionary\nevents.loc[:,'expert_consensus'] = events['expert_consensus'].map(event_ids)\n\n# Define the sample time point where the event occurs\nevents.loc[:,'eeg_label_offset_seconds'] = (events['eeg_label_offset_seconds']+25)*200\n\n# Convert the data frame into an array\nevents = events.values.astype(int)\n#print(events)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the events\nmne.viz.plot_events(events[:])\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, we generate epochs, representing time windows around events. Setting the starting time `tmin=-5` and ending time `tmax=5` creates a time window of 10 seconds around each event.","metadata":{}},{"cell_type":"code","source":"# Create the epochs\nepochs = mne.Epochs(raw, events, event_id = event_ids, tmin=-5, tmax=5, preload=True, baseline=(None, 0))\nprint(epochs.event_id)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the 10th first epochs for all channels.\nepochs.plot(n_epochs=10, events=True, picks = 'all', show_scrollbars=False, show_scalebars=False, scalings=scalings)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Many epochs overlap, likely due to closely spaced or identical events. To address the issue, we will select events that are spaced at least 10 seconds apart (non-overlapping events).","metadata":{}},{"cell_type":"code","source":"# Create the non overlapping events\nevents = df_multiple[['eeg_label_offset_seconds', 'expert_consensus']]\nnon_overlapping_events = pd.DataFrame(columns=events.columns)\n\n# Create the list of all eeg_label_offset_seconds\nlist_eeg_label_offset_seconds = list(df_multiple['eeg_label_offset_seconds'])\nprint(list_eeg_label_offset_seconds)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generate a list of eeg_label_offset_seconds with a minimum separation of 10 seconds\nnew_list = []\ncurrent_offset = 0\nmin_distance = 10\n\nwhile current_offset <= max(list_eeg_label_offset_seconds):\n    new_list.append(current_offset)\n    next_offset = next((x for x in list_eeg_label_offset_seconds if x >= current_offset + min_distance), None)\n    if next_offset is None:\n        break\n    current_offset = next_offset\n    \nprint(new_list)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create the non overlapping events\nevents = df_multiple[['eeg_label_offset_seconds', 'expert_consensus']]\nnon_overlapping_events = pd.DataFrame(columns=events.columns)\n\n# Create a mask using the new_list\nmask = events['eeg_label_offset_seconds'].isin(new_list)\n\n# Apply the mask to get non-overlapping events\nnon_overlapping_events = events[mask]\n\n# print the non_overlapping_events\nnon_overlapping_events.insert(1, 'New', 0)\nprint(non_overlapping_events)\n\n# Map 'expert_consensus' values to numerical labels using the 'event_ids' dictionary\nnon_overlapping_events.loc[:,'expert_consensus'] = non_overlapping_events['expert_consensus'].map(event_ids)\n\n# Define the sample time point where the event occurs\nnon_overlapping_events.loc[:,'eeg_label_offset_seconds'] = (non_overlapping_events['eeg_label_offset_seconds']+25)*200\n\n# Convert the data frame into an array\nnon_overlapping_events = non_overlapping_events.values.astype(int)\n#print(non_overlapping_events)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the events\nmne.viz.plot_events(non_overlapping_events[:])\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create the epochs\nepochs = mne.Epochs(raw, non_overlapping_events, event_id=event_ids, tmin=-5, tmax=5, preload=True, baseline=(None, 0))\nprint(epochs.event_id)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the 10th first epochs for all channels.\nepochs.plot(n_epochs=10, events=True, picks = 'all', show_scrollbars=False, show_scalebars=False, scalings=300)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It's possible to select only one sample of epochs.","metadata":{}},{"cell_type":"code","source":"# Observe the information of the epochs associated to the event Seizure \nepochs['Seizure']","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can get the information of the Epoch object.","metadata":{}},{"cell_type":"code","source":"# Observe the info\nepochs.info","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"An intriguing aspect is that we can visualized the sensors.","metadata":{}},{"cell_type":"code","source":"# Display the sensors\nepochs.plot_sensors(show_names=True)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Study of the particular eeg","metadata":{}},{"cell_type":"markdown","source":"1. **Visualize epochs through heatmaps**","metadata":{}},{"cell_type":"markdown","source":"Let's visualize the activity within the epochs as a heatmap.  We will obtain a plot which represents the total number of epochs for a correponding brain activity event within a 10-second window for each channel; each pixel’s color representing the signal value at that time sample for that epoch.","metadata":{}},{"cell_type":"code","source":"#Plot the total number of epochs (10 epochs) corresponding to the Seizure event within the 10-second window for the first and the eleven-th channel\n#(pick = [0] and pick = [10])\nepochs['Seizure'].plot_image(picks=[0])\nepochs['Seizure'].plot_image(picks=[10])\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2. **Visualize average epochs for one particular brain activity**\n\nNow, let’s take a look at some channels in the eeg averaged across epochs.","metadata":{}},{"cell_type":"code","source":"# Define the average eeg\naverage_epochs_seizure = epochs['Seizure'].average()\n\n# Get the measurement information\naverage_epochs_seizure.info\n\n# Display the result for all channels\naverage_epochs_seizure.plot(picks='all')\n\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note for these figures: As they are now, I don't feel like they are particularly exploitable.\n\n\nIt's also possible to look at topographic map of the previous figure.","metadata":{}},{"cell_type":"code","source":"# Display the topographic map\naverage_epochs_seizure.plot_topomap(times = [-2, -1, 0, 1, 2, 3, 4, 5])\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can also link it directly with the previous one","metadata":{}},{"cell_type":"code","source":"# Display the topographic map\naverage_epochs_seizure.plot_joint(times = [-2, -1, 0, 1, 2, 3, 4, 5])\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"By combining the two plots, we can directly observe the evolution of the average of epochs across the sensors. It is evident that the maximum amplitude occurs between 3s and 4s.","metadata":{}},{"cell_type":"markdown","source":"3. **Visualize the power spectral density**","metadata":{}},{"cell_type":"markdown","source":"Now let’s check out the frequency content of our epochs and display all channel types by averaging across epochs.","metadata":{}},{"cell_type":"code","source":"# Display the power spectral density (psd) \nepochs.compute_psd(fmin=0.01, fmax=20.0).plot(average=True, picks=\"all\")\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, let’s take a look at the spatial distributions of the PSD, averaged across epochs and frequency bands.","metadata":{}},{"cell_type":"code","source":"# Display the spatial distributions of the PSD\nepochs.compute_psd().plot_topomap(ch_type=\"eeg\", normalize=False, contours=0)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Something more interesting is to compute epochs for each brain activity. Let's take Seizure as example.","metadata":{}},{"cell_type":"code","source":"# Display the power spectral density (psd) for Seizure\nepochs['Seizure'].compute_psd(fmin=0.01, fmax=20.0).plot(average=True, picks=\"all\")\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display the spatial distributions of the PSD for Seizure\nepochs['Seizure'].compute_psd().plot_topomap(ch_type=\"eeg\", normalize=False, contours=0)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Time-frequency analysis\n\nLet's revisit the concept of time-frequency analysis: In signal processing, time–frequency analysis encompasses a set of techniques and methods employed to characterize and manipulate signals whose statistics vary over time, such as transient signals.\n\nIt represents a generalization and refinement of Fourier analysis, particularly applicable when the frequency characteristics of the signal change with time. Given that many signals of interest, such as speech, music, images, and medical signals, exhibit varying frequency characteristics, time–frequency analysis proves to be widely applicable across diverse fields.\n\nThere are several different ways to formulate a valid time–frequency distribution function, resulting in several well-known time–frequency distributions, such as: wavelet transform, STFT transform etc.\n\n(https://en.wikipedia.org/wiki/Time%E2%80%93frequency_analysis#:~:text=In%20signal%20processing%2C%20time%E2%80%93frequency%20analysis%20is%20a%20body%20of,time%2C%20such%20as%20transient%20signals.)\n\n\nThe goal is to explore the spectral content of the data, focusing on both frequency and time-frequency domains. Initially, we will address Epochs, but it's worth noting that time-frequency analysis can also be executed on a NumPy array.","metadata":{}},{"cell_type":"markdown","source":"1. **Frequency analysis**","metadata":{}},{"cell_type":"code","source":"# Plot the epochs corresponding to Seizure in the range of frequencies 0.01Hz and 20Hz\nepochs['Seizure'].plot_psd(fmin=0.01, fmax=20)\nplt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2. **Time-Frequency analysis**\n\nWe are now computing Time-Frequency Representations (TFRs) from our Epochs, specifically focusing on power and inter-trial coherence (ITC).\n\nVarious methods can be employed for TFRs, with the main ones being:\n\n- Multitaper method: This method involves creating several orthogonal tapering windows in TFR estimation, effectively reducing variance. Parameters such as time_bandwidth can be adjusted, impacting multitaper properties and resulting in a different TFR. Trade-offs between time and frequency resolution can be made to achieve a variance reduction.\n\n- Stockwell method: Utilizing a Gaussian window, this method balances temporal and spectral resolution. Notably, frequency bands are phase-normalized, ensuring strict comparability in terms of timing. The input signal can be recovered from the transform in a lossless manner, disregarding numerical errors. The width parameter is used to control spectral/temporal resolution by specifying different widths for the Gaussian window.\n\n- Morlet wavelets: These are sinusoidal waves with a Gaussian envelope. The n_cycles parameter allows control over the balance between spectral and temporal resolution by defining the number of cycles to include in the window.\n\nFor more informations https://mne.tools/1.0/auto_examples/time_frequency/time_frequency_simulated.html.\n\n\nThe functions used for these computations are:\n\n- mne.time_frequency.tfr_multitaper()\n- mne.time_frequency.tfr_stockwell()\n- mne.time_frequency.tfr_morlet()\n","metadata":{}},{"cell_type":"code","source":"# import the tfr\nfrom mne.time_frequency import tfr_morlet, tfr_multitaper, tfr_stockwell","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will exclusively utilize Morlet wavelets, a method primarily employed for Time-Frequency Representations (TFRs). Let's explore the available parameters in the mne.time_frequency.tfr_morlet function:\n\n- `inst`: The instance containing the data.\n- `freqs`: The frequencies of interest.\n- `n_cycles`: Number of cycles in the wavelet, either a fixed number or one per frequency. The number of cycles n_cycles and the frequencies of interest freqs define the temporal window length. This parameter controls the balance between spectral and temporal resolution.\n- `use_fft`: If `True`, use FFT-based convolution.\n- `return_itc`: If `True`, return inter-trial coherence (ITC).\n- `decim`: The decimation factor.\n- `n_jobs`: The number of jobs to run in parallel.\n- `picks`: Channels to include. If `None`, include all channels.\n- `zero_mean`: If `True`, subtract the mean of the time-domain data for each epoch.\n- `average`: If `True`, average the power across frequencies.\n- `output`: The type of output to return ('power' or 'complex').\n- `verbose`: If not `None`, override default verbose level.\n\nThese options provide flexibility in customizing the Time-Frequency Representation (TFR) based on specific analysis requirements. For more explanations, check this link https://mne.tools/stable/generated/mne.time_frequency.tfr_morlet.html#mne.time_frequency.tfr_morlet.\n\nIn the context of time-frequency analysis, particularly when using Morlet wavelets, the choice of frequency values and the number of cycles is crucial.","metadata":{}},{"cell_type":"code","source":"# Define the range of frequencies\n#freq = np.logspace(*np.log10([0.5, 20]), num=100)\nfreq = np.arange(0.5, 20, 0.01)\n\n# Define the number of cycles\nn_cycles = freq/2\n\n#  Define the Morlet wavelet transform for the first epoch associated to the event seizure\npower_seizure = tfr_morlet(epochs['Seizure'][0], freq, n_cycles = n_cycles, return_itc = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's break down the code snippet:\n\n1. **`freq = np.logspace(*np.log10([0.5, 20]), num=100)`**: This line generates an array `freq` containing 100 logarithmically spaced frequency values between 0.5 and 20. Logarithmic spacing is often used in frequency analysis, especially when dealing with a wide range of frequencies. This ensures better resolution at lower frequencies while still covering a broad spectrum. Adjusting these parameters allows us to explore various spectrogram configurations.\n\n2. **`n_cycles = freq/2`**: The number of cycles parameter for the Morlet wavelet is often related to the frequency of interest. In this case, it is setting `n_cycles` to be half of the frequency values in the `freq` array.\n\n3. **`power_seizure = tfr_morlet(epochs['Seizure'][0], freq, n_cycles=n_cycles, return_itc=False)`**: Finally, the Morlet wavelet transform (`tfr_morlet`) is applied to the data corresponding to the 'Seizure' event. It uses the generated frequency values (`freq`) and the associated number of cycles (`n_cycles`). The `return_itc=False` argument indicates that only the power, not the inter-trial coherence, should be returned.","metadata":{}},{"cell_type":"code","source":"# Plot the spectrogram for each channel\nfor title in ch_names:\n    power_seizure.plot(picks=title, title=title)\n    plt.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can now generate Time-Frequency Representations (TFR) for individual epochs related to seizure events and for each channel. ","metadata":{}},{"cell_type":"markdown","source":"# Conclusion\n\nIn conclusion, MNE offers valuable methods for extracting and analyzing distinct features of brain activity, streamlining the exploration of EEG data. \n\n\nLeveraging the technique presented by @cdeotte in his notebook on converting EEG to spectrograms (https://www.kaggle.com/code/cdeotte/how-to-make-spectrogram-from-eeg/notebook), I believe MNE provides the necessary tools to create various montage configurations and generate spectrograms with different TFR methods.","metadata":{}},{"cell_type":"markdown","source":"# To be continued! ","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}