Package mvpa :: Package mappers :: Module boxcar
[hide private]
[frames] | no frames]

Source Code for Module mvpa.mappers.boxcar

  1  #emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- 
  2  #ex: set sts=4 ts=4 sw=4 et: 
  3  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 
  4  # 
  5  #   See COPYING file distributed along with the PyMVPA package for the 
  6  #   copyright and license terms. 
  7  # 
  8  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 
  9  """Data mapper""" 
 10   
 11  __docformat__ = 'restructuredtext' 
 12   
 13  import numpy as N 
 14   
 15  from mvpa.base.dochelpers import enhancedDocString 
 16  from mvpa.mappers.base import Mapper 
 17  from mvpa.misc.support import isInVolume 
 18   
 19  if __debug__: 
 20      from mvpa.base import debug 
 21   
22 -class BoxcarMapper(Mapper):
23 """Mapper to combine multiple samples into a single sample. 24 25 .. note:: 26 27 This mapper is somewhat unconventional since it doesn't preserve number 28 of samples (ie the size of 0-th dimension). 29 """ 30 31 _COLLISION_RESOLUTIONS = ['mean'] 32
33 - def __init__(self, startpoints, boxlength, offset=0, 34 collision_resolution='mean'):
35 """ 36 :Parameters: 37 startpoints: sequence 38 Index values along the first axis of 'data'. 39 boxlength: int 40 The number of elements after 'startpoint' along the first axis of 41 'data' to be considered for the boxcar. 42 offset: int 43 The offset between the provided starting point and the actual start 44 of the boxcar. 45 collision_resolution : 'mean' 46 if a sample belonged to multiple output samples, then on reverse, 47 how to resolve the value 48 """ 49 Mapper.__init__(self) 50 51 startpoints = N.asanyarray(startpoints) 52 if N.issubdtype(startpoints.dtype, 'i'): 53 self.startpoints = startpoints 54 else: 55 if __debug__: 56 debug('MAP', "Boxcar: obtained startpoints are not of int type." 57 " Rounding and changing dtype") 58 self.startpoints = N.asanyarray(N.round(startpoints), dtype='i') 59 60 # Sanity checks 61 if boxlength < 1: 62 raise ValueError, "Boxlength lower than 1 makes no sense." 63 if boxlength - int(boxlength) != 0: 64 raise ValueError, "boxlength must be an integer value." 65 66 self.boxlength = int(boxlength) 67 self.offset = offset 68 self.__selectors = None 69 70 if not collision_resolution in self._COLLISION_RESOLUTIONS: 71 raise ValueError, "Unknown method to resolve the collision." \ 72 " Valid are %s" % self._COLLISION_RESOLUTIONS 73 self.__collision_resolution = collision_resolution
74 75 76 __doc__ = enhancedDocString('BoxcarMapper', locals(), Mapper) 77 78
79 - def __repr__(self):
80 s = super(BoxcarMapper, self).__repr__() 81 return s.replace("(", "(boxlength=%d, offset=%d, startpoints=%s, " 82 "collision_resolution='%s'" % 83 (self.boxlength, self.offset, str(self.startpoints), 84 str(self.__collision_resolution)), 1)
85 86
87 - def forward(self, data):
88 """Project an ND matrix into N+1D matrix 89 90 This method also handles the special of forward mapping a single 'raw' 91 sample. Such a sample is extended (by concatenating clones of itself) to 92 cover a full boxcar. This functionality is only availably after a full 93 data array has been forward mapped once. 94 95 :Returns: 96 array: (#startpoint, ...) 97 """ 98 # in case the mapper is already charged 99 if not self.__selectors is None: 100 # if we have a single 'raw' sample (not a boxcar) 101 # extend it to cover the full box -- useful if one 102 # wants to forward map a mask in raw dataspace (e.g. 103 # fMRI ROI or channel map) into an appropriate mask vector 104 if data.shape == self._outshape[2:]: 105 return N.asarray([data] * self.boxlength) 106 107 self._inshape = data.shape 108 109 startpoints = self.startpoints 110 offset = self.offset 111 boxlength = self.boxlength 112 113 # check for illegal boxes 114 for sp in self.startpoints: 115 if ( sp + offset + boxlength - 1 > len(data)-1 ) \ 116 or ( sp + offset < 0 ): 117 raise ValueError, \ 118 'Illegal box: start: %i, offset: %i, length: %i' \ 119 % (sp, offset, boxlength) 120 121 # build a list of list where each sublist contains the indexes of to be 122 # averaged data elements 123 self.__selectors = [ N.arange(i + offset, i + offset + boxlength) \ 124 for i in startpoints ] 125 selected = N.asarray([ data[ box ] for box in self.__selectors ]) 126 self._outshape = selected.shape 127 128 return selected
129 130
131 - def reverse(self, data):
132 """Uncombine features back into original space. 133 134 Samples which were not touched by forward will get value 0 assigned 135 """ 136 if data.shape == self._outshape: 137 # reconstruct to full input space from the provided data 138 # done below 139 pass 140 elif data.shape == self._outshape[1:]: 141 # single sample was given, simple return it again. 142 # this is done because other mappers also work with 'single' 143 # samples 144 return data 145 else: 146 raise ValueError, "BoxcarMapper operates either on single samples" \ 147 " %s or on the full dataset in 'reverse()' which must have " \ 148 "shape %s" % (`self._outshape[1:]`, `self._outshape`) 149 150 # the rest of this method deals with reconstructing the full input 151 # space from the boxcar samples 152 assert(data.shape[0] == len(self.__selectors)) # am I right? :) 153 154 output = N.zeros(self._inshape, dtype=data.dtype) 155 output_counts = N.zeros((self._inshape[0],), dtype=int) 156 157 for i, selector in enumerate(self.__selectors): 158 output[selector, ...] += data[i, ...] 159 output_counts[selector] += 1 160 161 # scale output 162 if self.__collision_resolution == 'mean': 163 # which samples how multiple sources? 164 g1 = output_counts > 1 165 # average them 166 # doing complicated transposing to be able to process array with 167 # nd > 2 168 output_ = output[g1].T 169 output_ /= output_counts[g1] 170 output[g1] = output_.T 171 172 return output
173 174
175 - def getInSize(self):
176 """Returns the number of original samples which were combined. 177 """ 178 179 return self._inshape[0]
180
181 - def isValidOutId(self, outId):
182 """Validate if OutId is valid 183 184 """ 185 try: 186 return isInVolume(outId, self._outshape[1:]) 187 except: 188 return False
189
190 - def isValidInId(self, inId):
191 """Validate if InId is valid 192 193 """ 194 try: 195 return isInVolume(inId, self._inshape[1:]) 196 except: 197 return False
198 199
200 - def getOutSize(self):
201 """Returns the number of output samples. 202 """ 203 204 return N.prod(self._outshape[1:])
205 206
207 - def selectOut(self, outIds):
208 """Just complain for now""" 209 raise NotImplementedError, \ 210 "For feature selection use MaskMapper on output of the %s mapper" \ 211 % self.__class__.__name__
212