Spindles analysis

Introductory notes:

This notebook presents spindles detection and analysis functionality.

Recommended reading:

  1. YASA: FAQ

  2. YASA: Spindles detection on specific sleep stages

  3. YASA: SpindlesResults class

  4. MNE: Time-frequency analysis: power and inter-trial coherence

  5. MNE: AverageTFR class

Import module

from sleepeegpy.pipeline import SpindlesPipe
from joblib import parallel_backend

parallel_backend("loky", n_jobs=-1)
from os import makedirs, path

Spindles detection

If you wish to change the path for output_dir ot input dir, change it below. If no such folders, they will be created automatically.

output_dir = "output_folder"  # Output path and name can be changed here
input_dir = "input_files"  # input files dir can be changed here
makedirs(input_dir, exist_ok=True)
makedirs(output_dir, exist_ok=True)

Add required files and data

  • Put all your files in the input folder.

  • Modify your eeg file name below. The file can be any format supported by the mne.read_raw() function.

  • Modify your hypnogram file name below

  • Make sure the hypno_freq is the right frequency.

  • For more information about the supported formats, see mne documentation

eeg_file_name = "resampled_raw.fif"  # add your eeg_path here
hypnogram_filename = "staging.txt"  # Hypnogram filename can be changed here (file must be in the input dir)
hypno_freq = 1
path_to_eeg = path.join(input_dir, eeg_file_name)
hypnogram_path = path.join(input_dir, hypnogram_filename)
spindles_pipe = SpindlesPipe(
    path_to_eeg=path_to_eeg,
    output_dir=output_dir,
    path_to_hypno=hypnogram_path,
    hypno_freq=hypno_freq,
)

Please refer to the YASA’s documentation for details about the arguments.

spindles_pipe.detect(
    picks=["eeg"],
    reference="average",
    include=(2, 3),
    freq_sp=(12, 15),
    freq_broad=(1, 30),
    duration=(0.5, 2),
    min_distance=500,
    thresh={"corr": 0.65, "rel_pow": 0.2, "rms": 1.5},
    multi_only=False,
    remove_outliers=False,
    verbose=False,
    save=True,
)

Please refer to the YASA’s documentation for details about the arguments.

spindles_pipe.results.summary(
    grp_chan=False, grp_stage=True, mask=None, aggfunc="mean", sort=True
)
Count Density Duration Amplitude RMS AbsPower RelPower Frequency Oscillations Symmetry
Stage
2 48125 439.497717 0.802510 35.844591 7.849986 1.739719 0.402655 13.149206 10.165049 0.513328
3 22294 116.722513 0.751883 40.712861 9.045872 1.862345 0.384414 13.194922 9.552705 0.516585
spindles_pipe.plot_average(
    save=True,
    center="Peak",
    hue="Stage",
    time_before=1,
    time_after=1,
    filt=(None, None),
    mask=None,
)
../_images/1a19d5a7d52545c3501ca101ea6b87317181a4f3d33375eaade7c9297c3c4600.png
spindles_pipe.plot_topomap(
    prop="Frequency",  #  Any of the event properties returned by pipe.results.summary().
    stage="N2",  # Stage to plot the topomap for.
    aggfunc="mean",  # Aggregation function for channel grouping.
    # Should contain at least index of the provided "stage".
    sleep_stages={"Wake": 0, "N1": 1, "N2": 2, "N3": 3, "REM": 4},
    axis=None,  # Whether to plot on provided matplotlib axis.
    save=True,  # Whether to save the plot as a file.
    topomap_args=dict(cmap="plasma"),  # Arguments passed to mne.viz.plot_topomap().
    cbar_args=None,  # Arguments passed to plt.colorbar().
)
../_images/2a99ec963b7a9949a0a261773efed59087653d57d90041efbf25635d8a9f8878.png
spindles_pipe.plot_topomap_collage(
    # Some of the event properties returned by pipe.results.summary().
    props=["Amplitude", "Frequency", "Duration"],
    aggfunc="mean",  # Aggregation function for channel grouping.
    # Tuple of strs or "all", e.g., ("N1", "REM") or "all" (plots all "sleep_stages").
    stages_to_plot="all",
    # Should contain at least indices of the provided "stages_to_plot".
    sleep_stages={"N2": 2, "N3": 3},
    low_percentile=5,  # Set min color value by percentile of the property data.
    high_percentile=95,  # Set max color value by percentile of the property data.
    fig=None,  # Instance of plt.Figure, a new fig will be created if None.
    save=True,  # Whether to save the plot as a file.
    topomap_args=dict(cmap="plasma"),  # Arguments passed to mne.viz.plot_topomap().
    cbar_args=None,  # Arguments passed to plt.colorbar().
)
../_images/548a8c84ea614dccf9a2ccf60df185d1ec579c8518cdf5749682dffa6b22ce37.png

