1
2
3
4
5
6
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
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
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
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
96 if not self.__selectors is None:
97
98
99
100
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
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
119
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
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
135
136 pass
137 elif data.shape == self._outshape[1:]:
138
139
140
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
148
149 assert(data.shape[0] == len(self.__selectors))
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
159 if self.__collision_resolution == 'mean':
160
161 g1 = output_counts > 1
162
163
164
165 output_ = output[g1].T
166 output_ /= output_counts[g1]
167 output[g1] = output_.T
168
169 return output
170
171
173 """Returns the number of original samples which were combined.
174 """
175
176 return self._inshape[0]
177
179 """Validate if OutId is valid
180
181 """
182 try:
183 return isInVolume(outId, self._outshape[1:])
184 except:
185 return False
186
188 """Validate if InId is valid
189
190 """
191 try:
192 return isInVolume(inId, self._inshape[1:])
193 except:
194 return False
195
196
198 """Returns the number of output samples.
199 """
200
201 return N.prod(self._outshape[1:])
202
203
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