Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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))
Ejemplo n.º 4
0
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))
Ejemplo n.º 5
0
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()