1
2
3
4
5
6
7
8
9 """Misc function performing operations on datasets which are based on scipy
10 """
11
12 __docformat__ = 'restructuredtext'
13
14 from mvpa.base import externals
15
16 import numpy as N
17 np = N
18
19 from operator import isSequenceType
20
21 from mvpa.datasets.base import Dataset, datasetmethod
22 from mvpa.misc.support import getBreakPoints
23
24 if externals.exists('scipy', raiseException=True):
25 from scipy import signal
26 from scipy.linalg import lstsq
27 from scipy.special import legendre
30
31
32 leg = legendre(n)
33 r = leg(x)
34 infs = np.isinf(r)
35 if np.any(infs):
36 r[infs] = leg(x[infs] + 1e-10)
37 return r
38
39 @datasetmethod
40 -def detrend(dataset, perchunk=False, model='linear',
41 polyord=None, opt_reg=None):
42 """
43 Given a dataset, detrend the data inplace either entirely
44 or per each chunk
45
46 :Parameters:
47 dataset : Dataset
48 dataset to operate on
49 perchunk : bool
50 either to operate on whole dataset at once or on each chunk
51 separately
52 model
53 Type of detrending model to run. If 'linear' or 'constant',
54 scipy.signal.detrend is used to perform a linear or demeaning
55 detrend. Polynomial detrending is activated when 'regress' is
56 used or when polyord or opt_reg are specified.
57 polyord : int or list
58 Order of the Legendre polynomial to remove from the data. This
59 will remove every polynomial up to and including the provided
60 value. For example, 3 will remove 0th, 1st, 2nd, and 3rd order
61 polynomials from the data. N.B.: The 0th polynomial is the
62 baseline shift, the 1st is the linear trend.
63 If you specify a single int and perchunk is True, then this value
64 is used for each chunk. You can also specify a different polyord
65 value for each chunk by providing a list or ndarray of polyord
66 values the length of the number of chunks.
67 opt_reg : ndarray
68 Optional ndarray of additional information to regress out from the
69 dataset. One example would be to regress out motion parameters.
70 As with the data, time is on the first axis.
71
72 """
73 if polyord is not None or opt_reg is not None:
74 model='regress'
75
76 if model in ['linear', 'constant']:
77
78 bp = 0
79
80 if perchunk:
81 try:
82 bp = getBreakPoints(dataset.chunks)
83 except ValueError, e:
84 raise ValueError, \
85 "Failed to assess break points between chunks. Often " \
86 "that is due to discontinuities within a chunk, which " \
87 "ruins idea of per-chunk detrending. Original " \
88 "exception was: %s" % str(e)
89
90 dataset.samples[:] = signal.detrend(dataset.samples, axis=0,
91 type=model, bp=bp)
92 elif model in ['regress']:
93
94 return __detrend_regress(dataset, perchunk=perchunk,
95 polyord=polyord, opt_reg=opt_reg)
96 else:
97
98 raise ValueError('Specified model type (%s) is unknown.'
99 % (model))
100
104 """
105 Given a dataset, perform a detrend inplace, regressing out polynomial
106 terms as well as optional regressors, such as motion parameters.
107
108 :Parameters:
109 dataset : Dataset
110 Dataset to operate on
111 perchunk : bool
112 Either to operate on whole dataset at once or on each chunk
113 separately. If perchunk is True, all the samples within a
114 chunk should be contiguous and the chunks should be sorted in
115 order from low to high.
116 polyord : int
117 Order of the Legendre polynomial to remove from the data. This
118 will remove every polynomial up to and including the provided
119 value. For example, 3 will remove 0th, 1st, 2nd, and 3rd order
120 polynomials from the data. N.B.: The 0th polynomial is the
121 baseline shift, the 1st is the linear trend.
122 If you specify a single int and perchunk is True, then this value
123 is used for each chunk. You can also specify a different polyord
124 value for each chunk by providing a list or ndarray of polyord
125 values the length of the number of chunks.
126 opt_reg : ndarray
127 Optional ndarray of additional information to regress out from the
128 dataset. One example would be to regress out motion parameters.
129 As with the data, time is on the first axis.
130 """
131
132
133 if not polyord is None:
134 if not isSequenceType(polyord):
135
136 polyord = [polyord]*len(dataset.uniquechunks)
137 elif perchunk and len(polyord) != len(dataset.uniquechunks):
138 raise ValueError("If you specify a sequence of polyord values " + \
139 "they sequence length must match the " + \
140 "number of unique chunks in the dataset.")
141
142
143 if perchunk:
144
145 uchunks = dataset.uniquechunks
146
147
148 reg = []
149 for n, chunk in enumerate(uchunks):
150
151 cinds = dataset.chunks == chunk
152
153
154 if not polyord is None:
155
156 x = N.linspace(-1, 1, cinds.sum())
157
158 for n in range(polyord[n] + 1):
159 newreg = N.zeros((dataset.nsamples, 1))
160 newreg[cinds, 0] = legendre_(n, x)
161 reg.append(newreg)
162 else:
163
164 reg = []
165
166 if not polyord is None:
167
168 x = N.linspace(-1, 1, dataset.nsamples)
169 for n in range(polyord[0] + 1):
170 reg.append(legendre_(n, x)[:, N.newaxis])
171
172
173 if not opt_reg is None:
174
175 reg.append(opt_reg)
176
177
178 if len(reg) > 0:
179 if len(reg) > 1:
180 regs = N.hstack(reg)
181 else:
182 regs = reg[0]
183 else:
184
185 raise ValueError("You must specify at least one " + \
186 "regressor to regress out.")
187
188
189 res = lstsq(regs, dataset.samples)
190
191
192 yhat = N.dot(regs, res[0])
193 dataset.samples -= yhat
194
195
196 return res, regs
197