def test_read_hypno(self): """Test function read_hypno.""" # TXT version : hyp_txt, sf_txt = read_hypno(self.to_tmp_dir('hyp.txt')) # HYP version : hyp_hyp, sf_hyp = read_hypno(self.to_tmp_dir('hyp.hyp')) assert np.array_equal(hyp_txt, hyp_hyp) assert sf_txt == sf_hyp
def get_sleep_stats(hypno_file, output_file=None): """Compute sleep statistics from hypnogram file and export them in csv. Sleep statistics specifications: * Time in Bed (TIB) : total duration of the hypnogram. * Total Dark Time (TDT) : duration of the hypnogram from beginning to last period of sleep. * Sleep Period Time (SPT) : duration from first to last period of sleep. * Wake After Sleep Onset (WASO) : duration of wake periods within SPT * Sleep Efficiency (SE) : TST / TDT * 100 (%). * Total Sleep Time (TST) : SPT - WASO. * W, N1, N2, N3 and REM: sleep stages duration. * % (W, ... REM) : sleep stages duration expressed in percentages of TDT. * Latencies: latencies of sleep stages from the beginning of the record. (All values except SE and percentages are expressed in minutes) Parameters ---------- hypno_file : string Full path to the hypnogram file. output_file : string | None Full path to the output file. If no file is provided, sleep statictics are print out to the terminal. """ # File conversion : if output_file is not None: # Check extension ext = os.path.splitext(output_file)[1][1:].strip().lower() if ext == '': output_file = output_file + '.csv' # Load hypnogram hypno, sf_hyp = read_hypno(hypno_file) if sf_hyp < 1: mult = int(np.round(len(hypno) / sf_hyp)) hypno = oversample_hypno(hypno, mult) sf_hyp = 1 # Get sleep stats stats = sleepstats(hypno, sf_hyp=sf_hyp) stats['File'] = hypno_file print('\nSLEEP STATS\n===========') keys, val = [''] * len(stats), [''] * len(stats) # Fill table : for num, (k, v) in enumerate(stats.items()): print(k, '\t', str(v)) # Remember variables : keys[int(num)] = k val[int(num)] = str(v) if output_file is not None: write_csv(output_file, zip(keys, val)) print('===========\nCSV file saved to:', output_file)
def test_read_hypno(self): """Test function read_hypno.""" hyp = self._get_hypno() sf, npts = 1., len(hyp) time = np.arange(npts) / sf for k in versions.keys(): for e in versions[k]: filename = self.to_tmp_dir('hyp_test_' + k + e) hyp_new, _ = read_hypno(filename, time=time) np.testing.assert_array_equal(hyp, hyp_new)
def __init__(self, data, channels, sf, hypno, href, preload, use_mne, downsample, kwargs_mne, annotations): """Init.""" # ========================== LOAD DATA ========================== # Dialog window if data is None : if data is None: data = dialog_load( self, "Open dataset", '', "Any EEG files (*.vhdr *.edf *.gdf *.bdf *.eeg " "*.egi *.mff *.cnt *.trc *.set *.rec);;" "BrainVision (*.vhdr);;EDF (*.edf);;" "GDF (*.gdf);;BDF (*.bdf);;Elan (*.eeg);;" "EGI (*.egi);;MFF (*.mff);;CNT (*.cnt);;" "Micromed (*.trc);;EEGLab (*.set);;REC (*.rec)") upath = os.path.split(data)[0] else: upath = '' if isinstance(data, str): # file is defined # ---------- USE SLEEP or MNE ---------- # Find file extension : file, ext = get_file_ext(data) # Force to use MNE if preload is False : use_mne = True if not preload else use_mne # Get if the file has to be loaded using Sleep or MNE python : sleep_ext = ['.eeg', '.vhdr', '.edf', '.trc', '.rec'] use_mne = True if ext not in sleep_ext else use_mne if use_mne: is_mne_installed(raise_error=True) # ---------- LOAD THE FILE ---------- if use_mne: # Load using MNE functions logger.debug("Load file using MNE-python") kwargs_mne['preload'] = preload args = mne_switch(file, ext, downsample, **kwargs_mne) else: # Load using Sleep functions logger.debug("Load file using Sleep") args = sleep_switch(file, ext, downsample) # Get output arguments : (sf, downsample, dsf, data, channels, n, offset, annot) = args info = ("Data successfully loaded (%s):" "\n- Sampling-frequency : %.2fHz" "\n- Number of time points (before down-sampling): %i" "\n- Down-sampling frequency : %.2fHz" "\n- Number of time points (after down-sampling): %i" "\n- Number of channels : %i") n_channels, n_pts_after = data.shape logger.info( info % (file + ext, sf, n, downsample, n_pts_after, n_channels)) PROFILER("Data file loaded", level=1) elif isinstance(data, np.ndarray): # array of data is defined if not isinstance(sf, (int, float)): raise ValueError("When passing raw data, the sampling " "frequency parameter, sf, must either be an " "integer or a float.") file = annot = None offset = datetime.time(0, 0, 0) dsf, downsample = get_dsf(downsample, sf) n = data.shape[1] data = data[:, ::dsf] else: raise IOError("The data should either be a string which refer to " "the path of a file or an array of raw data of shape" " (n_electrodes, n_time_points).") # Keep variables : self._file = file self._annot_file = np.c_[merge_annotations(annotations, annot)] self._N = n self._dsf = dsf self._sfori = float(sf) self._toffset = offset.hour * 3600. + offset.minute * 60. + \ offset.second time = np.arange(n)[::dsf] / sf self._sf = float(downsample) if downsample is not None else float(sf) # ========================== LOAD HYPNOGRAM ========================== # Dialog window for hypnogram : if hypno is False: hypno = None elif hypno is None: hypno = dialog_load( self, "Open hypnogram", upath, "Text file (*.txt);;Elan (*.hyp);;" "CSV file (*.csv);;EDF+ file(*.edf);" ";All files (*.*)") hypno = None if hypno == '' else hypno if isinstance(hypno, np.ndarray): # array_like if len(hypno) == n: hypno = hypno[::dsf] else: raise ValueError("Then length of the hypnogram must be the " "same as raw data") if isinstance(hypno, str): # (*.hyp / *.txt / *.csv) hypno, _ = read_hypno(hypno, time=time, datafile=file) # Oversample then downsample : hypno = oversample_hypno(hypno, self._N)[::dsf] PROFILER("Hypnogram file loaded", level=1) # ========================== CHECKING ========================== # ---------- DATA ---------- # Check data shape : if data.ndim is not 2: raise ValueError("The data must be a 2D array") nchan, npts = data.shape # ---------- CHANNELS ---------- if (channels is None) or (len(channels) != nchan): warn("The number of channels must be " + str(nchan) + ". Default " "channel names will be used instead.") channels = ['chan' + str(k) for k in range(nchan)] # Clean channel names : patterns = ['eeg', 'EEG', 'ref'] chanc = [] for c in channels: # Remove informations after . : c = c.split('.')[0] c = c.split('-')[0] # Exclude patterns : for i in patterns: c = c.replace(i, '') # Remove space : c = c.replace(' ', '') c = c.strip() chanc.append(c) # ---------- STAGE ORDER ---------- # href checking : absref = ['art', 'wake', 'n1', 'n2', 'n3', 'rem'] absint = [-1, 0, 1, 2, 3, 4] if href is None: href = absref elif (href is not None) and isinstance(href, list): # Force lower case : href = [k.lower() for k in href] # Check that all stage are present : for k in absref: if k not in href: raise ValueError(k + " not found in href.") # Force capitalize : href = [k.capitalize() for k in href] href[href.index('Rem')] = 'REM' else: raise ValueError("The href parameter must be a list of string and" " must contain 'art', 'wake', 'n1', 'n2', 'n3' " "and 'rem'") # Conversion variable : absref = ['Art', 'Wake', 'N1', 'N2', 'N3', 'REM'] conv = {absint[absref.index(k)]: absint[i] for i, k in enumerate(href)} # ---------- HYPNOGRAM ---------- if hypno is None: hypno = np.zeros((npts, ), dtype=np.float32) else: n = len(hypno) # Check hypno values : if (hypno.min() < -1.) or (hypno.max() > 4) or (n != npts): warn("\nHypnogram values must be comprised between -1 and 4 " "(see Iber et al. 2007). Use:\n-1 -> Art (optional)\n 0 " "-> Wake\n 1 -> N1\n 2 -> N2\n 3 -> N4\n 4 -> REM\nEmpty " "hypnogram will be used instead") hypno = np.zeros((npts, ), dtype=np.float32) # ---------- SCALING ---------- # Assume that the inter-quartile amplitude of EEG data is ~50 uV iqr_chan = iqr(data[:, :int(data.shape[1] / 4)], axis=-1) bad_iqr = iqr_chan < 1. if np.any(bad_iqr): mult_fact = np.zeros_like(iqr_chan) iqr_chan[iqr_chan == 0.] = 1. mult_fact[bad_iqr] = np.floor(np.log10(50. / iqr_chan[bad_iqr])) data *= 10.**mult_fact[..., np.newaxis] warn("Wrong channel data amplitude. ") # ---------- CONVERSION ----------= # Convert data and hypno to be contiguous and float 32 (for vispy): self._data = vispy_array(data) self._hypno = vispy_array(hypno) self._time = vispy_array(time) self._channels = chanc self._href = href self._hconv = conv PROFILER("Check data", level=1)
ax0.set_xlim(0, t_hyp.max()) ax0.set_ylabel('Stage') ax0.xaxis.set_visible(False) ax0.spines['right'].set_visible(False) ax0.spines['top'].set_visible(False) # Spectrogram (bottom axis) im = ax1.pcolormesh(t, f, Sxx, norm=norm, cmap=cmap, antialiased=True) ax1.set_xlim(0, t.max()) ax1.set_ylabel('Frequency [Hz]') ax1.set_xlabel('Time [hrs]') return fig, grid_spec, ax0, ax1 # read hypnogram old format (1sec) hypno, sf_hypno = read_hypno(hypno_file, time=None, datafile=None) # plot the hypnogram nicely optional to save a file: # write_fig_hyp(hypno, sf_hypno, grid=True, ascolor=True, file=None) # this is a built in function to calculate sleep stats: sleep_stats = yasa.sleep_statistics(hypno, sf_hypno) # TODO: checkout this epoch fix!! hypno_file = '/Users/rotemfalach/Documents/University/lab/data/UCLA scoring/P406_hypno.txt' raw = mne.io.read_raw_edf('/Users/rotemfalach/Documents/University/lab/data/P406_overnightData.edf') raw.pick_channels(['SEEG RA1-REF', 'SEEG LA1-REF', 'SEEG RAH1-REF', 'SEEG RAH2-REF', 'SEEG LAH1-REF', 'SEEG LAH2-REF', 'EEG C3-REF', 'EEG PZ-REF', 'EOG EOG1-REF', 'EOG EOG2-REF']) raw.resample(1000) # read hypnogram old format (1sec)