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