[pymvpa] ERNiftiDataset with different event durations

Matthias Ekman Matthias.Ekman at nf.mpg.de
Thu Jul 16 13:32:30 UTC 2009


Hi,


I am currently analysing an event-related design with different event 
durations (4, 6 or 8 sec.). As far as I can see, it is not possible to 
use the ERNiftiDataset for this purpose cause it takes the maximum 
boxlengths for "all" events.

        boxlength = max(durations)
        if __debug__:
            if not max(durations) == min(durations):
                warning('Boxcar mapper will use maximum boxlength (%i) 
of all '
                        'provided Events.'% boxlength)


I was wondering if it would be 'theoretically' possible to modify the 
'BoxcarMapper', so that it could also handle different event durations?

My idea was to modify mvpa/datasets/event.py like this:

        # we need a regular array, so all events must have a common
        # boxlength
#        boxlength = max(durations)
#        if __debug__:
#            if not max(durations) == min(durations):
#                warning('Boxcar mapper will use maximum boxlength (%i) 
of all '
#                        'provided Events.'% boxlength)
       
        # loop over events and extract (different) event durations
        boxlength = [e['duration'] for e in events]    

and  mvpa/mappers/boxcar.py like this:

<snip>

class BoxcarMapper(Mapper):
    """Mapper to combine multiple samples into a single sample.

    .. note::

      This mapper is somewhat unconventional since it doesn't preserve 
number
      of samples (ie the size of 0-th dimension).
    """

    _COLLISION_RESOLUTIONS = ['mean']

    def __init__(self, startpoints, boxlength, offset=0,
                 collision_resolution='mean'):
        """
        :Parameters:
          startpoints: sequence
            Index values along the first axis of 'data'.
          boxlength: int
            The number of elements after 'startpoint' along the first 
axis of
            'data' to be considered for the boxcar.
          offset: int
            The offset between the provided starting point and the 
actual start
            of the boxcar.
          collision_resolution : 'mean'
            if a sample belonged to multiple output samples, then on 
reverse,
            how to resolve the value
        """
        Mapper.__init__(self)

        startpoints = N.asanyarray(startpoints)
        if N.issubdtype(startpoints.dtype, 'i'):
            self.startpoints = startpoints
        else:
            if __debug__:
                debug('MAP', "Boxcar: obtained startpoints are not of 
int type."
                      " Rounding and changing dtype")
            self.startpoints = N.asanyarray(N.round(startpoints), dtype='i')

        # Sanity checks
#        if boxlength < 1:
#            raise ValueError, "Boxlength lower than 1 makes no sense."
#        if boxlength - int(boxlength) != 0:
#            raise ValueError, "boxlength must be an integer value."

        #self.boxlength = int(boxlength)
        boxlength = N.asanyarray(boxlength)
        if N.issubdtype(boxlength.dtype, 'i'):
            self.boxlength = boxlength
        else:
            if __debug__:
                debug('MAP', "Boxcar duration error...")       

        self.offset = offset
        self.__selectors = None

        if not collision_resolution in self._COLLISION_RESOLUTIONS:
            raise ValueError, "Unknown method to resolve the collision." \
                  " Valid are %s" % self._COLLISION_RESOLUTIONS
        self.__collision_resolution = collision_resolution


    __doc__ = enhancedDocString('BoxcarMapper', locals(), Mapper)


#    def __repr__(self):
#        s = super(BoxcarMapper, self).__repr__()
#        return s.replace("(", "(boxlength=%d, offset=%d, startpoints=%s, "
#                         "collision_resolution='%s'" %
#                         (self.boxlength, self.offset, 
str(self.startpoints),
#                          str(self.__collision_resolution)), 1)

    def __repr__(self):
        s = super(BoxcarMapper, self).__repr__()
        return s.replace("(", "(boxlength=%s, offset=%d, startpoints=%s, "
                         "collision_resolution='%s'" %
                         (str(self.boxlength), self.offset, 
str(self.startpoints),
                          str(self.__collision_resolution)), 1)

    def forward(self, data):
        """Project an ND matrix into N+1D matrix

        This method also handles the special of forward mapping a single 
'raw'
        sample. Such a sample is extended (by concatenating clones of 
itself) to
        cover a full boxcar. This functionality is only availably after 
a full
        data array has been forward mapped once.

        :Returns:
          array: (#startpoint, ...)
        """
        # in case the mapper is already charged
        if not self.__selectors is None:
            # if we have a single 'raw' sample (not a boxcar)
            # extend it to cover the full box -- useful if one
            # wants to forward map a mask in raw dataspace (e.g.
            # fMRI ROI or channel map) into an appropriate mask vector
            if data.shape == self._outshape[2:]:
                return N.asarray([data] * self.boxlength)

        self._inshape = data.shape

        startpoints = self.startpoints
        offset = self.offset
        boxlength = self.boxlength

        # check for illegal boxes
#        for sp in self.startpoints:
#            if ( sp + offset + boxlength - 1 > len(data)-1 ) \
#               or ( sp + offset < 0 ):
#                raise ValueError, \
#                      'Illegal box: start: %i, offset: %i, length: %i' \
#                      % (sp, offset, boxlength)

        boxcounter=0
        for sp in self.startpoints:
            if ( sp + offset + boxlength[boxcounter] - 1 > len(data)-1 ) \
               or ( sp + offset < 0 ):
                raise ValueError, \
                      'Illegal box: start: %i, offset: %i, length: %i' \
                      % (sp, offset, boxlength[boxcounter])
            boxcounter+= 1

        # build a list of list where each sublist contains the indexes 
of to be
        # averaged data elements     
#        self.__selectors = [ N.arange(i + offset, i + offset + boxlength) \
#                             for i in startpoints ]
#        selected = N.asarray([ data[ box ] for box in self.__selectors ])
#        self._outshape = selected.shape
#
#        return selected

         # working with fixed boxlength value
#        boxlength_dummy=4
#        self.__selectors = [ N.arange(i + offset, i + offset + 
boxlength_dummy \
#                             for i in startpoints ]

        # build a list of the boxlengths where each boxlength value has 
the same index as the
        # corresponding startpoint                            
        boxlengths = range(len(data))
        boxcounter=0
        for s in startpoints:
            boxlengths[s] = boxlength[boxcounter]
            boxcounter+= 1

        # build a list of list where each sublist contains the indexes 
of to be
        # averaged data elements                                
        self.__selectors = [ N.arange(i + offset, i + offset + 
boxlengths[i]) \
                             for i in startpoints ]
                                       
        selected = N.asarray([ data[ box ] for box in self.__selectors ])
        self._outshape = selected.shape

        return selected
      
    

<snip>

But than the problem occurs that it is not possible to save lists like

[1 2 3 4]
[21 22]
[37 38 39]
[56 57 58 59]
[63 64]
[68 69 70 71]
[75 76]
[86 87 88]
[92 93]
[ 97  98  99 100]
[104 105]
[114 115 116]
[136 137 138 139]
[143 144]

with N.asarray

Now I am reaching the point where I have no idea how to handle this. Do 
you have any idea, or future plans to implement a function which can 
deal with different event durations?


I really appreciate your help.
 Matthias Ekman




More information about the Pkg-ExpPsy-PyMVPA mailing list