spm_graph.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. function [Y,y,beta,Bcov,G] = spm_graph(SPM,XYZ,xG)
  2. % Return adjusted data for a given voxel location
  3. % FORMAT [Y,y,beta,Bcov,G] = spm_graph(SPM,XYZ,xG)
  4. %
  5. % SPM - structure containing generic details about the analysis
  6. % XYZ - [x y z]' coordinates {voxel}
  7. % xG - structure containing details about action to perform
  8. % .def - string describing data type to be returned. One of:
  9. % 'Contrast estimates and 90% C.I.'
  10. % 'Fitted responses'
  11. % 'Event-related responses'
  12. % 'Parametric responses'
  13. % 'Volterra Kernels'
  14. % .spec - structure containing specific details about returned data
  15. %
  16. % Y - fitted data for the selected voxel
  17. % y - adjusted data for the selected voxel
  18. % beta - parameter estimates (ML or MAP)
  19. % Bcov - covariance of parameter estimates (ML or conditional)
  20. % G - structure containing further data depending on xG details
  21. %
  22. % See spm_graph_ui for details.
  23. %__________________________________________________________________________
  24. % Copyright (C) 1996-2016 Wellcome Trust Centre for Neuroimaging
  25. % Karl Friston
  26. % $Id: spm_graph.m 6985 2017-01-11 11:51:49Z guillaume $
  27. if nargin == 3 && isstruct(SPM) && isstruct(XYZ) && ishandle(xG)
  28. warning('Syntax of spm_graph changed: call spm_graph_ui instead.');
  29. [Y,y,beta,Bcov] = spm_graph_ui(SPM,XYZ,xG); G = [];
  30. return
  31. end
  32. if nargin < 3, [xG,G] = deal(struct([])); end
  33. %==========================================================================
  34. %-Extract filtered and whitened data from files
  35. %==========================================================================
  36. Y = [];
  37. y = [];
  38. if ismember(xG.def,{'Fitted responses','Event-related responses'})
  39. try
  40. y = spm_data_read(SPM.xY.VY,'xyz',XYZ);
  41. catch
  42. try
  43. % remap files in SPM.xY.P if SPM.xY.VY is no longer valid
  44. %--------------------------------------------------------------
  45. SPM.xY.VY = spm_data_hdr_read(SPM.xY.P);
  46. y = spm_data_read(SPM.xY.VY,'xyz',XYZ);
  47. catch
  48. % data has been moved or renamed
  49. %--------------------------------------------------------------
  50. choice = questdlg({'Original data have been moved or renamed',...
  51. 'How to proceed next?'},...
  52. [mfilename ': data files missing...'],...
  53. 'Specify','Search','Ignore','Ignore');
  54. switch choice
  55. case 'Specify'
  56. [SPM.xY.P,sts] = ...
  57. spm_select(numel(SPM.xY.VY),'image','Select images');
  58. if ~sts
  59. [Y,y,beta,Bcov] = deal([]);
  60. spm('Pointer','Arrow');
  61. return;
  62. end
  63. SPM.xY.VY = spm_data_hdr_read(SPM.xY.P);
  64. for i = 1:numel(SPM.xY.VY)
  65. SPM.xY.VY(i).pinfo(1:2,:) = ...
  66. SPM.xY.VY(i).pinfo(1:2,:)*SPM.xGX.gSF(i);
  67. end
  68. y = spm_data_read(SPM.xY.VY,'xyz',XYZ);
  69. case 'Search'
  70. SPM.xY.VY = spm_check_filename(SPM.xY.VY);
  71. y = spm_data_read(SPM.xY.VY,'xyz',XYZ);
  72. otherwise
  73. y = [];
  74. end
  75. end
  76. end
  77. end
  78. if ~isempty(y), y = spm_filter(SPM.xX.K,SPM.xX.W*y); end
  79. %-Compute residuals
  80. %--------------------------------------------------------------------------
  81. if isempty(y)
  82. % make R = NaN so it will not be plotted
  83. %----------------------------------------------------------------------
  84. R = NaN(size(SPM.xX.X,1),1);
  85. else
  86. % residuals (non-whitened)
  87. %----------------------------------------------------------------------
  88. R = spm_sp('r',SPM.xX.xKXs,y);
  89. end
  90. %==========================================================================
  91. %-Get parameter and hyperparameter estimates
  92. %==========================================================================
  93. if ~isfield(SPM,'VCbeta') % xSPM.STAT ~= 'P'
  94. %-Parameter estimates: beta = xX.pKX*xX.K*y;
  95. %-Residual mean square: ResMS = sum(R.^2)/xX.trRV
  96. %----------------------------------------------------------------------
  97. beta = spm_data_read(SPM.Vbeta,'xyz',XYZ);
  98. ResMS = spm_data_read(SPM.VResMS,'xyz',XYZ);
  99. Bcov = ResMS*SPM.xX.Bcov;
  100. else
  101. % or conditional estimates with
  102. % Cov(b|y) through Taylor approximation
  103. %----------------------------------------------------------------------
  104. beta = spm_data_read(SPM.VCbeta, 'xyz', XYZ);
  105. if isfield(SPM.PPM,'VB')
  106. % Get approximate posterior covariance at ic
  107. % using Taylor-series approximation
  108. % Get posterior SD beta's
  109. Nk = size(SPM.xX.X,2);
  110. for k=1:Nk
  111. sd_beta(k,:) = spm_data_read(SPM.VPsd(k),'xyz',XYZ);
  112. end
  113. % Get AR coefficients
  114. nsess = length(SPM.Sess);
  115. for ss=1:nsess
  116. for p=1:SPM.PPM.AR_P
  117. Sess(ss).a(p,:) = spm_data_read(SPM.PPM.Sess(ss).VAR(p),'xyz',XYZ);
  118. end
  119. % Get noise SD
  120. Sess(ss).lambda = spm_data_read(SPM.PPM.Sess(ss).VHp,'xyz',XYZ);
  121. end
  122. % Which block are we in ?
  123. % this needs updating s.t xSPM contains labels of selected voxels
  124. v = find((SPM.xVol.XYZ(1,:)==XYZ(1))&(SPM.xVol.XYZ(2,:)==XYZ(2))&(SPM.xVol.XYZ(3,:)==XYZ(3)));
  125. block_index = SPM.xVol.labels(v);
  126. Bcov = zeros(Nk,Nk);
  127. for ss=1:nsess
  128. % Reconstuct approximation to voxel wise correlation matrix
  129. post_R = SPM.PPM.Sess(ss).block(block_index).mean.R;
  130. if SPM.PPM.AR_P > 0
  131. dh = Sess(ss).a(:,1)'-SPM.PPM.Sess(ss).block(block_index).mean.a;
  132. else
  133. dh = [];
  134. end
  135. dh = [dh Sess(ss).lambda(1)-SPM.PPM.Sess(ss).block(block_index).mean.lambda];
  136. for i=1:length(dh)
  137. post_R = post_R + SPM.PPM.Sess(ss).block(block_index).mean.dR(:,:,i)*dh(i);
  138. end
  139. % Get indexes of regressors specific to this session
  140. scol = SPM.Sess(ss).col;
  141. mean_col_index = SPM.Sess(nsess).col(end)+ss;
  142. scol = [scol mean_col_index];
  143. % Reconstuct approximation to voxel wise covariance matrix
  144. Bcov(scol,scol) = Bcov(scol,scol) + (sd_beta(scol,1)*sd_beta(scol,1)').*post_R;
  145. end
  146. else
  147. Bcov = SPM.PPM.Cby;
  148. for j = 1:length(SPM.PPM.l)
  149. l = spm_data_read(SPM.VHp(j),'xyz',XYZ);
  150. Bcov = Bcov + SPM.PPM.dC{j}*(l - SPM.PPM.l(j));
  151. end
  152. end
  153. end
  154. %-Return if plot hasn't been defined
  155. %--------------------------------------------------------------------------
  156. if isempty(xG) || ~isfield(xG,'def') || isempty(xG.def)
  157. return;
  158. end
  159. %==========================================================================
  160. %-Compute estimates
  161. %==========================================================================
  162. CI = 1.6449; % = spm_invNcdf(1 - 0.05);
  163. switch xG.def
  164. %-Parameter estimates
  165. %======================================================================
  166. case 'Contrast estimates and 90% C.I.'
  167. Ic = xG.spec.Ic; % contrast index
  168. if numel(Ic) == 1
  169. c = SPM.xCon(Ic).c; % contrast weights
  170. else
  171. c = Ic';
  172. end
  173. % compute contrast of parameter estimates and 90% C.I.
  174. %------------------------------------------------------------------
  175. cbeta = c'*beta;
  176. SE = sqrt(diag(c'*Bcov*c));
  177. CI = CI*SE;
  178. % returned values
  179. %------------------------------------------------------------------
  180. G.contrast = cbeta;
  181. G.standarderror = SE;
  182. G.interval = 2*CI;
  183. %-All fitted effects or selected effects
  184. %======================================================================
  185. case 'Fitted responses'
  186. Ic = xG.spec.Ic; % contrast index
  187. % predicted or adjusted response
  188. %------------------------------------------------------------------
  189. if xG.spec.predicted
  190. % fitted (predicted) data (Y = X1*beta)
  191. %--------------------------------------------------------------
  192. % this should be SPM.xX.xKXs.X instead of SPM.xX.X below
  193. Y = SPM.xX.X*SPM.xCon(Ic).c*pinv(SPM.xCon(Ic).c)*beta;
  194. else
  195. % fitted (corrected) data (Y = X1o*beta)
  196. %--------------------------------------------------------------
  197. Y = spm_FcUtil('Yc',SPM.xCon(Ic),SPM.xX.xKXs,beta);
  198. end
  199. % adjusted data
  200. %------------------------------------------------------------------
  201. y = Y + R;
  202. % ordinate
  203. %------------------------------------------------------------------
  204. switch char(fieldnames(xG.spec.x))
  205. case 'i' % an explanatory variable
  206. i = xG.spec.x.i;
  207. x = SPM.xX.xKXs.X(:,i);
  208. case 'scan' % scan or time
  209. if isfield(SPM.xY,'RT')
  210. x = SPM.xY.RT*[1:size(Y,1)]';
  211. else
  212. x = [1:size(Y,1)]';
  213. end
  214. case 'x' % user specified
  215. x = xG.spec.x.x;
  216. end
  217. % returned values
  218. %------------------------------------------------------------------
  219. G.x = x;
  220. %-Modeling evoked responses based on Sess
  221. %======================================================================
  222. case 'Event-related responses'
  223. dt = SPM.xBF.dt;
  224. s = xG.spec.Sess;
  225. u = xG.spec.u;
  226. % event-related response
  227. %------------------------------------------------------------------
  228. if isempty(y)
  229. warning(['Data not available. ' ...
  230. 'Plotting fitted response and 90% C.I. instead.']);
  231. xG.spec.Rplot = 'fitted response and 90% C.I.';
  232. end
  233. switch xG.spec.Rplot
  234. case 'fitted response and PSTH'
  235. % build a simple FIR model subpartition (X); bin size = TR
  236. %----------------------------------------------------------
  237. BIN = SPM.xY.RT;
  238. %BIN = max(2,BIN);
  239. xBF = SPM.xBF;
  240. U = SPM.Sess(s).U(u);
  241. U.u = U.u(:,1);
  242. xBF.name = 'Finite Impulse Response';
  243. xBF.order = round(32/BIN);
  244. xBF.length = xBF.order*BIN;
  245. xBF = spm_get_bf(xBF);
  246. BIN = xBF.length/xBF.order;
  247. X = spm_Volterra(U,xBF.bf,1);
  248. k = SPM.nscan(s);
  249. X = X([0:(k - 1)]*SPM.xBF.T + SPM.xBF.T0 + 32,:);
  250. % place X in SPM.xX.X
  251. %----------------------------------------------------------
  252. jX = SPM.Sess(s).row;
  253. iX = SPM.Sess(s).col(SPM.Sess(s).Fc(u).i);
  254. iX0 = [1:size(SPM.xX.X,2)];
  255. iX0(iX) = [];
  256. X = [X SPM.xX.X(jX,iX0)];
  257. X = SPM.xX.W(jX,jX)*X;
  258. X = [X SPM.xX.K(s).X0];
  259. % Re-estimate to get PSTH and CI
  260. %----------------------------------------------------------
  261. j = xBF.order;
  262. xX = spm_sp('Set',X);
  263. pX = spm_sp('x-',xX);
  264. PSTH = pX*y(jX);
  265. res = spm_sp('r',xX,y(jX));
  266. df = size(X,1) - size(X,2);
  267. bcov = pX*pX'*sum(res.^2)/df;
  268. PSTH = PSTH(1:j)/dt;
  269. PST = [1:j]*BIN - BIN/2;
  270. PCI = CI*sqrt(diag(bcov(1:j,(1:j))))/dt;
  271. end
  272. % basis functions and parameters
  273. %------------------------------------------------------------------
  274. X = SPM.xBF.bf/dt;
  275. x = ([1:size(X,1)] - 1)*dt;
  276. j = SPM.Sess(s).col(SPM.Sess(s).Fc(u).i(1:size(X,2)));
  277. B = beta(j);
  278. % fitted responses with standard error
  279. %------------------------------------------------------------------
  280. Y = X*B;
  281. CI = CI*sqrt(diag(X*Bcov(j,j)*X'));
  282. % peristimulus times and adjusted data (y = Y + R)
  283. %------------------------------------------------------------------
  284. pst = SPM.Sess(s).U(u).pst;
  285. bin = round(pst/dt);
  286. q = find((bin >= 0) & (bin < size(X,1)));
  287. y = R(SPM.Sess(s).row(:));
  288. pst = pst(q);
  289. y = y(q) + Y(bin(q) + 1);
  290. % returned values
  291. %------------------------------------------------------------------
  292. if strcmp(xG.spec.Rplot,'fitted response and PSTH')
  293. G.PST = PST;
  294. G.PSTH = PSTH;
  295. G.PCI = PCI;
  296. end
  297. G.x = x;
  298. G.CI = CI;
  299. G.pst = pst;
  300. %-Parametric responses
  301. %======================================================================
  302. case 'Parametric responses'
  303. s = xG.spec.Sess;
  304. u = xG.spec.u;
  305. p = xG.spec.p;
  306. % basis functions
  307. %------------------------------------------------------------------
  308. dt = SPM.xBF.dt;
  309. bf = SPM.xBF.bf;
  310. pst = ([1:size(bf,1)] - 1)*dt;
  311. % orthogonalised expansion of parameteric variable
  312. %------------------------------------------------------------------
  313. P = SPM.Sess(s).U(u).P(p).P;
  314. q = [];
  315. for i = 0:SPM.Sess(s).U(u).P(p).h;
  316. q = [q P.^i];
  317. end
  318. q = spm_orth(q);
  319. % parameter estimates for this effect
  320. %------------------------------------------------------------------
  321. B = beta(SPM.Sess(s).col(SPM.Sess(s).Fc(u).i));
  322. % reconstruct trial-specific responses
  323. %------------------------------------------------------------------
  324. Y = zeros(size(bf,1),size(q,1));
  325. uj = SPM.Sess(s).U(u).P(p).i;
  326. for i = 1:size(P,1)
  327. U = sparse(1,uj,q(i,:),1,size(SPM.Sess(s).U(u).u,2));
  328. X = kron(U,bf);
  329. Y(:,i) = X*B;
  330. end
  331. [P,j] = sort(P);
  332. Y = Y(:,j);
  333. % returned values
  334. %------------------------------------------------------------------
  335. G.pst = pst;
  336. G.P = P;
  337. %-Volterra Kernels
  338. %======================================================================
  339. case 'Volterra Kernels'
  340. s = xG.spec.Sess;
  341. u = xG.spec.u;
  342. % Parameter estimates and basis functions
  343. %------------------------------------------------------------------
  344. dt = SPM.xBF.dt;
  345. bf = SPM.xBF.bf/dt;
  346. pst = ([1:size(bf,1)] - 1)*dt;
  347. % second order kernel
  348. %------------------------------------------------------------------
  349. if u > length(SPM.Sess(s).U)
  350. % Parameter estimates and kernel
  351. %--------------------------------------------------------------
  352. B = beta(SPM.Sess(s).col(SPM.Sess(s).Fc(u).i));
  353. i = 1;
  354. Y = 0;
  355. for p = 1:size(bf,2)
  356. for q = 1:size(bf,2)
  357. Y = Y + B(i)*bf(:,p)*bf(:,q)';
  358. i = i + 1;
  359. end
  360. end
  361. % first order kernel
  362. %------------------------------------------------------------------
  363. else
  364. B = beta(SPM.Sess(s).col(SPM.Sess(s).Fc(u).i(1:size(bf,2))));
  365. Y = bf*B;
  366. end
  367. % returned values
  368. %------------------------------------------------------------------
  369. G.pst = pst;
  370. end