Constructs MNE AverageTFR object for every available sleep stage.

spindles_pipe.compute_tfr(
    freqs=(10, 20),  # Interval of frequencies of interest.
    n_freqs=100,  # TFR frequency resolution.
    time_before=1,  # Time in sec before the peak to use in TFR computation. Should be positive.
    time_after=1,  # Time in sec after the peak to use in TFR computation.
    method="morlet",  # TFR method: 'morlet' or 'multitaper'.
    save=True,  # Whether to save AverageTFR object per sleep stage as hdf5 file.
    overwrite=False,  # Whether to overwrite existing files.
)

spindles_pipe.tfrs
spindles_pipe.tfrs["N2"].data
array([[[165.44385771, 169.92036454, 174.23956786, ..., 251.72769742,
         245.62927484, 239.30345075],
        [163.99052483, 168.49114899, 172.83396985, ..., 247.9999998 ,
         241.99269192, 235.75219132],
        [162.56633576, 167.09405193, 171.46393358, ..., 244.37907057,
         238.45905738, 232.30020575],
        ...,
        [ 46.16793334,  47.01757305,  47.5113704 , ...,  63.95819156,
          63.84737753,  63.19868073],
        [ 45.74479396,  46.57959889,  47.0574021 , ...,  63.33866633,
          63.24374934,  62.61033302],
        [ 45.3302599 ,  46.15072422,  46.61304951, ...,  62.73090346,
          62.65140487,  62.03282812]],

       [[183.03852542, 188.05132799, 192.90103697, ..., 362.26134796,
         352.15546474, 341.75556073],
        [179.36273518, 184.3038714 , 189.08251403, ..., 354.76061295,
         344.79643891, 334.54188686],
        [175.76023984, 180.63134178, 185.34100882, ..., 347.22249221,
         337.41589585, 327.32177991],
        ...,
        [ 64.80923984,  65.85678097,  66.37301043, ...,  68.02721018,
          67.91046883,  67.22764763],
        [ 64.33684292,  65.36606319,  65.86121262, ...,  67.25489205,
          67.1579677 ,  66.49524398],
        [ 63.86404637,  64.87444836,  65.34807822, ...,  66.50109653,
          66.42300215,  65.77945011]],

       [[357.24759613, 368.793126  , 380.08658706, ..., 588.0948437 ,
         572.42633713, 556.26896238],
        [347.83432458, 359.13567367, 370.18398281, ..., 583.11395626,
         567.25240803, 550.91532737],
        [338.32955624, 349.36741722, 360.15064435, ..., 577.22434197,
         561.2249059 , 544.76243092],
        ...,
        [ 65.51413465,  66.88240809,  67.8213576 , ...,  89.48868791,
          90.12620337,  90.01072393],
        [ 65.18081622,  66.53743615,  67.45993565, ...,  88.95798722,
          89.60549199,  89.49516761],
        [ 64.83682909,  66.17995034,  67.0840514 , ...,  88.45008017,
          89.1058455 ,  88.99904655]],

       ...,

       [[169.34408048, 175.25910732, 181.08300933, ..., 288.11138222,
         278.18552574, 268.18170021],
        [165.91686525, 171.72118948, 177.4298448 , ..., 282.09107422,
         272.31825781, 262.46583089],
        [162.6619232 , 168.36491613, 173.96814788, ..., 276.14859022,
         266.53102087, 256.83215105],
        ...,
        [ 33.30850643,  33.94177284,  34.31373952, ...,  40.33239391,
          40.13736805,  39.62441846],
        [ 32.99837925,  33.6212056 ,  33.98223375, ...,  39.79041469,
          39.61710556,  39.12574162],
        [ 32.69783405,  33.31092644,  33.66175045, ...,  39.26461546,
          39.112192  ,  38.64157834]],

       [[137.05899418, 142.02440564, 146.9221348 , ..., 230.90050237,
         223.33624383, 215.66500415],
        [133.75877594, 138.62616574, 143.42396777, ..., 227.3662423 ,
         219.85924288, 212.2437449 ],
        [130.59154621, 135.36711042, 140.07158921, ..., 223.84530067,
         216.39593257, 208.83683256],
        ...,
        [ 23.97646908,  24.50242238,  24.84852637, ...,  33.73008036,
          33.47365102,  32.95415897],
        [ 23.77058193,  24.28991435,  24.62891084, ...,  33.32859906,
          33.08517034,  32.57863308],
        [ 23.57007977,  24.08317241,  24.4154638 , ...,  32.93668107,
          32.70590645,  32.21196209]],

       [[431.55839651, 445.42635453, 459.07103265, ..., 600.42672536,
         582.1431401 , 563.5192219 ],
        [426.92557365, 440.7304316 , 454.30294034, ..., 592.32535875,
         574.12609719, 555.58617416],
        [422.63123618, 436.39371621, 449.91558109, ..., 584.31384353,
         566.1959117 , 547.7378584 ],
        ...,
        [113.55675603, 117.02377124, 119.81135006, ..., 135.2187291 ,
         133.06098679, 129.97487896],
        [112.32289575, 115.73295768, 118.46154285, ..., 133.76381278,
         131.67057141, 128.64501669],
        [111.122582  , 114.47794916, 117.14985745, ..., 132.33726145,
         130.30705071, 127.34066112]]])
