├── module3_modeling ├── matlab4neuros_AdExspike.mlx ├── matlab4neuros_AdExspikeproject_part1.mlx ├── matlab4neuros_AdExspikeproject_part1_sol.mlx ├── README.md └── simulateAdEx.m ├── module1_spikes ├── README.md ├── matlab4neuros_module1_partial.m └── matlab4neuros_module1_sol.m ├── module2_EEG ├── README.md ├── matlab4neuros_module2_partial.m └── matlab4neuros_module2_sol.m ├── module4_FMRI ├── README.md ├── bluewhitered.m ├── matlab4neuros_module4_sol.m └── matlab4neuros_module4_partial.m ├── module5_CaImaging ├── README.md ├── matlab4neuros_module5_sol.m └── matlab4neuros_module5_partial.m └── README.md /module3_modeling/matlab4neuros_AdExspike.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/HEAD/module3_modeling/matlab4neuros_AdExspike.mlx -------------------------------------------------------------------------------- /module3_modeling/matlab4neuros_AdExspikeproject_part1.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/HEAD/module3_modeling/matlab4neuros_AdExspikeproject_part1.mlx -------------------------------------------------------------------------------- /module3_modeling/matlab4neuros_AdExspikeproject_part1_sol.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/HEAD/module3_modeling/matlab4neuros_AdExspikeproject_part1_sol.mlx -------------------------------------------------------------------------------- /module3_modeling/README.md: -------------------------------------------------------------------------------- 1 | Please download the following zip file to get all the code and data files: 2 | [https://sincxpress.com/neuroscience/matlab4neuros_module3.zip](https://sincxpress.com/neuroscience/matlab4neuros_module3.zip) 3 | 4 | 5 | 6 | Direct links to the videos in this module are below (to open videos in a new tab, press Ctrl+click or CMD+click). 7 | 8 | You can also access these on [YouTube here](https://www.youtube.com/watch?v=GHtEII7_jsk&list=PLn0OLiymPak1b2aYULx6hDVU7wSGEUJqw&index=15&ab_channel=MikeXCohen). 9 | 10 | 11 | | Video number | Link (press Ctrl+click or CMD+click) | 12 | | :-----: | :---: | 13 | | Video #1 | [![Video 1](https://img.youtube.com/vi/GHtEII7_jsk/hqdefault.jpg)](https://www.youtube.com/embed/GHtEII7_jsk) | 14 | | Video #2 | [![Video 2](https://img.youtube.com/vi/FnwlWZSOwdI/hqdefault.jpg)](https://www.youtube.com/embed/FnwlWZSOwdI) | 15 | | Video #3 | [![Video 3](https://img.youtube.com/vi/8lDF0acgRdI/hqdefault.jpg)](https://www.youtube.com/embed/8lDF0acgRdI) | 16 | | Video #4 | [![Video 4](https://img.youtube.com/vi/2CX6BfrkgWE/hqdefault.jpg)](https://www.youtube.com/embed/2CX6BfrkgWE) | 17 | | Video #5 | [![Video 5](https://img.youtube.com/vi/DYC_BTTlbEU/hqdefault.jpg)](https://www.youtube.com/embed/DYC_BTTlbEU) | 18 | 19 | -------------------------------------------------------------------------------- /module1_spikes/README.md: -------------------------------------------------------------------------------- 1 | Please download the .m code files above; those scripts include code to download the data zip file: 2 | [https://sincxpress.com/neuroscience/matlab4neuros_module1.zip](https://sincxpress.com/neuroscience/matlab4neuros_module1.zip) 3 | 4 | 5 | Direct links to the videos in this module are below (to open videos in a new tab, press Ctrl+click or CMD+click). 6 | 7 | You can also access these on [YouTube here](https://www.youtube.com/watch?v=ij8npj87Hg8&list=PLn0OLiymPak1b2aYULx6hDVU7wSGEUJqw&index=2&ab_channel=MikeXCohen). 8 | 9 | | Video number | Link (press Ctrl+click or CMD+click) | 10 | | :-----: | :---: | 11 | | Video #1 | [![Video 1](https://img.youtube.com/vi/ij8npj87Hg8/hqdefault.jpg)](https://www.youtube.com/embed/ij8npj87Hg8) | 12 | | Video #2 | [![Video 2](https://img.youtube.com/vi/OoOOlbMBW_Q/hqdefault.jpg)](https://www.youtube.com/embed/OoOOlbMBW_Q) | 13 | | Video #3 | [![Video 3](https://img.youtube.com/vi/YWP8G1qLrL4/hqdefault.jpg)](https://www.youtube.com/embed/YWP8G1qLrL4) | 14 | | Video #4 | [![Video 4](https://img.youtube.com/vi/AogdorH6FVo/hqdefault.jpg)](https://www.youtube.com/embed/AogdorH6FVo) | 15 | | Video #5 | [![Video 5](https://img.youtube.com/vi/QjdVuzwwx1k/hqdefault.jpg)](https://www.youtube.com/embed/QjdVuzwwx1k) | 16 | 17 | -------------------------------------------------------------------------------- /module3_modeling/simulateAdEx.m: -------------------------------------------------------------------------------- 1 | function membPoten = simulateAdEx(params) 2 | 3 | %membPoten = simulateAdEx(params) 4 | % Simulate a neuron according to the adaptive exponential model. 5 | % 6 | % INPUT: params - a structure containing simulation parameters, 7 | % with the following required fields: 8 | % 9 | % params.C : capacitance 10 | % params.gl : leak conductance 11 | % params.El : leak voltage 12 | % params.vt : resting membrane potential 13 | % params.delT : spike width 14 | % params.a : resonantor/integrator constant 15 | % params.tauW : adaptation decay time 16 | % params.b : adaptation jump (for updating w) 17 | % params.vreset : voltage reset value 18 | % params.simDur : simulation time in ms 19 | % params.dt : integration time step in ms 20 | % params.input : input current in pA 21 | % 22 | % 23 | % OUTPUT: membranePotential - A vector of the neuron's membrane 24 | % potential in mV. 25 | % 26 | % 27 | % Equations based on: 28 | % https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2798047/ 29 | % Read more about this model: 30 | % http://www.scholarpedia.org/article/Adaptive_exponential_integrate-and-fire_model 31 | % 32 | % 33 | 34 | end % end function 35 | -------------------------------------------------------------------------------- /module2_EEG/README.md: -------------------------------------------------------------------------------- 1 | Please download the .m code files above; those scripts include code to download the data zip file: 2 | [https://sincxpress.com/neuroscience/matlab4neuros_module2.zip](https://sincxpress.com/neuroscience/matlab4neuros_module2.zip) 3 | 4 | 5 | 6 | Direct links to the videos in this module are below (to open videos in a new tab, press Ctrl+click or CMD+click). 7 | 8 | You can also access these on [YouTube here](https://www.youtube.com/watch?v=J4bLplHyMAQ&list=PLn0OLiymPak1b2aYULx6hDVU7wSGEUJqw&index=8&ab_channel=MikeXCohen). 9 | 10 | | Video number | Link (press Ctrl+click or CMD+click) | 11 | | :-----: | :---: | 12 | | Video #1 | [![Video 1](https://img.youtube.com/vi/J4bLplHyMAQ/hqdefault.jpg)](https://www.youtube.com/embed/J4bLplHyMAQ) | 13 | | Video #2 | [![Video 2](https://img.youtube.com/vi/oWzk4O2_Be4/hqdefault.jpg)](https://www.youtube.com/embed/oWzk4O2_Be4) | 14 | | Video #3 | [![Video 3](https://img.youtube.com/vi/mGudmVJQrLg/hqdefault.jpg)](https://www.youtube.com/embed/mGudmVJQrLg) | 15 | | Video #4 | [![Video 4](https://img.youtube.com/vi/ShM_17LtGX8/hqdefault.jpg)](https://www.youtube.com/embed/ShM_17LtGX8) | 16 | | Video #5 | [![Video 5](https://img.youtube.com/vi/KhVDsIfUV1M/hqdefault.jpg)](https://www.youtube.com/embed/KhVDsIfUV1M) | 17 | | Video #6 | [![Video 6](https://img.youtube.com/vi/nld5KFIG_EU/hqdefault.jpg)](https://www.youtube.com/embed/nld5KFIG_EU) | 18 | | Video #7 | [![Video 7](https://img.youtube.com/vi/diVa6pWeC2A/hqdefault.jpg)](https://www.youtube.com/embed/diVa6pWeC2A) | 19 | 20 | -------------------------------------------------------------------------------- /module4_FMRI/README.md: -------------------------------------------------------------------------------- 1 | Please download the .m code files above; those scripts include code to download the data zip file: 2 | [https://sincxpress.com/neuroscience/matlab4neuros_module4.zip](https://sincxpress.com/neuroscience/matlab4neuros_module4.zip) 3 | 4 | 5 | 6 | Direct links to the videos in this module are below (to open videos in a new tab, press Ctrl+click or CMD+click). 7 | 8 | You can also access these on [YouTube here](https://www.youtube.com/watch?v=rwaBNxiCqvE&list=PLn0OLiymPak1b2aYULx6hDVU7wSGEUJqw&index=20&ab_channel=MikeXCohen). 9 | 10 | | Video number | Link (press Ctrl+click or CMD+click) | 11 | | :-----: | :---: | 12 | | Video #1 | [![Video 1](https://img.youtube.com/vi/rwaBNxiCqvE/hqdefault.jpg)](https://www.youtube.com/embed/rwaBNxiCqvE) | 13 | | Video #2 | [![Video 2](https://img.youtube.com/vi/8luJngEkwM8/hqdefault.jpg)](https://www.youtube.com/embed/8luJngEkwM8) | 14 | | Video #3 | [![Video 3](https://img.youtube.com/vi/odDTlDRlXIE/hqdefault.jpg)](https://www.youtube.com/embed/odDTlDRlXIE) | 15 | | Video #4 | [![Video 4](https://img.youtube.com/vi/FDZd3GihdDg/hqdefault.jpg)](https://www.youtube.com/embed/FDZd3GihdDg) | 16 | | Video #5 | [![Video 5](https://img.youtube.com/vi/RmnsNZHwdyo/hqdefault.jpg)](https://www.youtube.com/embed/RmnsNZHwdyo) | 17 | | Video #6 | [![Video 6](https://img.youtube.com/vi/uFPABs6yRYY/hqdefault.jpg)](https://www.youtube.com/embed/uFPABs6yRYY) | 18 | | Video #7 | [![Video 7](https://img.youtube.com/vi/IZVnRET8ogI/hqdefault.jpg)](https://www.youtube.com/embed/IZVnRET8ogI) | 19 | 20 | -------------------------------------------------------------------------------- /module5_CaImaging/README.md: -------------------------------------------------------------------------------- 1 | Please download the .m code files above; those scripts include code to download the data zip file: 2 | [https://sincxpress.com/neuroscience/matlab4neuros_module5.zip](https://sincxpress.com/neuroscience/matlab4neuros_module5.zip) 3 | 4 | 5 | Direct links to the videos in this module are below (to open videos in a new tab, press Ctrl+click or CMD+click). 6 | 7 | You can also access these on [YouTube here](https://www.youtube.com/watch?v=fNb_FwRLkcg&list=PLn0OLiymPak1b2aYULx6hDVU7wSGEUJqw&index=27&ab_channel=MikeXCohen). 8 | 9 | 10 | | Video number | Link (press Ctrl+click or CMD+click) | 11 | | :-----: | :---: | 12 | | Video #1 | [![Video 1](https://img.youtube.com/vi/fNb_FwRLkcg/hqdefault.jpg)](https://www.youtube.com/embed/fNb_FwRLkcg) | 13 | | Video #2 | [![Video 2](https://img.youtube.com/vi/6JxybPu0s9s/hqdefault.jpg)](https://www.youtube.com/embed/6JxybPu0s9s) | 14 | | Video #3 | [![Video 3](https://img.youtube.com/vi/oe6myYZvW3s/hqdefault.jpg)](https://www.youtube.com/embed/oe6myYZvW3s) | 15 | | Video #4 | [![Video 4](https://img.youtube.com/vi/tP7-ko1PzzY/hqdefault.jpg)](https://www.youtube.com/embed/tP7-ko1PzzY) | 16 | | Video #5 | [![Video 5](https://img.youtube.com/vi/49Qrn0QBYjM/hqdefault.jpg)](https://www.youtube.com/embed/49Qrn0QBYjM) | 17 | | Video #6 | [![Video 6](https://img.youtube.com/vi/fzVP11x83F0/hqdefault.jpg)](https://www.youtube.com/embed/fzVP11x83F0) | 18 | | Video #7 | [![Video 7](https://img.youtube.com/vi/CC7-HyWBuaE/hqdefault.jpg)](https://www.youtube.com/embed/CC7-HyWBuaE) | 19 | 20 | -------------------------------------------------------------------------------- /module4_FMRI/bluewhitered.m: -------------------------------------------------------------------------------- 1 | function newmap = bluewhitered(m) 2 | %BLUEWHITERED Blue, white, and red color map. 3 | % BLUEWHITERED(M) returns an M-by-3 matrix containing a blue to white 4 | % to red colormap, with white corresponding to the CAXIS value closest 5 | % to zero. This colormap is most useful for images and surface plots 6 | % with positive and negative values. BLUEWHITERED, by itself, is the 7 | % same length as the current colormap. 8 | % 9 | % Examples: 10 | % ------------------------------ 11 | % figure 12 | % imagesc(peaks(250)); 13 | % colormap(bluewhitered(256)), colorbar 14 | % 15 | % figure 16 | % imagesc(peaks(250), [0 8]) 17 | % colormap(bluewhitered), colorbar 18 | % 19 | % figure 20 | % imagesc(peaks(250), [-6 0]) 21 | % colormap(bluewhitered), colorbar 22 | % 23 | % figure 24 | % surf(peaks) 25 | % colormap(bluewhitered) 26 | % axis tight 27 | % 28 | % See also HSV, HOT, COOL, BONE, COPPER, PINK, FLAG, 29 | % COLORMAP, RGBPLOT. 30 | 31 | % SOURCE: 32 | % https://se.mathworks.com/matlabcentral/fileexchange/4058-bluewhitered 33 | 34 | 35 | if nargin < 1 36 | m = size(get(gcf,'colormap'),1); 37 | end 38 | 39 | 40 | bottom = [0 0 0.5]; 41 | botmiddle = [0 0.5 1]; 42 | middle = [1 1 1]; 43 | topmiddle = [1 0 0]; 44 | top = [0.5 0 0]; 45 | 46 | % Find middle 47 | lims = get(gca, 'CLim'); 48 | 49 | % Find ratio of negative to positive 50 | if (lims(1) < 0) & (lims(2) > 0) 51 | % It has both negative and positive 52 | % Find ratio of negative to positive 53 | ratio = abs(lims(1)) / (abs(lims(1)) + lims(2)); 54 | neglen = round(m*ratio); 55 | poslen = m - neglen; 56 | 57 | % Just negative 58 | new = [bottom; botmiddle; middle]; 59 | len = length(new); 60 | oldsteps = linspace(0, 1, len); 61 | newsteps = linspace(0, 1, neglen); 62 | newmap1 = zeros(neglen, 3); 63 | 64 | for i=1:3 65 | % Interpolate over RGB spaces of colormap 66 | newmap1(:,i) = min(max(interp1(oldsteps, new(:,i), newsteps)', 0), 1); 67 | end 68 | 69 | % Just positive 70 | new = [middle; topmiddle; top]; 71 | len = length(new); 72 | oldsteps = linspace(0, 1, len); 73 | newsteps = linspace(0, 1, poslen); 74 | newmap = zeros(poslen, 3); 75 | 76 | for i=1:3 77 | % Interpolate over RGB spaces of colormap 78 | newmap(:,i) = min(max(interp1(oldsteps, new(:,i), newsteps)', 0), 1); 79 | end 80 | 81 | % And put 'em together 82 | newmap = [newmap1; newmap]; 83 | 84 | elseif lims(1) >= 0 85 | % Just positive 86 | new = [middle; topmiddle; top]; 87 | len = length(new); 88 | oldsteps = linspace(0, 1, len); 89 | newsteps = linspace(0, 1, m); 90 | newmap = zeros(m, 3); 91 | 92 | for i=1:3 93 | % Interpolate over RGB spaces of colormap 94 | newmap(:,i) = min(max(interp1(oldsteps, new(:,i), newsteps)', 0), 1); 95 | end 96 | 97 | else 98 | % Just negative 99 | new = [bottom; botmiddle; middle]; 100 | len = length(new); 101 | oldsteps = linspace(0, 1, len); 102 | newsteps = linspace(0, 1, m); 103 | newmap = zeros(m, 3); 104 | 105 | for i=1:3 106 | % Interpolate over RGB spaces of colormap 107 | newmap(:,i) = min(max(interp1(oldsteps, new(:,i), newsteps)', 0), 1); 108 | end 109 | 110 | end 111 | % 112 | % m = 64; 113 | % new = [bottom; botmiddle; middle; topmiddle; top]; 114 | % % x = 1:m; 115 | % 116 | % oldsteps = linspace(0, 1, 5); 117 | % newsteps = linspace(0, 1, m); 118 | % newmap = zeros(m, 3); 119 | % 120 | % for i=1:3 121 | % % Interpolate over RGB spaces of colormap 122 | % newmap(:,i) = min(max(interp1(oldsteps, new(:,i), newsteps)', 0), 1); 123 | % end 124 | % 125 | % % set(gcf, 'colormap', newmap), colorbar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Essentials of Neuroscience with MATLAB 2 | 3 | The Essentials of Neuroscience With MATLAB course was developed to provide advanced undergraduates and early graduate students with a basic familiarity with MATLAB programming with an opportunity to deepen their expertise in neuroscience data analysis using MATLAB. Each module covers a range of specific data processing, analysis, and visualization skills that neuroscientists often need. In the process, each module teaches a specific set of MATLAB skills, although there is (both necessarily and by design) some overlap in skills across modules. 4 | 5 | Each module consists of: 6 | - 🎞️ **video instruction** (hosted on YouTube; direct video links in each module folder) 7 | - 🧑‍💻 a **code exercise** (hosted here on GitHub; coupled with the video instruction) 8 | - 💾 **sample data**, if applicable (hosted on [INCF TrainingSpace](https://training.incf.org/collection/essentials-neuroscience-matlab); downloaded automatically) 9 | - 🎒 a **homework exercise** (available for educators upon request) 10 | 11 | The course is designed for [cumulative educational flow](https://training.incf.org/sites/default/files/2022-11/image%20%283%29.png), such that maximal benefit will come from taking the modules in order. However, module access is unrestricted, which means that students will have the flexibility to custom-tailor their course progression if they wish to deviate from the recommended path. 12 | 13 | ### Course Overview Video 14 | [![Course overview video](https://img.youtube.com/vi/VnHeDXaCngg/hqdefault.jpg)](https://www.youtube.com/embed/VnHeDXaCngg) 15 | 16 | ### Course Modules 17 | 24 | 25 | | # | Module | Skills taught | Code Exercise | 26 | | :--- | :--- | :---: | :---: | 27 | | 1 | [Spiking data](https://github.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/tree/main/module1_spikes) | [![m1o_4xdown](https://github.com/user-attachments/assets/02824c25-2e70-4b0e-8eca-904b3b542d0b)](https://www.youtube.com/playlist?list=PLn0OLiymPak0VVit5lk5CDAkvLgMD15xi) | [![via this button](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=mikexcohen/EssentialsOfNeuroscienceWithMATLAB&file=/module1_spikes/matlab4neuros_module1_partial.m) | 28 | | 2 | [EEG](https://github.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/tree/main/module2_EEG) | [![m2o_4xdown](https://github.com/user-attachments/assets/56987894-3331-47de-bf7c-93bace3925b3)](https://www.youtube.com/playlist?list=PLn0OLiymPak2c239nIUcUMw5KDmfNR9-v) | [![via this button](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=mikexcohen/EssentialsOfNeuroscienceWithMATLAB&file=/module2_EEG/matlab4neuros_module2_partial.m) | 29 | | 3 | [Simulation](https://github.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/tree/main/module3_modeling) | [![m3o_4xdown](https://github.com/user-attachments/assets/1849372f-11c0-4540-9aba-874ee58d97c4)](https://www.youtube.com/playlist?list=PLn0OLiymPak0vPlZUd8VrrthQdzGR17kr) | [![via this button](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=mikexcohen/EssentialsOfNeuroscienceWithMATLAB&file=/module3_modeling/matlab4neuros_module3_partial.m) | 30 | | 4 | [fMRI](https://github.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/tree/main/module4_FMRI) | [![m4o_4xdown](https://github.com/user-attachments/assets/4b134115-22ad-4111-a4d4-879aa4c0819e)](https://www.youtube.com/playlist?list=PLn0OLiymPak1gP3F_4BFYDbmR2v5n-k9X) | [![via this button](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=mikexcohen/EssentialsOfNeuroscienceWithMATLAB&file=/module4_FMRI/matlab4neuros_module4_partial.m) | 31 | | 5 | [Calcium Imaging](https://github.com/mikexcohen/EssentialsOfNeuroscienceWithMATLAB/tree/main/module5_CaImaging) | [![m5o_4xdown](https://github.com/user-attachments/assets/f07f5e3c-4bb8-45b0-a753-cf7621465a96)](https://www.youtube.com/playlist?list=PLn0OLiymPak1i0V3AXCUJ6HvmqfB2Mf24) | [![via this button](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=mikexcohen/EssentialsOfNeuroscienceWithMATLAB&file=/module5_CaImaging/matlab4neuros_module5_partial.m) | 32 | 33 | 34 | Enjoy! 35 | 36 | -------------------------------------------------------------------------------- /module2_EEG/matlab4neuros_module2_partial.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 2: spectral EEG 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the partially completed code accompanying the video. 11 | % You can work through this code before looking at the solution. 12 | 13 | %% data sources 14 | 15 | % Relevant reference for this dataset: 16 | % Mora-Cortes, A.; Ridderinkhof, KR; Cohen, MX. (2018) Evaluating the feasibility of the 17 | % steady-state visual evoked potential (SSVEP) to study temporal attention. Psychophysiology. 18 | 19 | %% a clear MATLAB workspace is a clear mental workspace 20 | 21 | close all; clear; clc 22 | 23 | %% 24 | % -------------------------------------------------------- % 25 | % % 26 | % Video 2: Electrode locations in 2D and 3D % 27 | % % 28 | % -------------------------------------------------------- % 29 | % 30 | %% Load the data 31 | 32 | % download from online and unpack 33 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module2.zip',weboptions('Timeout',120)); 34 | unzip('file.zip'); 35 | 36 | 37 | % load data 38 | load SSVEPdata.mat 39 | 40 | 41 | % inspect the structure 42 | whos 43 | EEG 44 | 45 | %% plot of electrode locations 46 | 47 | figure(1), clf 48 | 49 | % plot the electrode positions in 3D 50 | plot3([EEG.chanlocs.X],,,'ko','markerfacecolor','k') 51 | 52 | % draw the text labels 53 | hold on 54 | for i=1:EEG.nbchan 55 | text(,,EEG.chanlocs(i).Z, ) 56 | end 57 | 58 | % make the plot look nicer and more interactive 59 | xlabel('X'), ylabel('Y'), zlabel('Z') 60 | title('Electrode locations') 61 | 62 | 63 | %% plot of electrode locations in 2D 64 | 65 | % plot ERPs for dimension-specific averaging 66 | figure(2), clf 67 | 68 | % show an empty topoplot 69 | topoplotIndie( ,EEG.chanlocs,'electrodes','numbers'); 70 | title('2D topographical map') 71 | 72 | 73 | %% 74 | % -------------------------------------------------------- % 75 | % % 76 | % Video 3: Spectral analysis via the FFT % 77 | % % 78 | % -------------------------------------------------------- % 79 | % 80 | %% 81 | 82 | %% spectral analysis 83 | 84 | % soft-code a channel to plot 85 | chan2plot = 22; 86 | 87 | 88 | % FFT of one channel, obtained in a loop over trials 89 | channelPower = zeros(EEG.pnts,EEG.trials); 90 | for triali=1:EEG.trials 91 | channelPower(:,triali) = abs(fft( )).^2; 92 | end 93 | 94 | % can you get the same result without using a loop? 95 | channelPower = 96 | 97 | % vector of frequencies 98 | hz = linspace(0,EEG.srate/2,floor(EEG.pnts/2)+1); 99 | 100 | %% visualization 101 | 102 | figure(3), clf 103 | h = plot(hz,channelPower(1:length(hz),:)); 104 | 105 | % plot the trial-average on top of the individual trials 106 | plot(hz,mean(channelPower(1:length(hz),:),pi),'k','linew',2) 107 | 108 | 109 | % set all individual lines to gray 110 | set(h,'color', 111 | 112 | 113 | % pretty the plot 114 | set(gca,'xlim',[5 30]) 115 | xlabel('Frequency (Hz)') 116 | ylabel('Power (a.u.)') 117 | title([ 'Power spectra from channel ' num2str(chan2plot) ]) 118 | 119 | %% 120 | % -------------------------------------------------------- % 121 | % % 122 | % Video 4: Image of channel spectra % 123 | % % 124 | % -------------------------------------------------------- % 125 | % 126 | %% 127 | 128 | %% all channel spectra 129 | 130 | % FFT of all channels at the same time, 131 | % and then average over all trials. 132 | allChannelPower = 133 | 134 | % and show in an image 135 | figure(4), clf 136 | imagesc(hz,[], 137 | set(gca,'xlim',[-3 0],'clim',[0 .2]) 138 | xlabel('Frequency (Hz)') 139 | ylabel('Channel index') 140 | title('Spectral power over all channels') 141 | colorbar 142 | 143 | %% now sort by channel X-coordinate 144 | 145 | % we only need the sorting index, not the sorted values 146 | 147 | 148 | % same plot as above 149 | figure(5), clf 150 | imagesc(hz,[],allChannelPower(sortXidx,1:length(hz))) 151 | set(gca,'xlim',[3 30],'clim',[0 .2]) 152 | xlabel('Frequency (Hz)') 153 | ylabel('<-- anterior -- Channel index -- posterior -->') 154 | title('Spectral power over all channels') 155 | colorbar 156 | 157 | %% 158 | % -------------------------------------------------------- % 159 | % % 160 | % Video 5: Topographical maps % 161 | % % 162 | % -------------------------------------------------------- % 163 | % 164 | %% 165 | 166 | %% topoplot of 20 and 24 Hz activity 167 | 168 | 169 | % find frequency boundaries 170 | hzidx(1) = dsearchn(hz',20); 171 | hzidx(2) = 172 | 173 | 174 | figure(6), clf 175 | for i=1:2 176 | % specify the subplot 177 | subplot(1,2,1) 178 | 179 | % call the topographical map function 180 | topoplotIndie( ,EEG.chanlocs,'numcontour',0); 181 | 182 | % set colorlimit and write the title 183 | set(gca,'clim',[0 .3]) 184 | title(Power at i Hz) 185 | end 186 | 187 | % explore some colormaps 188 | colormap parula 189 | 190 | 191 | %% 192 | % -------------------------------------------------------- % 193 | % % 194 | % Video 6: Endogenous alpha % 195 | % % 196 | % -------------------------------------------------------- % 197 | % 198 | %% 199 | 200 | %% 201 | 202 | % frequency boundaries 203 | freqrange = [8 12]; 204 | alphaidx = dsearchn(hz',freqrange); 205 | 206 | % topoplot of alpha over all time points 207 | figure(7), clf 208 | subplot(121) 209 | topoplotIndie(,EEG.chanlocs,'numcontour',0); 210 | title('Raw alpha power') 211 | 212 | 213 | %% alpha change from baseline 214 | 215 | % define time window and convert to indices 216 | timerange = [ ]; 217 | timeidx = dsearchn(timerange'); 218 | 219 | % compute new power matrices 220 | powerPreStim = mean(abs(fft(EEG.data(:,timeidx(1):timeidx(2),:),EEG.pnts,2)/EEG.pnts).^2,3); 221 | powerPstStim = 222 | 223 | 224 | % alpha power effect 225 | alphaPstVsPre = 10*log10( mean(powerPstStim(:,alphaidx(1):alphaidx(2)),2) ./ ... 226 | mean(powerPreStim(:,alphaidx(1):alphaidx(2)),2) ); 227 | 228 | 229 | %% show the topographical map 230 | 231 | subplot(122) 232 | topoplotIndie(alphaPstVsPre,EEG.chanlocs,'numcontour',0); 233 | set(gca,'clim',[-1 1]) 234 | title('Task-related alpha power') 235 | 236 | 237 | %% 238 | % -------------------------------------------------------- % 239 | % % 240 | % Video 7: Correlate alpha with SSVEP % 241 | % % 242 | % -------------------------------------------------------- % 243 | % 244 | %% 245 | 246 | %% 247 | 248 | figure(8), clf 249 | subplot(121) 250 | 251 | % create temp variables for convenience 252 | x = mean(allChannelPower(:,alphaidx(1):alphaidx(2)),2); 253 | y = allChannelPower(:,hzidx(1)); 254 | 255 | % scatter plot 256 | scatter() 257 | axis square 258 | xlabel('Raw alpha power') 259 | ylabel('20 Hz SSVEP power') 260 | 261 | % compute correlation and show in the title 262 | r = corrcoef(); 263 | title([ 'r = ' num2str(r) ]) 264 | 265 | 266 | % more temp variables (x here is short so left out) 267 | y = allChannelPower(:,hzidx(1)); 268 | 269 | % scatter plot 270 | subplot(122) 271 | scatter(alphaPstVsPre,y,120,'bs','markerfacecolor','b','markerfacealpha',.2) 272 | axis square 273 | xlabel('Task alpha power') 274 | ylabel('20 Hz SSVEP power') 275 | 276 | % correlation and title 277 | r = corrcoef(alphaPstVsPre,y); 278 | title([ 'r = ' num2str(r(2),3) ]) 279 | 280 | %% done. 281 | -------------------------------------------------------------------------------- /module1_spikes/matlab4neuros_module1_partial.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 1: spikes 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the partially completed code accompanying the video. 11 | % You can work through this code before looking at the solution. 12 | 13 | %% data sources 14 | 15 | % Main reference for this dataset: 16 | % Kohn, A., Smith, M.A. (2016) Utah array extracellular recordings of spontaneous and visually 17 | % evoked activity from anesthetized macaque primary visual cortex (V1). CRCNS.org 18 | % http://dx.doi.org/10.6080/K0NC5Z4X 19 | 20 | % Direct link to the data (downloading requires a free crcns.org account): 21 | % http://crcns.org/data-sets/vc/pvc-11 22 | 23 | % Direct link to the description of the data: 24 | % http://crcns.org/files/data/pvc-11/crcns_pvc-11_data_description.pdf 25 | 26 | % Direct link to the publication using these data: 27 | % https://www.jneurosci.org/content/jneuro/28/48/12591.full.pdf 28 | 29 | %% a clear MATLAB workspace is a clear mental workspace 30 | 31 | close all; clear; clc 32 | 33 | %% 34 | % -------------------------------------------------------- % 35 | % % 36 | % Video 2: Import data and convert spikes to data matrix % 37 | % % 38 | % -------------------------------------------------------- % 39 | % 40 | %% Load the data 41 | 42 | % download from online and unpack 43 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module1.zip',weboptions('Timeout',120)); 44 | unzip('file.zip'); 45 | 46 | % and import into matlab 47 | load('data_monkey1_gratings.mat') 48 | whos 49 | 50 | %% spike count for all trials 51 | 52 | % note about sizes: 53 | % data.EVENTS is [ neurons gratings trials ] 54 | 55 | % extract sizes of the data matrix 56 | [nNeurons,nGratings,nTrials] = size(data.EVENTS); 57 | 58 | % initialize a data matrix 59 | totalSpikeCount = zeros( ); 60 | 61 | 62 | % loop through all elements of the data matrix 63 | for neuroni=1:nNeurons 64 | for grati=1:nGratings 65 | for triali=1:nTrials 66 | 67 | % count the number of spikes in each cell 68 | totalSpikeCount(neuroni,grati,triali) = ; 69 | end 70 | end 71 | end 72 | 73 | %% and now using cellfun 74 | 75 | % a much more efficient method to get the same result 76 | totalSpikeCount2 = cellfun( ); 77 | 78 | 79 | % compare them to show they're equal 80 | size(totalSpikeCount) 81 | size(totalSpikeCount2) 82 | 83 | % logic: if A=B, then A-B=0 84 | differenceMatrix = totalSpikeCount - totalSpikeCount2; 85 | sum(differenceMatrix 86 | 87 | 88 | %% 89 | % ----------------------------------------------------------- % 90 | % % 91 | % Video 3: Histograms of spike counts over units and trials % 92 | % % 93 | % ----------------------------------------------------------- % 94 | % 95 | %% histogram of spike counts 96 | 97 | figure(1), clf 98 | 99 | subplot(211) 100 | histogram( ) 101 | xlabel('Number of spikes') 102 | ylabel('Count') 103 | title('Including zero-spike trials') 104 | 105 | %% repeat without no-spike trials 106 | 107 | subplot(212) 108 | histogram( nonzeros(totalSpikeCount),3 ) 109 | xlabel('Number of spikes') 110 | ylabel('Count') 111 | title('Excluding zero-spike trials') 112 | 113 | 114 | %% 115 | % ---------------------------------------------------- % 116 | % % 117 | % Video 4: Tuning curve for a randomly selected cell % 118 | % % 119 | % ---------------------------------------------------- % 120 | % 121 | %% pick a unit and compute its tuning curve 122 | 123 | % pick a neuron at random 124 | randomunit = 125 | 126 | % compute the average spike count over all trials per gradient 127 | averageSpikes = 128 | 129 | % find the maximum response 130 | maxval, = max(averageSpikes); 131 | 132 | %% visualize! 133 | 134 | % vector of stimulus gradient orientations in degrees 135 | gradientOrient = 0:30:330; 136 | 137 | % generate a figure 138 | figure(2), clf 139 | subplot(5,1,2:4) 140 | 141 | % bar plot 142 | bar(averageSpikes) 143 | xlabel('Gradient orientation') 144 | ylabel('Average spike count') 145 | title("Unit number " + randomunit + " 'prefers' " + gradientOrient(maxresp) + "$^\circ$",'Interpreter','latex') 146 | 147 | % make the figure look a bit nicer 148 | set(gca,'fontsize',14) 149 | set(gcf,'color','m') 150 | 151 | %% same data in a polar plot 152 | 153 | figure(3), clf 154 | polarplot(gradientOrient,averageSpikes) 155 | 156 | % hmm... not quite right. 157 | help polarplot 158 | polarplot(deg2rad(gradientOrient),averageSpikes,... 159 | 'ks-','linewidth',3,'markersize',14,'markerfacecolor','w') 160 | 161 | 162 | % make the graph connected (wrap around without a gap) 163 | 164 | 165 | 166 | %% 167 | % -------------------------------------------------- % 168 | % % 169 | % Video 5: Visualize a spatial map of spike counts % 170 | % % 171 | % -------------------------------------------------- % 172 | % 173 | %% gather the data 174 | 175 | % compute total number of APs per cell 176 | APsPerCell = 177 | 178 | % list the channels with units 179 | uniquechans = unique(data.CHANNELS(:,1)); 180 | 181 | %% create the data matrix 182 | 183 | % initialize output matrix 184 | spikesMap = zeros( 185 | 186 | % loop over all channels 187 | for chani=1:length(uniquechans) 188 | 189 | % find all units on this electrode 190 | whichchans = 191 | 192 | % map coordinate for this channel 193 | [row,col] = find(data.MAP==uniquechans(chani)); 194 | 195 | % average number of spikes for all units on this channel 196 | spikesMap(row,col) = 197 | end 198 | 199 | %% now let's see what it looks like 200 | 201 | figure(3), clf 202 | subplot(121) 203 | 204 | % draw the image 205 | imagesc(spikesMap) 206 | title('Firing rate map') 207 | 208 | % make the plot look nicer 209 | axis square 210 | colorbar 211 | 212 | % turn off x- and y-axis tick marks, and fix the color limit 213 | set(gca, 214 | 215 | % unipolar colormap 216 | colormap hot 217 | 218 | %% 219 | % -------------------------------------------------- % 220 | % % 221 | % Video 6: Visualize a spatial map of tuning angle % 222 | % % 223 | % -------------------------------------------------- % 224 | % 225 | %% 226 | 227 | % compute the average spike count over all trials per gradient 228 | averageSpikes = 229 | 230 | % find the maximum response and convert to degrees 231 | [maxval,maxresp] = max(averageSpikes,[],2); 232 | maxresp = gradientOrient(maxresp); 233 | 234 | %% create the data matrix 235 | 236 | % initialize output matrix as NaN's 237 | orientationMap = (size(data.MAP)); 238 | 239 | 240 | for chani=1:length(uniquechans) 241 | 242 | % find all units on this electrode 243 | whichchans = data.CHANNELS(:,1)==uniquechans(chani); 244 | 245 | % map coordinate for this channel 246 | [row,col] = find(data.MAP==uniquechans(chani)); 247 | 248 | % average number of spikes for all units on this channel 249 | orientationMap = mean(maxresp(whichchans)); 250 | end 251 | 252 | %% visualize using the same (unipolar) colormap as the previous map 253 | 254 | subplot(122) 255 | imagesc(orientationMap) 256 | axis square 257 | colorbar 258 | set(gca,'xtick',[],'ytick',[],'clim',[0 360]) 259 | title('Orientation map') 260 | 261 | %% ... but we have circular data 262 | 263 | % built-in circular colormap 264 | colormap hsv 265 | 266 | % create our own bimodal circular colormap 267 | c = copper(16); 268 | colormap([ c; flipud(c); c; flipud(c); ]) 269 | 270 | %% ... but nan's shouldn't have the same color as "0" 271 | 272 | pcolor() 273 | axis square, axis ij 274 | colorbar 275 | set(gca,'xtick',[],'ytick',[],'clim',[0 360]) 276 | xlabel('Spatial dimension X') 277 | ylabel('Spatial dimension Y') 278 | title('Orientation map') 279 | colormap() 280 | 281 | %% done. 282 | -------------------------------------------------------------------------------- /module2_EEG/matlab4neuros_module2_sol.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 2: spectral EEG 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the solutions code. 11 | % You should work through the "partial" file before looking at this. 12 | 13 | %% data sources 14 | 15 | % Relevant reference for this dataset: 16 | % Mora-Cortes, A.; Ridderinkhof, KR; Cohen, MX. (2018) Evaluating the feasibility of the 17 | % steady-state visual evoked potential (SSVEP) to study temporal attention. Psychophysiology. 18 | 19 | %% a clear MATLAB workspace is a clear mental workspace 20 | 21 | close all; clear; clc 22 | 23 | %% 24 | % -------------------------------------------------------- % 25 | % % 26 | % Video 2: Electrode locations in 2D and 3D % 27 | % % 28 | % -------------------------------------------------------- % 29 | % 30 | %% Load the data 31 | 32 | % download from online and unpack 33 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module2.zip',weboptions('Timeout',120)); 34 | unzip('file.zip'); 35 | 36 | 37 | % load data 38 | load SSVEPdata.mat 39 | 40 | 41 | % inspect the structure 42 | whos 43 | EEG 44 | 45 | %% plot of electrode locations in 3D 46 | 47 | figure(1), clf 48 | 49 | % plot the electrode positions in 3D 50 | plot3([EEG.chanlocs.X],[EEG.chanlocs.Y],[EEG.chanlocs.Z],'ko','markerfacecolor','k') 51 | 52 | % draw the text labels 53 | hold on 54 | for i=1:EEG.nbchan 55 | text(EEG.chanlocs(i).X+3,EEG.chanlocs(i).Y,EEG.chanlocs(i).Z,num2str(i)) 56 | end 57 | 58 | % make the plot look nicer and more interactive 59 | xlabel('X'), ylabel('Y'), zlabel('Z') 60 | title('Electrode locations') 61 | rotate3d on 62 | axis square 63 | 64 | %% plot of electrode locations in 2D 65 | 66 | % plot ERPs for dimension-specific averaging 67 | figure(2), clf 68 | 69 | % show an empty topoplot 70 | topoplotIndie(zeros(EEG.nbchan,1),EEG.chanlocs,'electrodes','numbers'); 71 | title('2D topographical map') 72 | 73 | %% 74 | % -------------------------------------------------------- % 75 | % % 76 | % Video 3: Spectral analysis via the FFT % 77 | % % 78 | % -------------------------------------------------------- % 79 | % 80 | %% 81 | 82 | %% spectral analysis 83 | 84 | % soft-code a channel to plot 85 | chan2plot = 22; 86 | 87 | 88 | % FFT of one channel 89 | channelPower = zeros(EEG.pnts,EEG.trials); 90 | for triali=1:EEG.trials 91 | channelPower(:,triali) = abs(fft(EEG.data(chan2plot,:,triali))).^2; 92 | end 93 | 94 | % without a loop 95 | channelPower = squeeze(abs(fft(EEG.data(chan2plot,:,:),[],2)).^2); 96 | 97 | % vector of frequencies 98 | hz = linspace(0,EEG.srate/2,floor(EEG.pnts/2)+1); 99 | 100 | %% visualization 101 | 102 | figure(3), clf, hold on 103 | h = plot(hz,channelPower(1:length(hz),:)); 104 | plot(hz,mean(channelPower(1:length(hz),:),2),'k','linew',2) 105 | 106 | % set all individual lines to black 107 | set(h,'color',.8*ones(3,1)) 108 | 109 | % pretty the plot 110 | set(gca,'xlim',[5 30]) 111 | xlabel('Frequency (Hz)') 112 | ylabel('Power (a.u.)') 113 | title([ 'Power spectra from channel ' num2str(chan2plot) ]) 114 | 115 | %% 116 | % -------------------------------------------------------- % 117 | % % 118 | % Video 4: Image of channel spectra % 119 | % % 120 | % -------------------------------------------------------- % 121 | % 122 | %% 123 | 124 | %% all channel spectra 125 | 126 | % FFT of all channels at the same time, 127 | % and then average over all trials. 128 | allChannelPower = mean(abs(fft(EEG.data,[],2)/EEG.pnts).^2,3); 129 | 130 | % and show in an image 131 | figure(4), clf 132 | imagesc(hz,[],allChannelPower(:,1:length(hz))) 133 | set(gca,'xlim',[3 30],'clim',[0 .2]) 134 | xlabel('Frequency (Hz)') 135 | ylabel('Channel index') 136 | title('Spectral power over all channels') 137 | colorbar 138 | 139 | %% now sort by channel X-coordinate 140 | 141 | % we only need the sorting index, not the sorted values 142 | [~,sortXidx] = sort([EEG.chanlocs.X]); 143 | 144 | % same plot as above 145 | figure(5), clf 146 | imagesc(hz,[],allChannelPower(sortXidx,1:length(hz))) 147 | set(gca,'xlim',[3 30],'clim',[0 .2]) 148 | xlabel('Frequency (Hz)') 149 | ylabel('<-- anterior -- Channel index -- posterior -->') 150 | title('Spectral power over all channels') 151 | colorbar 152 | 153 | %% 154 | % -------------------------------------------------------- % 155 | % % 156 | % Video 5: Topographical maps % 157 | % % 158 | % -------------------------------------------------------- % 159 | % 160 | %% 161 | 162 | %% topoplot of 20 and 24 Hz activity 163 | 164 | 165 | % find frequency boundaries 166 | hzidx(1) = dsearchn(hz',20); 167 | hzidx(2) = dsearchn(hz',24); 168 | 169 | 170 | figure(6), clf 171 | for i=1:2 172 | % specify the subplot 173 | subplot(1,2,i) 174 | 175 | % call the topographical map function 176 | topoplotIndie(allChannelPower(:,hzidx(i)),EEG.chanlocs,'numcontour',0); 177 | 178 | % set colorlimit and write the title 179 | set(gca,'clim',[0 .3]) 180 | title([ 'Power at ' num2str(hz(hzidx(i))) ' Hz' ]) 181 | end 182 | 183 | % explore some colormaps 184 | colormap parula 185 | 186 | 187 | %% 188 | % -------------------------------------------------------- % 189 | % % 190 | % Video 6: Endogenous alpha % 191 | % % 192 | % -------------------------------------------------------- % 193 | % 194 | %% 195 | 196 | %% 197 | 198 | % frequency boundaries 199 | freqrange = [8 12]; 200 | alphaidx = dsearchn(hz',freqrange'); 201 | 202 | % topoplot of alpha over all time points 203 | figure(7), clf 204 | subplot(121) 205 | topoplotIndie(mean(allChannelPower(:,alphaidx(1):alphaidx(2)),2),EEG.chanlocs,'numcontour',0); 206 | title('Raw alpha power') 207 | 208 | 209 | %% alpha change from baseline 210 | 211 | % define time window and convert to indices 212 | timerange = [ -1000 0 1000 ]; 213 | timeidx = dsearchn(EEG.times',timerange'); 214 | 215 | % compute new power matrices 216 | powerPreStim = mean(abs(fft(EEG.data(:,timeidx(1):timeidx(2),:),EEG.pnts,2)/EEG.pnts).^2,3); 217 | powerPstStim = mean(abs(fft(EEG.data(:,timeidx(2):timeidx(3),:),EEG.pnts,2)/EEG.pnts).^2,3); 218 | 219 | 220 | % alpha power effect 221 | alphaPstVsPre = 10*log10( mean(powerPstStim(:,alphaidx(1):alphaidx(2)),2) ./ ... 222 | mean(powerPreStim(:,alphaidx(1):alphaidx(2)),2) ); 223 | 224 | 225 | %% show the topographical map 226 | 227 | subplot(122) 228 | topoplotIndie(alphaPstVsPre,EEG.chanlocs,'numcontour',0); 229 | set(gca,'clim',[-1 1]) 230 | title('Task-related alpha power') 231 | 232 | 233 | %% 234 | % -------------------------------------------------------- % 235 | % % 236 | % Video 7: Correlate alpha with SSVEP % 237 | % % 238 | % -------------------------------------------------------- % 239 | % 240 | %% 241 | 242 | %% 243 | 244 | figure(8), clf 245 | subplot(121) 246 | 247 | % create temp variables for convenience 248 | x = mean(allChannelPower(:,alphaidx(1):alphaidx(2)),2); 249 | y = allChannelPower(:,hzidx(1)); 250 | 251 | % scatter plot 252 | scatter(x,y,120,'ro','markerfacecolor','r','markerfacealpha',.2) 253 | axis square 254 | xlabel('Raw alpha power') 255 | ylabel('20 Hz SSVEP power') 256 | 257 | % compute correlation and show in the title 258 | r = corrcoef(x,y); 259 | title([ 'r = ' num2str(r(2),3) ]) 260 | 261 | 262 | % more temp variables (x here is short so left out) 263 | y = allChannelPower(:,hzidx(1)); 264 | 265 | % scatter plot 266 | subplot(122) 267 | scatter(alphaPstVsPre,y,120,'bs','markerfacecolor','b','markerfacealpha',.2) 268 | axis square 269 | xlabel('Task alpha power') 270 | ylabel('20 Hz SSVEP power') 271 | 272 | % correlation and title 273 | r = corrcoef(alphaPstVsPre,y); 274 | title([ 'r = ' num2str(r(2),3) ]) 275 | 276 | 277 | %% done. 278 | -------------------------------------------------------------------------------- /module1_spikes/matlab4neuros_module1_sol.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 1: spikes 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the solutions code. 11 | % You should work through the "partial" file before looking at this. 12 | 13 | %% data sources 14 | 15 | % Main reference for this dataset: 16 | % Kohn, A., Smith, M.A. (2016) Utah array extracellular recordings of spontaneous and visually 17 | % evoked activity from anesthetized macaque primary visual cortex (V1). CRCNS.org 18 | % http://dx.doi.org/10.6080/K0NC5Z4X 19 | 20 | % Direct link to the data (downloading requires a free crcns.org account): 21 | % http://crcns.org/data-sets/vc/pvc-11 22 | 23 | % Direct link to the description of the data: 24 | % http://crcns.org/files/data/pvc-11/crcns_pvc-11_data_description.pdf 25 | 26 | % Direct link to the publication using these data: 27 | % https://www.jneurosci.org/content/jneuro/28/48/12591.full.pdf 28 | 29 | %% a clear MATLAB workspace is a clear mental workspace 30 | 31 | close all; clear; clc 32 | 33 | %% 34 | % -------------------------------------------------------- % 35 | % % 36 | % Video 2: Import data and convert spikes to data matrix % 37 | % % 38 | % -------------------------------------------------------- % 39 | % 40 | %% Load the data 41 | 42 | % download from online and unpack 43 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module1.zip',weboptions('Timeout',120)); 44 | unzip('file.zip'); 45 | 46 | % import to matlab 47 | load('data_monkey1_gratings.mat') 48 | whos 49 | 50 | %% spike count for all trials 51 | 52 | % note about sizes: 53 | % data.EVENTS is [ neurons gratings trials ] 54 | 55 | % extract sizes of the data matrix 56 | [nNeurons,nGratings,nTrials] = size(data.EVENTS); 57 | 58 | % initialize 59 | totalSpikeCount = zeros([nNeurons,nGratings,nTrials]); 60 | 61 | % loop through all elements of the data matrix 62 | for neuroni=1:nNeurons 63 | for grati=1:nGratings 64 | for triali=1:nTrials 65 | 66 | % count the number of spikes in each cell 67 | totalSpikeCount(neuroni,grati,triali) = numel(data.EVENTS{neuroni,grati,triali}); 68 | end 69 | end 70 | end 71 | 72 | %% and now using cellfun 73 | 74 | % a much more efficient method to get the same result 75 | totalSpikeCount2 = cellfun(@length,data.EVENTS); 76 | 77 | 78 | % compare them to show they're equal 79 | size(totalSpikeCount) 80 | size(totalSpikeCount2) 81 | 82 | % logic: if A=B, then A-B=0 83 | differenceMatrix = totalSpikeCount - totalSpikeCount2; 84 | sum(differenceMatrix(:)) 85 | 86 | 87 | %% 88 | % ----------------------------------------------------------- % 89 | % % 90 | % Video 3: Histograms of spike counts over units and trials % 91 | % % 92 | % ----------------------------------------------------------- % 93 | % 94 | %% histogram of spike counts 95 | 96 | figure(1), clf 97 | 98 | subplot(211) 99 | histogram( totalSpikeCount(:),40 ) 100 | xlabel('Number of spikes') 101 | ylabel('Count') 102 | title('Including zero-spike trials') 103 | 104 | %% repeat without no-spike trials 105 | 106 | subplot(212) 107 | histogram( nonzeros(totalSpikeCount),40 ) 108 | xlabel('Number of spikes') 109 | ylabel('Count') 110 | title('Excluding zero-spike trials') 111 | 112 | 113 | %% 114 | % ---------------------------------------------------- % 115 | % % 116 | % Video 4: Tuning curve for a randomly selected cell % 117 | % % 118 | % ---------------------------------------------------- % 119 | % 120 | %% pick a unit and compute its tuning curve 121 | 122 | % pick a neuron at random 123 | randomunit = randi(nNeurons,1); 124 | 125 | % compute the average spike count over all trials per orientation 126 | averageSpikes = mean(totalSpikeCount(randomunit,:,:),3); 127 | 128 | % find the maximum response 129 | [maxval,maxresp] = max(averageSpikes); 130 | 131 | %% visualize! 132 | 133 | % vector of stimulus orientations in degrees 134 | gradientOrient = 0:30:330; 135 | 136 | % generate a figure 137 | figure(2), clf 138 | subplot(5,1,2:4) 139 | 140 | % bar plot 141 | bar(gradientOrient,averageSpikes) 142 | xlabel('Gradient orientation') 143 | ylabel('Average spike count') 144 | title("Unit number " + randomunit + " 'prefers' " + gradientOrient(maxresp) + "$^\circ$",'Interpreter','latex') 145 | 146 | % make the figure look a bit nicer 147 | set(gca,'fontsize',14,'xticklabel',gradientOrient) 148 | set(gcf,'color','w') 149 | 150 | %% same data in a polar plot 151 | 152 | figure(3), clf 153 | polarplot(gradientOrient,averageSpikes) 154 | 155 | help polarplot 156 | polarplot(deg2rad(gradientOrient),averageSpikes,... 157 | 'ks-','linewidth',3,'markersize',14,'markerfacecolor','w') 158 | 159 | 160 | % make the graph connected (wrap around without a gap) 161 | th = [gradientOrient gradientOrient(1)]; 162 | r = [averageSpikes averageSpikes(1)]; 163 | polarplot(deg2rad(th),r,... 164 | 'ks-','linewidth',3,'markersize',14,'markerfacecolor','w') 165 | 166 | 167 | %% 168 | % -------------------------------------------------- % 169 | % % 170 | % Video 5: Visualize a spatial map of spike counts % 171 | % % 172 | % -------------------------------------------------- % 173 | % 174 | %% gather the data 175 | 176 | % first map is average number of spikes 177 | APsPerCell = mean(mean(totalSpikeCount,2),3); 178 | 179 | % list the channels with units 180 | uniquechans = unique(data.CHANNELS(:,1)); 181 | 182 | %% create the data matrix 183 | 184 | % initialize output matrix 185 | spikesMap = zeros(size(data.MAP)); 186 | 187 | % loop over all unique units 188 | for chani=1:length(uniquechans) 189 | 190 | % find all units on this electrode 191 | whichunits = data.CHANNELS(:,1)==uniquechans(chani); 192 | 193 | % map coordinate for this channel 194 | [row,col] = find(data.MAP==uniquechans(chani)); 195 | 196 | % average number of spikes for all units on this channel 197 | spikesMap(row,col) = mean(APsPerCell(whichunits)); 198 | end 199 | 200 | %% now let's see what it looks like 201 | 202 | figure(4), clf 203 | subplot(121) 204 | 205 | % draw the image 206 | imagesc(spikesMap) 207 | title('Firing rate map') 208 | 209 | % make the plot look nicer 210 | axis square 211 | colorbar 212 | set(gca,'xtick',[],'ytick',[],'clim',[0 20]) 213 | xlabel('Spatial dimension X') 214 | ylabel('Spatial dimension Y') 215 | colormap hot 216 | 217 | %% 218 | % -------------------------------------------------- % 219 | % % 220 | % Video 6: Visualize a spatial map of tuning angle % 221 | % % 222 | % -------------------------------------------------- % 223 | % 224 | %% 225 | 226 | % compute the average spike count over all trials per orientation 227 | averageSpikes = mean(totalSpikeCount,3); 228 | 229 | % find the maximum response and convert to degrees 230 | [maxval,maxresp] = max(averageSpikes,[],2); 231 | maxresp = gradientOrient(maxresp); 232 | 233 | %% create the data matrix 234 | 235 | % initialize output matrix 236 | orientationMap = nan(size(data.MAP)); 237 | 238 | for chani=1:length(uniquechans) 239 | 240 | % find all units on this electrode 241 | whichchans = data.CHANNELS(:,1)==uniquechans(chani); 242 | 243 | % map coordinate for this channel 244 | [row,col] = find(data.MAP==uniquechans(chani)); 245 | 246 | % average number of spikes for all units on this channel 247 | orientationMap(row,col) = mean(maxresp(whichchans)); 248 | end 249 | 250 | %% visualize using the same (unipolar) colormap as the previous map 251 | 252 | subplot(122) 253 | imagesc(orientationMap) 254 | axis square 255 | colorbar 256 | set(gca,'xtick',[],'ytick',[],'clim',[0 360]) 257 | xlabel('Spatial dimension X') 258 | ylabel('Spatial dimension Y') 259 | title('Orientation map') 260 | 261 | %% ... but we have circular data 262 | 263 | % built-in circular colormap (affects figure) 264 | colormap hsv 265 | 266 | % create our own bimodal circular colormap, only for active axis 267 | c = copper(16); 268 | % c = cool(16); % also a nice one ;) 269 | colormap(gca,[ c; flipud(c); c; flipud(c); ]) 270 | 271 | %% ... but nan's shouldn't have the same color as "0" 272 | 273 | pcolor(orientationMap) 274 | axis square, axis ij 275 | colorbar 276 | set(gca,'xtick',[],'ytick',[],'clim',[0 360]) 277 | xlabel('Spatial dimension X') 278 | ylabel('Spatial dimension Y') 279 | title('Orientation map') 280 | colormap(gca,[ c; flipud(c); c; flipud(c); ]) 281 | 282 | %% done. 283 | -------------------------------------------------------------------------------- /module5_CaImaging/matlab4neuros_module5_sol.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 5: Calcium imaging 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the solutions code. 11 | % You should work through the "partial" file before looking at this. 12 | 13 | %% data sources 14 | 15 | % Publication: 16 | % http://dx.doi.org/10.1016/j.neuron.2015.06.030 17 | % 18 | % Dataset: 19 | % http://crcns.org/data-sets/vc/pvc-10/about 20 | % 21 | 22 | 23 | %% a clear MATLAB workspace is a clear mental workspace 24 | 25 | close all; clear; clc 26 | 27 | %% 28 | % -------------------------------------------------------- % 29 | % % 30 | % Video 2: Animate calcium fluctuations over time % 31 | % % 32 | % -------------------------------------------------------- % 33 | % 34 | %% Load the data 35 | 36 | % download from online and unpack 37 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module5.zip',weboptions('Timeout',120)); 38 | unzip('file.zip'); 39 | 40 | 41 | load('mouse10depth125green.mat') 42 | 43 | % inspect the data 44 | whos 45 | 46 | %% 47 | 48 | % sampling rate is 30 Hz according to dataset info 49 | srate = 30; 50 | 51 | % we can create an arbitrary time vector 52 | timevec = (0:length(green)-1) / srate; 53 | timevec([1 end]) 54 | 55 | % variable to store the number of time points 56 | npnts = length(timevec); 57 | 58 | %% show a movie as we've done in previous modules 59 | 60 | figure(1), clf 61 | 62 | % setup the figure and title with handles 63 | imgh = imagesc(green{1}); 64 | tith = title('hello.'); 65 | set(gca,'clim',[-1 1]*900) 66 | axis square 67 | 68 | 69 | % select a number of frames to animate 70 | nFrames = 500; % don't need to go through the entire movie 71 | 72 | % now for the movie! 73 | for framei=1:nFrames 74 | 75 | % update the image color data 76 | set(imgh,'CData',green{framei}) 77 | 78 | % update the title 79 | set(tith,'String',timevec(framei) + " sec") 80 | set(tith,'String',sprintf('%.2f sec',timevec(framei))) 81 | 82 | % real-time delay (minus a smidge for graphics updating) 83 | pause(1/srate - .01) 84 | end 85 | 86 | %% 87 | % -------------------------------------------------------- % 88 | % % 89 | % Video 3: Convert data from cell to matrix % 90 | % % 91 | % -------------------------------------------------------- % 92 | % 93 | %% 94 | 95 | %% 96 | 97 | % initialize matrix 98 | data = zeros([size(green{1}) npnts]); 99 | 100 | % populate the matrix with data, one slice at a time 101 | for i=1:npnts 102 | data(:,:,i) = green{i}; 103 | end 104 | 105 | %% save space by removing unnecessary data 106 | 107 | whos 108 | 109 | % convert bytes to gb 110 | varSizeGB = 7177894304 / 1024^3; 111 | 112 | clear green % free up ~7 gb! 113 | 114 | %% 115 | % -------------------------------------------------------- % 116 | % % 117 | % Video 4: Image processing to reduce background noise % 118 | % % 119 | % -------------------------------------------------------- % 120 | % 121 | %% 122 | 123 | %% identify and remove "background noise" 124 | 125 | % STEP 1a: compute the average map 126 | avemap = squeeze(mean(data,3)); 127 | 128 | % normalize to a range of [0 1] 129 | avemap = avemap-min(avemap(:)); 130 | avemap = avemap./max(avemap,[],'all'); 131 | 132 | 133 | % STEP 1b: apply MATLAB's local adaptive histogram equalization 134 | avemap = adapthisteq(avemap); 135 | 136 | 137 | % STEP 2: estimate the background as a fuzzy version of the image 138 | background = imgaussfilt(avemap,10); 139 | 140 | 141 | % STEP 3: create the boosted-SNR map by subtracting the 'background' 142 | foreground = avemap-background; 143 | 144 | 145 | %% pause to see what we've done so far 146 | 147 | figure(3), clf 148 | colormap hot 149 | 150 | % the average map (many code statements in one line!) 151 | subplot(221), imagesc(avemap), axis square, title('Mean') 152 | 153 | 154 | % the background image 155 | subplot(222) 156 | imagesc(background) 157 | axis square 158 | title('Background') 159 | 160 | % the foreground image 161 | subplot(223) 162 | imagesc(foreground) 163 | axis square 164 | title('Isolated') 165 | set(gca,'clim',[0 .3]) 166 | % colorbar 167 | 168 | %% continuing... 169 | 170 | % STEP 4: threshold the foreground map 171 | threshval = .05; 172 | threshimg = foreground > threshval; 173 | 174 | 175 | % and visualize that 176 | subplot(224) 177 | imagesc(threshimg) 178 | axis square 179 | title('binarized') 180 | 181 | 182 | %% 183 | % -------------------------------------------------------- % 184 | % % 185 | % Video 5: Identify neurons based on contiguity % 186 | % % 187 | % -------------------------------------------------------- % 188 | % 189 | %% 190 | 191 | 192 | % get cluster information 193 | islands = bwconncomp(threshimg); 194 | 195 | % identify the cluster sizes 196 | cellsizes = cellfun(@length,islands.PixelIdxList); 197 | 198 | % find small and large cells 199 | cells2cut = cellsizes<15 | cellsizes>100; 200 | 201 | % remove those cells 202 | islands.PixelIdxList(cells2cut) = []; 203 | 204 | % update the number of remaining clusters ("neurons") 205 | islands.NumObjects = numel(islands.PixelIdxList); 206 | 207 | 208 | % finally, recreate the threshold image without rejected clusters 209 | threshimgFilt = false(size(avemap)); 210 | for i=1:islands.NumObjects 211 | threshimgFilt(islands.PixelIdxList{i}) = true; 212 | end 213 | 214 | %% visualize 215 | 216 | % same as in previous video, redrawn for the before/after show 217 | figure(4), clf 218 | subplot(121) 219 | imagesc(threshimg) 220 | axis square 221 | title('binarized (original)') 222 | colormap gray 223 | 224 | 225 | % show again for comparison 226 | subplot(122) 227 | imagesc(threshimgFilt) 228 | axis square 229 | title('binarized (filtered)') 230 | 231 | %% 232 | % -------------------------------------------------------- % 233 | % % 234 | % Video 6: High-pass filter the time series data % 235 | % % 236 | % -------------------------------------------------------- % 237 | % 238 | %% 239 | 240 | %% get time courses from all "neurons" 241 | 242 | % initialize time series matrix 243 | neuronts = zeros(islands.NumObjects,npnts); 244 | 245 | % extract data from each cell over time 246 | for celli=1:islands.NumObjects 247 | 248 | % done per time point because cells are 2D 249 | for timei=1:npnts 250 | 251 | % get the entire map from this time point 252 | tmp = squeeze(data(:,:,timei)); 253 | 254 | % compute the average of all pixels in this time point 255 | neuronts(celli,timei) = mean( tmp(islands.PixelIdxList{celli}) ); 256 | end 257 | end 258 | 259 | %% visualize some time courses 260 | 261 | % show one neuron 262 | figure(5), clf 263 | 264 | subplot(511) 265 | plot(timevec,neuronts(37,:)) 266 | ylabel('Brightness (a.u.)') 267 | set(gca,'xlim',timevec([1 end]),'xticklabel',[]) 268 | box off 269 | 270 | % show all neurons at a time 271 | subplot(5,1,2:5) 272 | imagesc(timevec,[],neuronts) 273 | xlabel('Time (sec.)') 274 | ylabel('Cell number') 275 | 276 | % another way of visualizing 277 | figure(6), clf 278 | plot(timevec,neuronts) 279 | ylabel('Brightness (a.u.)') 280 | title('Fluorescence of all neurons') 281 | set(gca,'xlim',timevec([1 end])) 282 | xlabel('Time (sec.)') 283 | 284 | %% convert to dF 285 | 286 | neuronts = bsxfun(@rdivide,neuronts,mean(neuronts,2)); 287 | 288 | % then recreate the previous figure for comparison 289 | 290 | 291 | %% filter the time series 292 | 293 | % wide-band filter 294 | filterrange = [.5 13]; % units are Hz 295 | 296 | % create filter coefficients 297 | [b,a] = butter(5,filterrange./(srate/2)); % scale to Nyquist units 298 | 299 | % filter the data 300 | neurontsFilt = neuronts; 301 | for neuroni=1:size(neurontsFilt,1) 302 | % The function filtfilt applies the filter forwards and backwards in 303 | % time, which gives the non-phase-shifted version of the signal. 304 | neurontsFilt(neuroni,:) = filtfilt(b,a,neuronts(neuroni,:)); 305 | end 306 | 307 | %% compare the time series 308 | 309 | figure(8), clf, hold on 310 | plot(timevec,neuronts(37,:)-1) 311 | plot(timevec,neurontsFilt(37,:)) 312 | 313 | legend({'Original','Filtered'}) 314 | xlabel('Time (sec.)') 315 | ylabel('Brightness (a.u.)') 316 | set(gca,'xlim',timevec([1 end])) 317 | zoom on 318 | 319 | %% 320 | % -------------------------------------------------------- % 321 | % % 322 | % Video 7: Compute and visualize a PCA % 323 | % % 324 | % -------------------------------------------------------- % 325 | % 326 | %% 327 | 328 | %% run the PCA 329 | 330 | [pca_ts,pca_scores,eigenvalues] = pca(neurontsFilt); 331 | 332 | 333 | % compare the first PC against the average of all cells 334 | figure(9), clf 335 | plot(timevec,mean(neurontsFilt,1), timevec,pca_ts(:,1)) 336 | 337 | % make the plot look nicer 338 | legend({'Average neurons','Top PC'}) 339 | xlabel('Time (sec.)') 340 | ylabel('Brightness (a.u.)') 341 | set(gca,'xlim',timevec([1 end])) 342 | zoom on 343 | 344 | % can also try plotting one against the other, and correlation 345 | 346 | %% visualize the PC weightings 347 | 348 | % create a map of neurons, colored by their PCA score 349 | pcmap = zeros(size(avemap)); 350 | for i=1:islands.NumObjects 351 | pcmap(islands.PixelIdxList{i}) = pca_scores(i,1); 352 | end 353 | 354 | % create a grayscale image in RGB format 355 | anatomy = cat(3,avemap,avemap,avemap); 356 | 357 | % now we can visualize the map 358 | figure(10), clf 359 | imagesc(anatomy) 360 | 361 | % and then plot the PCmap on top 362 | hold on 363 | h = imagesc(pcmap); 364 | 365 | %% change the transparency of the pcmap 366 | 367 | set(h,'AlphaData',threshimgFilt) 368 | set(gca,'clim',[-1 1]) 369 | colormap(bluewhitered) 370 | axis square 371 | 372 | %% done. 373 | -------------------------------------------------------------------------------- /module5_CaImaging/matlab4neuros_module5_partial.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 5: Calcium imaging 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the partially completed code accompanying the video. 11 | % You can work through this code before looking at the solution. 12 | 13 | %% data sources 14 | 15 | % Publication: 16 | % http://dx.doi.org/10.1016/j.neuron.2015.06.030 17 | % 18 | % Dataset: 19 | % http://crcns.org/data-sets/vc/pvc-10/about 20 | % 21 | 22 | 23 | %% a clear MATLAB workspace is a clear mental workspace 24 | 25 | close all; clear; clc 26 | 27 | %% 28 | % -------------------------------------------------------- % 29 | % % 30 | % Video 2: Animate calcium fluctuations over time % 31 | % % 32 | % -------------------------------------------------------- % 33 | % 34 | %% Load the data 35 | 36 | % download from online and unpack 37 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module5.zip',weboptions('Timeout',120)); 38 | unzip('file.zip'); 39 | 40 | 41 | load('mouse10depth125green.mat') 42 | 43 | % inspect the data 44 | whos 45 | 46 | % sampling rate is 30 Hz according to dataset info 47 | srate = 30; 48 | 49 | % we can create an arbitrary time vector 50 | timevec = 51 | 52 | % check the time range by printing the first and last time points 53 | 54 | 55 | % variable to store the number of time points 56 | npnts = 57 | 58 | %% show a movie as we've done in previous modules 59 | 60 | figure(1), clf 61 | 62 | % setup the figure and title with handles 63 | imgh = imagesc(); 64 | tith = title(); 65 | set(gca,'clim',[1 1]*900) 66 | axis square 67 | 68 | 69 | % select a number of frames to animate 70 | nFrames = 500; % don't need to go through the entire movie 71 | 72 | % now for the movie! 73 | for framei=1:nFrames 74 | 75 | % update the image color data 76 | set(imgh 77 | 78 | % update the title 79 | set(tith,'String', 80 | 81 | % real-time delay (minus a smidge for graphics updating) 82 | pause(.01) 83 | end 84 | 85 | %% 86 | % -------------------------------------------------------- % 87 | % % 88 | % Video 3: Convert data from cell to matrix % 89 | % % 90 | % -------------------------------------------------------- % 91 | % 92 | %% 93 | 94 | %% 95 | 96 | % initialize data matrix (what size should it be?) 97 | data = zeros 98 | 99 | % populate the matrix with data, one slice at a time 100 | for i=1:npnts 101 | 102 | end 103 | 104 | %% save space by removing unnecessary data 105 | 106 | whos 107 | 108 | % convert bytes to gb 109 | varSizeGB = ; 110 | 111 | % clear the cell array from the workspace 112 | % how much gets freed up?? 113 | 114 | %% 115 | % -------------------------------------------------------- % 116 | % % 117 | % Video 4: Image processing to reduce background noise % 118 | % % 119 | % -------------------------------------------------------- % 120 | % 121 | %% 122 | 123 | %% identify and remove "background noise" 124 | 125 | % STEP 1a: compute the average map 126 | avemap = 127 | 128 | % normalize to a range of [0 1] 129 | % this is done by (1) subtracting the smallest value and (2) dividing by the largest 130 | avemap = 131 | avemap = 132 | 133 | 134 | % STEP 1b: apply MATLAB's local adaptive histogram equalization 135 | avemap = adapthisteq(); 136 | 137 | 138 | % STEP 2: estimate the background as a fuzzy version of the image 139 | background = imgaussfilt(); 140 | 141 | 142 | % STEP 3: create the boosted-SNR map by subtracting the 'background' 143 | foreground = 144 | 145 | 146 | %% pause to see what we've done so far 147 | 148 | figure(3), clf 149 | colormap hot 150 | 151 | % the average map (many code statements in one line!) 152 | subplot(221), imagesc(avemap), axis square, title('Mean') 153 | 154 | 155 | % the background image 156 | subplot(222) 157 | imagesc(background) 158 | axis square 159 | title() 160 | 161 | % the foreground image 162 | subplot(223) 163 | imagesc(foreground) 164 | axis square 165 | title('Isolated') 166 | % find a good color limit (via trial-and-error) 167 | set(gca,'clim',) 168 | % colorbar 169 | 170 | %% continuing... 171 | 172 | % STEP 4: threshold the foreground map 173 | threshval = % pick a threshold here based on inspecting the foreground map 174 | threshimg = ; % create a boolean map of all pixels brighter than the threshold 175 | 176 | 177 | % and visualize that 178 | subplot(224) 179 | imagesc(threshimg) 180 | axis square 181 | title('binarized') 182 | 183 | 184 | 185 | %% 186 | % -------------------------------------------------------- % 187 | % % 188 | % Video 5: Identify neurons based on contiguity % 189 | % % 190 | % -------------------------------------------------------- % 191 | % 192 | %% 193 | 194 | % get cluster information 195 | islands = bwconncomp(threshimg); 196 | 197 | % identify the cluster sizes 198 | cellsizes = cellfun(@length,islands..); 199 | 200 | % find small and large cells 201 | cells2cut = cellsizes<15 | cellsizes>100; 202 | 203 | % and remove those cells 204 | islands.PixelIdxList( ) = []; 205 | 206 | % update the number of remaining clusters ("neurons") 207 | islands.NumObjects = 208 | 209 | 210 | % finally, recreate the threshold image without rejected clusters 211 | threshimgFilt = false(size(avemap)); 212 | for i=1:islands.NumObjects 213 | threshimgFilt(islands.PixelIdxList{i}) = % what value shall we assign these pixels? 214 | end 215 | 216 | 217 | %% visualize 218 | 219 | % same as in previous video, redrawn for the before/after show 220 | figure(4), clf 221 | subplot(121) 222 | imagesc(threshimg) 223 | axis square 224 | title('binarized (original)') 225 | colormap gray 226 | 227 | 228 | 229 | % show again for comparison 230 | subplot(122) 231 | imagesc(threshimgFilt) 232 | axis square 233 | title('binarized (filtered)') 234 | 235 | %% 236 | % -------------------------------------------------------- % 237 | % % 238 | % Video 6: High-pass filter the time series data % 239 | % % 240 | % -------------------------------------------------------- % 241 | % 242 | %% 243 | 244 | %% get time courses from all "neurons" 245 | 246 | % initialize time series matrix 247 | neuronts = 248 | 249 | % extract data from each cell over time 250 | for celli=1: 251 | 252 | % done per time point because cells are 2D 253 | for timei=1:npnts 254 | 255 | % get the entire map from this time point 256 | tmp = squeeze(data(:,:,timei)); 257 | 258 | % compute the average of all pixels in this time point 259 | neuronts(celli,timei) = tmp(islands.PixelIdxList{celli}); 260 | end 261 | end 262 | 263 | %% visualize some time courses 264 | 265 | figure(5), clf 266 | 267 | % show time course from neuron #37 268 | subplot(511) 269 | plot(timevec,neuronts(37)) 270 | ylabel('Brightness (a.u.)') 271 | set(gca,'xlim',timevec([1 end]),'xticklabel',[]) 272 | box off 273 | 274 | % show all neurons at a time 275 | subplot(5,1,2:5) 276 | imagesc(timevec,neuronts) 277 | xlabel('Time (sec.)') 278 | ylabel('Cell number') 279 | 280 | 281 | % show all neurons at the same time 282 | figure(6), clf 283 | plot(timevec,neuronts) 284 | ylabel('Brightness (a.u.)') 285 | title('Fluorescence of all neurons') 286 | set(gca,'xlim',timevec([1 end])) 287 | xlabel('Time (sec.)') 288 | 289 | %% convert to dF 290 | 291 | % divide by the average over time 292 | neuronts = bsxfun(@rdivide,neuronts,mean(neuronts,npnts)); 293 | 294 | 295 | % then recreate the previous figure for comparison 296 | 297 | 298 | %% filter the time series 299 | 300 | % wide-band filter 301 | filterrange = [ ]; % units are Hz 302 | 303 | % create filter coefficients 304 | % check the help file for butter for correct usage 305 | [b,a] = butter(); 306 | 307 | % filter the data 308 | neurontsFilt = neuronts; % initialization without zeros 309 | for neuroni=1:size(neurontsFilt,1) 310 | % The function filtfilt applies the filter forwards and backwards in 311 | % time, which gives the non-phase-shifted version of the signal. 312 | neurontsFilt(neuroni,:) = filtfilt(b,a,neuronts(neuroni,:)); 313 | end 314 | 315 | %% compare the time series 316 | 317 | figure(8), clf 318 | 319 | % plot the time course of neuron #37 before and after filtering 320 | plot(timevec, 321 | plot(timevec, 322 | 323 | legend({'Original','Filtered'}) 324 | xlabel('Time (sec.)') 325 | ylabel('Brightness (a.u.)') 326 | set(gca,'xlim',timevec([1 end])) 327 | zoom on 328 | 329 | %% 330 | % -------------------------------------------------------- % 331 | % % 332 | % Video 7: Compute and visualize a PCA % 333 | % % 334 | % -------------------------------------------------------- % 335 | % 336 | %% 337 | 338 | %% run the PCA 339 | 340 | % compute the PCA 341 | [pca_ts,pca_scores,eigenvalues] = pca(neurontsFilt); 342 | 343 | 344 | figure(9), clf 345 | 346 | % compare the first PC against the average of all cells 347 | plot(timevec, , timevec,) 348 | 349 | % make the plot look nicer 350 | legend({'Average neurons','Top PC'}) 351 | xlabel('Time (sec.)') 352 | ylabel('Brightness (a.u.)') 353 | set(gca,'xlim',timevec([1 end])) 354 | zoom on 355 | 356 | %% visualize the PC weightings 357 | 358 | % create a map of neurons, colored by their PCA score 359 | pcmap = zeros(size(avemap)); 360 | for i=1:islands.NumObjects 361 | pcmap(islands.PixelIdxList{i}) = ; 362 | end 363 | 364 | % create a grayscale image in RGB format 365 | anatomy = cat(3,,,); 366 | 367 | % now we can visualize the map 368 | figure(10), clf 369 | imagesc(anatomy) 370 | 371 | % and then plot the PCmap on top 372 | hold on 373 | h = imagesc(pcmap); 374 | 375 | %% change the transparency of the pcmap 376 | 377 | set(h,'AlphaData',threshimgFilt) 378 | set(gca,'clim',[-1 1]) 379 | colormap(bluewhitered) 380 | axis square 381 | 382 | %% done. 383 | -------------------------------------------------------------------------------- /module4_FMRI/matlab4neuros_module4_sol.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 4: FMRI 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the solutions code. 11 | % You should work through the "partial" file before looking at this. 12 | 13 | %% data sources 14 | 15 | % Publication (behind paywall): 16 | % https://www.nature.com/articles/s41592-020-0941-6 17 | % 18 | % Related publication on biorxiv: 19 | % https://www.biorxiv.org/content/10.1101/868455v1 20 | % 21 | % Dataset: 22 | % https://osf.io/j2wsc/wiki/home/ 23 | % 24 | % YouTube video about the method described in the paper: 25 | % https://www.youtube.com/watch?v=Sz13i-9EtmA 26 | 27 | 28 | %% a clear MATLAB workspace is a clear mental workspace 29 | 30 | close all; clear; clc 31 | 32 | %% 33 | % -------------------------------------------------------- % 34 | % % 35 | % Video 2: Visualize flatmaps % 36 | % % 37 | % -------------------------------------------------------- % 38 | % 39 | %% Load the data 40 | 41 | % download from online and unpack 42 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module4.zip',weboptions('Timeout',120)); 43 | unzip('file.zip'); 44 | 45 | 46 | load('exampledataset.mat') 47 | 48 | % explore the data 49 | 50 | % 121695 voxels 51 | numvoxels = size(data{1},1); 52 | 53 | 54 | %% visualize a time point and standard deviation map 55 | 56 | % produce the spatial map at one time-point 57 | onetimepoint = data{1}(:,50); 58 | onetimepoint = onetimepoint(imglookup); 59 | onetimepoint(extrapmask) = NaN; 60 | 61 | % let's see what it looks like! 62 | figure(1), clf 63 | subplot(121) 64 | imagesc(onetimepoint) 65 | title('One time point') 66 | 67 | 68 | % standard deviation map 69 | stdmap = std(data{1},[],2); 70 | stdmap = stdmap(imglookup); 71 | stdmap(extrapmask) = NaN; 72 | 73 | % and show that one 74 | subplot(122) 75 | imagesc(stdmap) 76 | title('Standard deviation map') 77 | set(gca,'clim',[0 200]) 78 | 79 | % force the axis to have isotropic pixel size 80 | axis image 81 | %axis square % also try this one! 82 | 83 | 84 | %% 85 | % -------------------------------------------------------- % 86 | % % 87 | % Video 3: Preprocess BOLD signal data % 88 | % % 89 | % -------------------------------------------------------- % 90 | % 91 | %% 92 | 93 | %% time courses of a random voxel 94 | 95 | % pick a voxel to show time course from 96 | voxel2plot = 12121; 97 | 98 | % setup a subplot in the middle of the figure 99 | figure(2), clf 100 | subplot(4,1,2:3) 101 | 102 | % create a time vector converted from TRs to seconds 103 | time = (0:size(data{1},2)-1) / (1/tr); % note: 1/tr is the sampling rate 104 | 105 | % plot the data 106 | plot(time,data{1}(voxel2plot,:),'k','linew',2) 107 | 108 | % and make the plot more interpretable 109 | set(gca,'xlim',time([1 end])) 110 | xlabel('Time (s)') 111 | ylabel('BOLD signal (a.u.)') 112 | title([ 'BOLD time course from voxel #' num2str(voxel2plot) ]) 113 | 114 | %% convert to percent change and detrend 115 | 116 | for runi=1:4 117 | 118 | % convert to %change via broadcasting 119 | meanvals = mean(data{runi},2); 120 | data{runi} = 100*(data{runi}-meanvals)./meanvals; 121 | 122 | % detrending. note the orientation 123 | data{runi} = detrend(data{runi}')'; 124 | end 125 | 126 | 127 | % Now re-run figures 1 and 2 in new figures. 128 | 129 | 130 | %% 131 | % -------------------------------------------------------- % 132 | % % 133 | % Video 4: Trial-average BOLD response matrix % 134 | % % 135 | % -------------------------------------------------------- % 136 | % 137 | %% design matrix 138 | 139 | figure(5), clf 140 | 141 | % the design matrix as lines 142 | plot(time,design{1},'s-') 143 | 144 | 145 | % perhaps it's easier to visualize as an image? 146 | imagesc(design{1}) 147 | xlabel('Condition number') 148 | ylabel('Time (TR)') 149 | colormap gray 150 | 151 | %% show events plotted on top of the time course 152 | 153 | figure(6), clf, hold on 154 | 155 | % plot time course from one voxel as illustration 156 | plot(data{1}(voxel2plot,:),'k','linew',2) 157 | 158 | 159 | 160 | % find all time points with this event 161 | eventonsets = find(design{1}(:,1)); 162 | 163 | % plot a dashed line at each of those events 164 | for ei=1:length(eventonsets) 165 | plot([1 1]*eventonsets(ei),get(gca,'ylim'),'m--') 166 | end 167 | 168 | xlabel('Time (TR)') 169 | ylabel('Detrended BOLD') 170 | legend({'Voxel t.s.','Event 1'}) 171 | 172 | %% create event-related BOLD response around each eventtype 173 | 174 | % time vector (converted to seconds!) 175 | timebounds = [ -2 15 ]; % in TRs 176 | timevec = ( timebounds(1):timebounds(2) ) / (1/tr); 177 | 178 | % initialize matrix (condition X voxels X time) 179 | erBOLD = zeros(6,numvoxels,length(timevec)); 180 | 181 | 182 | % loop over condition 183 | for condi=1:6 184 | 185 | % loop over runs 186 | for runi=1:4 187 | 188 | % find all events of this condition in this run 189 | events = find(design{runi}(:,condi)); 190 | 191 | % extract peri-event time series in a temporary variable 192 | tmp = zeros(numvoxels,length(timevec)); 193 | for ei=1:length(events) 194 | tmp = tmp + data{runi}(:,events(ei)+timebounds(1):events(ei)+timebounds(2)); 195 | end 196 | 197 | % then add the trial-averaged event to the matrix 198 | erBOLD(condi,:,:) = squeeze(erBOLD(condi,:,:)) + tmp/ei; 199 | end 200 | end 201 | 202 | % divide for mean 203 | erBOLD = erBOLD/runi; 204 | 205 | %% visualize the event-related data matrix for one stimulus type 206 | 207 | figure(7), clf 208 | 209 | % show the result as an image, with seconds as x-axis label 210 | imagesc(timevec,[],squeeze(erBOLD(1,:,:))) 211 | title('erBOLD matrix for one condition') 212 | set(gca,'clim',[-1 1]*3) 213 | xlabel('Time (s)') 214 | ylabel('Voxel index') 215 | 216 | % now try 'axis image' here. Does it look better? 217 | 218 | %% 219 | % -------------------------------------------------------- % 220 | % % 221 | % Video 5: Animation of BOLD responses over time % 222 | % % 223 | % -------------------------------------------------------- % 224 | % 225 | %% 226 | 227 | %% make a gaussian for the spatial smoothing 228 | 229 | % create a Gaussian (arbitrary and hand-selected size and width) 230 | [Y,X] = meshgrid(linspace(-4,4,21)); 231 | G = exp( -(X.^2+Y.^2)/10 ); 232 | G = G./sum(G(:)); 233 | 234 | % let's see it! 235 | figure(8), clf 236 | imagesc(G) 237 | axis square 238 | 239 | %% now for the animation 240 | 241 | % clear the figure again 242 | figure(8), clf 243 | 244 | % using tiledlayout to create a 2x3 grid of subplots 245 | t = tiledlayout(2,3,'TileSpacing','Compact'); 246 | 247 | 248 | % In most animations, it's better to setup the figure first using handles, 249 | % and then update the handles instead of redrawing the entire figure. 250 | for condi=1:6 251 | 252 | % go to the next tile (subplot) 253 | nexttile 254 | 255 | % create an image. We just need the handle; the data don't matter. 256 | imh(condi) = imagesc(randn(size(imglookup))); 257 | 258 | % settings for each image. 259 | % These settings will remain during the animation. 260 | set(gca,'clim',[-1 1]*3) 261 | axis off, axis image 262 | title([ 'Condition ' num2str(condi) ]) 263 | set(gca,'fontsize',14) 264 | end 265 | 266 | 267 | %%% now that the figure is setup, we can run through the animation 268 | for timei=1:size(erBOLD,3) 269 | 270 | % loop over conditions 271 | for condi=1:6 272 | 273 | % get the map from this time point 274 | timepointmap = squeeze(erBOLD(condi,:,timei)); 275 | timepointmap = timepointmap(imglookup); 276 | 277 | % smooth with a Gaussian 278 | timepointmap = conv2(timepointmap,G,'same'); 279 | timepointmap(extrapmask) = NaN; 280 | 281 | % update the color data 282 | set(imh(condi),'CData',timepointmap) 283 | end % end of condition loop 284 | 285 | 286 | % update the plot title 287 | set(get(t,'Title'),'String',[ 'Brain maps at time ' num2str(timevec(timei)) ' s.' ]) 288 | 289 | % pause to allow MATLAB to update, and the audience to absorb the information 290 | pause(.2) 291 | end 292 | 293 | %% 294 | % -------------------------------------------------------- % 295 | % % 296 | % Video 6: Visualize the BOLD response from one voxel % 297 | % % 298 | % -------------------------------------------------------- % 299 | % 300 | %% pick a voxel in the graph and show an ERP from that pixel 301 | 302 | % step 1: turn datacursormode on and click on a map 303 | 304 | % step 2a: manually write down the xy coordinates 305 | pix2plot = [463 465]; 306 | 307 | % step 2b: use the mouse to export to workspace 308 | pix2plot = pix2plot.Position; 309 | 310 | 311 | %% now show the time courses from that pixel 312 | 313 | % convert from 2D coordinates to index 314 | dataidx = imglookup(pix2plot(1),pix2plot(2)); 315 | 316 | % and plot! 317 | figure(9), clf, hold on 318 | plot(timevec,squeeze(erBOLD(:,dataidx,:)),'linew',2) 319 | 320 | % some extra lines 321 | plot(timevec([1 end]),[0 0],'k') 322 | plot([0 0],get(gca,'ylim'),'k--') 323 | 324 | % make the plot look nicer 325 | xlabel('Time (s)') 326 | ylabel('BOLD response') 327 | set(gca,'xlim',timevec([1 end])) 328 | legend({'Cond. 1','Cond. 2','Cond. 3','Cond. 4','Cond. 5','Cond. 6'}) 329 | 330 | %% 331 | % -------------------------------------------------------- % 332 | % % 333 | % Video 7: T-test on condition differences % 334 | % % 335 | % -------------------------------------------------------- % 336 | % 337 | %% simple t-test at 6s after two events 338 | 339 | % initialize maps to empty 340 | map1 = []; 341 | map6 = []; 342 | 343 | 344 | % loop over experiment runs 345 | for runi=1:4 346 | 347 | % find all occurances of events 1 and 6 348 | events1 = find(design{runi}(:,1)); 349 | events6 = find(design{runi}(:,6)); 350 | 351 | % extract peri-event time series (hard-coded to 6 TRs post-onset) 352 | map1 = cat(2,map1,data{runi}(:,events1+6)); 353 | map6 = cat(2,map6,data{runi}(:,events6+6)); 354 | end 355 | 356 | % run t-test! 357 | [~,p,~,tmap] = ttest(map1',map6'); 358 | 359 | %% visualize the map of t-values 360 | 361 | figure(10), clf 362 | 363 | % extract the t-values 364 | tvals = tmap.tstat; 365 | 366 | % statistical thresholding 367 | tvals(p>.01) = 0; 368 | 369 | % create and visualize the image 370 | tmapbrain = tvals(imglookup); 371 | tmapbrain(extrapmask) = 0; 372 | imagesc(tmapbrain) 373 | 374 | % various color adjustments 375 | set(gca,'clim',[-1 1]*10) 376 | colormap(bluewhitered(64)) 377 | colorbar 378 | 379 | % final few niceties 380 | axis image, axis off 381 | set(gcf,'color','w') 382 | title('T-test on conditions 1 vs. 6') 383 | 384 | %% done. 385 | -------------------------------------------------------------------------------- /module4_FMRI/matlab4neuros_module4_partial.m: -------------------------------------------------------------------------------- 1 | %% 2 | % COURSE: MATLAB training for early-career neuroscientists 3 | % URL: ... 4 | % 5 | % SECTION: Module 4: FMRI 6 | % 7 | % TEACHER: Mike X Cohen, sincxpress.com 8 | % 9 | 10 | % NOTE: This is the partially completed code accompanying the video. 11 | % You can work through this code before looking at the solution. 12 | 13 | %% data sources 14 | 15 | % Publication (behind paywall): 16 | % https://www.nature.com/articles/s41592-020-0941-6 17 | % 18 | % Related publication on biorxiv: 19 | % https://www.biorxiv.org/content/10.1101/868455v1 20 | % 21 | % Dataset: 22 | % https://osf.io/j2wsc/wiki/home/ 23 | % 24 | % YouTube video about the method described in the paper: 25 | % https://www.youtube.com/watch?v=Sz13i-9EtmA 26 | 27 | 28 | %% a clear MATLAB workspace is a clear mental workspace 29 | 30 | close all; clear; clc 31 | 32 | %% 33 | % -------------------------------------------------------- % 34 | % % 35 | % Video 2: Visualize flatmaps % 36 | % % 37 | % -------------------------------------------------------- % 38 | % 39 | %% Load the data 40 | 41 | % download from online and unpack 42 | websave('file.zip','https://sincxpress.com/neuroscience/matlab4neuros_module4.zip',weboptions('Timeout',120)); 43 | unzip('file.zip'); 44 | 45 | 46 | load('exampledataset.mat') 47 | 48 | % explore the data 49 | 50 | % how many voxels are there? 51 | numvoxels = size(data{1},1); 52 | 53 | 54 | %% visualize a time point and standard deviation map 55 | 56 | % produce the spatial map at one time-point 57 | onetimepoint = data{1}(:,50); 58 | onetimepoint = onetimepoint(imglookup); 59 | 60 | % set all non-brain voxels to NaN (not a number) 61 | onetimepoint(extrapmask) = ; 62 | 63 | % let's see what it looks like! 64 | figure(1), clf 65 | subplot(121) 66 | imagesc 67 | title('One time point') 68 | 69 | 70 | % standard deviation map 71 | stdmap = % compute the standard deviation over time in dataset 1 72 | % convert from a vector to a matrix as above 73 | 74 | % and show that one 75 | subplot(122) 76 | imagesc(stdmap) 77 | title % add an informative title! 78 | set(gca,'clim',[0 200]) 79 | 80 | % force the axis to have isotropic aspect ratio 81 | %axis image 82 | %axis square % also try this one! 83 | 84 | 85 | %% 86 | % -------------------------------------------------------- % 87 | % % 88 | % Video 3: Preprocess BOLD signal data % 89 | % % 90 | % -------------------------------------------------------- % 91 | % 92 | %% 93 | 94 | %% time courses of a random voxel 95 | 96 | % pick a voxel to show time course from 97 | voxel2plot = 12121; 98 | 99 | % setup a subplot in the middle of the figure 100 | figure(2), clf 101 | subplot(4,1,2:3) 102 | 103 | % create a time vector converted from TRs to seconds 104 | time = % note: variable tr is the repetition time; thus, 1/tr is the sampling rate 105 | 106 | % plot the data from the selected voxel over time 107 | plot(time,,'k','linew',2) 108 | 109 | % and make the plot more interpretable 110 | set(gca,'xlim',time([1 end])) 111 | xlabel('Time (s)') 112 | ylabel('BOLD signal (a.u.)') 113 | title( 'BOLD time course from voxel #' voxel2plot ) % hmm.... 114 | 115 | %% convert to percent change and detrend 116 | 117 | for runi=0 118 | 119 | % convert to %change via broadcasting 120 | meanvals = mean(data{runi},theDimension); 121 | data{runi} = pi*(data{runi}-meanvals) .* meanvals; 122 | 123 | % detrending. note the orientation 124 | data{runi} = detrend(data{runi}')'; 125 | end 126 | 127 | 128 | % Now re-run figures 1 and 2 in new figures. 129 | 130 | 131 | %% 132 | % -------------------------------------------------------- % 133 | % % 134 | % Video 4: Trial-average BOLD response matrix % 135 | % % 136 | % -------------------------------------------------------- % 137 | % 138 | %% design matrix 139 | 140 | figure(5), clf 141 | 142 | % the design matrix (from the first run) as lines 143 | plot(,,'s-') 144 | 145 | 146 | % perhaps it's easier to visualize as an image? 147 | imagesc 148 | xlabel('Condition number') 149 | ylabel('Time (TR)') 150 | colormap gray 151 | 152 | %% show events plotted on top of the time course 153 | 154 | figure(6), clf, hold on 155 | 156 | % plot time course from one voxel as illustration 157 | plot(data{1}(voxel2plot,:),'k','linew',2) 158 | 159 | 160 | 161 | % find all time points with this event 162 | eventonsets = find( 163 | 164 | % plot a dashed line at each of those events 165 | for ei=1:length(eventonsets) 166 | plot(eventonsets(ei),get(gca,'ylim'),'m--') 167 | end 168 | 169 | xlabel('Time (TR)') 170 | ylabel('Detrended BOLD') 171 | legend({'Voxel t.s.','Event 1'}) 172 | 173 | %% create event-related BOLD response around each eventtype 174 | 175 | % time vector (converted to seconds!) 176 | timebounds = [ -2 15 ]; % in TRs 177 | timevec = ( timebounds(1):timebounds(2) ) / 178 | 179 | % initialize matrix (condition X voxels X time) 180 | erBOLD = zeros(); 181 | 182 | 183 | % loop over condition 184 | for condi=1:6 185 | 186 | % loop over runs 187 | for runi=1:4 188 | 189 | % find all events of this condition in this run 190 | events = find(design{runi}(:,condi)); 191 | 192 | % extract peri-event time series in a temporary variable 193 | tmp = zeros(numvoxels,length(timevec)); 194 | for ei=1:length(events) 195 | tmp = tmp + data{runi} % more code here... 196 | end 197 | 198 | % then add the trial-averaged event to the matrix 199 | erBOLD(condi,:,:) = squeeze(erBOLD(condi,:,:)) + tmp/ei; 200 | end 201 | end 202 | 203 | % divide for mean 204 | erBOLD = erBOLD/runi; 205 | 206 | %% visualize the event-related data matrix for one stimulus type 207 | 208 | figure(7), clf 209 | 210 | % show the result as an image, with seconds as x-axis label 211 | imagecs(squeeze(erBOLD(1,:,:))) 212 | title('erBOLD matrix for one condition') 213 | % find a nice color limit with symmetric boundaries 214 | xlabel('Time (s)') 215 | ylabel('Voxel index') 216 | 217 | % now try 'axis image' here. Does it look better? 218 | 219 | %% 220 | % -------------------------------------------------------- % 221 | % % 222 | % Video 5: Animation of BOLD responses over time % 223 | % % 224 | % -------------------------------------------------------- % 225 | % 226 | %% 227 | 228 | %% make a gaussian for the spatial smoothing 229 | 230 | % create a Gaussian (arbitrary and hand-selected size and width) 231 | [Y,X] = meshgrid(linspace(-4,4,21)); 232 | G = exp( -(X.^2+Y.^2)/10 ); 233 | G = G./sum(G(:)); 234 | 235 | % let's see it! 236 | 237 | 238 | %% now for the animation 239 | 240 | % clear the figure again 241 | figure(8), clf 242 | 243 | % using tiledlayout to create a 2x3 grid of subplots 244 | t = tiledlayout(2,3,'TileSpacing','Compact'); 245 | 246 | 247 | % In most animations, it's better to setup the figure first using handles, 248 | % and then update the handles instead of redrawing the entire figure. 249 | for condi=1:6 250 | 251 | % go to the next tile (subplot) 252 | nexttile 253 | 254 | % create an image. We just need the handle; the data don't matter. 255 | imh(condi) = imagesc(randn(size(imglookup))); 256 | 257 | % settings for each image. 258 | % These settings will remain during the animation. 259 | set(gca,'clim',[-1 1]*100000) % good color limits? 260 | axis off, axis image 261 | title([ 'Condition ' num2str(condi) ]) 262 | set(gca,'fontsize',140) % good font size? 263 | end 264 | 265 | 266 | %%% now that the figure is setup, we can run through the animation 267 | for timei=1:size(erBOLD,3) 268 | 269 | % loop over conditions 270 | for condi=1:6 271 | 272 | % get the map from this time point 273 | timepointmap = squeeze(erBOLD(condi,:,timei)); 274 | timepointmap = timepointmap(imglookup); 275 | 276 | % smooth with a Gaussian 277 | timepointmap = conv2(timepointmap,G,'same'); 278 | timepointmap(extrapmask) = NaN; 279 | 280 | % update the color data 281 | set(imh(condi),'CData' 282 | end % end of condition loop 283 | 284 | 285 | % update the plot title 286 | set(get(t,'Title'),'String',[ 'Brain maps at time ' num2str(timevec) ' s.' ]) 287 | 288 | % pause to allow MATLAB to update, and the audience to absorb the information 289 | pause(.2) 290 | end 291 | 292 | %% 293 | % -------------------------------------------------------- % 294 | % % 295 | % Video 6: Visualize the BOLD response from one voxel % 296 | % % 297 | % -------------------------------------------------------- % 298 | % 299 | %% pick a voxel in the graph and show an ERP from that pixel 300 | 301 | % step 1: turn datacursormode on and click on a map 302 | 303 | % step 2a: manually write down the xy coordinates 304 | pix2plot = [463 465]; 305 | 306 | % step 2b: use the mouse to export to workspace 307 | pix2plot = ; 308 | 309 | 310 | %% now show the time courses from that pixel 311 | 312 | % convert from 2D coordinates to index 313 | dataidx = imglookup(); 314 | 315 | % and plot! 316 | figure(9), clf, hold on 317 | plot(timevec,squeeze(erBOLD(:,dataidx,:)),'linew',2) 318 | 319 | % some extra lines 320 | plot(timevec([1 end]),[0 0],'k') 321 | % here draw a vertical black dashed line at time=0 322 | 323 | % make the plot look nicer 324 | xlabel('Time (s)') 325 | ylabel('BOLD response') 326 | set(gca,'xlim',timevec([1 end])) 327 | legend({'Cond. 1','Cond. 2','Cond. 3','Cond. 4','Cond. 5','Cond. 6'}) 328 | 329 | %% 330 | % -------------------------------------------------------- % 331 | % % 332 | % Video 7: T-test on condition differences % 333 | % % 334 | % -------------------------------------------------------- % 335 | % 336 | %% simple t-test at 6s after two events 337 | 338 | % initialize maps to empty 339 | map1 = []; 340 | map6 = []; 341 | 342 | 343 | % loop over experiment runs 344 | for runi=1:4 345 | 346 | % find all occurances of events 1 and 6 347 | events1 = find(design{runi} 348 | events6 = find(design{runi} 349 | 350 | % extract peri-event time series (hard-coded to 6 TRs post-onset) 351 | map1 = cat(2,map1,data{runi}(:,events1+6)); 352 | map6 = 353 | end 354 | 355 | % run t-test! 356 | %%% IMPORTANT: sanity-check the outputs! 357 | [~,p,~,tmap] = ttest(map1,map6); 358 | 359 | %% visualize the map of t-values 360 | 361 | figure(10), clf 362 | 363 | % extract the t-values 364 | tvals = tmap.; 365 | 366 | % statistical thresholding: set all voxels with p>.01 to be 0 367 | 368 | 369 | % create and visualize the image 370 | tmapbrain = tvals(imglookup); 371 | tmapbrain(extrapmask) = 0; 372 | imagesc(tmapbrain) 373 | 374 | % various color adjustments 375 | set(gca,'clim',[-1 1]*10) 376 | colormap(bluewhitered(64)) 377 | % add a colorbar to facilitate interpretation 378 | 379 | % final few niceties 380 | axis image, axis off 381 | set(gcf,'color','w') 382 | title('T-test on conditions 1 vs. 6') 383 | 384 | %% done. 385 | --------------------------------------------------------------------------------