OSCR

A mosaic of whole-body representations on the human precentral gyrus.

Code ↔ Paper

25 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 25 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Methods › Neural representations of whole-body movements in the motor cortex › Recurrent neural network classifier ↔ Code/Utils/Python/RNN/neural_decoder_trainer.py, lines 82–127 · score 0.81 · torch.nn.CrossEntropyLoss, PyTorch, CTC, layer, linear, day
  2. [2] § Methods › Neural representations of whole-body movements in the motor cortex › Correlation between homologous limb movement representations ↔ extendedDataFigs.m, lines 1204–1310 · score 0.78 · inferior ventral PCG, superior ventral PCG, correlation matrices, middle PCG, dorsal PCG, homologous
  3. [3] § Methods › Neural representations of whole-body movements in the motor cortex › Neural-tuning strength ↔ mainFigs.m, lines 255–319 · score 0.78 · tuning strength matrix, right leg, left leg, left arm, right arm, head
  4. [4] § A compositional whole-body neural code ↔ extendedDataFigs.m, lines 1204–1310 · score 0.75 · superior ventral PCG, limb pairs, arm leg, correlation matrices, dorsal PCG, movements
  5. [5] § Methods › Neural representations of whole-body movements in the motor cortex › Data exclusion ↔ Code/Utils/Python/RNN/whole_body_pipeline.py, lines 64–67 · score 0.75 · T17 m2, C2 d1, T11 d2, T17 m1
  6. [6] § Neural tuning to the whole body in the PCG ↔ mainFigs.m, lines 78–117 · score 0.75 · T16 v1, T15 m1, T15 v1, T12 v1, arm movements, inferior
  7. [7] § Methods › Neural representations of whole-body movements in the motor cortex › Peristimulus time histograms ↔ mainFigs.m, lines 123–210 · score 0.73 · Gaussian smoothing kernel, window relative, go cue, ms, TX, binned
  8. [8] § Methods › Neural representations of whole-body movements in the motor cortex › Neural-tuning strength ↔ Code/Utils/cvVectorStats/cvDistance.m, the whole file · a weak match · score 0.71 · confidence interval, cvDistance, Euclidean distance, jackknife, resampling, vector
  9. [9] § Methods › Neural representations of whole-body movements in the motor cortex › Neural-tuning strength ↔ mainFigs.m, lines 78–117 · score 0.70 · inferior frontal sulcus, superior frontal sulcus, landmarks, IFS, SFS, location
  10. [10] § Methods › Neural representations of whole-body movements in the motor cortex › Neural-tuning strength ↔ mainFigs.m, lines 123–210 · score 0.70 · binned spike, Euclidean distance, go cue, ms, strength, block
  11. [11] § Methods › Neural representations of whole-body movements in the motor cortex › Movement-independent neural coding of laterality ↔ Code/Utils/dPCA/apply_mPCA_general.m, lines 105–152 · score 0.69 · reduce bias, cross validated variance, computation, split, PCA, marginalizing
  12. [12] § Methods › Neural representations of whole-body movements in the motor cortex › Recurrent neural network classifier ↔ Code/Utils/Python/RNN/model.py, the whole file · a weak match · score 0.67 · RNN layer, PyTorch, padded, CTC, linear, day
  13. [13] § Neural tuning to the whole body in the PCG ↔ extendedDataFigs.m, lines 89–112 · score 0.66 · T16 v1, T15 m1, T15 v1, T12 v1, dorsal, PCG
  14. [14] § Methods › Neural representations of whole-body movements in the motor cortex › Neural dimensionality of canonical and non-canonical movements ↔ extendedDataFigs.m, lines 614–652 · score 0.60 · pairwise neural distances, canonical movement, ratio, modulation, dorsal, ventral
  15. [15] § Methods › Neural representations of whole-body movements in the motor cortex › Correlation between homologous limb movement representations ↔ mainFigs.m, lines 645–690 · score 0.60 · homologous movements, limb pair, correlation matrix
  16. [16] § Whole-body decoding from PCG sites ↔ Code/Utils/Python/RNN/03_evaluate_rnns.ipynb, lines 148–249 · score 0.58 · confusion matrix, decoding accuracy, T12 v1, PCG, movement
  17. [17] § A compositional whole-body neural code ↔ mainFigs.m, lines 734–807 · score 0.57 · superior ventral PCG, correlation matrices, dorsal PCG, leg, arm
  18. [18] § Methods › Neural representations of whole-body movements in the motor cortex › Neural dimensionality of canonical and non-canonical movements ↔ Code/Utils/tuningAnalyses.m, lines 217–319 · score 0.57 · pairwise neural distances, cvDistance, Jackknife, Tuning, movement
  19. [19] § Whole-body decoding from PCG sites ↔ Code/Utils/Python/RNN/03_evaluate_rnns.ipynb, lines 148–249 · score 0.56 · confusion matrix, decoding accuracies, T12 v1, RNN, trained, PCG
  20. [20] § Methods › Neural representations of whole-body movements in the motor cortex › Neural dimensionality of canonical and non-canonical movements ↔ Code/Utils/Python/cvPCA/Mosaic_cvPCA.ipynb, lines 112–154 · score 0.56 · cross validated PCA, halves, X1, X2, repetitions, split
  21. [21] § A compositional whole-body neural code ↔ mainFigs.m, lines 645–690 · score 0.54 · homologous movements, limb pair, arm leg, correlation, matrix
  22. [22] § A compositional whole-body neural code ↔ extendedDataFigs.m, lines 1315–1360 · score 0.53 · right toes curl, flow, correlation, legs, arms, matrix
  23. [23] § Neural tuning to the whole body in the PCG ↔ mainFigs.m, lines 321–408 · score 0.52 · modulation strength, tuning strength, IFS, SFS, inferior, square
  24. [24] § Methods › Neural representations of whole-body movements in the motor cortex › Movement-independent neural coding of laterality ↔ extendedDataFigs.m, lines 114–192 · score 0.52 · left arm, right arm, monolithic, TX, binned, electrodes
  25. [25] § Methods › Neural representations of whole-body movements in the motor cortex › PCA of array-tuning properties ↔ mainFigs.m, lines 516–611 · score 0.51 · Classification accuracies, atan2, HSV, angle, modulation, PCA

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

