Structures in Matlab are a great tool for organizing, referencing, and analyzing your data. They allow you to dynamically access your data in ways that you can’t with numerical or cell arrays. Follow along with the below code and comments to see how to use structs when working with data in Matlab.
%% Understanding Struct
% Blake Porter
% April 5th, 2019
% Struct's are a data format in matlab that can store [numerical data] and {cell arrays} in
% one convenient and accessible location
% Structs can also be dynamically referenced, meaning you can create new structs easily based on the name of things
% For example, rather than making three variables for three treatment groups, you can make one struct that stores all three. Then you could easily
% add a 4th group by say, referencing a {list} of your group names
% Structs are also intuitvely structured and can have hierarchy, sort of like folders in your computer just rather than using \, you use .
% E:\Blake_Porter_Neuro\Matlab
% would be
% E.Blake_Porter_Neuro.Matlab
% Let's say we have three groups and we measure firing rates of neurons
% 1: Control
% 2: Treatment 1
% 3: Treatment 2
%% Without using structs, you may do something like create a variable for each group and store the data inside:
ctrl_FR = [];
t1_FR = [];
t2_FR = [];
%% Or let's do the same, but with some dummy data
ctrl_FR = rand(25,1)+1; % 25 rows, 1 column
t1_FR = rand(25,1)+2; % pretend its 25 trials from 1 neuron
t2_FR = rand(25,1)+3; % and each element is the firing rate for that trial
% +1 and +2 to make them a little different
%% So this is okay, but if we want to start doing some analysis, we'd have to do everything three times on three different lines
% For example, getting the mean firing rate
ctrl_mFR = mean(ctrl_FR);
t1_FR = mean(t1_FR);
t2_FR = mean(t2_FR);
% That's pretty annoying, and we only have three groups!
%% Lets use structs instead
% First we will get the name of what we want our struct fields (sort of like folders on your computer) to be called
groups = {'ctrl' 't1' 't2'};
%% now we will initialize our struct
data = struct;
%% data is currently empty, so now we can start to fill it with our data
% but here is where the dynamic referencing comes into play
% we can loop through our groups list to access our different groups
for currGroup = 1:length(groups) % for each name in groups
data.(groups{currGroup}).FR = rand(25,1)+currGroup; % when using dynamic referencing in structs
% enclose everything in normal (parethesis)
% then put your desired name inside as a string
% here we loop through groups and asign them their fake firing rates
% note I am only doing +currGroup to make them different
% normally here, you may be doing something like importing from neuralynx
end % for each group
%% Now our data struct has three fields, one for each group in groups
% within each field is the dummy firing rates
% For analysis we can keep looping through groups now instead of writing out a line for every group
% For example, calculate the mean firing rates
for currGroup = 1:length(groups) % for each name in groups
data.(groups{currGroup}).mFR = mean(data.(groups{currGroup}).FR);
end % for each group
%% Structs now make it trivial to use code from on experiment to another
% for example, what if we run a similar experiment but with three treatments?
% rather than have to go back and add extra lines of code everywhere for t3
% we just add it to our groups list and run it again
% This:
ctrl_FR = rand(25,1)+1;
t1_FR = rand(25,1)+2;
t2_FR = rand(25,1)+3;
t3_FR = rand(25,1)+4;
ctrl_mFR = mean(ctrl_FR);
t1_FR = mean(t1_FR);
t2_FR = mean(t2_FR);
t3_FR = mean(t3_FR);
% Becomes this:
groups = {'ctrl' 't1' 't2' 't3'};
for currGroup = 1:length(groups) % for each name in groups
data.(groups{currGroup}).FR = rand(25,1)+currGroup;
data.(groups{currGroup}).mFR = mean(data.(groups{currGroup}).FR); % combined in the same loop
end % for each group
%% This is also great for things like stats
for currGroup = 1:length(groups) % for each name in groups
data.all(:,currGroup) = data.(groups{currGroup}).FR; % asign each column in data.all to a group
end % for each group
[p,tbl,stats] = anova1(data.all);
%% And graphing
figure;
hold on
for currGroup = 1:length(groups) % for each name in groups
xLoc(1:length(data.(groups{currGroup}).FR)) = currGroup; % x location on graph
scatter(xLoc,data.(groups{currGroup}).FR,'filled'); % graph each group
line([currGroup-0.2 currGroup+0.2],[data.(groups{currGroup}).mFR data.(groups{currGroup}).mFR],'LineStyle','-','color','k','linewidth',2)
errorbar(currGroup,data.(groups{currGroup}).mFR,std(data.(groups{currGroup}).FR)/sqrt(length(data.(groups{currGroup}).FR)),'k','capsize',10,'linewidth',2);
end % for each group
hold off
xlim([0 5])
%%
clear
%% Structs can get even more powerful as you use more levels of them
% for example we could have multiple brains regions and multiple frequencey bands we are interested in
regions = {'ACC' 'AI' 'VTA'}; % brain regions we recorded from
bandList = [0.1 4; 7 12; 15 35; 40 100]; % frequency bands of interest
data = struct;
%% Create dummy LFP traces
for currRegion = 1:length(regions)
data.(regions{currRegion}).LFP = rand(1,10000)-0.5;
end % curr region
%% Filter LFPs based on band lists
Fs = 1000;
for currRegion = 1:length(regions)
for currBand = 1:length(bandList(:,1))
Wn = [bandList(currBand,1) bandList(currBand,2)]/(Fs/2);
if bandList(currBand,1) <= 4 % order cant be high when fq band is low or shit fucks up
order = 1;
else
order = 3;
end
[b,a] = butter(order,Wn); % design filter
% Filter
data.(regions{currRegion}).(['Band',num2str(currBand)]).fLFP = filtfilt(b,a,data.(regions{currRegion}).LFP);
% Power
hilb =hilbert(data.(regions{currRegion}).(['Band',num2str(currBand)]).fLFP);
hilb = imag(hilb);
data.(regions{currRegion}).(['Band',num2str(currBand)]).amp=sqrt(data.(regions{currRegion}).(['Band',num2str(currBand)]).fLFP.^2 + hilb.^2);
data.(regions{currRegion}).(['Band',num2str(currBand)]).phase= atan2(hilb,data.(regions{currRegion}).(['Band',num2str(currBand)]).fLFP);
end % curr band
end % curr region
%% Graph
for currRegion = 1:length(regions)
figure;
hold on
plot(data.(regions{currRegion}).LFP)
for currBand = 1:length(bandList(:,1))
plot(data.(regions{currRegion}).(['Band',num2str(currBand)]).fLFP+currBand) % + currBand just moves it up the y Axis
end % currBand
hold off
title([regions{currRegion}])
yticks([0 1 2 3 4])
yticklabels({'raw' [num2str(bandList(1,1)),'-',num2str(bandList(1,2))] [num2str(bandList(2,1)),'-',num2str(bandList(2,2))] [num2str(bandList(3,1)),'-',num2str(bandList(3,2))] [num2str(bandList(4,1)),'-',num2str(bandList(4,2))]});
end % curr Region
%% As before, since everything is dynamic referencing, we could simply add another brain region or take away a frequency band and all of this code will still work!
regions = {'ACC' 'AI' 'VTA' 'HPC'}; % brain regions we recorded from
bandList = [7 12; 15 35; 40 100; 150 250]; % frequency bands of interest

This work by Blake Porter is licensed under a Creative Commons Attribution-Non Commercial-ShareAlike 4.0 International License
Leave a Reply