def plot_erp_psd(d, freq_range=(2, 60), fig=None, **kwargs): ''' Plot the power spectral density (PSD), calculated using Welch' method, of all channels, averaged over all trials. This means for each trial, the PSD is computed and the resulting PSDs are averaged. In addition to the keyword arguments accepted by this function, any keyword arguments to the matplotlib.mlab.psd function can also be specified and will be passed along. Parameters ---------- d : :class:`psychic.DataSet` The dataset to plot the PSD of freq_range : pair of floats (default: (2, 60)) The minimum and maximum frequency to plot in Hz fig : handle to matplotlib figure (default: None) If specified, the plot will be drawn in this figure. Returns ------- fig : handle to matplotlib figure The resulting figure See also -------- :func:`psychic.plot_psd` :func:`matplotlib.mlab.psd` ''' assert d.data.ndim == 3, 'Expecting EEG data cut in trials' if fig is None: fig = plt.figure(figsize=(8,5)) Fs = psychic.get_samplerate(d) NFFT = d.data.shape[1] # Ensure even NFFT if NFFT % 2 != 0: NFFT += 1 for channel in range(d.data.shape[0]): all_psd = [] for trial in d: psd, freqs = mlab.psd(trial.data[channel,:,0], NFFT=NFFT, Fs=Fs, *kwargs) all_psd.append(psd) plt.plot(freqs, np.mean(np.array(all_psd), axis=0)) plt.xlim(freq_range[0], freq_range[1]) plt.xlabel('Frequency (Hz)') plt.ylabel('Power (Db)') plt.legend(d.feat_lab[0]) plt.title('Power spectral density, averaged over trials') return fig
def plot_psd(d, freq_range=(2, 60), fig=None, **kwargs): ''' Plot the power spectral density (PSD), calculated using Welch' method, of all channels. In addition to the keyword arguments accepted by this function, any keyword arguments to the matplotlib.mlab.psd function can also be specified and will be passed along. Parameters ---------- d : :class:`psychic.DataSet` The dataset to plot the PSD of freq_range : pair of floats (default: (2, 60)) The minimum and maximum frequency to plot in Hz fig : handle to matplotlib figure (default: None) If specified, the plot will be drawn in this figure. Returns ------- fig : handle to matplotlib figure The resulting figure See also -------- :func:`psychic.plot_erp_psd` :func:`matplotlib.mlab.psd` ''' assert d.data.ndim == 2, 'Expecting continuous EEG data' if fig is None: fig = plt.figure(figsize=(8,5)) Fs = psychic.get_samplerate(d) NFFT = d.ninstances # Ensure even NFFT if NFFT % 2 != 0: NFFT += 1 for channel in range(d.nfeatures): psd, freqs = mlab.psd(d.data[channel,:], NFFT=NFFT, Fs=Fs, *kwargs) plt.plot(freqs, psd) plt.xlim(freq_range[0], freq_range[1]) plt.xlabel('Frequency (Hz)') plt.ylabel('Power (Db)') plt.legend(d.feat_lab[0]) plt.title('Power spectral density') return fig
def plot_specgrams( data, samplerate=None, NFFT=256, freq_range=[0.1, 50], fig=None): ''' For each channel, plot a spectogram. ''' if fig is None: fig = plot.figure() if samplerate is None: samplerate = psychic.get_samplerate(data) num_channels = data.nfeatures num_cols = max(1, num_channels/8) num_rows = min(num_channels, 8) fig.subplots_adjust(hspace=0) for channel in range(num_channels): col = channel / num_rows row = channel % num_rows ax = plot.subplot(num_rows, num_cols, num_cols*row+col+1) s,freqs,_,_ = plot.specgram(data.data[channel,:], NFFT, samplerate, noverlap=NFFT/2, xextent=(np.min(data.ids), np.max(data.ids))) selection = np.logical_and(freqs >= freq_range[0], freqs <= freq_range[1]) s = s[selection,:] freqs = freqs[selection] plot.ylim(freq_range[0], freq_range[1]) plot.clim(np.min(np.log(s)), np.max(np.log(s))) ax.xaxis.grid(True, which='major', color='w') ax.yaxis.grid(False) if data.feat_lab is not None: plot.ylabel(data.feat_lab[channel]) else: plot.ylabel('CH%02d' % (channel+1)) if row == num_rows-1 or channel == num_channels-1: plot.xlabel('Time (s)') else: [label.set_visible(False) for label in ax.get_xticklabels()] [tick.set_visible(False) for tick in ax.get_xticklines()] return fig
def train_(self, d): if self.time_range is None: self.time_range = (0, d.feat_lab[1][-1]) self.time_idx = (0, d.data.shape[1]+1) else: sample_rate = psychic.get_samplerate(d) offset = d.feat_lab[1][0] * sample_rate self.time_idx = [int(x * sample_rate - offset) for x in self.time_range] self.time_idx[1] += 1 erp = psychic.erp(d) if type(self.classes) == int: template = erp.data[:,:,self.classes] else: template = erp.data[:,:,self.classes[0]] - erp.data[:,:,self.classes[1]] self.template = DataSet( data = template, ids = [erp.feat_lab[1]], feat_lab=[erp.feat_lab[0]], ) if self.peak_ch is None: peak = (self.time_idx[0] + np.argmax(np.abs(np.sum( self.template.data[:, self.time_idx[0]:self.time_idx[1]], axis=0)))) else: if type(self.peak_ch) == str: self.peak_ch = self.template.feat_lab[0].index(self.peak_ch) peak = (self.time_idx[0] + np.argmax(np.abs(self.template.data[self.peak_ch, self.time_idx[0]:self.time_idx[1]]))) self.spatial_template = self.template.data[:, [peak]] sigma_x = psychic.nodes.spatialfilter.plain_cov0(self.template) sigma_x += self.reg * np.eye(sigma_x.shape[0]) sigma_x_i = np.linalg.inv(sigma_x) W_spatial = sigma_x_i.dot(self.spatial_template) self.temp_template = psychic.nodes.spatialfilter.sfilter_plain(self.template, W_spatial) data = self.temp_template.data.copy() data[:,:self.time_idx[0]] = 0 data[:,self.time_idx[1]:] = 0 feat_lab=['temp'] self.temp_template = DataSet(data=data, feat_lab=feat_lab, default=self.temp_template)
def __init__(self, file, sample_rate=0, num_channels=0, header={}, dataset=None): try: self.f = open(file, 'wb') if isinstance(file, str) else file except: raise if dataset is not None: # Figure out some values from the datafile header = header.copy() header['n_channels'] = dataset.nfeatures if dataset.feat_lab is not None: header['label'] = list(dataset.feat_lab[0]) sample_rate = psychic.get_samplerate(dataset) record_length = header['record_length'] if 'record_length' in header else 1 header['n_samples_per_record'] = [int(sample_rate*record_length) for x in range(header['n_channels'])] # Use supplied header or defaults self.id_code = '\xffBIOSEMI' self.local_subject_id = header['local_subject_id'] if 'local_subject_id' in header else '' self.local_recording_id = header['local_recording_id'] if 'local_recording_id' in header else '' start_date_time = header['date_time'] if 'date_time' in header else datetime.datetime.now() self.start_date = header['start_date'] if 'start_date' in header else start_date_time.strftime('%d.%m.%y') self.start_time = header['start_time'] if 'start_time' in header else start_date_time.strftime('%H.%M.%S') self.format = header['format'] if 'format' in header else '24BIT' self.n_records = header['n_records'] if 'n_records' in header else -1 self.record_length = header['record_length'] if 'record_length' in header else 1 n_channels = header['n_channels'] if 'n_channels' in header else num_channels assert n_channels > 0, 'Please supply the number of channels.' self.n_channels = n_channels self.label = header['label'] if 'label' in header else [('channel %d' % (x+1)) for x in range(n_channels)] self.transducer_type = header['transducer_type'] if 'transducer_type' in header else ['unknown' for x in range(n_channels)] self.units = header['units'] if 'units' in header else ['uV' for x in range(n_channels)] self.physical_min = header['physical_min'] if 'physical_min' in header else [-1 for x in range(n_channels)] self.physical_max = header['physical_max'] if 'physical_max' in header else [1 for x in range(n_channels)] self.digital_min = header['digital_min'] if 'digital_min' in header else [-1000 for x in range(n_channels)] self.digital_max = header['digital_max'] if 'digital_max' in header else [1000 for x in range(n_channels)] self.prefiltering = header['prefiltering'] if 'prefiltering' in header else ['' for x in range(n_channels)] self.n_samples_per_record = header['n_samples_per_record'] if 'n_samples_per_record' in header else [sample_rate for x in range(n_channels)] assert len(np.unique(self.n_samples_per_record)) == 1, 'Sample rates differ for different channels' assert self.n_samples_per_record[0] > 0, 'Number of samples per record cannot be determined. Please specify a sample rate.' self.reserved = header['reserved'] if 'reserved' in header else ['Reserved' for x in range(n_channels)] self.records_written = 0 self.samples_left_in_record = None # Append status channel if necessary if not 'Status' in self.label: self.append_status_channel() self.gain = np.array(self.physical_max) - np.array(self.physical_min) self.gain = self.gain.astype(np.float) self.gain /= np.array(self.digital_max) - np.array(self.digital_min) self.inv_gain = 1 / self.gain self.offset = np.array(self.physical_min) - self.gain * np.array(self.digital_min) self.header_written = False