def testEventDataset(self): # baisc checks self.failUnlessRaises(DatasetError, EventDataset) # simple data samples = N.arange(240).reshape(10, 2, 3, 4) # copy constructor does not work on non-2D data self.failUnlessRaises(DatasetError, EventDataset, samples=samples) # try case without extra features evs = [Event(onset=2, duration=2, label=1, chunk=2), Event(onset=5, duration=1, label=2, chunk=2), Event(onset=7, duration=2, label=3, chunk=4)] ds = EventDataset(samples=samples, events=evs) self.failUnless(ds.nfeatures == 48) self.failUnless(ds.nsamples == 3) self.failUnless((ds.labels == [1,2,3]).all()) self.failUnless((ds.chunks == [2,2,4]).all()) mr = ds.mapReverse(N.arange(48)) self.failUnless((mr == N.arange(48).reshape(2,2,3,4)).all()) # try case with extra features evs = [Event(onset=2, duration=2, label=1, features=[2,3]), Event(onset=5, duration=2, label=1, features=[4,5]), Event(onset=7, duration=2, label=1, features=[6,7]),] ds = EventDataset(samples=samples, events=evs) # we have 2 additional features self.failUnless(ds.nfeatures == 50) self.failUnless(ds.nsamples == 3) self.failUnless((ds.labels == [1,1,1]).all()) self.failUnless((ds.chunks == [0,1,2]).all()) # now for the long awaited -- map back into two distinct # feature spaces mr = ds.mapReverse(N.arange(50)) # we get two sets of feature spaces (samples and extra features) self.failUnless(len(mr) == 2) msamples, mxfeat = mr # the sample side should be identical to the case without extra features self.failUnless((msamples == N.arange(48).reshape(2,2,3,4)).all()) # the extra features should be flat self.failUnless((mxfeat == (48,49)).all()) # now take a look at orig = ds.O self.failUnless(len(mr) == 2) osamples, oextra = orig self.failUnless((oextra == [[2,3],[4,5],[6,7]]).all()) self.failUnless(osamples.shape == samples.shape) # check that all samples not covered by an event are zero filt = N.array([True,True,False,False,True,False,False,False,False,True]) self.failUnless(N.sum(osamples[filt]) == 0) self.failUnless((osamples[N.negative(filt)] > 0).all())
def __init__(self, samples=None, events=None, mask=None, evconv=False, storeoffset=False, tr=None, enforce_dim=4, **kwargs): """ :Paramaters: mask: str | NiftiImage | ndarray Filename of a NIfTI image or a `NiftiImage` instance or an ndarray of appropriate shape. evconv: bool Convert event definitions using `onset` and `duration` in some temporal unit into #sample notation. storeoffset: Bool Whether to store temproal offset information when converting Events into descrete time. Only considered when evconv == True. tr: float Temporal distance of two adjacent NIfTI volumes. This can be used to override the corresponding value in the NIfTI header. enforce_dim : int or None If not None, it is the dimensionality of the data to be enforced, commonly 4D for the data, and 3D for the mask in case of fMRI. """ # check if we are in copy constructor mode if events is None: EventDataset.__init__(self, samples=samples, events=events, mask=mask, **kwargs) return nifti = getNiftiFromAnySource(samples, ensure=True, enforce_dim=enforce_dim) # no copying samples = nifti.data # do not put the whole NiftiImage in the dict as this will most # likely be deepcopy'ed at some point and ensuring data integrity # of the complex Python-C-Swig hybrid might be a tricky task. # Only storing the header dict should achieve the same and is more # memory efficient and even simpler dsattr = {'niftihdr': nifti.header} # determine TR, take from NIfTI header by default dt = nifti.rtime # override if necessary if not tr is None: dt = tr # NiftiDataset uses a DescreteMetric with cartesian # distance and element size from the NIfTI header # 'voxdim' is (x,y,z) while 'samples' are (t,z,y,x) elementsize = [dt] + [i for i in reversed(nifti.voxdim)] # XXX metric might be inappropriate if boxcar has length 1 # might move metric setup after baseclass init and check what has # really happened metric = DescreteMetric(elementsize=elementsize, distance_function=cartesianDistance) # convert EVs if necessary -- not altering original if evconv: if dt == 0: raise ValueError, "'dt' cannot be zero when converting Events" events = [ev.asDescreteTime(dt, storeoffset) for ev in events] else: # do not touch the original events = deepcopy(events) # forcefully convert onset and duration into integers, as expected # by the baseclass for ev in events: oldonset = ev['onset'] oldduration = ev['duration'] ev['onset'] = int(ev['onset']) ev['duration'] = int(ev['duration']) if not oldonset == ev['onset'] \ or not oldduration == ev['duration']: warning("Loosing information during automatic integer " "conversion of EVs. Consider an explicit conversion" " by setting `evconv` in ERNiftiDataset().") # pull mask array from NIfTI (if present) if mask is None: pass elif isinstance(mask, N.ndarray): # plain array can be passed on to base class pass else: mask_nim = getNiftiFromAnySource(mask) if not mask_nim is None: mask = getNiftiData(mask_nim) else: raise ValueError, "Cannot load mask from '%s'" % mask # finally init baseclass EventDataset.__init__(self, samples=samples, events=events, mask=mask, dametric=metric, dsattr=dsattr, **kwargs)