25
25
%
26
26
% Phase-encoding analysis tool.
27
27
%
28
- % pea = PEA(params ) creates an instance of the PEA class.
29
- % params is a structure with 7 required fields
28
+ % pea = PEA(parameters ) creates an instance of the PEA class.
29
+ % parameters is a structure with 7 required fields
30
30
% - f_sampling: sampling frequency (1/TR)
31
31
% - f_stim : stimulation frequency
32
32
% - n_samples : number of samples (volumes)
46
46
% function (e.g. help PEA.fitting)
47
47
%
48
48
% typical workflow:
49
- % 1. pea = PEA(params );
49
+ % 1. pea = PEA(parameters );
50
50
% 2. pea.set_delay(delay);
51
51
% 3. pea.set_direction(direction);
52
52
% 4. results = pea.fitting(data);
74
74
75
75
methods (Access = public )
76
76
77
- function self = PEA(params )
77
+ function self = PEA(parameters )
78
78
% constructor
79
79
self.is = ' PEA tool' ;
80
80
81
- self.f_sampling = params .f_sampling ;
82
- self.f_stim = params .f_stim ;
81
+ self.f_sampling = parameters .f_sampling ;
82
+ self.f_stim = parameters .f_stim ;
83
83
self.p_sampling = 1 / self .f_sampling ;
84
- self.n_samples = params .n_samples ;
85
- self.n_rows = params .n_rows ;
86
- self.n_cols = params .n_cols ;
87
- self.n_slices = params .n_slices ;
84
+ self.n_samples = parameters .n_samples ;
85
+ self.n_rows = parameters .n_rows ;
86
+ self.n_cols = parameters .n_cols ;
87
+ self.n_slices = parameters .n_slices ;
88
88
self.n_total = self .n_rows * self .n_cols * self .n_slices ;
89
89
self.t = (0 : self .p_sampling : self .p_sampling * self .n_samples - 1 );
90
90
self.delay = 0 ;
@@ -132,7 +132,7 @@ function set_direction(self,direction)
132
132
end
133
133
end
134
134
135
- function results = fitting(self ,data )
135
+ function results = fitting(self ,data , varargin )
136
136
% identifies phase and amplitude at stimulation frequency for
137
137
% each voxel and returns a structure with the following fields
138
138
% - Phase
@@ -146,50 +146,77 @@ function set_direction(self,direction)
146
146
% required inputs are
147
147
% - data : a matrix of empirically observed BOLD timecourses
148
148
% whose columns correspond to time (volumes).
149
+ %
150
+ % optional inputs are
151
+ % - threshold: minimum voxel intensity (default = 100.0)
152
+ % - mask : binary mask for selecting voxels
149
153
150
154
text = ' performing phase encoding analysis...' ;
151
155
fprintf(' %s\n ' ,text )
152
156
wb = waitbar(0 ,text ,' Name' ,self .is );
153
157
158
+ p = inputParser ;
159
+ addRequired(p ,' data' ,@isnumeric );
160
+ addOptional(p ,' threshold' ,100 );
161
+ addOptional(p ,' mask' ,[]);
162
+ p .parse(data ,varargin{: });
163
+
164
+ data = single(p .Results .data );
165
+ threshold = p .Results .threshold ;
166
+ mask = p .Results .mask ;
167
+
154
168
F = exp(self .direction * 2i * pi * self .f_stim *(self .t - self .delay ));
155
169
X = zscore([real(F )' ,imag(F )' ]);
156
- XX = (X ' *X )\X ' ;
157
- data = zscore(reshape(single(data(1 : self .n_samples ,: ,: ,: )),...
158
- self .n_samples ,self .n_total ));
159
- std_signal = std(data );
170
+
171
+ data = reshape(single(data(1 : self .n_samples ,: ,: ,: )),...
172
+ self .n_samples ,self .n_total );
173
+
174
+ mean_signal = mean(data );
175
+
176
+ if isempty(mask )
177
+ mask = mean_signal >= threshold ;
178
+ end
179
+ mask = mask(: );
180
+ voxel_index = find(mask );
181
+ n_voxels = numel(voxel_index );
182
+
183
+ data = zscore(data(: , mask ));
184
+
185
+ beta = (X ' * X ) \ X ' * data ;
186
+
187
+ Y_ = X * beta ;
188
+ residuals = data - Y_ ;
189
+
160
190
results.Phase = zeros(self .n_total ,1 );
161
191
results.Amplitude = zeros(self .n_total ,1 );
162
192
results.F_stat = zeros(self .n_total ,1 );
163
193
results.P_value = ones(self .n_total ,1 );
164
194
165
195
df1 = 1 ;
166
196
df2 = self .n_samples - 2 ;
167
- for v= 1 : self .n_total
168
- if std_signal > 0
169
- b = XX * data(: ,v );
170
- y = X * b ;
171
-
172
- % estimate and correct for autocorrelation
173
- r = data(: ,v ) - y ;
174
- T = [[0 ;r(1 : end - 1 )],[zeros(2 ,1 );r(1 : end - 2 )]];
175
- W = [1 ; -((T ' * T ) \ T ' * r )];
176
- Xc(: ,1 ) = [X(: ,1 ),[0 ;X(1 : end - 1 ,1 )],[zeros(2 ,1 );X(1 : end - 2 ,1 )]] * W ;
177
- Xc(: ,2 ) = [X(: ,2 ),[0 ;X(1 : end - 1 ,2 )],[zeros(2 ,1 );X(1 : end - 2 ,2 )]] * W ;
178
- Dc = [data(: ,v ),[0 ;data(1 : end - 1 ,v )],[zeros(2 ,1 );data(1 : end - 2 ,v )]] * W ;
179
- % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180
-
181
- b = (Xc ' * Xc ) \ Xc ' * Dc ;
182
- y = Xc * b ;
183
- mu = mean(DC );
184
- MSM = (y - mu )' *(y - mu )/df1 ;
185
- MSE = (y - Dc )' *(y - Dc )/df2 ;
186
-
187
- results .Phase(v ) = angle(b(1 )+b(2 )*1i );
188
- results .Amplitude(v ) = abs(b(1 )+b(2 )*1i );
189
- results .F_stat(v ) = MSM / MSE ;
190
- results .P_value(v ) = max(1 - fcdf(MSM / MSE ,df1 ,df2 ),1e- 20 );
191
- end
192
- waitbar(v / self .n_total ,wb )
197
+ for m= 1 : n_voxels
198
+ v = voxel_index(m );
199
+
200
+ % estimate and correct for autocorrelation
201
+ T = [[0 ;residuals(1 : end - 1 ,m )],[zeros(2 ,1 ); residuals(1 : end - 2 ,m )]];
202
+ W = [1 ; -((T ' * T ) \ T ' * residuals(: ,m ))];
203
+ Xc(: ,1 ) = [X(: ,1 ), [0 ;X(1 : end - 1 ,1 )], [zeros(2 ,1 );X(1 : end - 2 ,1 )]] * W ;
204
+ Xc(: ,2 ) = [X(: ,2 ), [0 ;X(1 : end - 1 ,2 )], [zeros(2 ,1 );X(1 : end - 2 ,2 )]] * W ;
205
+ Dc = [data(: ,m ), [0 ;data(1 : end - 1 ,m )], [zeros(2 ,1 );data(1 : end - 2 ,m )]] * W ;
206
+ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207
+
208
+ b = (Xc ' * Xc ) \ Xc ' * Dc ;
209
+ y = Xc * b ;
210
+ mu = mean(DC );
211
+ MSM = (y - mu )' *(y - mu )/df1 ;
212
+ MSE = (y - Dc )' *(y - Dc )/df2 ;
213
+
214
+ results .Phase(v ) = angle(b(1 )+b(2 )*1i );
215
+ results .Amplitude(v ) = abs(b(1 )+b(2 )*1i );
216
+ results .F_stat(v ) = MSM / MSE ;
217
+ results .P_value(v ) = max(1 - fcdf(MSM / MSE ,df1 ,df2 ),1e- 20 );
218
+
219
+ waitbar(m / n_voxels ,wb )
193
220
end
194
221
195
222
results.Phase = reshape(results .Phase ,...
0 commit comments