spindles_pipe.tfrs["N2"].to_data_frame()
time freq E1 E10 E100 E101 E102 E103 E104 E105 ... E91 E92 E93 E94 E95 E96 E97 E98 E99 VREF
0 -1.000 10.0 165.443858 183.038525 357.247596 407.714768 367.061131 403.271235 439.417678 457.963162 ... 191.327237 191.225602 181.691803 208.996170 198.116414 177.822697 192.377679 169.344080 137.058994 431.558397
1 -0.996 10.0 169.920365 188.051328 368.793126 421.133352 377.604580 416.300607 454.302290 473.374693 ... 197.382219 197.558606 188.066255 215.492143 204.044079 183.797165 198.590959 175.259107 142.024406 445.426355
2 -0.992 10.0 174.239568 192.901037 380.086587 434.450949 387.836302 429.071681 468.968133 488.561540 ... 203.319116 203.775189 194.335510 221.796086 209.799195 189.672437 204.685274 181.083009 146.922135 459.071033
3 -0.988 10.0 178.391438 197.575344 391.093778 447.647728 397.730075 441.553881 483.380503 503.488717 ... 209.123305 209.858019 200.480026 227.885826 215.364812 195.433170 210.645100 186.798909 151.736835 472.466480
4 -0.984 10.0 182.367373 202.063155 401.782197 460.704946 407.262053 453.719253 497.506821 518.123475 ... 214.781062 215.790568 206.480974 233.740487 220.725338 201.064896 216.455858 192.390601 156.453546 485.589296
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
50095 0.984 20.0 61.349063 65.048047 85.063819 113.433422 88.394031 124.506086 149.952411 121.336701 ... 42.527688 66.486529 41.358480 43.371996 44.445922 38.431979 55.741899 38.668228 32.639920 133.629536
50096 0.988 20.0 62.286521 66.031386 87.079793 115.201462 89.856116 126.484048 152.059416 121.359426 ... 42.937436 67.461370 41.915039 43.619696 45.178900 38.646578 56.871620 39.109777 32.910099 133.434442
50097 0.992 20.0 62.730903 66.501097 88.450080 116.113594 90.658314 127.473423 152.945087 120.539369 ... 43.032323 67.901196 42.151561 43.561366 45.569074 38.588749 57.545199 39.264615 32.936681 132.337261
50098 0.996 20.0 62.651405 66.423002 89.105845 116.103670 90.741003 127.405634 152.538994 118.845436 ... 42.792571 67.772755 42.045046 43.179045 45.587985 38.243719 57.726269 39.112192 32.705906 130.307051
50099 1.000 20.0 62.032828 65.779450 88.999047 115.132624 90.066964 126.244182 150.810430 116.272835 ... 42.208679 67.060129 41.583470 42.464753 45.218892 37.605378 57.393721 38.641578 32.211962 127.340661

50100 rows × 259 columns

spindles_pipe.tfrs["N2"].plot(["E101"]),
spindles_pipe.tfrs["N3"].plot(["E101"])
No baseline correction applied
../_images/3d680730b99b69f0c074a5426cefd6191681c7264ae725b00c78f5f9cd4aaa28.png
No baseline correction applied
../_images/b0c61786343d6b7777852e29352d72937db25eb4b1868fadc683b824ae03f464.png
[<Figure size 640x480 with 2 Axes>]

If you want to load saved TFR files:

spindles_pipe.read_tfrs(dirpath=None)