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 if boxlength < 1: 61 raise ValueError, "Boxlength lower than 1 makes no sense." 62 63 self.boxlength = boxlength 64 self.offset = offset 65 self.__selectors = None 66 67 if not collision_resolution in self._COLLISION_RESOLUTIONS: 68 raise ValueError, "Unknown method to resolve the collision." \ 69 " Valid are %s" % self._COLLISION_RESOLUTIONS 70 self.__collision_resolution = collision_resolution
71 72 73 __doc__ = enhancedDocString('BoxcarMapper', locals(), Mapper) 74 75
76 - def __repr__(self):
77 s = super(BoxcarMapper, self).__repr__() 78 return s.replace("(", "(boxlength=%d, offset=%d, startpoints=%s, " 79 "collision_resolution='%s'" % 80 (self.boxlength, self.offset, str(self.startpoints), 81 str(self.__collision_resolution)), 1)
82 83
84 - def forward(self, data):
85 """Project an ND matrix into N+1D matrix 86 87 This method also handles the special of forward mapping a single 'raw' 88 sample. Such a sample is extended (by concatenating clones of itself) to 89 cover a full boxcar. This functionality is only availably after a full 90 data array has been forward mapped once. 91 92 :Returns: 93 array: (#startpoint, ...) 94 """ 95 # in case the mapper is already charged 96 if not self.__selectors is None: 97 # if we have a single 'raw' sample (not a boxcar) 98 # extend it to cover the full box -- useful if one 99 # wants to forward map a mask in raw dataspace (e.g. 100 # fMRI ROI or channel map) into an appropriate mask vector 101 if data.shape == self._outshape[2:]: 102 return N.asarray([data] * self.boxlength) 103 104 self._inshape = data.shape 105 106 startpoints = self.startpoints 107 offset = self.offset 108 boxlength = self.boxlength 109 110 # check for illegal boxes 111 for sp in self.startpoints: 112 if ( sp + offset + boxlength - 1 > len(data)-1 ) \ 113 or ( sp + offset < 0 ): 114 raise ValueError, \ 115 'Illegal box: start: %i, offset: %i, length: %i' \ 116 % (sp, offset, boxlength) 117 118 # build a list of list where each sublist contains the indexes of to be 119 # averaged data elements 120 self.__selectors = [ N.arange(i + offset, i + offset + boxlength) \ 121 for i in startpoints ] 122 selected = N.asarray([ data[ box ] for box in self.__selectors ]) 123 self._outshape = selected.shape 124 125 return selected
126 127
128 - def reverse(self, data):
129 """Uncombine features back into original space. 130 131 Samples which were not touched by forward will get value 0 assigned 132 """ 133 if data.shape == self._outshape: 134 # reconstruct to full input space from the provided data 135 # done below 136 pass 137 elif data.shape == self._outshape[1:]: 138 # single sample was given, simple return it again. 139 # this is done because other mappers also work with 'single' 140 # samples 141 return data 142 else: 143 raise ValueError, "BoxcarMapper operates either on single samples" \ 144 " %s or on the full dataset in 'reverse()' which must have " \ 145 "shape %s" % (`self._outshape[1:]`, `self._outshape`) 146 147 # the rest of this method deals with reconstructing the full input 148 # space from the boxcar samples 149 assert(data.shape[0] == len(self.__selectors)) # am I right? :) 150 151 output = N.zeros(self._inshape, dtype=data.dtype) 152 output_counts = N.zeros((self._inshape[0],), dtype=int) 153 154 for i, selector in enumerate(self.__selectors): 155 output[selector, ...] += data[i, ...] 156 output_counts[selector] += 1 157 158 # scale output 159 if self.__collision_resolution == 'mean': 160 # which samples how multiple sources? 161 g1 = output_counts > 1 162 # average them 163 # doing complicated transposing to be able to process array with 164 # nd > 2 165 output_ = output[g1].T 166 output_ /= output_counts[g1] 167 output[g1] = output_.T 168 169 return output
170 171
172 - def getInSize(self):
173 """Returns the number of original samples which were combined. 174 """ 175 176 return self._inshape[0]
177
178 - def isValidOutId(self, outId):
179 """Validate if OutId is valid 180 181 """ 182 try: 183 return isInVolume(outId, self._outshape[1:]) 184 except: 185 return False
186
187 - def isValidInId(self, inId):
188 """Validate if InId is valid 189 190 """ 191 try: 192 return isInVolume(inId, self._inshape[1:]) 193 except: 194 return False
195 196
197 - def getOutSize(self):
198 """Returns the number of output samples. 199 """ 200 201 return N.prod(self._outshape[1:])
202 203
204 - def selectOut(self, outIds):
205 """Just complain for now""" 206 raise NotImplementedError, \ 207 "For feature selection use MaskMapper on output of the %s mapper" \ 208 % self.__class__.__name__
209