def erp(d, classes=None, enforce_equal_n=True): ''' For each class, calculate the Event Related Potential by averaging the corresponding trials. Note: no baselining is performed, see :func:`psychic.baseline`. Parameters ---------- data : :class:`psychic.DataSet` The trials classes: list (optional) When specified, the ERP is only calculated for the classes with the given indices. enforce_equal_n : bool (default=True) When set, each ERP is calculated by averaging the same number of trials. For example, if class1 has m and class2 has n trials and m > n. The ERP for class1 will be calculated by taking n random trials from class1. Returns ------- d : :class:`psychic.DataSet` A DataSet containing for each class the ERP. - ``d.data``: [channels x samples x classes] - ``d.labels``: The class labels. Each class has one instance (one ERP). ''' assert d.data.ndim > 2 if classes is None or len(classes) == 0: # Take all classes with >0 instances classes = [cl for cl in range(d.nclasses) if d.ninstances_per_class[cl] > 0] assert len(classes) > 0, \ 'No valid classes specified and no classes found with >0 instances' num_trials = np.min( np.array(d.ninstances_per_class)[classes] ) assert num_trials > 0, 'For one or more classes there are no instances!' # Calculate ERP erp = np.zeros(d.data.shape[:-1] + (len(classes),)) for i,cl in enumerate(classes): trials = d.get_class(cl).data if enforce_equal_n: # Enforce an equal number of trials for all classes. Picking them # at random. Otherwise the ERPs will be skewed, simply because a # different number of trials are averaged. idx = range(trials.shape[-1])[:num_trials] np.random.shuffle(idx) erp[...,i] = np.mean(trials[...,idx], axis=trials.ndim-1) else: erp[...,i] = np.mean(trials, axis=trials.ndim-1) labels = helpers.to_one_of_n(classes).astype(np.bool) ids = np.atleast_2d(classes) cl_lab = [lab for i,lab in enumerate(d.cl_lab) if i in classes] return DataSet(data=erp, labels=labels, ids=ids, cl_lab=cl_lab, default=d)
def wieland_spirals(): ''' Famous non-linear binary 2D problem with intertwined spirals. ''' i = np.arange(97) theta = np.pi * i / 16. r = 6.5 * (104 - i) / 104. data = np.array([r * np.cos(theta), r * np.sin(theta)]) data = np.hstack([data, -data]) labels = to_one_of_n(np.hstack([np.zeros(i.size), np.ones(i.size)])) return DataSet(data, labels)
def gaussian_dataset(ninstances=[50, 50]): ''' Simple Gaussian dataset with a variable number of instances for up to 3 classes. ''' mus = [\ [0, 0], [2, 1], [5, 6]] sigmas = [\ [[1, 2], [2, 5]], [[1, 2], [2, 5]], [[1, -1], [-1, 2]]] assert len(ninstances) <= 3 data, labels = [], [] for (ci, n) in enumerate(ninstances): data.append(np.random.multivariate_normal(mus[ci], sigmas[ci], n).T) labels.extend(np.ones(n, np.int) * ci) return DataSet(np.hstack(data), to_one_of_n(labels))
def gaussian_dataset(ninstances=[50, 50]): ''' Simple Gaussian dataset with a variable number of instances for up to 3 classes. ''' mus = [\ [0, 0], [2, 1], [5, 6]] sigmas = [\ [[1, 2], [2, 5]], [[1, 2], [2, 5]], [[1, -1], [-1, 2]]] assert len(ninstances) <= 3 nclasses = len(ninstances) Xs, Ys = [], [] for (ci, n) in enumerate(ninstances): Xs.append(np.random.multivariate_normal(mus[ci], sigmas[ci], n).T) Ys.extend(np.ones(n) * ci) return DataSet(X=np.hstack(Xs), Y=to_one_of_n(Ys))
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()