MATLAB · 881 lines · 34 KB · MIT · 10 matches

  1. %% mainFigs.m — Main figures for "A mosaic of whole-body representations on the human precentral gyrus"
  2. %
  3. % Reference:
  4. % Deo et al. (2026). A mosaic of whole-body representations on the
  5. % human precentral gyrus.
  6. %
  7. % Description:
  8. % Reproduces all main figures (Figures 1–5) reported in the paper.
  9. % Companion script: supplementalFigs.m (selected extended data figures).
  10. %
  11. % Workflow:
  12. % 1. Load participant data and define analysis parameters.
  13. % 2. Run core tuning analysis for each participant/array.
  14. % 3. Generate Figures 1–5 from the resulting analysis outputs.
  15. %
  16. % Requirements:
  17. % - MATLAB R2025b or later.
  18. % - This repository, with all sub-folders on the MATLAB path
  19. % (the script calls `addpath(genpath('./'))`).
  20. % - Participant `.mat` files (downloaded separately from the data
  21. % repository) placed in `Data/`. See `Data/README.md` for the data
  22. % structure and `README.md` for setup instructions.
  23. %
  24. % Usage:
  25. % Set MATLAB's working directory to the top-level project folder
  26. % (the one containing this script), then run:
  27. % >> mainFigs
  28. % Outputs are written into `Data/MainFigs/`.
  29. %
  30. % Repository: https://github.com/d-r-deo/pcg-mosaic
  31. %
  32. % Author: Darrel Deo
  33. % Contact: [email hidden]
  34. % Last updated: 2026-04-27
  35. %
  36. warning('off', 'all'); % Suppress warnings
  37. close all; clear all; clc;
  38. %% ========== SETUP & PATHS ==========
  39. AnalysisFolder = 'MainFigs';
  40. data_dir = ['Data' filesep]; % location of data to analyze
  41. save_dir = [data_dir AnalysisFolder filesep]; % top-level directory to store results
  42. % Add current directory and subdirectories to path
  43. addpath(genpath('./'));
  44. colMap = 'viridis'; % set colormap to use for most heatmaps
  45. %% ========== PARTICIPANT & ARRAY DEFINITIONS ==========
  46. % Participant enumeration
  47. datNames = {'T5','T12','T11','C1','C2', 'T15', 'T16', 'T17'};
  48. [T5, T12, T11, C1, C2, T15, T16, T17] = deal(1, 2, 3, 4, 5, 6, 7, 8);
  49. % Array definitions per participant
  50. pDat = cell(numel(datNames), 1);
  51. pDat{T5}.arrays = {'T5-d1', 'T5-d2'};
  52. pDat{T12}.arrays = {'T12-v1', 'T12-v2'};
  53. pDat{T11}.arrays = {'T11-d1'};
  54. pDat{C1}.arrays = {'C1-d1', 'C1-d2'};
  55. pDat{C2}.arrays = {'C2-d2'};
  56. pDat{T15}.arrays = {'T15-m1', 'T15-v1', 'T15-v2', 'T15-v3'};
  57. pDat{T16}.arrays = {'T16-m1', 'T16-v1', 'T16-d2', 'T16-d1'};
  58. pDat{T17}.arrays = {'T17-v2', 'T17-v1', 'T17-d2', 'T17-d1'};
  59. % Flatten array order for quick lookup
  60. pDat_order = {};
  61. for p = 1:numel(pDat)
  62. pDat_order = [pDat_order pDat{p}.arrays];
  63. end
  64. % Define spatial ordering for visualizations
  65. reorderNames = {'C2-d2', 'C1-d1', 'C1-d2','T17-d1', 'T5-d1', 'T5-d2','T17-d2', 'T16-d1',...
  66. 'T16-d2','T11-d1','T15-m1','T16-m1','T15-v1','T12-v1', 'T15-v2', 'T17-v1',...
  67. 'T12-v2', 'T15-v3', 'T17-v2','T16-v1'};
  68. % Define movement set order for visualizations
  69. reorderMoveSets = {'Speech', 'Face', 'Head', 'RightArm', 'RightLeg', 'LeftArm', 'LeftLeg'};
  70. %% ========== ANALYSIS WINDOWS & MOVEMENT DEFINITIONS ==========
  71. % Time windows (in 20ms bins) relative to trigger cue(s), matches order of arrays in pDat_order
  72. cueName = 'GoCue'; % 'GoCue' or 'Delay' as trigger cue
  73. goWindows = { [15, 75-1], [15, 50-1], [15, 75-1], [15, 75-1], [15, 75-1], [15, 125-1], [15, 125-1], [15, 125-1]}; % whole go window after 300ms reaction time
  74. delayWindows = {[-50, 0], [-50 0], [-50 0], [-50, 0], [-50, 0], [-50, 0], [-50, 0], [-50, 0]}; % same delay window for each participant
  75. % Movement condition codes (see README for full mapping)
  76. movSetNames = {'DoNothing', 'Speech','Face','Head','LeftLeg','LeftArm','RightLeg','RightArm'};
  77. % Define the movement sets corresponding to the set names above (note separate set for C1 to remove head noise conditions).
  78. movSets_Regular = {[1], [42 43 44 45], [2 23 24 25 46], [3 4 5 6],...
  79. [7 8 9 10 15 16 17 18], [11 12 13 14 19 20 21 22],...
  80. [26 27 28 29 34 35 36 37], [30 31 32 33 38 39 40 41]};
  81. movSets_C1 = {[1], [42 43 44 45], [2 23 24 25 46], [4 5],...
  82. [7 8 9 10 15 16 17 18], [11 12 13 14 19 20 21 22],...
  83. [26 27 28 29 34 35 36 37], [30 31 32 33 38 39 40 41]};
  84. [~, moveSetsReorder_inds] = ismember(reorderMoveSets, movSetNames); % to be used to re-order movement sets for classification matrix
  85. % Arm movement conditions for laterality analysis
  86. Rarm = [38 39 40 41 30 31 32 33]; % Down Left Right Up Raise-left Raise-right Close Open
  87. Larm = [19 21 20 22 12 11 13 14]; % Down Right Left Up Raise-right Raise-left Close Open
  88. % PCG spatial locations of arrays
  89. % Inf = inferior-most limit, IFS = inferior frontal sulcus landmark, SFS = superior frontal sulcus landmark, Sup = superior-most limit)
  90. Inf = 1; IFS = 2; SFS = 3; Sup = 4;
  91. participantID = [C2 C1 C1 T17 T5 T5 T17 T16 T16 T11 T15 T16 T15 T12 T15 T17 T12 T15 T17 T16];
  92. arrayLoc = [SFS+0.4 SFS+0.35 SFS+0.2 SFS-0.1 SFS+0.2 SFS+0.1 SFS-0.2 SFS+0.1 SFS SFS-0.1...
  93. SFS-0.5 SFS-0.6 IFS+0.2 IFS IFS-0.2 IFS-0.25 IFS-0.5 IFS-0.5 IFS-0.4 IFS-0.7];
  94. % Array groupings by PCG region
  95. DorsalSet = {'T5-d1', 'T5-d2', 'T11-d1', 'C1-d1', 'C1-d2', 'C2-d2', 'T16-d2', 'T16-d1', 'T17-d2', 'T17-d1'};
  96. MiddleSet = {'T15-m1', 'T16-m1'};
  97. VentralSupSet = {'T15-v1', 'T12-v1', 'T17-v1'};
  98. VentralInfSet = {'T15-v2', 'T15-v3', 'T12-v2', 'T16-v1', 'T17-v2'};
  99. setInds = {find(ismember(pDat_order, DorsalSet)), ...
  100. find(ismember(pDat_order, MiddleSet)), ...
  101. find(ismember(pDat_order, VentralSupSet)), ...
  102. find(ismember(pDat_order, VentralInfSet))};
  103. %% ========== FIGURE 1 PSTH SELECTION ==========
  104. plotPSTHs_array = [T5 T12 T15 T16]; % participants to plot psths for
  105. movSets_PSTH = {[42 46 4 10 13 27 32]}; % Selected movements for display
  106. %% ========== CORE ANALYSIS LOOP: Process each participant/array ==========
  107. fprintf('============ STARTING MAIN ANALYSIS LOOP ============\n');
  108. for d = 1:numel(datNames) % iterate through each participant
  109. % Load data
  110. Dat = load([data_dir datNames{d} '.mat']);
  111. Dat = Dat.DataMat;
  112. % Extract and normalize spike features
  113. tx = Dat.tx_blkMeanSub; % block-wise mean removed tx spike features
  114. msFeat_s = tx .* 50; % Convert to seconds (20ms binned data)
  115. msFeat = (msFeat_s - nanmean(msFeat_s,1)) ./ nanstd(msFeat_s,1); % Z-score
  116. % Remove NaN channels
  117. tmp = sum(isnan(msFeat),1);
  118. indsKeep = find(tmp ~= size(msFeat,1));
  119. % Setup save directory
  120. saveDir = [save_dir datNames{d}];
  121. mkdir(saveDir);
  122. % Initialize analysis structure
  123. sDat = struct();
  124. sDat.movementCodes = Dat.trialCue; % extract the movement cue per trial
  125. sDat.movementNames = Dat.cueList; % extract the master cue list as a struct of strings
  126. sDat.movementSets = {movSets_Regular{2:end}}; % trim off the Do Nothing condition when defining movement sets
  127. sDat.movementSetNames = {movSetNames{2:end}}; % trim off the Do Nothing set
  128. sDat.doNothingCode = 1; % define the 'Do Nothing' cue number, which is 1
  129. % TOGGLE FLAGS (modify to enable/disable analyses)
  130. sDat.plotPSTHs = false; % will plot PSTHs for all movement sets
  131. sDat.fullDistanceMatrix = true; % will compute the euclidean distances between each pair of movement conditions
  132. sDat.withinGroupCorrelation = true; % will compute the correlations between each pair of movement conditions
  133. sDat.plotBarPlots = true; % will compute the modulation strength for each movement condition
  134. sDat.classifyAll = true; % will perform x-val naive bayes classification
  135. sDat.perform_mPCA = true; % Laterality analysis
  136. % Define parameters
  137. sDat.goTimes = Dat.goCue; % define the time points upon which to trigger analyses
  138. sDat.plottingWindow = [-100, 150]; % time window relative to each go cue to plot for PSTHs
  139. sDat.binWidth = 0.02; % bin width in seconds for the data (20ms binned data)
  140. sDat.mPCA_smoothWidth = 4; % standard deviation (in samples) of the Gaussian smoothing kernel
  141. sDat.latMoveSet = [Rarm; Larm]; % set of movements for laterality analysis
  142. sDat.latMoveName = 'Arms'; % laterality analysis name
  143. % Iterate through trigger cues
  144. if strcmp(cueName, 'GoCue') % perform analyses relative to go cues
  145. sDat.analysisWindow = goWindows{d};
  146. else % perform analyses relative to delay cues
  147. sDat.analysisWindow = delayWindows{d};
  148. end
  149. % Iterate through arrays
  150. for chanSetIdx = 1:numel(Dat.chanSets)
  151. % Skip arrays not in analysis list
  152. if ~ismember(Dat.chanSetNames{chanSetIdx}, pDat_order)
  153. continue;
  154. end
  155. fprintf('Processing: %s - %s (%s)\n', datNames{d}, Dat.chanSetNames{chanSetIdx}, cueName);
  156. % Extract channels for this array
  157. channels = Dat.chanSets{chanSetIdx};
  158. channels = channels(ismember(channels,indsKeep));
  159. % Run analysis
  160. sDat.chanSets = Dat.chanSets{chanSetIdx};
  161. sDat.saveDir = [saveDir filesep Dat.chanSetNames{chanSetIdx} 'Tuning_' cueName];
  162. sDat.features = msFeat(:, channels);
  163. tuningAnalyses(sDat); % Main analysis function
  164. end
  165. pause(1); close all; pause(1);
  166. % Plot PSTHs for selected participants
  167. if ismember(d, plotPSTHs_array)
  168. sDat_psth = sDat;
  169. sDat_psth.saveDir = [saveDir filesep 'PSTHs'];
  170. sDat_psth.analysisWindow = goWindows{d};
  171. sDat_psth.movementSets = movSets_PSTH;
  172. sDat_psth.movementSetNames = {'WholeBody'};
  173. sDat_psth.features = Dat.tx.* 50; % raw binned spikes
  174. plotPSTHs(sDat_psth);
  175. end
  176. end
  177. fprintf('============ ANALYSIS LOOP COMPLETE ============\n');
  178. %% ========== FIGURE GENERATION: Load pre-computed results and visualize ==========
  179. %% FIGURE 1c: PSTHs
  180. fprintf('\n\n*******************************************************************************\n');
  181. fprintf(' FIGURE 1\n');
  182. fprintf('*******************************************************************************\n\n');
  183. fprintf('Generating Figure 1c - PSTHs\n');
  184. T5_psth_loc = [save_dir 'T5' filesep 'PSTHs' filesep 'psth_WholeBody' filesep 'psth_3_WholeBody.fig'];
  185. T15_psth_loc = [save_dir 'T15' filesep 'PSTHs' filesep 'psth_WholeBody' filesep 'psth_4_WholeBody.fig'];
  186. T12_psth_loc = [save_dir 'T12' filesep 'PSTHs' filesep 'psth_WholeBody' filesep 'psth_1_WholeBody.fig'];
  187. T16_psth_loc = [save_dir 'T16' filesep 'PSTHs' filesep 'psth_WholeBody' filesep 'psth_2_WholeBody.fig'];
  188. % Open the source .fig files
  189. fig1 = openfig(T5_psth_loc);
  190. fig2 = openfig(T15_psth_loc);
  191. fig3 = openfig(T12_psth_loc);
  192. fig4 = openfig(T16_psth_loc);
  193. % Open the source .fig files
  194. fig_specs = struct('fig_handle', {fig1, fig2, fig3, fig4}, ...
  195. 'electrode_idx', {25, 45, 47, 61});
  196. fig1 = figure('Name', 'Figure 1c - PSTHs', 'Position',[2 546 1679 302]); % create a new figure
  197. for p = 1: numel(fig_specs)
  198. subplot(1,4,p);
  199. axesHandles = flip(findall(fig_specs(p).fig_handle, 'Type', 'axes'));
  200. sourceAx = axesHandles(fig_specs(p).electrode_idx);
  201. newAx = copyobj(sourceAx, fig1);
  202. subplotPos = get(gca, 'Position');
  203. set(newAx, 'Position', subplotPos);
  204. delete(gca);
  205. end
  206. % save Figure 1
  207. figure(fig1);
  208. saveDir = [save_dir 'Fig1' filesep];
  209. mkdir(saveDir);
  210. saveName = [saveDir 'Fig1c_PSTHs'];
  211. exportPNGFigure(gcf, saveName);
  212. %% FIGURE 2a: Tuning Strength Heatmap
  213. fprintf('\n\n*******************************************************************************\n');
  214. fprintf(' FIGURE 2\n');
  215. fprintf('*******************************************************************************\n\n');
  216. fprintf('Generating Figure 2a - Tuning Strength Heatmap\n');
  217. ModMagHeatMap = []; % initialize matrix to hold results
  218. ModMagHeatMap_Labels = {};
  219. % Accumulate modulation strength across all arrays
  220. for p = 1:numel(pDat)
  221. for d = 1:numel(pDat{p}.arrays)
  222. % load current array's modulation strength matrix
  223. cDat = load([save_dir datNames{p} filesep pDat{p}.arrays{d} ...
  224. 'Tuning_' cueName filesep 'modMag_sorted.mat']);
  225. means = cDat.modMag_sorted(:,1)'; % extract 1xM vector of avg. mod strength for each individual movement condition
  226. non_sig = (cDat.modMag_sorted(:,2) < 0)'; % any lower CI bound below 0 is non-significant
  227. means(non_sig) = NaN;
  228. % Special handling for C1 (head noise)
  229. if p == C1
  230. means([10 13]) = NaN;
  231. end
  232. % stack the modulation array
  233. ModMagHeatMap = [ModMagHeatMap; means];
  234. ModMagHeatMap_Labels = [ModMagHeatMap_Labels pDat{p}.arrays{d}];
  235. moveNames = cDat.movLabels_sorted;
  236. end
  237. end
  238. % Normalize rows and resort array order
  239. ModMagHeatMap_RowWiseNorm = ModMagHeatMap ./ max(ModMagHeatMap')';
  240. resortedInds = sort_by_reference(ModMagHeatMap_Labels, reorderNames);
  241. vals = ModMagHeatMap_RowWiseNorm(resortedInds, :);
  242. array_labels = ModMagHeatMap_Labels(resortedInds);
  243. % Resort movements by category
  244. Speech = 1:4; Orofacial = 5:9; Head = 10:13;
  245. LeftLeg = 14:21; RightLeg = 30:37; LeftArm = 22:29; RightArm = 38:45;
  246. movementInds_resorted = [Speech Orofacial Head RightArm RightLeg LeftArm LeftLeg];
  247. vals = vals(:, movementInds_resorted);
  248. labels = moveNames(movementInds_resorted);
  249. % Plot
  250. figure('Name','Figure 2a','Units','pixels','Position',[2 520 1422 450]);
  251. imagesc(vals, [0 1]); colormap(colMap); colorbar;
  252. yticks(1:numel(array_labels)); yticklabels(array_labels);
  253. xticks(1:numel(labels)); xticklabels(labels);
  254. title('Normalized Modulation Strength'); set(gca,'FontSize',12);
  255. % Mark non-significant cells
  256. for i = 1:size(vals,1)
  257. for j = 1:size(vals,2)
  258. if isnan(vals(i,j))
  259. text(j,i,'X','Color',[1 1 1],'FontSize',12,'HorizontalAlignment','center');
  260. end
  261. end
  262. end
  263. % Save figure
  264. saveDir = [save_dir 'Fig2' filesep];
  265. mkdir(saveDir);
  266. exportPNGFigure(gcf, [saveDir 'Fig2a_TuningStrength_Heatmap']);
  267. %% FIGURE 2b: PCG Layout with Set-Wise Modulation
  268. fprintf('Generating Figure 2b - PCG Layout\n');
  269. % Load pre-computed set-wise modulation data
  270. dataName = ['ModMag_' cueName '.mat'];
  271. datLoc = [save_dir 'TuningStrength' filesep dataName];
  272. % Define movement indices for set-wise extraction
  273. movInds_reg = {[1:4], [5:9], [10:13], [14:21], [22:29], [30:37], [38:45]};
  274. movInds_C1 = {[1:4], [5:9], [11:12], [14:21], [22:29], [30:37], [38:45]};
  275. % Compile set-wise modulation strengths across all participants and arrays
  276. for p = 1:numel(pDat)
  277. arrays = pDat{p}.arrays;
  278. meanArray = [];
  279. for d = 1:numel(arrays)
  280. cDat = load([save_dir datNames{p} filesep arrays{d} 'Tuning_' cueName filesep 'modMag_sorted.mat']);
  281. means = cDat.modMag_sorted(:,1)';
  282. means_tmp = zeros(1, numel(movInds_reg));
  283. for movSet_idx = 1:numel(movInds_reg)
  284. inds = movInds_reg{movSet_idx};
  285. if p == C1
  286. inds = movInds_C1{movSet_idx};
  287. end
  288. means_tmp(movSet_idx) = nanmean(means(inds));
  289. end
  290. meanArray = [meanArray; means_tmp];
  291. end
  292. pDat{p}.ModMag.(cueName) = meanArray;
  293. end
  294. % Stack in order of array location to match PCG layout
  295. stackedMean = [];
  296. stackedMean_labels = {};
  297. for p = 1:numel(pDat)
  298. stackedMean = [stackedMean; pDat{p}.ModMag.(cueName)];
  299. stackedMean_labels = [stackedMean_labels pDat{p}.arrays];
  300. end
  301. % Re-shuffle array indices to match spatial PCG ordering
  302. currOrder = stackedMean_labels;
  303. resortedInds = [];
  304. for itor = 1:numel(reorderNames)
  305. resortedInds = [resortedInds find(strcmp(currOrder, reorderNames{itor}) == 1)];
  306. end
  307. vals = stackedMean(resortedInds, :);
  308. array = stackedMean_labels(resortedInds);
  309. colLabels = {'Speech','Face','Head','LeftLeg','LeftArm','RightLeg','RightArm'};
  310. % Resort columns to display order: arms and legs grouped together
  311. col_resort = [];
  312. for itor = 1:numel(reorderMoveSets)
  313. col_resort = [col_resort find(strcmp(colLabels, reorderMoveSets{itor}) == 1)];
  314. end
  315. vals = vals(:, col_resort);
  316. movSet = colLabels(col_resort);
  317. % Normalize each row by its maximum
  318. vals = vals ./ max(vals')';
  319. % Save ModMag data for later use (e.g., Figure 4)
  320. tuningStrengthDir = [save_dir 'TuningStrength' filesep];
  321. mkdir(tuningStrengthDir);
  322. DatMat = [];
  323. DatMat.vals_normalized = vals;
  324. DatMat.xlabels = movSet;
  325. DatMat.ylabels = array;
  326. save([tuningStrengthDir 'ModMag_' cueName], 'DatMat');
  327. % Plot on PCG layout
  328. figure('Name','Figure 2b - PCG Layout','Position',[5 373 1127 655]);
  329. for m = 1:numel(movSet)
  330. subplot(1, numel(movSet), m);
  331. currVals = vals(:, m);
  332. scatter(participantID, arrayLoc, 400, currVals, 'filled', 'square');
  333. title(movSet{m}); ylim([1 4]); clim([0 1]);
  334. if m == 1
  335. yticks(1:4); yticklabels({'Inferior', 'IFS', 'SFS', 'Superior'});
  336. ylabel('Precentral gyrus');
  337. else
  338. yticks(1:4); yticklabels({});
  339. end
  340. xticks(1:numel(datNames)); xticklabels(datNames);
  341. set(gca,'FontSize',7); set(gca,'color','none'); grid on; colormap(colMap);
  342. end
  343. exportPNGFigure(gcf, [save_dir 'Fig2' filesep 'Fig2b_PCGLayout_' cueName]);
  344. %% FIGURE 3a [Alternative]: Naive Bayes Decoding
  345. fprintf('\n\n*******************************************************************************\n');
  346. fprintf(' FIGURE 3\n');
  347. fprintf('*******************************************************************************\n\n');
  348. % Please note that the figure in the manuscript was generated using an
  349. % RNN in Python. A simplified Naive Bayes classifier was used for this section
  350. fprintf('Generating Figure 3a Alternative - Naive Bayes Decoding Accuracy\n');
  351. saveLoc = [save_dir 'Fig3' filesep];
  352. mkdir(saveLoc);
  353. Accuracy = [];
  354. sig = [];
  355. % Accumulate per-array decoding accuracies
  356. for a = 1:numel(reorderNames)
  357. currParticipant = strtok(reorderNames{a}, '-');
  358. Cmat = load([save_dir currParticipant filesep reorderNames{a} 'Tuning_' cueName filesep 'ClassificationMatrix.mat']);
  359. Cmat_subsets = load([save_dir currParticipant filesep reorderNames{a} 'Tuning_' cueName filesep 'ClassificationMatrix_Subsets.mat']);
  360. Cmat_subsets = Cmat_subsets.SubsetClassification;
  361. % Stack overall + per-movement-set accuracies
  362. currAcc = Cmat.decodingAccuracy;
  363. binoCI = Cmat.binoCI;
  364. chanceLevel = 1 / size(Cmat.classMat, 1);
  365. for subset = 1:numel(Cmat_subsets)
  366. binoCI = [binoCI; Cmat_subsets{subset}.binoCI];
  367. chanceLevel = [chanceLevel; 1 / numel(Cmat_subsets{subset}.moveSet)];
  368. currAcc = [currAcc Cmat_subsets{subset}.decodingAccuracy];
  369. end
  370. % Check significance
  371. currSig = (binoCI(:,1) > chanceLevel)';
  372. sig = [sig; currSig];
  373. Accuracy = [Accuracy; currAcc];
  374. end
  375. % Prepend chance level row
  376. Accuracy = [chanceLevel'; Accuracy];
  377. sig = [ones(numel(chanceLevel), 1)'; sig];
  378. arrayLabels = ['Chance'; reorderNames'];
  379. % Resort movement labels by category
  380. movLabels = {'All'};
  381. for i = 1:numel(Cmat_subsets)
  382. movLabels = [movLabels; Cmat_subsets{i}.moveSetName];
  383. end
  384. if exist('lbls', 'var'), movLabels = lbls; end
  385. % Plot accuracy matrix
  386. figure('Name', 'Figure 3a - Naive Bayes', 'Position',[560 528 560 420]);
  387. imagesc(Accuracy, [0.2 1]); colormap(colMap); colorbar;
  388. xticks(1:size(Accuracy,2)); xticklabels(movLabels);
  389. yticks(1:size(Accuracy,1)); yticklabels(arrayLabels);
  390. title(sprintf('Naive Bayes Decoding - %s', cueName)); set(gca,'FontSize',12);
  391. % plot text in cells
  392. [R,C] = ndgrid(1:size(Accuracy,1), 1:size(Accuracy,2));
  393. text(C(:)'-0.25, R(:)', string(round(Accuracy(:),2)),'FontSize',12,'Color',[1 1 1]);
  394. % Mark non-significant cells
  395. for i = 1:size(sig,1)
  396. for j = 1:size(sig,2)
  397. if sig(i,j) == 0
  398. text(j, i, 'X', 'Color',[1 0 0], 'FontSize',10, 'HorizontalAlignment','right');
  399. end
  400. end
  401. end
  402. exportPNGFigure(gcf, [saveLoc 'Fig3a_NaiveBayes_' cueName]);
  403. %% FIGURE 3a [Manuscript]: Decoding Accuracy (RNN)
  404. fprintf('Generating Figure 3a - RNN Decoding Accuracy\n');
  405. saveLoc = [save_dir 'Fig3' filesep];
  406. mkdir(saveLoc);
  407. rnn = createRnnClassificationAccuracyMat_fromPaper(); % hardcoded from actual paper results
  408. Accuracy = [rnn.chanceLevels ; rnn.classificationAccuracy];
  409. figure('Name', 'Figure 3a - RNN', 'Position',[560 528 560 420]);
  410. imagesc(Accuracy); colormap(colMap); clim([0.2 1]);
  411. xticks(1:numel(rnn.movementSetLabels)); xticklabels(rnn.movementSetLabels);
  412. yticks(1:size(Accuracy,1));
  413. yticklabels(['Chance'; cellstr(rnn.arrayOrder)]);
  414. title('RNN Decoding Accuracy'); colorbar;
  415. [R,C] = ndgrid(1:size(Accuracy,1), 1:size(Accuracy,2)); % plot text in cells
  416. text(C(:)'-0.25, R(:)', string(round(Accuracy(:),2)),'FontSize',12,'Color',[1 1 1]);
  417. exportPNGFigure(gcf, [saveLoc 'Fig3a_RNN_' cueName]);
  418. %% FIGURE 3b [Alternative]: Naive Bayes Classifier
  419. % Please note that the figure in the manuscript was generated using an
  420. % RNN in Python. A simplified Naive Bayes classifier was used for this section
  421. fprintf('Generating Figure 3b Alternative - Naive Bayes T12 Confusion Matrix \n');
  422. saveLoc = [save_dir 'Fig3' filesep];
  423. mkdir(saveLoc);
  424. T12_v1_dir = [save_dir 'T12' filesep 'T12-v1Tuning_GoCue' filesep 'allMovementsClassification.fig'];
  425. openfig(T12_v1_dir);
  426. axis square;
  427. set(gca, 'FontSize', 8);
  428. % save into Figure 3 folder
  429. exportPNGFigure(gcf, [saveLoc 'Fig3b_NaiveBayes_ConfMat' cueName]);
  430. %% FIGURE 4a: PCA of Modulation + RNN Accuracies
  431. fprintf('\n\n*******************************************************************************\n');
  432. fprintf(' FIGURE 4\n');
  433. fprintf('*******************************************************************************\n\n');
  434. fprintf('Generating Figure 4a - PCA Analysis\n');
  435. saveLoc = [save_dir 'Fig4' filesep];
  436. mkdir(saveLoc);
  437. addpath(genpath('./'));
  438. % Load modulation strengths and RNN decoding accuracies
  439. modDat = load([save_dir 'TuningStrength' filesep 'ModMag_' cueName '.mat']);
  440. rnn = createRnnClassificationAccuracyMat_fromPaper(); % hardcoded from actual paper results
  441. % Stack features
  442. vals = [modDat.DatMat.vals_normalized, rnn.classificationAccuracy(:,2:end)];
  443. arrayNames = modDat.DatMat.ylabels;
  444. % Run PCA
  445. [COEFF, SCORES, LATENT, ~, EXPLAINED] = pca(vals);
  446. arrSets = {1:10,11:12,13:20};
  447. % Plot PCA projections
  448. figure('Name', 'Figure 4a - PCA', 'Position', [2 276 865 752]);
  449. hold on;
  450. colList = [];
  451. for x=1:size(SCORES,1)
  452. if ismember(x,arrSets{1})
  453. cIdx = 1;
  454. elseif ismember(x,arrSets{2})
  455. cIdx = 2;
  456. else
  457. cIdx = 3;
  458. end
  459. uv = SCORES(x,1:2);
  460. rotAngle = 0;
  461. rotMat = [[cosd(rotAngle); sind(rotAngle);],[cosd(rotAngle+90); sind(rotAngle+90)]];
  462. uv = rotMat*uv';
  463. radius = sqrt(sum(uv.^2));
  464. angle = (atan2(uv(2), uv(1))+pi)/(2*pi);
  465. radius = radius * 1;
  466. radius(radius>1)=1;
  467. thisColor = hsv2rgb(angle, radius, 0.85);
  468. colList = [colList; squeeze(thisColor)'];
  469. plot(SCORES(x,1),SCORES(x,2),'o','MarkerFaceColor',thisColor,'Color',thisColor);
  470. text(SCORES(x,1),SCORES(x,2)+0.025,arrayNames{x},'Color',thisColor);
  471. end
  472. xlabel('PC1');
  473. ylabel('PC2');
  474. axis equal;
  475. xlim([-1.2,1.2]);
  476. ylim([-1,1]);
  477. plot(get(gca,'XLim'),[0,0],'--k');
  478. plot([0,0],get(gca,'YLim'),'--k');
  479. exportPNGFigure(gcf, [saveLoc 'Fig4a_PCA_' cueName]);
  480. % Create the color legend
  481. % create grid of PC1 / PC2 values
  482. N = 201; % resolution of the square
  483. [PC1, PC2] = meshgrid(linspace(-1,1,N));
  484. % apply same rotation and color mapping
  485. uv = [PC1(:) PC2(:)].';
  486. rotMat = [[cosd(rotAngle); sind(rotAngle)], ...
  487. [cosd(rotAngle+90); sind(rotAngle+90)]];
  488. uv = rotMat * uv;
  489. radius = sqrt(sum(uv.^2,1));
  490. angle = (atan2(uv(2,:), uv(1,:)) + pi) / (2*pi); % 0..1
  491. radius(radius>1) = 1; % clamp to 1
  492. HSV = [angle(:) radius(:) 0.85*ones(numel(angle),1)];
  493. RGB = hsv2rgb(HSV);
  494. % reshape back to image
  495. R = reshape(RGB(:,1), N, N);
  496. G = reshape(RGB(:,2), N, N);
  497. B = reshape(RGB(:,3), N, N);
  498. legendImg = cat(3,R,G,B);
  499. % show legend
  500. figure('Name','PCA color legend', 'Position', [2 854 234 174]);
  501. image(linspace(-1,1,N), linspace(-1,1,N), legendImg);
  502. set(gca,'YDir','normal');
  503. axis square tight;
  504. xlabel('PC1'); ylabel('PC2');
  505. exportPNGFigure(gcf, [saveLoc 'Fig4a_PCA_Legend']);
  506. %% FIGURE 4b: PCA Coefficients
  507. fprintf('Generating Figure 4b - PCA Coefficients\n');
  508. cueSetNames = {'Speech','Face','Head','RArm','RLeg','LArm','LLeg',...
  509. 'Speech','Face','Head','RArm','RLeg','LArm','LLeg'};
  510. figure('Name', 'Figure 4b - PCA Coefficients', 'Position', [2 674 866 354]);
  511. hold on;
  512. % Plot modulation strength coefficients (solid)
  513. plot(COEFF(1:7,1), '-o', 'LineWidth', 2, 'Color', [0 0.4470 0.7410]);
  514. plot(COEFF(1:7,2), '-o', 'LineWidth', 2, 'Color', [0.8500 0.3250 0.0980]);
  515. % Plot RNN accuracy coefficients (dashed)
  516. plot(COEFF(8:end,1), '--o', 'LineWidth', 2, 'Color', [0 0.4470 0.7410]);
  517. plot(COEFF(8:end,2), '--o', 'LineWidth', 2, 'Color', [0.8500 0.3250 0.0980]);
  518. plot(get(gca,'XLim'), [0 0], '--k');
  519. set(gca, 'XTick', 1:14, 'XTickLabel', cueSetNames, 'XTickLabelRotation', 45);
  520. legend({'PC1','PC2'}); ylabel('Coefficient');
  521. exportPNGFigure(gcf, [saveLoc 'Fig4b_PCA_Coefficients_' cueName]);
  522. %% FIGURE 4c: PCA on PCG Layout
  523. fprintf('Generating Figure 4c - PCA PCG Layout\n');
  524. figure('Name','Figure 4c - PCA PCG layout','Position',[1 1 272 1005]);
  525. scatter(participantID, arrayLoc, 400, colList, 'filled','square');
  526. yticks(1:4); yticklabels({'Inferior', 'IFS', 'SFS', 'Superior'});
  527. ylabel('Precentral gyrus');
  528. xticks(1:numel(datNames)); xticklabels(datNames);
  529. set(gca,'FontSize',12); set(gca,'color','none'); grid on;
  530. exportPNGFigure(gcf, [saveLoc 'Fig4c_PCA_PCGLayout_' cueName]);
  531. %% FIGURE 5a: Group-Averaged Correlation Matrices
  532. fprintf('\n\n*******************************************************************************\n');
  533. fprintf(' FIGURE 5\n');
  534. fprintf('*******************************************************************************\n\n');
  535. fprintf('Generating Figure 5a - Group-Averaged Correlations\n');
  536. saveLoc = [save_dir 'Fig5' filesep];
  537. mkdir(saveLoc);
  538. % Define movement condition sets for each limb pair
  539. Corr_resortedInds = {[38:45 22:29], [30:37 14:21], [38:45 34:37 30:33], [22:29 18:21 14:17]};
  540. Corr_resortedNames = {'R-L-Arms', 'R-L-Legs', 'R-Arm-Leg', 'L-Arm-Leg'};
  541. % Reordering indices for homologous movements
  542. subCorrMat_resortInds = {[1:8; 2 1 3 4 5 7 6 8], [1:8; 1 3 2 4 6 5 7 8], [1:8; 1:8], [1:8; 1:8]};
  543. shuff = {};
  544. stacked_diag_meanCorrs = [];
  545. stacked_arrayIDs = {};
  546. corrStack = zeros(46, 46, 20);
  547. sigArray = zeros(20, 4);
  548. itor = 1;
  549. % Iterate through each dataset
  550. for d = 1:numel(datNames)
  551. Dat = load([data_dir datNames{d} '.mat']);
  552. Dat = Dat.DataMat;
  553. % Iterate through each array for the current participant
  554. for chanSetIdx = 1:numel(Dat.chanSets)
  555. if ~ismember(Dat.chanSetNames{chanSetIdx}, pDat_order)
  556. continue;
  557. end
  558. % Load full correlation matrix
  559. c = load([save_dir datNames{d} '/' Dat.chanSetNames{chanSetIdx} 'Tuning_' cueName filesep 'Correlation.mat']);
  560. corrStack(:,:,itor) = c.corrMat;
  561. % Process each limb pair
  562. corr_data = {};
  563. mean_diag_vals = [];
  564. mean_offdiag_vals = [];
  565. for sub_corr = 1:numel(Corr_resortedInds)
  566. subset = Corr_resortedInds{sub_corr};
  567. cMat = c.corrMat(subset, subset);
  568. % Extract off-diagonal sub-matrix
  569. indsTake_Y = ((length(subset)/2)+1):length(subset);
  570. indsTake_X = 1:(length(subset)/2);
  571. subMat = cMat(indsTake_X, indsTake_Y);
  572. % Reorder for laterality alignment
  573. currReorderInds_X = subCorrMat_resortInds{sub_corr}(1,:);
  574. currReorderInds_Y = subCorrMat_resortInds{sub_corr}(2,:);
  575. subMat_reorder = subMat(currReorderInds_X, currReorderInds_Y);
  576. % Store correlation data
  577. corr_data{sub_corr,1} = diag(subMat_reorder);
  578. corr_data{sub_corr,3} = subMat_reorder;
  579. % Calculate mean values
  580. mean_diag_vals(sub_corr) = mean(corr_data{sub_corr,1});
  581. off_diag_mask = ones(size(subMat_reorder));
  582. for m = 1:length(subMat_reorder)
  583. off_diag_mask(m,m) = 0;
  584. end
  585. corr_data{sub_corr,2} = subMat_reorder(find(off_diag_mask == 1));
  586. mean_offdiag_vals(sub_corr) = mean(corr_data{sub_corr,2});
  587. % Shuffle test for significance
  588. [n,m] = size(subMat_reorder);
  589. shuffSamples = zeros(1,10000);
  590. for nrand = 1:10000
  591. shuffMat = subMat_reorder(randperm(n), randperm(m));
  592. shuffSamples(nrand) = mean(diag(shuffMat));
  593. end
  594. stdInt = [prctile(shuffSamples,2.5) prctile(shuffSamples,97.5)];
  595. testVal = mean_diag_vals(sub_corr);
  596. sigArray(itor, sub_corr) = (testVal < stdInt(1)) || (stdInt(2) < testVal);
  597. stacked_diag_meanCorrs(itor, sub_corr) = testVal;
  598. end
  599. stacked_arrayIDs{itor} = Dat.chanSetNames{chanSetIdx};
  600. save([save_dir datNames{d} '/' Dat.chanSetNames{chanSetIdx} 'Tuning_' cueName filesep 'SubCorrelations'], 'corr_data', 'mean_diag_vals', 'mean_offdiag_vals', 'Corr_resortedNames');
  601. itor = itor + 1;
  602. end
  603. end
  604. % Reorder by array location
  605. currOrder = stacked_arrayIDs;
  606. resortedInds = [];
  607. for itor = 1:numel(reorderNames)
  608. resortedInds = [resortedInds find(strcmp(currOrder, reorderNames{itor}) == 1)];
  609. end
  610. vals = stacked_diag_meanCorrs(resortedInds, :);
  611. labels = {stacked_arrayIDs{resortedInds}};
  612. sigs = sigArray(resortedInds, :);
  613. % Plot representational similarity summary
  614. figure('Name','Fig5b - Summary of Representational Similarity', 'Position',[1 65 1728 963]);
  615. [R,C] = ndgrid(1:size(sigs,1), 1:size(sigs,2));
  616. sig_vals = sigs(:);
  617. ind_NotSig = find(sig_vals == 0);
  618. imagesc(vals, [-1 1]);
  619. colormap(flipud(redblue));
  620. colorbar;
  621. yticks(1:numel(labels)); yticklabels(labels); set(gca, 'FontSize', 18);
  622. xticks(1:4); xticklabels(Corr_resortedNames); xtickangle(45);
  623. text(C(ind_NotSig), R(ind_NotSig), 'X', 'FontSize', 16);
  624. axis equal;
  625. exportPNGFigure(gcf, [saveLoc 'Fig5b_RepresentationalSimilarity_' cueName]);
  626. % Build set indices
  627. setInds_corr = {find(ismember(stacked_arrayIDs, DorsalSet)), ...
  628. find(ismember(stacked_arrayIDs, MiddleSet)), ...
  629. find(ismember(stacked_arrayIDs, VentralSupSet)), ...
  630. find(ismember(stacked_arrayIDs, VentralInfSet))};
  631. % Plot group-averaged correlation matrices
  632. figure('Name','Fig5a - Avg. Correlations', 'Units','pixels', 'Position',[1 65 1728 963]);
  633. setIndsToPlot = [1 3];
  634. setNames = {'Dorsal PCG', 'Superior-Ventral PCG'};
  635. plot_itor = 1;
  636. for sets = 1:numel(setIndsToPlot)
  637. i = setIndsToPlot(sets);
  638. currMat = corrStack(:, :, setInds_corr{i});
  639. currMat(currMat < -1) = -1;
  640. currMat(currMat > 1) = 1;
  641. cMat = mean(currMat, 3);
  642. % Extract R Arm x Leg sub-matrix
  643. sub_corr = 3;
  644. subset = Corr_resortedInds{sub_corr};
  645. subplot(1, numel(setIndsToPlot), plot_itor);
  646. sMat = cMat(subset, subset);
  647. indsTake_Y = ((length(subset)/2)+1):length(subset);
  648. indsTake_X = 1:(length(subset)/2);
  649. subMat = sMat(indsTake_X, indsTake_Y);
  650. currReorderInds_X = subCorrMat_resortInds{sub_corr}(1,:);
  651. currReorderInds_Y = subCorrMat_resortInds{sub_corr}(2,:);
  652. subMat_reorder = subMat(currReorderInds_X, currReorderInds_Y);
  653. mvNames = c.mvNames(subset);
  654. lbl_x = {mvNames{currReorderInds_X}};
  655. lbl_y = {mvNames{currReorderInds_Y + numel(lbl_x)}};
  656. imagesc(subMat_reorder, [-1 1]);
  657. axis square;
  658. colormap(flipud(redblue));
  659. colorbar;
  660. set(gca, 'XTick', 1:length(lbl_y), 'XTickLabels', lbl_y, 'XTickLabelRotation', 45);
  661. set(gca, 'YTick', 1:length(lbl_x), 'YTickLabels', lbl_x);
  662. set(gca, 'YDir', 'normal');
  663. title(setNames{sets});
  664. plot_itor = plot_itor + 1;
  665. end
  666. exportPNGFigure(gcf, [saveLoc 'Fig5a_AvgCorrelations_' cueName]);
  667. %% Figure 5c & 5d - Laterality Analysis and Marginalized Variance
  668. fprintf('Generating Figure 5c/5d - Laterality Analysis\n');
  669. latSetName = 'Arms';
  670. plotScatter = {'T5-d2', 'C2-d2', 'T15-m1', 'T16-v1'};
  671. Marginalized_variance = [];
  672. scatter_projections = figure('Name','Fig5c - Single Trial Projections', 'Position',[1 65 1728 963]);
  673. % Iterate through each array
  674. for array = 1:numel(reorderNames)
  675. participant = strtok(reorderNames{array}, '-');
  676. % Load mPCA output for this array
  677. mPCA_out_dir = [save_dir participant filesep reorderNames{array} 'Tuning_' cueName filesep 'Laterality_' latSetName filesep 'mPCA_out.mat'];
  678. load(mPCA_out_dir);
  679. % Compute marginalized variance
  680. cmv = mPCA_out.explVar.totalMarginalizedVar;
  681. cmv = cmv ./ sum(cmv, 2);
  682. Marginalized_variance = [Marginalized_variance; cmv];
  683. % Plot for selected arrays
  684. if ismember(reorderNames{array}, plotScatter)
  685. % Extract and process features
  686. featVals = mPCA_out.featureVals;
  687. featVals_avgWind = squeeze(nanmean(featVals, 4));
  688. stackedFeats = [];
  689. trialfactor = [];
  690. for lat = 1:size(featVals_avgWind, 2)
  691. for mv = 1:size(featVals_avgWind, 3)
  692. currVals = squeeze(featVals_avgWind(:, lat, mv, :));
  693. currVals = zscore(currVals);
  694. stackedFeats = [stackedFeats currVals];
  695. trialfactor = [trialfactor repmat([lat; mv], 1, size(currVals, 2))];
  696. end
  697. end
  698. % PCA projection
  699. [COEFF, SCORE, latent, tsquared, explained, mu] = pca(stackedFeats');
  700. proj = stackedFeats' * COEFF;
  701. indsR = find(trialfactor(1,:) == 1);
  702. indsL = find(trialfactor(1,:) == 2);
  703. % Plot on scatter figure
  704. figure(scatter_projections);
  705. subplot(1, numel(plotScatter), find(strcmp(plotScatter, reorderNames{array}) == 1));
  706. hold on;
  707. scatter(proj(indsR,1), proj(indsR,2), 50, 'r', 'filled');
  708. scatter(proj(indsL,1), proj(indsL,2), 50, 'b', 'filled');
  709. legend('Right arm', 'Left arm');
  710. title(sprintf('%s', strrep([reorderNames{array} '-' latSetName], '_', '-')));
  711. axis([-10 10 -10 10]);
  712. axis square;
  713. end
  714. end
  715. figure(scatter_projections);
  716. exportPNGFigure(gcf, [saveLoc 'Fig5c_PCA_Projections_' cueName]);
  717. % Plot marginalized variances
  718. figure('Name', 'Fig5d - Marginalized Variance from mPCA', 'Position',[1 258 339 769]);
  719. imagesc(Marginalized_variance, [0 0.75]);
  720. colormap(colMap);
  721. colorbar;
  722. yticks(1:numel(reorderNames)); yticklabels(reorderNames); set(gca, 'FontSize', 16);
  723. xticks(1:4); xticklabels({'Laterality', 'Movement', 'L x M', 'Time'});
  724. title('Marginalized Variance');
  725. exportPNGFigure(gcf, [saveLoc 'Fig5d_MargVar_' cueName]);
  726. fprintf('============ ALL FIGURES COMPLETE ============\n');

mainFigs.m at commit 5776845, under MIT · at the source

Overview

Authors: Darrel R Deo1,2, Elizaveta V Okorokova3,4, Anna L Pritchard5, Nick V Hahn1, Nicholas S Card4, Samuel R Nason-Tomaszewski5, Justin Jude6, Thomas Hosman7,8, Eun Young Choi1, Deqiang Qiu5,9, Yuguang Meng9, Maitreyee Wairagkar4, Claire Nicolas6, Foram B Kamdar1, Carrina Iacobacci4, Alexander Acosta6, Leigh R Hochberg6,7,8,10, Sydney S Cash6, Ziv M Williams11,12, Daniel B Rubin6
and 8 other authorsDavid M Brandman4, Sergey D Stavisky4, Nicholas AuYong5,13,14, Chethan Pandarinath5,13, John E Downey3, Sliman J Bensmaia3, Jaimie M Henderson1,2,15, Francis R Willett1
15 affiliations
  1. Department of Neurosurgery, Stanford University, Stanford, CA USA
  2. Wu Tsai Neurosciences Institute, Stanford University, Stanford, CA USA
  3. Department of Organismal Biology and Anatomy, University of Chicago, Chicago, IL USA
  4. Department of Neurological Surgery, University of California Davis, Davis, CA USA
  5. Wallace H. Coulter Department of Biomedical Engineering, Emory University and Georgia Institute of Technology, Atlanta, GA USA
  6. Center for Neurotechnology and Neurorecovery, Department of Neurology, Massachusetts General Hospital, Harvard Medical School, Boston, MA USA
  7. VA Center for Neurorestoration and Neurotechnology, Rehabilitation R&D Service, Providence VA Medical Center, Providence, RI USA
  8. School of Engineering, Brown University, Providence, RI USA
  9. Department of Radiology and Imaging Sciences, Emory University, Atlanta, Georgia USA
  10. Robert J. and Nancy D. Carney Institute for Brain Science, Brown University, Providence, RI USA
  11. Department of Neurosurgery, Massachusetts General Hospital, Boston, MA USA
  12. Program in Neuroscience, Harvard-MIT Program in Health Sciences and Technology, Harvard Medical School, Boston, MA USA
  13. Department of Neurosurgery, Emory University, Atlanta, GA USA
  14. Department of Cell Biology, Emory University, Atlanta, GA USA
  15. Bio-X Institute, Stanford University, Stanford, CA USA
Institutions: Stanford Medicine (United States); Stanford University (United States); University of Chicago (United States); University of California, Davis (United States); Georgia Institute of Technology (United States); Emory University (United States); The Wallace H. Coulter Department of Biomedical Engineering (United States); Harvard University (United States); Massachusetts General Hospital (United States); Brown University (United States); Providence VA Medical Center (United States); Massachusetts Institute of Technology (United States)
Journal: Nature, volume 656, issue 8128, pages 680-687
Dates: received 14 September 2024; accepted 12 May 2026; published online 17 June 2026; in print 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41586-026-10653-x · PMID 42310450 · PMCID PMC13489936 · OpenAlex W7164991137
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), other condition (population), systems (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, Single-unit activity, calcium imaging
Keywords: Motor cortex, Brain-machine interface
MeSH: Brain Mapping*, Motor Cortex*, Adult, Amyotrophic Lateral Sclerosis, Brain-Computer Interfaces, Extremities, Female, Humans, Male, Microelectrodes, Movement, Paralysis, Speech, Spinal Cord Injuries (* major topic)
Topic: EEG and Brain-Computer Interfaces (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: cited by 3 papers (Europe PMC); 67 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

Its files are read in the Code ↔ Paper reader above, with 25 matches between paragraphs and lines of code.

fwillett/cvVectorStats

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 291e70af0797ffe3761d68c2aa3187931a1038f2, 26 March 2020
Languages: MATLAB (22)
Size: 25 files, 22 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file, tests
Not found: CITATION.cff, environment file, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
24 files

d-r-deo/pcg-mosaic

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 57768453f983aa0a0d10ba441e53abaa00ae55e3, 26 June 2026
Languages: MATLAB (79), Python (8), Jupyter (5)
Size: 112 files, 92 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file, environment (Code/Utils/Python/cvPCA/environment.yml, Code/Utils/Python/RNN/environment.yml, Code/Utils/Python/RNN/requirements.txt, Code/Utils/dPCA/dPCA-master/python/requirements.txt, Code/Utils/dPCA/dPCA-master/python/setup.py), tests, 5 notebooks
Not found: CITATION.cff, continuous integration, documentation
Tools: Statistics and Machine Learning Toolbox (25 files), NumPy (8 files), PyTorch (5 files), SciPy (4 files), scikit-learn (3 files), Matplotlib (2 files), Optimization Toolbox (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
94 files

cffan/neural_seq_decoder

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: e01c313fb88c2158e55724bef0922e795eb81f55, 31 July 2024
Languages: Python (7), Jupyter (1), Shell (1)
Size: 16 files, 9 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, environment (pyproject.toml, setup.cfg, setup.py), 1 notebook
Not found: license file, CITATION.cff, tests, continuous integration, documentation
Tools: PyTorch (5 files), NumPy (3 files), Matplotlib (2 files), SciPy (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
10 files

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41586-026-10653-x.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 123 scripts, each with its path and the digest of its content;
  • 25 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41586-026-10653-x.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Publisher: n/a → Nature Portfolio

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 28 authors, 2 keywords, 14 MeSH terms, 66 references.

Cite

This paper

Deo, D. R., Okorokova, E. V., Pritchard, A. L., Hahn, N. V., Card, N. S., Nason-Tomaszewski, S. R., Jude, J., Hosman, T., Choi, E. Y., Qiu, D., Meng, Y., Wairagkar, M., Nicolas, C., Kamdar, F. B., Iacobacci, C., Acosta, A., Hochberg, L. R., Cash, S. S., Williams, Z. M., . . . Willett, F. R. (2026). A mosaic of whole-body representations on the human precentral gyrus. Nature, 656(8128), 680-687. https://doi.org/10.1038/s41586-026-10653-x

BibTeX

@article{deo2026mosaic,
author = {Deo, Darrel R and Okorokova, Elizaveta V and Pritchard, Anna L and Hahn, Nick V and Card, Nicholas S and Nason-Tomaszewski, Samuel R and Jude, Justin and Hosman, Thomas and Choi, Eun Young and Qiu, Deqiang and Meng, Yuguang and Wairagkar, Maitreyee and Nicolas, Claire and Kamdar, Foram B and Iacobacci, Carrina and Acosta, Alexander and Hochberg, Leigh R and Cash, Sydney S and Williams, Ziv M and Rubin, Daniel B and Brandman, David M and Stavisky, Sergey D and AuYong, Nicholas and Pandarinath, Chethan and Downey, John E and Bensmaia, Sliman J and Henderson, Jaimie M and Willett, Francis R},
title = {{A mosaic of whole-body representations on the human precentral gyrus}},
journal = {Nature},
year = {2026},
month = jun,
volume = {656},
number = {8128},
pages = {680--687},
publisher = {Nature Portfolio},
issn = {0028-0836},
doi = {10.1038/s41586-026-10653-x},
url = {https://doi.org/10.1038/s41586-026-10653-x},
pmid = {42310450},
pmcid = {PMC13489936}
}

RIS

TY - JOUR
AU - Deo, Darrel R
AU - Okorokova, Elizaveta V
AU - Pritchard, Anna L
AU - Hahn, Nick V
AU - Card, Nicholas S
AU - Nason-Tomaszewski, Samuel R
AU - Jude, Justin
AU - Hosman, Thomas
AU - Choi, Eun Young
AU - Qiu, Deqiang
AU - Meng, Yuguang
AU - Wairagkar, Maitreyee
AU - Nicolas, Claire
AU - Kamdar, Foram B
AU - Iacobacci, Carrina
AU - Acosta, Alexander
AU - Hochberg, Leigh R
AU - Cash, Sydney S
AU - Williams, Ziv M
AU - Rubin, Daniel B
AU - Brandman, David M
AU - Stavisky, Sergey D
AU - AuYong, Nicholas
AU - Pandarinath, Chethan
AU - Downey, John E
AU - Bensmaia, Sliman J
AU - Henderson, Jaimie M
AU - Willett, Francis R
TI - A mosaic of whole-body representations on the human precentral gyrus
T2 - Nature
J2 - Nature
PY - 2026
DA - 2026/06/17
VL - 656
IS - 8128
SP - 680
EP - 687
SN - 0028-0836
PB - Nature Portfolio
DO - 10.1038/s41586-026-10653-x
UR - https://doi.org/10.1038/s41586-026-10653-x
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41586-026-10653-x",
"type": "article-journal",
"title": "A mosaic of whole-body representations on the human precentral gyrus",
"container-title": "Nature",
"author": [
{
"family": "Deo",
"given": "Darrel R"
},
{
"family": "Okorokova",
"given": "Elizaveta V"
},
{
"family": "Pritchard",
"given": "Anna L"
},
{
"family": "Hahn",
"given": "Nick V"
},
{
"family": "Card",
"given": "Nicholas S"
},
{
"family": "Nason-Tomaszewski",
"given": "Samuel R"
},
{
"family": "Jude",
"given": "Justin"
},
{
"family": "Hosman",
"given": "Thomas"
},
{
"family": "Choi",
"given": "Eun Young"
},
{
"family": "Qiu",
"given": "Deqiang"
},
{
"family": "Meng",
"given": "Yuguang"
},
{
"family": "Wairagkar",
"given": "Maitreyee"
},
{
"family": "Nicolas",
"given": "Claire"
},
{
"family": "Kamdar",
"given": "Foram B"
},
{
"family": "Iacobacci",
"given": "Carrina"
},
{
"family": "Acosta",
"given": "Alexander"
},
{
"family": "Hochberg",
"given": "Leigh R"
},
{
"family": "Cash",
"given": "Sydney S"
},
{
"family": "Williams",
"given": "Ziv M"
},
{
"family": "Rubin",
"given": "Daniel B"
},
{
"family": "Brandman",
"given": "David M"
},
{
"family": "Stavisky",
"given": "Sergey D"
},
{
"family": "AuYong",
"given": "Nicholas"
},
{
"family": "Pandarinath",
"given": "Chethan"
},
{
"family": "Downey",
"given": "John E"
},
{
"family": "Bensmaia",
"given": "Sliman J"
},
{
"family": "Henderson",
"given": "Jaimie M"
},
{
"family": "Willett",
"given": "Francis R"
}
],
"container-title-short": "Nature",
"volume": "656",
"issue": "8128",
"page": "680-687",
"DOI": "10.1038/s41586-026-10653-x",
"PMID": "42310450",
"PMCID": "PMC13489936",
"ISSN": "0028-0836",
"publisher": "Nature Portfolio",
"URL": "https://doi.org/10.1038/s41586-026-10653-x",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
17
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41467-026-71284-4 [code]
Encoding of speech modes and loudness in ventral precentral gyrus.
Journal: Nature communications
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, 5 references, 7 authors
[2] doi:10.1038/s41591-026-04414-6 [code]
Long-term independent use of an intracortical brain-computer interface for speech and cursor control.
Journal: Nature medicine
In common: SciPy, Matplotlib, NumPy, other condition, 5 references, 5 authors
[3] doi:10.1038/s41586-026-10691-5 [code]
Mapping the neuronal building blocks of human language with language models.
Journal: Nature
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, 3 references, 2 authors
[4] doi:10.1038/s41586-026-10448-0 [code]
Plasticity and language in the anaesthetized human hippocampus.
Journal: Nature
In common: PyTorch, Statistics and Machine Learning Toolbox, SciPy, 2 other tools, 2 authors
[5] doi:10.1016/j.isci.2026.116903 [code]
Neurobiological and behavioral relevance of intrinsic functional connectome constraints on task-evoked neural activation.
Journal: iScience
In common: PyTorch, Statistics and Machine Learning Toolbox, scikit-learn, 3 other tools, humanconnectome.org/study/hcp-young-adult, 2 references
[6] doi:10.1093/braincomms/fcag127 [code]
Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex.
Journal: Brain communications
In common: scikit-learn, SciPy, Matplotlib, 1 other tool, systems, other condition, 4 references
[7] doi:10.1186/s12916-026-04903-y [code]
Structural connectome architecture and biological vulnerability shape cortical atrophy in cocaine use disorder.
Journal: BMC medicine
In common: Statistics and Machine Learning Toolbox, scikit-learn, SciPy, 2 other tools, humanconnectome.org/study/hcp-young-adult, other condition, 2 references
[8] doi:10.1038/s41467-026-75455-1 [code]
Shared latent representations of speech production for cross-patient speech decoding.
Journal: Nature communications
In common: PyTorch, Statistics and Machine Learning Toolbox, scikit-learn, 3 other tools, 3 references
[9] doi:10.1038/s41467-026-72931-6 [code]
Three parsimonious spatiotemporal patterns in cerebellum reveal individual traits in function and behavior.
Journal: Nature communications
In common: PyTorch, Statistics and Machine Learning Toolbox, scikit-learn, 3 other tools, humanconnectome.org/study/hcp-young-adult, 1 reference
[10] doi:10.1016/j.neuroimage.2026.122171 [code]
A conserved node degree-based backbone and flexible hub organization of brain connectome during naturalistic movie watching.
Journal: NeuroImage
In common: PyTorch, Statistics and Machine Learning Toolbox, scikit-learn, 3 other tools, humanconnectome.org/study/hcp-young-adult, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.