function Analyze_Spatial_Contrast_Adaptation_Stimulus(varargin) % % %%% Analyze_Spatial_Contrast_Adaptation_Stimulus %%% % % This function analyzes the response of the ganglion cells to the spatial % contrast adaptation stimulus. This is a supplement code for the Khani & Gollisch % (2017) study about the diversity of spatial contrast adaptation in the mouse retina. % This function accompanys dataset of Khani and Gollisch (2017). The dataset is available at: % https://gin.g-node.org/gollischlab/Khani_and_Gollisch_2017_RGC_spiketrains_for_spatial_contrast_adaptation % This function first loads the data in a format that is provided in by the % supplementary database of the study. It then generate PSTHs, spike-triggered % average (STA) and nonlinearity functions for the selected cell. % % ===============================Inputs==================================== % % datapath : path to the folder in the database. % stimulusnumber : number to select correct stimulus. % cellnumber : an example cell number from the list of good cells. % clusternumber : a cluster number for the selected cell. % %================================Output==================================== % % PSTH, STAs and nonlinearities for the selected cell. % %--------------------------------------------------------------------------------------------------% %---------- main-function ----------% %--------------------------------------------------------------------------------------------------% % first load the data for an example cell [dp, stimulusnumber, cellnumber, clusternumber] = select_example_cell(varargin{:}); % analyze the data ca_data = sca_anylsis(dp, stimulusnumber, cellnumber, clusternumber); % plotting plot_sca(ca_data, cellnumber, clusternumber); end %--------------------------------------------------------------------------------------------------% %---------- sub-functions ----------% %--------------------------------------------------------------------------------------------------% function expdata = sca_anylsis(dp, stimulusnum, cellnum, clusternum,varargin) % expdata = load_data_for_selected_cell(dp, stimulusnum, cellnum, clusternum); % set all the required parameters para = expdata.stimpara; para.refreshrate = 60; para.upsampleratio = ceil(1e3/para.refreshrate); para.numNLbins = 40; para.meanIntensity = 0.5; para.statime = fliplr(linspace(0,(1e3/60*para.Nblinks)*(para.numNLbins/para.upsampleratio),para.numNLbins)); Nruns = floor((length(expdata.frametimes)-1)/(para.Nblinks*para.switchFrames)); para.Nframes = Nruns*para.Nblinks*para.switchFrames; para.Nrepeats = floor(Nruns/2); % number of runs per trial; para.binlength = 250 / 1e3; % 250 ms each bin % for the PSTH para.low_contduration = para.switchFrames ./ para.refreshrate; para.high_contduration = para.switchFrames ./ para.refreshrate; para.psthtime_low = linspace(0, para.low_contduration ,(para.low_contduration / para.binlength)+1); para.psthtime_high = linspace(0, para.high_contduration ,(para.high_contduration / para.binlength)+1); para.trialduration = para.low_contduration + para.high_contduration; para.psthtimelh = linspace(0, para.trialduration,(para.trialduration / para.binlength)); % loading clusters and frametimings ft = expdata.frametimes; % work in seconds ftimes = ft(1:para.Nframes); % get frame times per trial ftimes = reshape(ftimes,para.switchFrames,Nruns)'; ftimes_high = ftimes(2:2:end,:); ftimes_high = ftimes_high(1:para.Nrepeats,:); ftimes_low = ftimes(1:2:end,:); ftimes_low = ftimes_low(1:para.Nrepeats,:); % getting the stimulus right! stimulus = Spatial_Contrast_Adaptation_Stimulus(para.seed,para.Nframes,para.switchFrames,Nruns, para.meanIntensity); % binning spikes spkbins = (histc(expdata.spiketimes,ft(1:para.Nframes))); %#ok spkbins = reshape(spkbins,para.switchFrames,Nruns)'; spkbins_high = spkbins(2:2:end,:); spkbins_high = spkbins_high(1:para.Nrepeats,:); spkbins_low = spkbins(1:2:end,:); spkbins_low = spkbins_low(1:para.Nrepeats,:); % setting output expdata.ftimes.high = ftimes_high; expdata.ftimes.low = ftimes_low; expdata.stimulus = stimulus; expdata.stimpara = para; % calculating rasters and psth expdata = calc_rasters_psth(expdata); % calculating STAs and nonlinearities for each contrast and location [locX_high.sta,locX_high.nlx,locX_high.nly,locX_high.time] = calculate_sta_nonlinearity(spkbins_high, expdata.stimulus.locX_high, para); [locX_low.sta,locX_low.nlx,locX_low.nly,locX_low.time] = calculate_sta_nonlinearity(spkbins_low, expdata.stimulus.locX_low, para); [locY_high.sta,locY_high.nlx,locY_high.nly,locY_high.time] = calculate_sta_nonlinearity(spkbins_high, expdata.stimulus.locY_high, para); [locY_low.sta,locY_low.nlx,locY_low.nly,locY_low.time] = calculate_sta_nonlinearity(spkbins_low, expdata.stimulus.locY_low, para); % setting output expdata.locXhigh = locX_high; expdata.locX_low = locX_low; expdata.locY_high = locY_high; expdata.locY_low = locY_low; end %--------------------------------------------------------------------------------------------------% function expdata = calc_rasters_psth(expdata) fthigh = expdata.ftimes.high; ftlow = expdata.ftimes.low; spks = expdata.spiketimes; para = expdata.stimpara; [ras_high, ras_low] = deal(cell(para.Nrepeats,1)); delay = 50/1e3; % ignore first spikes after contrast switch for ii = 1:para.Nrepeats ras_high{ii} = spks(and(spks >= fthigh(ii,1),spks <= fthigh(ii,end))) - fthigh(ii,1); ras_low{ii} = spks(and(spks >= (ftlow(ii,1)+delay),spks <= ftlow(ii,end))) - (ftlow(ii,1)+delay); end rasters_high = CelltoMatUE(ras_high); rasters_low = CelltoMatUE(ras_low); psth_high = mean(histc(rasters_high',para.psthtime_high),2)*(1/para.binlength); %#ok psth_high = psth_high(1:end-1); psth_high(end) = psth_high(end-1); para.psthtime_high = para.psthtime_high(1:end-1); psth_low = mean(histc(rasters_low',para.psthtime_low),2)*(1/para.binlength); %#ok psth_low = psth_low(1:end-1); psth_low(end) = psth_low(end-1); para.psthtime_low = para.psthtime_low(1:end-1); expdata.rasters_high = rasters_high; expdata.rasters_low = rasters_low; expdata.psth_high = psth_high'; expdata.psth_low = psth_low'; expdata.psthlh = [psth_low; psth_high]'; end function [staNorm,nlx,nly,timeVec] = calculate_sta_nonlinearity(runningbin, stimulus, para) toHzratio = para.Nblinks/para.refreshrate; % normalize the response to Hz rnstimEn = zeros(para.Nrepeats,para.numNLbins, para.switchFrames-para.numNLbins+1); for itrial = 1 : para.Nrepeats stimtrial = stimulus(itrial,:); hankelMat = hankel(1:para.numNLbins,para.numNLbins:para.switchFrames); rnstimEn(itrial,:,:) = stimtrial(hankelMat); end runningbin = reshape(permute(runningbin(:,para.numNLbins:end,:),[1 3 2]),1,[]); rnstimEn = reshape(permute(rnstimEn, [2 1 3]), para.numNLbins,[]); sta = bsxfun(@rdivide,runningbin*rnstimEn',sum(runningbin,2)); staNorm = bsxfun(@times,sta,1./sqrt(sum(sta.^2,2))); timeVec = (-(para.numNLbins-1):1:0)*toHzratio; %in seconds runningGenerators = staNorm * rnstimEn; [nly,nlx] = calculate_nonlinearity(runningGenerators', runningbin', para.numNLbins, toHzratio); end %--------------------------------------------------------------------------------------------------% function [ values, centers,sevalues] = calculate_nonlinearity(linearOutput, spikes, nbins, dt) qtStep = 1/nbins; qtEdges = 0:qtStep:1; genEdges = quantile(linearOutput, qtEdges); if all(isnan(genEdges)) % this is for cases without spikes where everything is NaNs values = zeros(size(genEdges)); sevalues = zeros(size(genEdges)); else [a, spikebins] = histc(linearOutput,genEdges); %#ok values = accumarray(spikebins, spikes', [nbins+1 1], @sum); sdvalues = accumarray(spikebins, spikes', [nbins+1 1], @std); values = values./a/dt; sevalues = sdvalues./sqrt(a)/dt; end values(end) = []; sevalues(end) = []; centers = genEdges(1:nbins)+diff(genEdges)/2; values = transpose(values); sevalues = transpose(sevalues); end %--------------------------------------------------------------------------------------------------% function [dp, stimulusnumber, cellnumber, clusternumber] = select_example_cell(varargin) % first parse the user inputs p = inputParser(); % check the user options. p.addParameter('datapath', [], @(x) ischar(x) || isstring(x)); p.addParameter('stimulusnumber', [], @isnumeric); p.addParameter('cellnumber', [], @isnumeric); p.addParameter('clusternumber', [], @isnumeric); p.addParameter('help', false, @(x) islogical(x) || (isnumeric(x) && ismember(x,[0,1]))); p.parse(varargin{:}); % defualt parameters defpara = p.Results; if isempty(defpara.datapath) dp = uigetdir('Select an experiment folder:'); else dp = defpara.datapath; end if isempty(defpara.cellnumber) || isempty(defpara.clusternumber) listofgoodcells = load([dp,filesep,'list_of_good_cells.txt'],'-ascii'); cellabels = sprintfc('cell %g, cluster %g',listofgoodcells); if numel(cellabels) > 1 idx = listdlg('PromptString',{'Select an example ganglion cell:'},... 'SelectionMode','single','ListString',cellabels,'listsize',[250 250]); else idx = 1; end cellnumber = listofgoodcells(idx,1); clusternumber = listofgoodcells(idx,2); else cellnumber = defpara.cellnumber; clusternumber = defpara.clusternumber; end if isempty(defpara.stimulusnumber) stimnames = importdata([dp,filesep,'stimuli_names.txt']); canames = stimnames(~(cellfun('isempty',(strfind(stimnames,'Contrast_Adaptation'))))); if numel(canames) > 1 idx = listdlg('PromptString',{'Select an example dataset:'},... 'SelectionMode','single','ListString',canames,'listsize',[450 100]); canames = canames{idx}; end stimulusnumber = str2double(extractBefore(canames,'_')); else stimulusnumber = defpara.stimulusnumber; end end %--------------------------------------------------------------------------------------------------% function expdata = load_data_for_selected_cell(dp, stimnum, cellnum, clusternum) % ftfolder = [dp,filesep,'frametimes']; if ~exist(ftfolder,'dir'), ftfolder = dp; end rasfolder = [dp,filesep,'spiketimes']; if ~exist(rasfolder,'dir'), rasfolder = dp; end paramfolder = [dp,filesep,'stimulusparameters']; if ~exist(paramfolder,'dir'), paramfolder = dp; end ftnames = dir([ftfolder, filesep, num2str(stimnum,'%02g'),'*_frametimings.txt']); rastername = [rasfolder, filesep, sprintf('%d_SP_C%d%02d.txt', stimnum, cellnum, clusternum)]; warning('off','all'); % this is supress the warning about long names, hopefully nothing happen in between these lines! expdata.spiketimes = load(rastername, '-ascii'); expdata.frametimes = load([ftfolder,filesep,ftnames.name], '-ascii'); stimpath = dir([paramfolder,filesep,num2str(stimnum,'%02g'),'*_parameters.txt']); % now reading the txt file with parameters fid = fopen([paramfolder,filesep,stimpath.name]); tline = fgetl(fid); while ischar(tline) tline = fgetl(fid); if tline == -1, break; end fn = extractBefore(tline,' = '); if isempty(fn), continue; end val = extractAfter(tline,' = '); % to convert to double if ~isnan(str2double(val)) val = str2double(val); end % to convert to logical if strcmp(val,'true'), val = true; end if strcmp(val,'false'), val = false; end % store the values in a structure stimpara.(fn) = val; end fclose(fid); expdata.stimpara = stimpara; expdata.name = extractBefore(ftnames.name,'_frametimings.txt'); expdata.paraname = extractBefore(stimpath.name,'_parameters.txt'); if strcmp(expdata.name , expdata.paraname) expdata = rmfield(expdata,'paraname'); end warning('on','all'); end %--------------------------------------------------------------------------------------------------% function OutputMat = CelltoMatUE (input_cell) % inptsize = size(input_cell); maxLength = max(cellfun('length',input_cell)); if inptsize(2) > inptsize(1) OutputMat = cell2mat(cellfun(@(x)cat(1,x,nan(maxLength-length(x),1)),input_cell,'un',0)); else OutputMat = cell2mat(cellfun(@(x)cat(2,x',nan(1,maxLength-length(x))),input_cell,'un',0)); end end %--------------------------------------------------------------------------------------------------% %---------- plotting modules ----------% %--------------------------------------------------------------------------------------------------% function plot_sca(ca_data, cellnum, clusternum) % putting data together to make plotting easier para = ca_data.stimpara; sta = [ca_data.locXhigh.sta;ca_data.locX_low.sta;ca_data.locY_high.sta;ca_data.locY_low.sta]'; statime = - [ca_data.locXhigh.time;ca_data.locX_low.time;ca_data.locY_high.time;ca_data.locY_low.time]'*1e3; nlx = [ca_data.locXhigh.nlx;ca_data.locX_low.nlx;ca_data.locY_high.nlx;ca_data.locY_low.nlx]'; nly = [ca_data.locXhigh.nly;ca_data.locX_low.nly;ca_data.locY_high.nly;ca_data.locY_low.nly]'; figure('pos',[600 50 900 950],'color','w'); % get plots title and legend labels titr = {'Location X', 'Location Y'}; if para.twofixed l = {'high-low','low-low'}; s = 'Spatial contrast adaptation'; else l = {'high-low','low-high'}; s = 'Spatial contrast adaptation (alternating stimulus)'; end % first plotting psths subplot(3,2,[1,2]) bar(para.low_contduration+para.psthtime_high(1:end-1),ca_data.psth_high); hold on; bar(para.psthtime_low(1:end-1),ca_data.psth_low); pbaspect([3 1 1]); box off; xlabel('Time (sec)'); ylabel('Rate (Hz)'); xlim([0 para.low_contduration+para.high_contduration]); xticks([0 para.low_contduration para.low_contduration+para.high_contduration]); yAx = get(gca,'YLim'); yAx = ceil(yAx(2)/2)*2; yticks([0 yAx/2 yAx]); legend(l,'Location','northwest'); legend boxoff; % plotting STAs and nonlinearities for ii = 1:2 subplot(3,2,ii+2) plot(statime(:,2*(ii-1)+1),sta(:,2*(ii-1)+1),statime(:,2*ii),sta(:,2*ii),'LineWidth',2); pbaspect([1.2 1 1]); box off; axis([0 600 -0.5 0.5]); title(['STA ',titr{ii}]); xlabel('Delay (sec)'); ylabel('Filter (a.u.)'); if ii==2, legend(l,'Location','southeast'); legend boxoff; end subplot(3,2,ii+4) plot(nlx(:,2*(ii-1)+1),nly(:,2*(ii-1)+1),nlx(:,2*ii),nly(:,2*ii),'LineWidth',2); pbaspect([1.2 1 1]); box off; ax = gca; yAx = ceil(ax.YLim(2)/2)*2; axis([-3.1 3.1 0 yAx]); title(['Nonlinearity ',titr{ii}]); xlabel('Input'); ylabel('\Delta Rate (Hz)'); %ax.XAxisLocation = 'origin'; ax.YAxisLocation = 'origin'; yticks(0:yAx/2:yAx); xticks(-3:1:3); end % mega title on top annotation('textbox', [0.1, 0.95, 0.8, 0], 'string', [s, ' properties of cell ',... num2str(cellnum),', cluster ',num2str(clusternum)],'FontSize',16,'EdgeColor','none',... 'HorizontalAlignment','center','VerticalAlignment','middle'); end %--------------------------------------------------------------------------------------------------%