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

Source Code for Module mvpa.mappers.boxcar

  1  # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 
  2  # vi: set ft=python 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. Got data of shape %s" \ 149 % (self._outshape[1:], self._outshape, data.shape) 150 151 # the rest of this method deals with reconstructing the full input 152 # space from the boxcar samples 153 assert(data.shape[0] == len(self.__selectors)) # am I right? :) 154 155 output = N.zeros(self._inshape, dtype=data.dtype) 156 output_counts = N.zeros((self._inshape[0],), dtype=int) 157 158 for i, selector in enumerate(self.__selectors): 159 output[selector, ...] += data[i, ...] 160 output_counts[selector] += 1 161 162 # scale output 163 if self.__collision_resolution == 'mean': 164 # which samples how multiple sources? 165 g1 = output_counts > 1 166 # average them 167 # doing complicated transposing to be able to process array with 168 # nd > 2 169 output_ = output[g1].T 170 output_ /= output_counts[g1] 171 output[g1] = output_.T 172 173 return output
174 175
176 - def getInSize(self):
177 """Returns the number of original samples which were combined. 178 """ 179 180 return self._inshape[0]
181
182 - def isValidOutId(self, outId):
183 """Validate if OutId is valid 184 185 """ 186 try: 187 return isInVolume(outId, self._outshape[1:]) 188 except: 189 return False
190
191 - def isValidInId(self, inId):
192 """Validate if InId is valid 193 194 """ 195 try: 196 return isInVolume(inId, self._inshape[1:]) 197 except: 198 return False
199 200
201 - def getOutSize(self):
202 """Returns the number of output samples. 203 """ 204 205 return N.prod(self._outshape[1:])
206 207
208 - def selectOut(self, outIds):
209 """Just complain for now""" 210 raise NotImplementedError, \ 211 "For feature selection use MaskMapper on output of the %s mapper" \ 212 % self.__class__.__name__
213