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
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
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
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
99 if not self.__selectors is None:
100
101
102
103
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
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
122
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
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
138
139 pass
140 elif data.shape == self._outshape[1:]:
141
142
143
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
152
153 assert(data.shape[0] == len(self.__selectors))
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
163 if self.__collision_resolution == 'mean':
164
165 g1 = output_counts > 1
166
167
168
169 output_ = output[g1].T
170 output_ /= output_counts[g1]
171 output[g1] = output_.T
172
173 return output
174
175
177 """Returns the number of original samples which were combined.
178 """
179
180 return self._inshape[0]
181
183 """Validate if OutId is valid
184
185 """
186 try:
187 return isInVolume(outId, self._outshape[1:])
188 except:
189 return False
190
192 """Validate if InId is valid
193
194 """
195 try:
196 return isInVolume(inId, self._inshape[1:])
197 except:
198 return False
199
200
202 """Returns the number of output samples.
203 """
204
205 return N.prod(self._outshape[1:])
206
207
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