<jupyter_start><jupyter_text>Tutorial on Computing HFOs (Part 3)==========================In this tutorial, we will walk through how to compare detectors. As in part 2, we will demonstrate this on a sample dataset that is defined in [1]. We will demonstrate compar of the following detectors:- Line Length detector- RMS detector- Morphology detector (used in the paper)Dataset Preprocessing---------------------Note that the data has been converted to BIDS to facilitate easy loading using ``mne-bids`` package. Another thing to note is that the authors in this dataset reported HFOs detected using bipolar montage. In addition, they only analyzed HFOs for a subset of the recording channels.In order to compare results to a monopolar reference, we define an HFO to be "found" if there was an HFO in either of the corresponding bipolar contacts.References----------[1] <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>. Resection of high frequency oscillations predicts seizure outcome in the individual patient.Scientific Reports. 2017;7(1):13836.https://www.nature.com/articles/s41598-017-13064-1doi:10.1038/s41598-017-13064-1<jupyter_code># first let's load in all our packages
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import sys
import os
import re
import pandas as pd


from mne_bids import (read_raw_bids, BIDSPath, 
                      get_entity_vals, get_datatypes, 
                      make_report)
from mne_bids.stats import count_events

import mne
from mne import make_ad_hoc_cov

basepath = os.path.join(os.getcwd(), "../..")
sys.path.append(basepath)
from mne_hfo import LineLengthDetector, RMSDetector
from mne_hfo.compare import compare_detectors<jupyter_output><empty_output><jupyter_text>1 Working with Real DataWe are now going to work with the dataset from Fedele et al. linked above  1.1 Load in Real DataWe are going to skip intermediate/display steps since you did this in part 2<jupyter_code>%%capture
# this may change depending on where you store the data
root = "C:/Users/patri/Dropbox/fedele_hfo_data"
subjects = get_entity_vals(root, 'subject')
sessions = get_entity_vals(root, 'session')
subjectID = subjects[0]
sessionID = sessions[0]
bids_path = BIDSPath(subject=subjectID, session=sessionID,
                     datatype='ieeg', 
                     suffix='ieeg',
                     extension='.vhdr', root=root)

# get first matching dataset
fpath = bids_path.match()[0]
# load dataset into mne Raw object
extra_params = dict(preload=True)
raw = read_raw_bids(fpath, extra_params)<jupyter_output><empty_output><jupyter_text>1.2 Perform pre-processing steps<jupyter_code>def convert_to_bipolar(raw, drop_originals=True):
    original_ch_names = raw.ch_names
    ch_names_sorted = sorted(original_ch_names)
    ch_pairs = [] 
    for first, second in zip(ch_names_sorted, ch_names_sorted[1:]):
        firstName = re.sub(r'[0-9]+', '', first)
        secondName = re.sub(r'[0-9]+', '', second)
        if firstName == secondName:
            ch_pairs.append((first,second))
    for ch_pair in ch_pairs:
        raw = mne.set_bipolar_reference(raw, ch_pair[0], ch_pair[1], drop_refs=False)
    if drop_originals:
        raw = raw.drop_channels(original_ch_names)
    return raw
%%capture
raw = convert_to_bipolar(raw)<jupyter_output><empty_output><jupyter_text>1.3 Perform Detection with Both Detectors<jupyter_code># Set Key Word Arguments for the Line Length Detector and generate the class object
kwargs = {
    'filter_band': (80, 250), # (l_freq, h_freq)
    'threshold': 3, # Number of st. deviations
    'win_size': 100, # Sliding window size in samples
    'overlap': 0.25, # Fraction of window overlap [0, 1]
    'hfo_name': "ripple"
}
ll_detector = LineLengthDetector(**kwargs)

# We use the same arguments for the RMS Detecor
rms_detector = RMSDetector(**kwargs)
%%capture
# perform the fits
ll_detector = ll_detector.fit(raw)
rms_detector = rms_detector.fit(raw)<jupyter_output><empty_output><jupyter_text>2 Compare detections  2.1 Perform the comparisonsWe will calculate both the mutual information and cohen kappa scores to find agreement between detectors<jupyter_code>mutual_info = compare_detectors(ll_detector, rms_detector, method="mutual-info")
kappa = compare_detectors(ll_detector, rms_detector, method="cohen-kappa")<jupyter_output><empty_output><jupyter_text>2.2 Visualize the comparisons First let's observe what the compare function returns<jupyter_code>mutual_info<jupyter_output><empty_output><jupyter_text>As you can see, the compare function gives a dictionary where the keys are the channels and the values are the metric value, in this case the mutual information. We can use these values to plot a barchart to display which channels the detections had the most agreement on.  2.1.1 Extract the right values<jupyter_code>ch_names = list(mutual_info.keys())
mutual_info_vals = list(mutual_info.values())
kappa_vals = list(kappa.values())<jupyter_output><empty_output><jupyter_text>2.1.2 Visualize the mutual info<jupyter_code>fig, ax = plt.subplots(figsize=(20, 3))
bar = ax.bar(x=np.arange(len(ch_names)), height=mutual_info_vals)

ax.set_xticks(np.arange(len(ch_names)))
ax.set_xticklabels(ch_names)

plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")

ax.set_title("Mutual info between RMS and LineLength Detectors")
#fig.tight_layout()
plt.show()<jupyter_output><empty_output><jupyter_text>2.1.3 Visualize the Kappa score<jupyter_code>fig, ax = plt.subplots(figsize=(20, 3))
bar = ax.bar(x=np.arange(len(ch_names)), height=kappa_vals)

ax.set_xticks(np.arange(len(ch_names)))
ax.set_xticklabels(ch_names)

plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")

ax.set_title("Cohen Kappa between RMS and LineLength Detectors")
#fig.tight_layout()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10594668489405332, 'fraction_numerical': 0.017942583732057416, 'mean_word_length': 5.148109243697479, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '458148', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1980, 'n_tokens_neox': 1866, 'n_words': 545, 'path': 'docs/tutorials/comparing_detectors.ipynb', 'repo_name': 'pmyers16/mne-hfo', 'size': '5876'}