def cut_segments(d, marker_tuples, offsets=[0, 0]): ''' Cut a dataset into segment using (start_marker, end_marker) tuples. Returns a list with DataSets. ''' start_off, end_off = offsets segments = [] e, ei = markers_to_events(d.ys.flat) for (sm, em) in marker_tuples: segments.extend(find_segments(e, ei, sm, em)) segments.sort() return [d[s + start_off:e + end_off] for (s, e) in segments]
def slice(d, markers_to_class, offsets): ''' Slice function, used to extract fixed-length segments of EEG from a recording. Returns [segment x frames x channel] ''' assert len(d.feat_shape) == 1 assert d.nclasses == 1 start_off, end_off = offsets xs, ys, ids = [], [], [] feat_shape = (end_off - start_off,) + d.feat_shape cl_lab = sorted(set(markers_to_class.values())) events, events_i = markers_to_events(d.ys.flat) for (mark, cl) in markers_to_class.items(): cl_i = cl_lab.index(cl) for i in events_i[events==mark]: # fails if there is *ONE* event (start, end) = i + start_off, i + end_off if start < 0 or end > d.ninstances: logging.getLogger('psychic.utils.slice').warning( 'Cannot extract slice [%d, %d] for class %s' % (start, end, cl)) continue dslice = d[start:end] xs.append(dslice.xs) ys.append(cl_i) ids.append(d.ids[i, :]) m = len(xs) xs = np.asarray(xs).reshape(m, np.prod(feat_shape)) ys = np.asarray(ys) ys = helpers.to_one_of_n(ys.T, class_rows=range(len(cl_lab))).T ids = np.asarray(ids).reshape(m, d.ids.shape[1]) event_time = np.arange(start_off, end_off) / float(get_samplerate(d)) time_lab = ['%.3f' % ti for ti in event_time] feat_nd_lab = [time_lab, d.feat_lab if d.feat_lab else ['f%d' % i for i in range(d.nfeatures)]] feat_dim_lab = ['time', 'channels'] d = DataSet(xs=xs, ys=ys, ids=ids, cl_lab=cl_lab, feat_shape=feat_shape, feat_nd_lab=feat_nd_lab, feat_dim_lab=feat_dim_lab, default=d) return d.sorted()
def cut_segments(d, marker_tuples, offsets=[0, 0]): """ Cut a dataset into segments using (start_marker, end_marker) tuples. Parameters ---------- d : :class:`psychic.DataSet` Continuous data to cut into segments. marker_tuples : list of tuples A list of (start_marker, end_marker) marker codes delimiting each type of segment. Returns ------- data : list of :class:`psychic.DataSet` A list with datasets. """ start_off, end_off = offsets segments = [] e, ei, _ = markers_to_events(d.labels.flat) for (sm, em) in marker_tuples: segments.extend(find_segments(e, ei, sm, em)) segments.sort() return [d[s + start_off : e + end_off] for (s, e) in segments]
def plot_eeg( data, samplerate=None, vspace=None, draw_markers=True, mirror_y=False, fig=None, mcolors=['b', 'r', 'g', 'c', 'm', 'y', 'k', '#ffaa00'], mlinestyles=['-','-','-','-','-','-','-','-'], mlinewidths=[1,1,1,1,1,1,1,1], start=0): ''' Plot EEG data contained in a golem dataset. Parameters ---------- data : :class:`psychic.DataSet` The data to plot. Assumed to be continuous data (channels x time) samplerate : float (optional) The sample rate of the data. When omitted, :func:`psychic.get_samplerate` is used to estimate it. vspace : float (optional) The amount of vertical spacing between channels. When omitted, the minimum value is taken so that no channels overlap. draw_markers : bool (default=True) When set, event markers are drawn as vertical lines in the plot. mirror_y : bool (default=False) When set, negative is plotted up. Some publications use this style of plotting. fig : :class:`matplotlib.Figure` (optional) Normally, a new figure is created to hold the plot. However, the user can specify the figure in which to draw. This is useful if the user wants to remain in control of the size of the figure and the location of the axes. mcolors : list (optional) Sets a color for each marker type. The vertical lines and text labels for events of a given type will be drawn in the specified color. Values are given as matplotlib color specifications. See: http://matplotlib.org/api/colors_api.html mlinestyles : list (optional) Line style specifications for each marker type. See: http://matplotlib.org/1.3.0/api/pyplot_api.html#matplotlib.pyplot.plot mlinewidths : list (optional) Line width specifications for each marker type. Vertical lines are drawn at the specified widths. Values are given in points. start : float (default=0) Time which is to be taken as t=0. Normally when plotting a time range, the time axis will reflect absolute time. For example, when plotting the time range 2 to 4 seconds, the time axis will start at 2 seconds. Setting the ``start`` parameter to 2 will in this case make the time axis range from 0 to 2 seconds, and setting this parameter to 3 will make the time axis range from -1 to 1 seconds. Returns ------- fig : :class:`matplotlib.Figure` The figure object containing the plot. When a figure is specified with the ``fig`` parameter, the same figure is returned. ''' assert data.data.ndim == 2 num_channels, num_samples = data.data.shape # Spread out the channels if vspace is None: vspace = np.max(np.max(data.data, axis=1) - np.min(data.data, axis=1)) bases = vspace * np.arange(0, num_channels)[::-1] - np.mean(data.data, axis=1) to_plot = data.data + np.tile( bases, (num_samples,1) ).T if fig is None: fig = plot.figure() # Plot EEG fig.subplots_adjust(right=0.85) axes = plot.subplot(111) _draw_eeg_frame(num_channels, vspace, data.ids.T-start, data.feat_lab[0], mirror_y) plot.plot(data.ids.T-start, to_plot.T) # Draw markers if draw_markers: trans = transforms.blended_transform_factory(axes.transData, axes.transAxes) events, offsets, _ = markers_to_events(data.labels[0,:]) eventi = {} for i,e in enumerate(np.unique(events)): eventi[e] = i for e,o in zip(events, offsets): i = eventi[e] x = data.ids[0,o] # In data coordinates y = 1.01 # In axes coordinates plot.axvline(x, color=mcolors[i%len(mcolors)], linestyle=mlinestyles[i%len(mlinestyles)], linewidth=mlinewidths[i%len(mlinewidths)]) plot.text(x, y, str(e), transform=trans, ha='center', va='bottom') plot.ylabel('Channels') plot.xlabel('Time (s)') plot.grid() return fig
def slice(d, markers_to_class, offsets): ''' Slice function, used to extract fixed-length segments (called trials) of EEG from a recording. Opposite of :func:`psychic.concatenate_trials`. Segments are sliced based on the onset of some event code. Given for example an EEG recording which contains two marker codes: 1. Left finger tapping 2. Right finger tapping Trials can be extracted in the following manner: >>> import psychic >>> d = psychic.load_bdf(psychic.find_data_path('priming-short.bdf')) >>> mdict = {1:'related', 2:'unrelated'} >>> sample_rate = psychic.get_samplerate(d) >>> begin = int(-0.2 * sample_rate) >>> end = int(1.0 * sample_rate) >>> trials = psychic.slice(d, mdict, (begin, end)) >>> print trials DataSet with 208 instances, 12280 features [40x307], 2 classes: [104, 104], extras: [] Parameters ---------- markers_to_class : dict A dictionary containing as keys the event codes to use as onset of the trial and as values a class label for the resulting trials. For example ``{1:'left finger tapping', 2:'right finger tapping'}`` offsets : tuple Indicates the time (start, end), relative to the onset of marker, to extract as trial. Values are given in samples. Returns ------- d : :class:`DataSet` The extracted segments: - ``d.data``: [channels x samples x trials] - ``d.labels``: [classes x trials] - ``d.ids``: Timestamps indicating the marker onsets - ``d.cl_lab``: The class labels as specified in the ``markers_to_class`` dictionary - ``d.feat_lab``: Feature labels for the axes [channels (strings), time in seconds (floats)] ''' assert len(d.feat_shape) == 1, 'Data must be continuous EEG.' assert d.labels.shape[0] == 1 and d.labels.dtype == np.int, ('Labels are' 'the wrong format. It must be an integer marker stream.') start_off, end_off = offsets data, labels, ids = [], [], [] cl_lab = sorted(set(markers_to_class.values())) events, events_i, events_d = markers.markers_to_events(d.labels.flat) for (mark, cl) in markers_to_class.items(): cl_i = cl_lab.index(cl) for i in events_i[events==mark]: # fails if there is *ONE* event (start, end) = i + start_off, i + end_off if start < 0 or end > d.ninstances: logging.getLogger('psychic.utils.slice').warning( 'Cannot extract slice [%d, %d] for class %s' % (start, end, cl)) continue dslice = d[start:end] data.append(dslice.data) labels.append(cl_i) ids.append(d.ids[:,i]) event_time = np.arange(start_off, end_off) / float(utils.get_samplerate(d)) feat_lab = [d.feat_lab[0], event_time.tolist()] if len(data) == 0: data = np.zeros(d.feat_shape + (len(event_time),0)) labels = np.zeros((len(cl_lab),0)) ids = np.zeros((1,0)) else: data = np.concatenate([x[...,np.newaxis] for x in data], axis=2) labels = helpers.to_one_of_n(labels, class_rows=range(len(cl_lab))) ids = np.atleast_2d(np.vstack(ids).T) feat_dim_lab = ['channels', 'time'] d = DataSet( data=data, labels=labels, ids=ids, cl_lab=cl_lab, feat_lab=feat_lab, feat_dim_lab=feat_dim_lab, default=d ) return d.sorted()