From 52056be491df19fcc1efec1864124f63d2dee10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20P=C3=A9rez-Ortega?= Date: Thu, 2 May 2019 13:00:45 -0400 Subject: [PATCH] Uploading files --- .DS_Store | Bin 0 -> 6148 bytes CCidx2_JP.m | 39 + Change_Names_MEA_Channels.m | 15 + Circle_Mask.m | 13 + ClustIdx_JP.m | 62 + Compare_Times.m | 89 + ContrastIdx_JP.m | 38 + DaviesIdx_JP.m | 45 + Delete_Consecutive_Coactivation.m | 15 + Delete_Raster_Activity_Between_Trials.m | 15 + Divide_Peaks.m | 25 + DunnIdx_JP.m | 32 + Extract_Raster_Peaks.m | 21 + Filter_MEA_Channel.m | 28 + Find_Peaks_Or_Valleys.m | 269 ++ Find_Threshold_In_Cumulative_Distribution.m | 23 + Fit_Power_Law.m | 18 + Get_Adjacency_From_Raster.m | 27 + Get_Adjacency_From_Sequence.m | 18 + Get_And_Filter_Coactivity.m | 18 + Get_Circular_XY.m | 27 + Get_Efficiency.m | 24 + Get_Entropy.m | 22 + Get_Force_XY.m | 21 + Get_Groups_Of_Neurons.m | 51 + Get_ISI.m | 11 + Get_Instant_Freq.m | 34 + Get_Network_Activation_Sequence.m | 74 + Get_Normalized_Instant_Freq.m | 39 + Get_Peak_Or_Valley_Indices.m | 13 + Get_Peak_Vectors.m | 149 + Get_Peaks_Sequence_Sorted.m | 23 + Get_Peaks_Similarity.m | 23 + Get_Peaks_Similarity_OLD.m | 84 + Get_Raster_From_MEA.m | 41 + Get_Raster_From_MEA_Channels.m | 44 + Get_Raster_From_Time_Stamps.m | 49 + Get_Significant_Network_From_Raster.m | 65 + ...cant_Network_From_Raster_And_Correlation.m | 82 + Get_SmallWorld_Properties.m | 70 + Get_Spike_Rate.m | 17 + Get_Spikes_Shape_From_MEA_Channels.m | 52 + Get_XY_Ensembles.m | 77 + Get_Zscore_From_Raster.m | 20 + Hold_Axes.m | 8 + Hold_Figure.m | 8 + Jitter_Raster.m | 42 + Join_Peaks.m | 24 + Make_Regular_Ring_Network.m | 50 + Neural_Ensemble_Analysis.fig | Bin 0 -> 68480 bytes Neural_Ensemble_Analysis.m | 2906 +++++++++++++++++ PeaksVectors_JP_OLD.m | 33 + Plot_Activation_Order.m | 95 + Plot_Adjacencies_And_Network.m | 55 + Plot_Changes_In_Neurons.m | 19 + Plot_Coactivity.m | 56 + Plot_Degree_Distribution.m | 76 + Plot_Edge.m | 84 + Plot_Ensembles.m | 65 + Plot_Hierarchy.m | 60 + Plot_Network.m | 111 + Plot_Peaks_On_Coactivity.m | 34 + Plot_Raster.m | 52 + Plot_Rich_Club.m | 52 + Plot_Sequence_Graph_By_Width.m | 50 + Plot_Sequences.m | 71 + Plot_Similarity.m | 31 + Plot_Similarity_With_Dendrogram.m | 39 + Plot_Spikes.m | 35 + Plot_Spikes_From_ISI.m | 24 + Plot_States_By_Width.m | 53 + Plot_States_Sequence.m | 54 + Plot_WD_Network.m | 70 + Plot_WU_Network.m | 105 + RV_coefficient.m | 12 + Read_Colors.m | 45 + Remove_Oscillations.m | 15 + Reshape_Raster.m | 21 + Save_Figure.m | 43 + Save_Raster_By_Windows.m | 20 + Save_Raster_Min_By_Min.m | 16 + Scale.m | 36 + Set_Axes.m | 5 + Set_Colormap_Blue_White_Red.m | 11 + Set_Figure.m | 23 + Set_Label_Time.m | 29 + Shuffle_Test.m | 113 + Shuffle_Test_For_Every_Link.m | 46 + Shuffle_Test_For_Weighted_Matrix.m | 67 + Sort_Activation_Position.m | 63 + Sort_Raster.m | 21 + Z_Score_Coactivity.m | 51 + charpath.m | 79 + clustering_coef_bu.m | 28 + density_und.m | 28 + dist_corr.m | 6 + distance_bin.m | 45 + distcorr.m | 35 + makerandCIJ_und.m | 25 + rich_club_bu.m | 49 + shuffle.m | 116 + 101 files changed, 7332 insertions(+) create mode 100644 .DS_Store create mode 100644 CCidx2_JP.m create mode 100644 Change_Names_MEA_Channels.m create mode 100644 Circle_Mask.m create mode 100644 ClustIdx_JP.m create mode 100644 Compare_Times.m create mode 100644 ContrastIdx_JP.m create mode 100644 DaviesIdx_JP.m create mode 100644 Delete_Consecutive_Coactivation.m create mode 100644 Delete_Raster_Activity_Between_Trials.m create mode 100644 Divide_Peaks.m create mode 100644 DunnIdx_JP.m create mode 100644 Extract_Raster_Peaks.m create mode 100644 Filter_MEA_Channel.m create mode 100644 Find_Peaks_Or_Valleys.m create mode 100644 Find_Threshold_In_Cumulative_Distribution.m create mode 100644 Fit_Power_Law.m create mode 100644 Get_Adjacency_From_Raster.m create mode 100644 Get_Adjacency_From_Sequence.m create mode 100644 Get_And_Filter_Coactivity.m create mode 100644 Get_Circular_XY.m create mode 100644 Get_Efficiency.m create mode 100644 Get_Entropy.m create mode 100644 Get_Force_XY.m create mode 100644 Get_Groups_Of_Neurons.m create mode 100644 Get_ISI.m create mode 100644 Get_Instant_Freq.m create mode 100644 Get_Network_Activation_Sequence.m create mode 100644 Get_Normalized_Instant_Freq.m create mode 100644 Get_Peak_Or_Valley_Indices.m create mode 100644 Get_Peak_Vectors.m create mode 100644 Get_Peaks_Sequence_Sorted.m create mode 100644 Get_Peaks_Similarity.m create mode 100644 Get_Peaks_Similarity_OLD.m create mode 100644 Get_Raster_From_MEA.m create mode 100644 Get_Raster_From_MEA_Channels.m create mode 100644 Get_Raster_From_Time_Stamps.m create mode 100644 Get_Significant_Network_From_Raster.m create mode 100644 Get_Significant_Network_From_Raster_And_Correlation.m create mode 100644 Get_SmallWorld_Properties.m create mode 100644 Get_Spike_Rate.m create mode 100644 Get_Spikes_Shape_From_MEA_Channels.m create mode 100644 Get_XY_Ensembles.m create mode 100644 Get_Zscore_From_Raster.m create mode 100644 Hold_Axes.m create mode 100644 Hold_Figure.m create mode 100644 Jitter_Raster.m create mode 100644 Join_Peaks.m create mode 100644 Make_Regular_Ring_Network.m create mode 100644 Neural_Ensemble_Analysis.fig create mode 100644 Neural_Ensemble_Analysis.m create mode 100644 PeaksVectors_JP_OLD.m create mode 100644 Plot_Activation_Order.m create mode 100644 Plot_Adjacencies_And_Network.m create mode 100644 Plot_Changes_In_Neurons.m create mode 100644 Plot_Coactivity.m create mode 100644 Plot_Degree_Distribution.m create mode 100644 Plot_Edge.m create mode 100644 Plot_Ensembles.m create mode 100644 Plot_Hierarchy.m create mode 100644 Plot_Network.m create mode 100644 Plot_Peaks_On_Coactivity.m create mode 100644 Plot_Raster.m create mode 100644 Plot_Rich_Club.m create mode 100644 Plot_Sequence_Graph_By_Width.m create mode 100644 Plot_Sequences.m create mode 100644 Plot_Similarity.m create mode 100644 Plot_Similarity_With_Dendrogram.m create mode 100644 Plot_Spikes.m create mode 100644 Plot_Spikes_From_ISI.m create mode 100644 Plot_States_By_Width.m create mode 100644 Plot_States_Sequence.m create mode 100644 Plot_WD_Network.m create mode 100644 Plot_WU_Network.m create mode 100644 RV_coefficient.m create mode 100644 Read_Colors.m create mode 100644 Remove_Oscillations.m create mode 100644 Reshape_Raster.m create mode 100644 Save_Figure.m create mode 100644 Save_Raster_By_Windows.m create mode 100644 Save_Raster_Min_By_Min.m create mode 100644 Scale.m create mode 100644 Set_Axes.m create mode 100644 Set_Colormap_Blue_White_Red.m create mode 100644 Set_Figure.m create mode 100644 Set_Label_Time.m create mode 100644 Shuffle_Test.m create mode 100644 Shuffle_Test_For_Every_Link.m create mode 100644 Shuffle_Test_For_Weighted_Matrix.m create mode 100644 Sort_Activation_Position.m create mode 100644 Sort_Raster.m create mode 100644 Z_Score_Coactivity.m create mode 100644 charpath.m create mode 100644 clustering_coef_bu.m create mode 100644 density_und.m create mode 100644 dist_corr.m create mode 100644 distance_bin.m create mode 100644 distcorr.m create mode 100644 makerandCIJ_und.m create mode 100644 rich_club_bu.m create mode 100644 shuffle.m diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e01012ecd916c37e9f816cb061e3eedb5e7980d6 GIT binary patch literal 6148 zcmeHK%Sr=55UkN00=eXv7X-iO=g_ zZ}y9Mv)gZ(afS!(X@xEI1+I06joKA`H8xmK9~iqK1NWFym+U>$yXLRPD|y^s8EN%Vq&VBnuIkOtLVRdQ2)w|?24+_jnQiA_ZES}`cJJ0Ah;$T>36 dpvAM;%qtvQLm5T=i_a && i_m<=f_a || f_m>=i_a && f_m<=f_a || i_m<=i_a && f_m>=f_a) + if abs(i_m-i_a)<100 + diff_i=i_a-i_m; + diff_f=f_a-f_m; + n_coincidence=n_coincidence+1; + coincidence(n_coincidence,:)=[i_m i_a f_m f_a]; + diffs(n_coincidence,:)=[diff_i diff_f]; + auto_actual=j+1; + break; + % If are differents + elseif(i_m>f_a) + auto_extra=auto_extra+1; + elseif(f_m 0); + case 2 + id = find(raster_analysis.Peaks.Indices(range) > 0)+range(1)-1; + case 3 + if(peaks) + id = find(raster_analysis.Peaks.Indices(range) > 0)+range(1)-1; + else + id = find(raster_analysis.Peaks.Indices(range) == 0)+range(1)-1; + end + end + raster_peaks = raster_analysis.Raster.Raster(:,id); +end \ No newline at end of file diff --git a/Filter_MEA_Channel.m b/Filter_MEA_Channel.m new file mode 100644 index 0000000..9dcb3d0 --- /dev/null +++ b/Filter_MEA_Channel.m @@ -0,0 +1,28 @@ +% Filter MEA +% +% Lab Peńa-Ortega +% 250 - 7,500 Hz band pass filter +% butterworth order 2 +% +% By Pérez-Ortega Jesús, jan-2019 + +function filtered = Filter_MEA_Channel(signal) + + fs = 25000; % sampling frequency + + % Filter design + order=2; + % low pass + fc = 7500; % cutoff frequency + [b,a] = butter(order,fc/(fs/2),'low'); + + % Filter 1 + y = filter(b,a,signal); + + % high pass + fc = 250; % cutoff frequency + [b,a] = butter(order,fc/(fs/2),'high'); + + % Filter 2 + filtered = filter(b,a,y); +end \ No newline at end of file diff --git a/Find_Peaks_Or_Valleys.m b/Find_Peaks_Or_Valleys.m new file mode 100644 index 0000000..474d8f1 --- /dev/null +++ b/Find_Peaks_Or_Valleys.m @@ -0,0 +1,269 @@ +function [indices,widths,amplitudes,ini_fin] = Find_Peaks_Or_Valleys(data,threshold,join,... +detect_peaks,minimum_width,fixed_width,ignore_ini_fin) +% Find peaks from signal by a given threshold. +% +% [indices,widths,amplitudes,ini_fin] = Find_Peaks_Or_Valleys(data,threshold,join,... +% detect_peaks,minimum_width,fixed_width,ignore_ini_fin) +% +% Inputs +% data = data as vector Fx1 (F = #frames) +% threshold = threshold +% join = set mode to get peaks (0 = each vector above threshold is a peak; +% 1 = joining of adjacent vectors above the threshold is a peak) +% +% Outputs +% indices = Fx1 vector containing the peak indices +% +% 1. Get indices above threshold +% 2. Ignore initial and final peaks (or valleys) - Optional +% 3. Restriction to minimum width - Optional +% 4. Set a fixed width - Optional +% 5. Set the number at each peak (or valley) +% 6. Join peaks or valleys - Optional +% +% ..:: by Jesus E. Perez-Ortega ::.. Feb-2012 +% JP debug 1-oct-2012 +% JP add 3rd input to join or not +% modified March-2018 +% modified April-2018 +% modified May-2018 +% modified Jul-2018 +% modified Nov-2018 +% modified Dec-2018 +% modified Jan-2019 + +if(nargin==6) + ignore_ini_fin=false; +elseif(nargin==5) + fixed_width = 0; + ignore_ini_fin=false; +elseif(nargin==4) + minimum_width=0; + fixed_width=0; + ignore_ini_fin=false; +end + +% 0. Correct signal data +if(size(data,1)==1) + data = data'; +end +original_data = data; + +% 1. Get peak or valley indices +[idx,count] = Get_Peak_Or_Valley_Indices(data,threshold,detect_peaks); +% Size of data +F = numel(data); +indices=zeros(F,1); +if ~count + if(detect_peaks) + disp('No peaks found!') + widths = []; + amplitudes = []; + ini_fin = []; + else + disp('No valleys found!') + widths = []; + amplitudes = []; + ini_fin = []; + end + return +end + +% 2. Ignore initial and final peak +if ignore_ini_fin + + % Delete if start above threshold + last=1; + idx=idx'; + for i=idx + if(last==i) + if detect_peaks + data(i)=threshold-1; + else + data(i)=threshold+1; + end + last=last+1; + else + break; + end + end + + % Delete if ends above threshold + last = F; + idx = flipud(idx); + for i = idx + if last==i + if detect_peaks + data(i)=threshold-1; + else + data(i)=threshold+1; + end + last=last-1; + else + break; + end + end + + % Get peak or valley indices + [idx,count] = Get_Peak_Or_Valley_Indices(data,threshold,detect_peaks); + if ~count + if detect_peaks + disp('No peaks found!') + else + disp('No valleys found!') + end + return + end +end + +% 3. Minimum width (after join peaks or valleys) +if minimum_width + + % Join peaks or valleys + is = find(idx~=[0; idx(1:numel(idx)-1)+1]); % index of same peak + % number of total peaks or valleys + count = numel(is); + if count + for j = 1:count-1 + indices(idx(is(j)):idx(is(j+1)-1),1)=j; % set #peak + end + indices(idx(is(count)):max(idx),1)=count; + end + + % Get peaks or valleys width + widths=[]; + for i=1:count + widths(i)=length(find(indices==i)); + end + + % Evaluate peaks less than or equal to minimum width + idx_eval=find(widths<=minimum_width); + widths=widths(idx_eval); + + % number of peaks to eliminate + count_less=length(widths); + + % Detect initial and final times + if count_less>0 + for i=1:count_less + peak=find(indices==idx_eval(i)); + ini_peak=peak(1); + end_peak=peak(end); + if(detect_peaks) + data(ini_peak:end_peak)=threshold-1; + else + data(ini_peak:end_peak)=threshold+1; + end + end + end + + % Get peak or valley indices + [idx,count] = Get_Peak_Or_Valley_Indices(data,threshold,detect_peaks); + if ~count + if(detect_peaks) + disp('No peaks found!') + else + disp('No valleys found!') + end + return + end +end + +% 4. Set fixed width +if fixed_width + last_end = -1; + end_before = false; + for i=idx' + if i==last_end+1 + if detect_peaks + data(i)=threshold-1; + else + data(i)=threshold+1; + end + if end_before + end_before = false; + else + last_end = i; + end + else + if i>last_end + if fixed_width<0 + ini=i+fixed_width; + fin=i-1; + if(ini<0) + ini=0; + end + fixed_width_peak=ini:fin; + last_end = fin+1; + else + fin = i+fixed_width-1; + if(fin>F) + fin=F; + end + fixed_width_peak=i:fin; + last_end = fin; + end + + if detect_peaks + data(fixed_width_peak)=threshold+1; + if fixed_width<0 + data(fixed_width_peak-fixed_width)=threshold-1; + elseif sum(data(fixed_width_peak)threshold) + end_before = true; + end + end + end + end + end + + % Get peak or valley indices + [idx,count] = Get_Peak_Or_Valley_Indices(data,threshold,detect_peaks); +end + +% 5. Put numbers to peaks +indices=zeros(F,1); +for i=1:count + indices(idx(i))=i; +end + +% 6. Join peaks or valleys +if join + is = find(idx~=[0; idx(1:numel(idx)-1)+1]); % index of same peak + + % number of total peaks + count = numel(is); + if count + for j = 1:count-1 + indices(idx(is(j)):idx(is(j+1)-1),1)=j; % set #peak + end + indices(idx(is(count)):max(idx),1)=count; + end +end + +% Get peaks or valleys width +widths = zeros(count,1); +ini_fin = zeros(count,2); +for i = 1:count + id = find(indices==i); + ini_fin(i,1) = id(1); + ini_fin(i,2) = id(end); + widths(i) = length(id); +end + +% Get peaks or valleys amplitud +amplitudes = zeros(count,1); +for i = 1:count + if detect_peaks + value = max(original_data(indices==i)); + else + value = min(original_data(indices==i)); + end + amplitudes(i) = value; +end \ No newline at end of file diff --git a/Find_Threshold_In_Cumulative_Distribution.m b/Find_Threshold_In_Cumulative_Distribution.m new file mode 100644 index 0000000..3c6a6d4 --- /dev/null +++ b/Find_Threshold_In_Cumulative_Distribution.m @@ -0,0 +1,23 @@ +% Find the threshold upper a given alpha +% +% Jesús Pérez-Ortega nov 2018 + +function th = Find_Threshold_In_Cumulative_Distribution(data,alpha) + + if(min(data)==max(data)) + th = max(data)+1; + else + if(max(data)<=1) + x = 0:0.001:1; + elseif(max(data)<=10) + x = 0:0.01:10; + else + x = 0:max(data); + end + y = hist(data,x); + cdy = cumsum(y); + cdy = cdy/max(cdy); + id = find(cdy>(1-alpha),1,'first'); + th = x(id); + end +end \ No newline at end of file diff --git a/Fit_Power_Law.m b/Fit_Power_Law.m new file mode 100644 index 0000000..b211554 --- /dev/null +++ b/Fit_Power_Law.m @@ -0,0 +1,18 @@ +% Fit_Power_Law +% +% Jesús E. Pérez-Ortega - june 2018 + +function [slope, intercept, R2] = Fit_Power_Law(x,y) + + % Set power law function + power_law = fittype('a*x^b'); + + % Compute fit + [fit1,gof] = fit(x,y,power_law,'StartPoint',[1 -1]); + coeffs = coeffvalues(fit1); + + % Get coefficients + slope=coeffs(2); + intercept=coeffs(1); + R2=gof.rsquare; +end diff --git a/Get_Adjacency_From_Raster.m b/Get_Adjacency_From_Raster.m new file mode 100644 index 0000000..64eaef0 --- /dev/null +++ b/Get_Adjacency_From_Raster.m @@ -0,0 +1,27 @@ +% Get adjacency matrix from raster peaks +% +% Negative numbers and NaNs are set to 0s +% +% Pérez-Ortega Jesús - march 2018 +% Modified - may 2018 +% Modified - june 2018 + +function adjacency=Get_Adjacency_From_Raster(raster,connectivity_method) + cells=size(raster,1); + switch(connectivity_method) + case 'coactivity' + %warning('Data is set to binary to obtain the adjacency matrix.') + raster=double(raster>0); + adjacency=raster*raster'.*(1-eye(cells)); + case 'jaccard' + %warning('Data is set to binary to obtain the adjacency matrix.') + raster=double(raster>0); + adjacency=squareform(1-pdist(raster,'jaccard')); + adjacency(isnan(adjacency))=0; + otherwise + adjacency=corr(raster','type',connectivity_method); + adjacency(adjacency<0)=0; + adjacency(isnan(adjacency))=0; + adjacency=adjacency.*(ones(cells)-eye(cells)); + end +end \ No newline at end of file diff --git a/Get_Adjacency_From_Sequence.m b/Get_Adjacency_From_Sequence.m new file mode 100644 index 0000000..960dde2 --- /dev/null +++ b/Get_Adjacency_From_Sequence.m @@ -0,0 +1,18 @@ +% Get a directed adjacency matrix from a sequence +% +% Get a directed adjacency matrix including loops. +% +% Pérez-Ortega Jesús - march 2018 + +function Adjacency = Get_Adjacency_From_Sequence(sequence) + n=length(sequence); + max_state=max(sequence); + Adjacency=zeros(max_state); + + for i=1:n-1 + j=i+1; + state_i=sequence(i); + state_j=sequence(j); + Adjacency(state_i,state_j)=Adjacency(state_i,state_j)+1; + end +end \ No newline at end of file diff --git a/Get_And_Filter_Coactivity.m b/Get_And_Filter_Coactivity.m new file mode 100644 index 0000000..dd00026 --- /dev/null +++ b/Get_And_Filter_Coactivity.m @@ -0,0 +1,18 @@ +% Get Coactivity +% +% double smoothing filter +% +% By Jesús Pérez-Ortega jan-2018 +% modified feb-2018 +% modified april-2018 + +function smooth_coactivity = Get_And_Filter_Coactivity(raster,bin) + coactivity=sum(raster)'; + if(bin>1) + % double smoothing + smooth_coactivity=smooth(coactivity,bin); + %smooth_coactivity=smooth(smooth_coactivity,bin); + else + smooth_coactivity=coactivity; + end +end \ No newline at end of file diff --git a/Get_Circular_XY.m b/Get_Circular_XY.m new file mode 100644 index 0000000..882a424 --- /dev/null +++ b/Get_Circular_XY.m @@ -0,0 +1,27 @@ +function [xy,step] = Get_Circular_XY(n,radio,offset) +% Get circular coordinates for n elements given a radio and offset in +% degrees +% +% [xy,step] = Get_Circular_XY(n,radio,offset) +% +% Pérez-Ortega Jesús - April 2018 +% modified April 2019 + +switch(nargin) + case 1 + radio = 1; + offset = 0; + case 2 + offset = 0; +end + +% convert to radians +offset = offset*pi/180; + +if (n==1) + xy=[0 0]; + step = 0; +else + xy=[cos(offset+2*pi*(1:n)'/n) sin(offset+2*pi*(1:n)'/n)].*radio; + step = 360/n; +end \ No newline at end of file diff --git a/Get_Efficiency.m b/Get_Efficiency.m new file mode 100644 index 0000000..e290694 --- /dev/null +++ b/Get_Efficiency.m @@ -0,0 +1,24 @@ +% Get efficiency between a group of vectors +% +% Jesus Perez-Ortega - Sep 2018 + +function [Efficiency,Error] = Get_Efficiency(vectors) + [n,length_vector]=size(vectors); + if(n>1) + % Compute efficiency + Error=zeros(n); + for i=1:n-1 + for j=i+1:n + error_ij=length(find(abs(vectors(i,:)-vectors(j,:)))); + Error(i,j)=error_ij; + Error(j,i)=error_ij; + end + end + Error=Error./length_vector; + Efficiency=1-Error; + else + warning('There are no data to compare!') + Efficiency=[]; + Error=[]; + end +end diff --git a/Get_Entropy.m b/Get_Entropy.m new file mode 100644 index 0000000..f22b169 --- /dev/null +++ b/Get_Entropy.m @@ -0,0 +1,22 @@ +function entropy = Get_Entropy(frequencies) +% Entropy from a list of frequencies +% +% By Pérez-Ortega Jesús, Feb 2019 + +n = length(frequencies); +total = sum(frequencies); + +if(~total) + entropy = nan; + return +end + +p = frequencies/total; + +entropy = 0; +for i = 1:n + if(p(i)) + entropy = entropy + p(i)*log2(p(i)); + end +end +entropy = -entropy; diff --git a/Get_Force_XY.m b/Get_Force_XY.m new file mode 100644 index 0000000..4b73d3a --- /dev/null +++ b/Get_Force_XY.m @@ -0,0 +1,21 @@ +function xy = Get_Force_XY(adjacency) +% Get coordinates to plot network in 2D with force layout +% +% xy = Get_Force_XY(adjacency) +% +% Jesús Pérez-Ortega nov 2018 + +if isempty(adjacency) + xy = []; + return +end + +% Get coordinates +G=graph(adjacency); +figure(); +%G_plot=plot(G,'Layout','force','usegravity',true); +%G_plot=plot(G,'Layout','force','weighteffect','direct'); +%G_plot=plot(G,'Layout','force','usegravity',true,'iterations',250); +G_plot=plot(G,'Layout','force','iterations',250); +xy = [G_plot.XData' G_plot.YData']; +close; \ No newline at end of file diff --git a/Get_Groups_Of_Neurons.m b/Get_Groups_Of_Neurons.m new file mode 100644 index 0000000..93b2243 --- /dev/null +++ b/Get_Groups_Of_Neurons.m @@ -0,0 +1,51 @@ +% Get groups of neurons +% +% By Jesús Pérez-Ortega jan-2018 + +function [CellsGroups Indexes IndexesComb] = Get_Groups_Of_Neurons(Freqs,Groups,idx_vector_state,th_freq_group) + + % Get states of each neuron + C=size(Freqs,1); + CellsGroups=zeros(Groups,C); + CellsAcGroups=zeros(Groups,C); + for i=1:Groups + PeaksGroupIdx= idx_vector_state==i; + n_peaks_group=length(PeaksGroupIdx); + CellsAcGroups(i,:)=sum(Freqs(:,PeaksGroupIdx),2)/n_peaks_group; + CellsGroup=CellsAcGroups(i,:)>th_freq_group; + CellsGroups(i,CellsGroup)=i; + end + + % Get states combinations + [Groups C]=size(CellsGroups); + States=[]; Labels={}; + Idx=[]; Indexes=[]; + Combinations=2^Groups-1; + for i=1:Combinations + bin_number=dec2bin(i,Groups); + GroupsToCompare=[]; + Label=[]; + for j=1:Groups + Compare=str2num(bin_number(j)); + if Compare + GroupsToCompare=[GroupsToCompare; CellsGroups(j,:)>=1]; + if Label + Label=[Label ',' num2str(j)]; + else + Label=num2str(j); + end + else + GroupsToCompare=[GroupsToCompare; CellsGroups(j,:)==0]; + end + end + Idx{i}=find(sum(GroupsToCompare,1)==Groups); + States(i)=length(Idx{i})/C; + Labels(i)={Label}; + end + [Labels idxSort]=sort(Labels); + States=States(idxSort); + for i=1:Combinations + Indexes=[Indexes Idx{idxSort(i)}]; + IndexesComb(Idx{i})=i; + end +end \ No newline at end of file diff --git a/Get_ISI.m b/Get_ISI.m new file mode 100644 index 0000000..e29a1b1 --- /dev/null +++ b/Get_ISI.m @@ -0,0 +1,11 @@ +% Get Inter spike interval (ISI) +% +% inputs: data = vector with spikes (0: no spike, 1: spike) +% f = samples per second (sampling frequency) +% +% By Jesús E. Pérez-Ortega - jan - 2019 + +function isi = Get_ISI(data,f) + id = find(data); + isi = diff(id)/f; +end \ No newline at end of file diff --git a/Get_Instant_Freq.m b/Get_Instant_Freq.m new file mode 100644 index 0000000..7d5f647 --- /dev/null +++ b/Get_Instant_Freq.m @@ -0,0 +1,34 @@ +% Plot instantaneous frequency (inverse of ISI) +% +% inputs: data = vector with spikes (0: no spike, 1: spike) +% sps = samples per second (sampling frequency) +% +% By Jesús E. Pérez-Ortega - may 2017 +% modified jan-2018 + +function frequencies = Get_Instant_Freq(data,sps) + + N=length(data); + frequencies=zeros(1,N); + + idx=find(data); + + % if there are spikes + if (~isempty(idx)) + % If first data is not a spike + if (idx(1)~=1) + frequencies(1:idx(1))=sps/(idx(1)-1); + end + + % All ISI inverse + n=length(idx)-1; + for j=1:n + frequencies(idx(j):idx(j+1))=sps/(idx(j+1)-idx(j)); + end + + % If last data is not a spike + if(idx(n+1)~=N) + frequencies(idx(n+1):N)=sps/(N-idx(n+1)); + end + end +end \ No newline at end of file diff --git a/Get_Network_Activation_Sequence.m b/Get_Network_Activation_Sequence.m new file mode 100644 index 0000000..604b6e1 --- /dev/null +++ b/Get_Network_Activation_Sequence.m @@ -0,0 +1,74 @@ +% Get network activation sequence +% +% Jesús Pérez-Ortega - Dic 2018 +% modified Jan 2019 +function [sequence,singles] = Get_Network_Activation_Sequence(raster,network,link_sequence) + + if(nargin==2) + link_sequence = false; + end + + %[shorted,short_times]=Delete_Consecutive_Coactivation(raster); + n_length=size(raster,2); + adjacency=Get_Adjacency_From_Raster(raster,'coactivity'); + coincidence=double(adjacency>0).*network; + + if(link_sequence) + coincidence = squareform(coincidence,'tovector'); + end + + %total_links=sum(adjacency_core_inspiration(:)); + %n_co=1; + %single_links=0; + + sequence=[]; + for i=1:n_length + single=Get_Adjacency_From_Raster(raster(:,i),'coactivity'); + if(link_sequence) + single=squareform(single,'tovector'); + end + single_coincidence=single.*coincidence; + sequence=[sequence setdiff(find(sum(single_coincidence)),sequence)]; + singles{i}=single_coincidence; + + % Plot network + %{ + core_coactivation=single.*adjacency_core_inspiration; + single_links=single_links+sum(coactivation_remaining(:)); + + if(sum(coactivation_remaining(:))~=0) + %Set_Figure('Network',[0 0 300 300]); + net_color=colors(short_times(l),:); + node_size=10; + subplot(5,11,n_co+11*(m-1)) + %Plot_WU_Network(core_coactivation,[],net_color,mean([net_color; 0.5 0.5 0.5]),node_size); hold on + Plot_WU_Network(coactivation_remaining,[],net_color,mean([net_color; 0.5 0.5 0.5]),node_size) + title([num2str(short_times(l)) ' ms (' num2str(round(single_links/total_links*100)) '%)']) + if(n_co==1) + ylabel(['inspiration #' num2str(k)]) + end + subplot(5,11,11*m) + Plot_WU_Network(coactivation_remaining,[],net_color,mean([net_color; 0.5 0.5 0.5]),node_size);hold on + % frame = getframe(gcf); + % writeVideo(v,frame); + n_co=n_co+1; + if(n_co>10) + break; + end + end + remaining=remaining-coactivation_remaining; + if(remaining==0) + break; + end + %input('press any key to continue...') + %} + end + %{ + m=m+1; + if(m>5) + Save_Figure(['Networks_' num2str(j) '_' num2str(ceil(k/5))]); + close + m=1; + end + %} +end \ No newline at end of file diff --git a/Get_Normalized_Instant_Freq.m b/Get_Normalized_Instant_Freq.m new file mode 100644 index 0000000..f399602 --- /dev/null +++ b/Get_Normalized_Instant_Freq.m @@ -0,0 +1,39 @@ +% Get Normalized Instantaneous Frequency from raster +% +% Instantaneous frequency = inverse of ISI +% +% By Jesús Pérez-Ortega jan-2018 +% modified feb-2018 +% modified jan-2019 + +function freqs = Get_Normalized_Instant_Freq(data,fps,norm_type,bin) + + if(nargin==3) + bin=0; + end + + [c,f]=size(data); + freqs=zeros([c f]); + for i=1:c + % Get frequencies + freqs(i,:)=Get_Instant_Freq(data(i,:),fps); + + if(~freqs(i,:)) + freqs(i,:)=zeros(1,f); + else + % Normalize the firing frequency + if (bin) + freqs(i,:)=smooth(freqs(i,:),bin); + end + if (~strcmp(norm_type,'none')) + freqs(i,:)=freqs(i,:)-mean(freqs(i,:)); + switch(norm_type) + case 'norm' + freqs(i,:)=freqs(i,:)/max(freqs(i,:)); % Normalize + case 'zscore' + freqs(i,:)=freqs(i,:)/std(freqs(i,:)); % Z-score + end + end + end + end +end \ No newline at end of file diff --git a/Get_Peak_Or_Valley_Indices.m b/Get_Peak_Or_Valley_Indices.m new file mode 100644 index 0000000..ad6a5cd --- /dev/null +++ b/Get_Peak_Or_Valley_Indices.m @@ -0,0 +1,13 @@ +% Get peak or valley indices +% +% by Jesús E. Pérez-Ortega - Feb 2012 + +function [indices,count] = Get_Peak_Or_Valley_Indices(data,threshold,detect_peaks) + if(detect_peaks) + indices = find(data>=threshold); + else + % detect valleys + indices = find(data0; + end + case 'average' + vectors=zeros(peaks,C); + for i=1:peaks + DataPeak_i=data(:,peak_indices==i); + vectors(i,:)=mean(DataPeak_i,2); + end + case 'network' + vectors=zeros(peaks,C*(C-1)/2); + for i=1:peaks + DataPeak_i=data(:,peak_indices==i); + A=Get_Adjacency_From_Raster(Reshape_Raster(DataPeak_i,bin_network),... + connectivity_method); + + % Get significant network + %{ +% extra=250; +% id = find(peak_indices==i); +% id = id(1)-extra:id(end)+extra; +% DataPeak_i = data(:,id); + iterations = 20; + alpha = 0.05; + single_th = true; + shuffle_method = 'time_shift'; + A = Get_Significant_Network_From_Raster(DataPeak_i,bin_network,iterations,... + alpha,connectivity_method,shuffle_method,single_th); + %} + vectors(i,:)=squareform(A,'tovector'); + end + end + else + disp('There are no data peaks!') + end +end + +%{ + if (n_peaks) + switch(Sim_method) + case 'RV' % 2h + peaks=max(PeaksIdx); + + C=size(Data,1); + DataPeaks3=zeros(C,C,peaks); + DataPeaks3_2=zeros(C,C,peaks); + + for i=1:peaks + I=Data(:,PeaksIdx==i); + DataPeaks3(:,:,i)=I*I'; + end + for i=1:peaks + Trace_DataPeaks3(i)=trace(abs(DataPeaks3(:,:,i)^2)); + end + Sim=eye(peaks); + for i=1:(peaks-1) + for j=(i+1):peaks + Sim(i,j) = trace(DataPeaks3(:,:,i)*DataPeaks3(:,:,j))/... + sqrt(Trace_DataPeaks3(i)*Trace_DataPeaks3(j)); + Sim(j,i) = Sim(i,j); + end + end + Sim=(Sim+1)/2; % Normalization + case 'Network Hamming Coactivity' % 5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P>0,'Coactivity'); + A=A/size(P,2); + A(A<1)=0; + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Hamming')); + Sim=1-Distance; + case 'Network Pearson Euclidean' %5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P,'Pearson'); + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Euclidean')); + Sim=1-Distance/max(Distance(:)); + case 'Network Pearson-Size Euclidean' % 5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P,'Pearson')*size(P,2); + A(A<0)=0; + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Euclidean')); + Sim=1-Distance/max(Distance(:)); + case 'Network Pearson-Size Euclidean' % 5min + % Convert to binary if needed + DataPeaks=PeaksVectors_JP(Data,PeaksIdx,Vector_method); + + % Similarity + Distance=squareform(pdist(DataPeaks,Sim_method)); + Sim=1-Distance/max(Distance(:)); % Normalization + otherwise + % Convert to binary if needed + DataPeaks=PeaksVectors_JP(Data,PeaksIdx,Vector_method); + + % Similarity + Distance=squareform(pdist(DataPeaks,Sim_method)); + Sim=1-Distance/max(Distance(:)); % Normalization + end + end + %} diff --git a/Get_Peaks_Sequence_Sorted.m b/Get_Peaks_Sequence_Sorted.m new file mode 100644 index 0000000..a613f25 --- /dev/null +++ b/Get_Peaks_Sequence_Sorted.m @@ -0,0 +1,23 @@ +% Get the states sequence in order of appearence +% +% By Jesús Pérez-Ortega jan-2018 +% modified march 2018 +% modified may 2018 + +function [peaks_sequence_sorted,idx_sort] = Get_Peaks_Sequence_Sorted(peaks_sequence) + % Get peak sequence + idx_group=[]; + groups=max(peaks_sequence); + for i=1:groups + idx_group=[idx_group find(peaks_sequence==i,1,'first')]; + end + [~,idx_sort]=sort(idx_group); + + % Get sequence sorted + peaks_sequence_sorted=peaks_sequence; + j=1; + for i=idx_sort + peaks_sequence_sorted(peaks_sequence==i)=j; + j=j+1; + end +end \ No newline at end of file diff --git a/Get_Peaks_Similarity.m b/Get_Peaks_Similarity.m new file mode 100644 index 0000000..7bc800d --- /dev/null +++ b/Get_Peaks_Similarity.m @@ -0,0 +1,23 @@ +% Similarity Index +% +% Compute similarity between peak vectors +% +% By Jes?s P?rez-Ortega Jan-2018 +% modified Sep-2018 + +function [Sim,Distance] = Get_Peaks_Similarity(vectors,Sim_method) + if(size(vectors,1)>1) + % Compute similarity + Distance=squareform(pdist(vectors,Sim_method)); + if(max(Distance(:))==0) + Sim=ones(length(Distance)); + else + Sim=1-Distance/max(Distance(:)); % Normalization + %Sim=1./(1+Distance); + end + else + warning('There are no data to compare!') + Sim=[]; + Distance=[]; + end +end \ No newline at end of file diff --git a/Get_Peaks_Similarity_OLD.m b/Get_Peaks_Similarity_OLD.m new file mode 100644 index 0000000..da238ba --- /dev/null +++ b/Get_Peaks_Similarity_OLD.m @@ -0,0 +1,84 @@ +% Similarity Index +% +% Get the peaks from coactivity and compute similarity between peaks +% +% By Jesús Pérez-Ortega jan-2018 +% modified march-2018 + +function [Sim PeaksIdx DataPeaks] = Get_Peaks_Similarity_OLD(Data,Smooth_Co,Th,Join,Binary,Sim_method) + + % Find peaks + PeaksIdx=FindPeaks_JP(Smooth_Co,Th,Join); + n_peaks=max(PeaksIdx); + + if (n_peaks) + switch(Sim_method) + case 'RV' % 2h + peaks=max(PeaksIdx); + + C=size(Data,1); + DataPeaks3=zeros(C,C,peaks); + DataPeaks3_2=zeros(C,C,peaks); + + for i=1:peaks + I=Data(:,PeaksIdx==i); + DataPeaks3(:,:,i)=I*I'; + end + for i=1:peaks + Trace_DataPeaks3(i)=trace(abs(DataPeaks3(:,:,i)^2)); + end + Sim=eye(peaks); + for i=1:(peaks-1) + for j=(i+1):peaks + Sim(i,j) = trace(DataPeaks3(:,:,i)*DataPeaks3(:,:,j))/sqrt(Trace_DataPeaks3(i)*Trace_DataPeaks3(j)); + Sim(j,i) = Sim(i,j); + end + end + Sim=(Sim+1)/2; % Normalization + case 'Network Hamming Coactivity' % 5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P>0,'Coactivity'); + A=A/size(P,2); + A(A<1)=0; + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Hamming')); + Sim=1-Distance; + case 'Network Pearson Euclidean' %5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P,'Pearson'); + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Euclidean')); + Sim=1-Distance/max(Distance(:)); + case 'Network Pearson-Size Euclidean' % 5min + peaks=max(PeaksIdx); + for i=1:peaks + P=Data(:,PeaksIdx==i); + A=Get_Adjacency_From_Raster(P,'Pearson')*size(P,2); + A(A<0)=0; + As(i,:)=squareform(A,'tovector'); + end + Distance=squareform(pdist(As,'Euclidean')); + Sim=1-Distance/max(Distance(:)); + case 'Network Pearson-Size Euclidean' % 5min + % Convert to binary if needed + DataPeaks=PeaksVectors_JP(Data,PeaksIdx,Binary); + + % Similarity + Distance=squareform(pdist(DataPeaks,Sim_method)); + Sim=1-Distance/max(Distance(:)); % Normalization + otherwise + % Convert to binary if needed + DataPeaks=PeaksVectors_JP(Data,PeaksIdx,Binary); + + % Similarity + Distance=squareform(pdist(DataPeaks,Sim_method)); + Sim=1-Distance/max(Distance(:)); % Normalization + end + end +end \ No newline at end of file diff --git a/Get_Raster_From_MEA.m b/Get_Raster_From_MEA.m new file mode 100644 index 0000000..dc47d07 --- /dev/null +++ b/Get_Raster_From_MEA.m @@ -0,0 +1,41 @@ +% Build a raster from Multi-Electrode Array (MEA) +% +% Each variable is a matrix with 79 +% columns. The 2nd column indicate the unit and the 3rd column indicate the +% time stamp. +% +% Pérez-Ortega Jesús - Sep 2018 + +function raster = Get_Raster_From_MEA(channel_data,window_ms,omit) + if(nargin==1) + window_ms=1; + omit=[]; + elseif(nargin==2) + omit=[]; + end + + n_channels=unique(channel_data(:,1))'; + + % Get the time length of the raster + max_time=ceil(max(channel_data(:,3)))*1000; + + % Set raster size + raster=zeros(1,max_time); + + % Build raster + k=1; + for i=n_channels + if(~ismember(i,omit)) + channel=channel_data(channel_data(:,1)==i,:); + n_units=max(channel(:,2)); + for j=1:n_units + time_stamps_unit=channel(channel(:,2)==j,3); + spikes_id=round(time_stamps_unit*1000/window_ms)+1; + if ~isnan(spikes_id) + raster(k,spikes_id)=1; + end + k=k+1; + end + end + end +end \ No newline at end of file diff --git a/Get_Raster_From_MEA_Channels.m b/Get_Raster_From_MEA_Channels.m new file mode 100644 index 0000000..c14cc38 --- /dev/null +++ b/Get_Raster_From_MEA_Channels.m @@ -0,0 +1,44 @@ +% Build a raster from Multi-Electrode Array (MEA) +% +% Read channels from the workspace. Each variable is a matrix with 79 +% columns. The 2nd column indicate the unit and the 3rd column indicate the +% time stamp. +% +% Pérez-Ortega Jesús - March 2018 + +function raster = Get_Raster_From_MEA_Channels(window_ms) + if(nargin==0) + window_ms=1; + end + + channel_names=evalin('base','who'); + n_channels=length(channel_names); + + % Get the time length of the raster + max_time=0; + for i=1:n_channels + max_i=evalin('base',['max(' channel_names{i} '(:,3));']); + if(max_i>max_time) + max_time=max_i; + end + end + max_time=ceil(max_time)*1000; + + % Set raster size + raster=zeros(1,max_time); + + % Build raster + k=1; + for i=1:n_channels + channel=evalin('base',[channel_names{i} ';']); + n_units=max(channel(:,2)); + for j=1:n_units + time_stamps_unit=channel(channel(:,2)==j,3); + spikes_id=round(time_stamps_unit*1000/window_ms)+1; + if ~isnan(spikes_id) + raster(k,spikes_id)=1; + end + k=k+1; + end + end +end \ No newline at end of file diff --git a/Get_Raster_From_Time_Stamps.m b/Get_Raster_From_Time_Stamps.m new file mode 100644 index 0000000..31be580 --- /dev/null +++ b/Get_Raster_From_Time_Stamps.m @@ -0,0 +1,49 @@ +% Convert raw data (time stamps in seconds) to raster +% +% Time stamps have to be in the workspace, and window defines the bin of the raster. +% +% By Jesús Pérez-Ortega July-2018 + +function raster=Get_Raster_From_Time_Stamps(window,initial_time,final_time) + + if(nargin==1) + partial=false; + elseif(nargin==3) + partial=true; + end + + unit_names=evalin('base','who'); + n_cells=length(unit_names); + + if (partial) + max_time=ceil((final_time-initial_time)*1000/window); + else + % detect length of raster + max_time=0; + for cell=1:n_cells + data_cell=evalin('base',unit_names{cell}); + max_cell=max(data_cell); + if (max_cell>max_time) + max_time=max_cell; + end + end + max_time=ceil(max_time*1000/window); + end + + % Define raster size + raster=zeros(n_cells,max_time); + + % Fill the raster + for cell=1:n_cells + data_cell=evalin('base',unit_names{cell}); + spikes=round(data_cell*1000/window)+1; + if ~isnan(spikes) + if(partial) + spikes=spikes(spikes>=initial_time*1000/window); + spikes=spikes(spikes<=final_time*1000/window); + spikes=spikes-floor(initial_time*1000/window)+1; + end + raster(cell,spikes)=1; + end + end +end \ No newline at end of file diff --git a/Get_Significant_Network_From_Raster.m b/Get_Significant_Network_From_Raster.m new file mode 100644 index 0000000..daddfce --- /dev/null +++ b/Get_Significant_Network_From_Raster.m @@ -0,0 +1,65 @@ +% Get significant network from raster +% +% Jesús Pérez-Ortega, nov 2018 + +function [A_significant,A_raw,th,As] = Get_Significant_Network_From_Raster(raster,bin,... + iterations,alpha,network_method,shuffle_method,single_th) + + if(nargin == 6) + single_th = false; + elseif(nargin == 5) + single_th = false; + network_method = 'coactivity'; + elseif(nargin == 4) + single_th = false; + shuffle_method = 'time_shift'; + elseif(nargin == 3) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + elseif(nargin == 2) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + iterations = 1000; + elseif(nargin == 1) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + iterations = 1000; + bin=1; + end + + % Reduce raster in bin + raster_bin = Reshape_Raster(raster,bin); + + % Get original adjacency network + A_raw = Get_Adjacency_From_Raster(raster_bin,network_method); + + % Random versions + As = []; + for i = 1:iterations + shuffled = shuffle(raster,shuffle_method); + shuffled_bin = Reshape_Raster(shuffled,bin); + As(i,:) = squareform(Get_Adjacency_From_Raster(shuffled_bin,network_method),... + 'tovector'); + end + + if(single_th) + n_edges = size(As,2); + th = zeros(1,n_edges); + for i = 1:n_edges + th_i = Find_Threshold_In_Cumulative_Distribution(As(:,i),alpha); + th(i) = th_i; + end + th = squareform(th); + else + th = Find_Threshold_In_Cumulative_Distribution(As(:),alpha); + end + + % Get significant adjacency + A_significant = A_raw>th; +end diff --git a/Get_Significant_Network_From_Raster_And_Correlation.m b/Get_Significant_Network_From_Raster_And_Correlation.m new file mode 100644 index 0000000..ee43c0b --- /dev/null +++ b/Get_Significant_Network_From_Raster_And_Correlation.m @@ -0,0 +1,82 @@ +% Get significant network from raster an coactivity +% +% Jesús Pérez-Ortega, nov 2018 + +function [A_significant,A_raw,th] = Get_Significant_Network_From_Raster_And_Correlation(raster,... + correlation,bin,iterations,alpha,network_method,shuffle_method,single_th) + + if(nargin == 6) + single_th = false; + elseif(nargin == 5) + single_th = false; + network_method = 'coactivity'; + elseif(nargin == 4) + single_th = false; + shuffle_method = 'time_shift'; + elseif(nargin == 3) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + elseif(nargin == 2) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + iterations = 1000; + elseif(nargin == 1) + single_th = false; + shuffle_method = 'time_shift'; + network_method = 'coactivity'; + alpha = 0.05; + iterations = 1000; + bin=1; + end + + % Reduce raster in bin + raster_bin = Reshape_Raster(raster,bin); + neurons = size(raster_bin,1); + + % Get original adjacency network + A_raw = Get_Adjacency_From_Raster(raster_bin,network_method); + + % Get adjacency with correlation weights + co(1,:) = correlation; + ws = co'*co; + + A_raw_co = A_raw.*ws; + + % Random versions + As_shuffled = []; + for i = 1:iterations + shuffled = shuffle(raster,shuffle_method); + shuffled_bin = Reshape_Raster(shuffled,bin); + + % Get adjacency + A_shuffled = Get_Adjacency_From_Raster(shuffled_bin,network_method); + + % Get correlation with the coactivity + %ws = reshape(weights(randperm(neurons*neurons)),neurons,neurons); + co = co(randperm(neurons)); + ws = co'*co; + + + A_shuffled_co = A_shuffled.*ws; + As_shuffled(i,:) = squareform(A_shuffled_co,'tovector'); + end + + if(single_th) + n_edges = size(As_shuffled,2); + th = zeros(1,n_edges); + for i = 1:n_edges + th_i = Find_Threshold_In_Cumulative_Distribution(As_shuffled(:,i),alpha); + th(i) = th_i; + end + th = squareform(th); + else + th = Find_Threshold_In_Cumulative_Distribution(As_shuffled(:),alpha); + end + + % Get significant adjacency + A_significant = A_raw_co>th; +end diff --git a/Get_SmallWorld_Properties.m b/Get_SmallWorld_Properties.m new file mode 100644 index 0000000..215a5a3 --- /dev/null +++ b/Get_SmallWorld_Properties.m @@ -0,0 +1,70 @@ +function properties = Get_SmallWorld_Properties(adjacency,only_connected) +% Get the Small-World properties +% +% Pérez-Ortega Jesús E. +% jul 2018 +% modified Sep 18 +% modified Jan 19 + + if(nargin==1) + only_connected=false; + end + + if(only_connected) + nodes_connected=sum(adjacency)>0; + adjacency=adjacency(nodes_connected,nodes_connected); + end + + % Total nodes and total edges + N = length(adjacency); + Links = sum(adjacency); + K = sum(Links)/2; + K_mean = 2*K/N; + Kmax= N*(N-1)/2; + Rho = K/Kmax; + + + % Real + D=distance_bin(adjacency); % distance + [L,E]=charpath(D); % characteristic path length and efficiency + Clocal=clustering_coef_bu(adjacency); % local clustering coefficient + C=mean(Clocal); % clustering coefficient + + % Regular + Reg=Make_Regular_Ring_Network(N,K); + Dreg=distance_bin(Reg); % distance + [Lreg,Ereg]=charpath(Dreg); % characteristic path length and efficiency + Clocalreg=clustering_coef_bu(Reg); % local clustering coefficient + Creg=mean(Clocalreg); % clustering coefficient + + % Red aleatoria + Rand=makerandCIJ_und(N,K); + Drand=distance_bin(Rand); % distance + [Lrand,Erand]=charpath(Drand); % characteristic path length and efficiency + Clocalrand=clustering_coef_bu(Rand); % local clustering coefficient + Crand=mean(Clocalrand); % clustering coefficient + + % Medida de small-world + Omega=Lrand/L-C/Creg; + + properties.N=N; + properties.K=K; + properties.K_mean=K_mean; + properties.Rho=Rho; + properties.Links=Links; + + properties.Clocal=Clocal; + properties.C=C; + properties.L=L; + properties.E=E; + + properties.Creg=Creg; + properties.Lreg=Lreg; + properties.Ereg=Ereg; + + properties.Crand=Crand; + properties.Lrand=Lrand; + properties.Erand=Erand; + + properties.Omega=Omega; +end \ No newline at end of file diff --git a/Get_Spike_Rate.m b/Get_Spike_Rate.m new file mode 100644 index 0000000..f1a147c --- /dev/null +++ b/Get_Spike_Rate.m @@ -0,0 +1,17 @@ +% Get z-score of spike rate of a single neuron +% +% By Jesús Pérez-Ortega aug-2018 + +function spike_rate = Get_Spike_Rate(spikes,bin,step) + n=length(spikes); + n_sum=floor((n-bin)/step+1); + + spike_rate=zeros(1,n); + for i=1:n_sum + % Get spike rate + ini=(i-1)*step+1; + fin=(i-1)*step+bin; + spike_rate(ini:(ini+step-1))=sum(spikes(ini:fin)); + end + spike_rate = spike_rate(1:n); +end \ No newline at end of file diff --git a/Get_Spikes_Shape_From_MEA_Channels.m b/Get_Spikes_Shape_From_MEA_Channels.m new file mode 100644 index 0000000..14b2c4a --- /dev/null +++ b/Get_Spikes_Shape_From_MEA_Channels.m @@ -0,0 +1,52 @@ +% Get the spikes shape from Multi-Electrode Array (MEA) +% +% Read channels from the workspace. Each variable is a matrix with 79 +% columns. The shape of the spikes is from 4-79 to thecolumn indicate the unit and the 3rd column indicate the +% time stamp. +% +% Pérez-Ortega Jesús - March 2018 + +function [mean_shapes min_shapes max_shapes] = Get_Spikes_Shape_From_MEA_Channels(plot_save) + if(nargin==0) + plot_save=1; + end + + channel_names=evalin('base','who'); + n_channels=length(channel_names); + + % Build raster + if(plot_save) + Set_Figure('Spikes shape',[0 0 1000 800]); + end + + k=1; + for i=1:n_channels + channel=evalin('base',[channel_names{i} ';']); + n_units=max(channel(:,2)); + for j=1:n_units + spikes_shape=channel(channel(:,2)==j,4:end); + max_shapes(k,:)=max(spikes_shape); + min_shapes(k,:)=min(spikes_shape); + mean_shapes(k,:)=mean(spikes_shape); + + if(plot_save) + subplot(5,6,k) + mins=min_shapes(k,:); + maxs=max_shapes(k,:)-mins; + + h=area([mins;maxs]',-10000,'LineStyle',':'); hold on + set(h(2),'FaceColor', [0.9 0.9 1]) + set(h(1),'FaceColor', [1 1 1]) + + plot(mean_shapes(k,:),'b') + ylim([min(mins) -min(mins)]) + title(['Channel ' num2str(i) ' - unit ' num2str(j)]) + + if(~mod(k+5,6)) + ylabel('Amplitude (mV)') + end + end + k=k+1; + end + end +end \ No newline at end of file diff --git a/Get_XY_Ensembles.m b/Get_XY_Ensembles.m new file mode 100644 index 0000000..e81c963 --- /dev/null +++ b/Get_XY_Ensembles.m @@ -0,0 +1,77 @@ +function [xy, xy_colors, id, structure] = Get_XY_Ensembles(networks) +% Get coordinate of ensembles +% +% [xy, xy_colors, id, structure] = Get_XY_Ensembles(networks) +% +% Jesus Perez-Ortega April-19 + +% Get number of networks +n_networks = length(networks); +n_neurons = length(networks{1}); +colors = Read_Colors(n_networks); + +% Get identity of neurons +structure = zeros(n_networks,n_neurons); +network = zeros(n_neurons); +for i = 1:n_networks + id_i = sum(networks{i})>0; + if ~isempty(id_i) + structure(i,id_i) = i; + network = network | networks{i}; + end +end + +% Set XY for neurons without connections +id_inactive = find(sum(structure>0)==0); +xy_inactive = Get_Circular_XY(length(id_inactive),3); + +% Set XY for neurons of specificity +alone = sum(structure>0)==1; +xy_base = Get_Circular_XY(n_networks,2); +xy_specific = []; +id_specific = []; +for i = 1:n_networks + % get neurons id + id_i = find(alone & structure(i,:)); + + + if length(id_i)==1 + % Add to the existing + xy_specific = [xy_specific; [0 0]]; + id_specific = [id_specific id_i]; + elseif length(id_i)>1 + % Get network specific-state neurons + net = networks{i}(id_i,id_i); + + % Get coordinates + xy_i = Scale(Get_Force_XY(net),-1,1) + xy_base(i,:); + + % Add to the existing + xy_specific = [xy_specific; xy_i]; + id_specific = [id_specific id_i]; + end +end +% Set XY for hub neurons +id_hubs = find(sum(structure>0)>1); +[~,id_new] = Sort_Raster(flipud(structure(:,id_hubs))','ascend'); +id_hubs = id_hubs(id_new); +xy_hubs = Scale(Get_Force_XY(network(id_hubs,id_hubs)),-1,1); + +% Join +id = [id_specific id_hubs id_inactive]; +xy = [xy_specific; xy_hubs; xy_inactive]; +structure = structure(:,id); + +% Colors +xy_colors = zeros(n_neurons,3); +for i = 1:n_neurons + id_color = find(structure(:,i)); + if isempty(id_color) + xy_colors(i,:) = [0 0 0]; + elseif length(id_color)==1 + xy_colors(i,:) = colors(id_color,:); + else + xy_colors(i,:) = [0.5 0.5 0.5]; + %xy_colors(i,:) = mean(colors(id_color,:)); + end +end \ No newline at end of file diff --git a/Get_Zscore_From_Raster.m b/Get_Zscore_From_Raster.m new file mode 100644 index 0000000..dc541c9 --- /dev/null +++ b/Get_Zscore_From_Raster.m @@ -0,0 +1,20 @@ +% Get z-score of spike rate from raster +% +% By Jesús Pérez-Ortega aug-2018 + +function spike_rate = Get_Zscore_From_Raster(raster,bin,step) + + [c,f]=size(raster); + + spike_rate=zeros([c f]); + for i=1:c + % Get frequencies + spike_rate(i,:)=Get_Spike_Rate(raster(i,:),bin,step); + + % z-score + spike_rate(i,:)=spike_rate(i,:)-mean(spike_rate(i,:)); + if(std(spike_rate(i,:))) + spike_rate(i,:)=spike_rate(i,:)/std(spike_rate(i,:)); + end + end +end \ No newline at end of file diff --git a/Hold_Axes.m b/Hold_Axes.m new file mode 100644 index 0000000..5c64fc5 --- /dev/null +++ b/Hold_Axes.m @@ -0,0 +1,8 @@ +function h = Hold_Axes(axes_tag) +%% Set the axes specified by its tag +% +% Jesus Perez-Ortega March-18 +h = findobj('Tag',axes_tag); +if(~isempty(h)) + axes(h); +end \ No newline at end of file diff --git a/Hold_Figure.m b/Hold_Figure.m new file mode 100644 index 0000000..8230ba8 --- /dev/null +++ b/Hold_Figure.m @@ -0,0 +1,8 @@ +%% Hold on Figure +% +% Jesús Pérez-Ortega March-18 + +function Hold_Figure(TitleName) + h=findobj('name',TitleName); + figure(h); +end \ No newline at end of file diff --git a/Jitter_Raster.m b/Jitter_Raster.m new file mode 100644 index 0000000..aa09161 --- /dev/null +++ b/Jitter_Raster.m @@ -0,0 +1,42 @@ +% Jitter a raster with an specific bin +% +% Jes?s P?rez-Ortega - Sep 2018 + +function jittered = Jitter_Raster(raster,bin,exact_bin) + if(nargin==2) + exact_bin=false; + end + + [n_cells,n_samples]=size(raster); + + jittered=zeros(n_cells,n_samples); + for i=1:n_cells + cell=raster(i,:); + + t=find(cell); + + jit=rand(1,length(t)); + if(exact_bin) + jit(jit>0.5)=bin; + jit(jit<=0.5)=-bin; + else + jit=round(jit*2*bin)-bin; + end + + t_jit=t+jit; + t_jit=sort(t_jit); + + if(max(t_jit)>n_samples) + n_extra=length(find(t_jit>n_samples)); + t_jit=t_jit(1:end-n_extra); + end + + if(min(t_jit)<1) + n_extra=length(find(t_jit<1)); + t_jit=t_jit(1+n_extra:end); + end + + jittered(i,t_jit)=1; + + end +end \ No newline at end of file diff --git a/Join_Peaks.m b/Join_Peaks.m new file mode 100644 index 0000000..a5b3696 --- /dev/null +++ b/Join_Peaks.m @@ -0,0 +1,24 @@ +%% Join Peaks +% Modified jan-2018 +function Peaks = Join_Peaks(StatesIdx,PeaksIdx) + + NS=length(StatesIdx); + NP=length(PeaksIdx); + if NS~=NP + for i=1:NS + idx(i)=find(PeaksIdx==i,1,'first'); + end + else + idx=find(PeaksIdx); + end + + F=length(idx); + Peaks(1)=StatesIdx(1); + j=2; + for i=2:F + if idx(i-1)~=idx(i)-1 + Peaks(j)=StatesIdx(i); + j=j+1; + end + end +end \ No newline at end of file diff --git a/Make_Regular_Ring_Network.m b/Make_Regular_Ring_Network.m new file mode 100644 index 0000000..38dfaa3 --- /dev/null +++ b/Make_Regular_Ring_Network.m @@ -0,0 +1,50 @@ +% Generate Ring Regular Network +% +% This function generates binary undirected network with ring regular +% connections. +% +% regular_network = Make_Regular_Ring_Network(N,K) +% +% Inputs: +% N = number of nodes +% K = number of links +% +% Outputs: +% Reg = binary undirected adjacent matrix of ring regular network +% +% ..:: by Jesús E. Pérez-Ortega ::.. March-2013 +% Modified July-2018 + +function regular_network = Make_Regular_Ring_Network(N,K) + k=2*K/N; + regular_network=zeros(N); + km=round(ceil(k)/2); + for i=1:N + n_b=i-[km:-1:1]; + n_a=i+[1:km]; + + initial=find(n_b<1); + if initial + n_init=length(initial); + n_b(1:n_init)=[N:-1:(N-n_init+1)]; + end + + final=find(n_a>N); + if final + n_final=length(final); + n_a(end:-1:(end-n_final+1))=[1:n_final]; + end + + regular_network(i,[n_b, n_a])=1; + regular_network([n_b, n_a],i)=1; + end + + % Remove excess connections + n_remove=(sum(sum(regular_network))-ceil(k*N))/2; + [i,j]=find(triu(regular_network,2)); + idx=round(rand(1,n_remove)*length(i)); + for l=1:n_remove + regular_network(i(idx(l)),j(idx(l)))=0; + regular_network(j(idx(l)),i(idx(l)))=0; + end +end \ No newline at end of file diff --git a/Neural_Ensemble_Analysis.fig b/Neural_Ensemble_Analysis.fig new file mode 100644 index 0000000000000000000000000000000000000000..5f240c43deb45d1c3461b74e2fada9579832153f GIT binary patch literal 68480 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 z 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 z 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 hr2E^21})1LynOyOIlpuY&9Cmaximum) + user_entry=maximum; + elseif(user_entrymaximum) + user_entry=maximum; + elseif(user_entrymaximum) + user_entry=maximum; + elseif(user_entry1 && alpha<0) + alpha=0.05; + end + + shuffle_method=str2num(answer{3}); + if(isempty(shuffle_method)) + shuffle_method=1; + end + switch(shuffle_method) + case 1 + shuffle_method='time_shift'; + case 2 + shuffle_method='frames'; + case 3 + shuffle_method='time'; + case 4 + shuffle_method='isi'; + case 5 + shuffle_method='cell'; + case 6 + shuffle_method='exchange'; + otherwise + shuffle_method='time_shift'; + end + + interval_by_cell=str2num(answer{4}); + if(isempty(interval_by_cell)) + interval_by_cell=1; + end + + % Read experiment + experiment=Read_Experiment(handles); + raster=experiment.Raster.Raster; + name=experiment.Raster.Name; + smooth_window=experiment.Coactivity.SmoothWindow; + z_score=experiment.Coactivity.ZScore; + zscore_window=experiment.Coactivity.ZScoreWindow; + only_mean=experiment.Coactivity.ZScoreOnlyMean; + remove_oscillations=experiment.Coactivity.RemoveOscillations; + + % Process + plot=false; + coactivity_threshold_shuffeld_test = Shuffle_Test(raster,smooth_window,... + iterations,shuffle_method,alpha,interval_by_cell,remove_oscillations,... + z_score,only_mean,zscore_window,plot); + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Shuffle test (' shuffle_method ') - ' name]); + end + + % Write experiment + experiment.Coactivity.ShuffleMethod=shuffle_method; + experiment.Coactivity.CoactivityThresholdShuffleTest=... + coactivity_threshold_shuffeld_test; + experiment.Coactivity.AlphaShuffleTest=alpha; + Write_Experiment(handles,experiment); + end + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Get coactivity +function btnGetCoactivity_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + tic + + % Read experiment + experiment=Read_Experiment(handles); + raster=experiment.Raster.Raster; + smooth_window=experiment.Coactivity.SmoothWindow; + z_score=experiment.Coactivity.ZScore; + zscore_window=experiment.Coactivity.ZScoreWindow; + only_mean=experiment.Coactivity.ZScoreOnlyMean; + remove_oscillations=experiment.Coactivity.RemoveOscillations; + + % Get coactivity + coactivity = Get_And_Filter_Coactivity(raster,smooth_window); + + % Remove Oscillations + if(remove_oscillations) + percentage = 0.1; + [coactivity,removed]= Remove_Oscillations(coactivity,percentage); + experiment.Coactivity.CoactivityRemoved=removed; + end + + % Z-score + if(z_score) + coactivity=Z_Score_Coactivity(coactivity,zscore_window,only_mean); + end + + % Write experiment + experiment.Coactivity.Coactivity=coactivity; + experiment.Plot.Correlation = []; + experiment.Plot.JaccardCorrelation = []; + experiment.Plot.CorrelationState = []; + + Write_Experiment(handles,experiment); + + % Refresh coactivity threshold + txtCoactivityThreshold_Callback(handles.txtCoactivityThreshold,[],handles); + + % Enable options on sorting raster/frequencies + set(handles.popSortingNeurons,'string',{'no sorting','activity','activation',... + 'jaccard correlation','linear correlation'}) + set(handles.popSortingNeurons,'value',1) + + + % Disable/Enable buttons + Disable_Buttons(handles,'Peaks') + Enable_Buttons(handles,'Peaks') + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) + time=toc; disp(['Done in ' num2str(time) ' s.']) +end + +%% Get neural vectors +function btnGetVectors_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + tic + + % Read experiment + experiment=Read_Experiment(handles); + coactivity=experiment.Coactivity.Coactivity; + coactivity_threshold=experiment.Coactivity.CoactivityThreshold; + vector_method=experiment.Peaks.VectorMethod; + network_method=experiment.Peaks.NetworkMethod; + fixed_width=experiment.Peaks.FixedWidth; + join=experiment.Peaks.Join; + divide_peaks=experiment.Peaks.DividePeaks; + use_spikes=experiment.Peaks.UseSpikes; + fixed_width_window=experiment.Peaks.FixedWidthWindow; + division_window=experiment.Peaks.DivisionWindow; + samples_per_second=experiment.Raster.SamplesPerSecond; + detect_peaks_or_valleys=experiment.Peaks.DetectPeaksOrValleys; + + if join + % Ask for minimum + prompt = {['Enter the minimum time width in ms to consider peak'... + ' (0 = without restriction):']}; + title = 'Enter parameters'; + dims = [1 50]; + default_input = {'0'}; + answer = inputdlg(prompt,title,dims,default_input); + if(~isempty(answer)) + minimum_width=str2num(answer{1})/1000*samples_per_second; + if(isempty(minimum_width)) + minimum_width=0; + elseif(minimum_width<1) + minimum_width=0; + end + else + minimum_width=0; + end + %} + else + minimum_width=0; + end + + if(use_spikes) + data=experiment.Raster.Raster; + else + data=experiment.Raster.Frequencies; + end + + if(fixed_width) + width=fixed_width_window; + else + width=0; + end + + if detect_peaks_or_valleys + if(get(handles.popPeaksValleys,'value')==3) + stimuli = experiment.Peaks.Stimuli; + detect_peaks = true; + ignore_ini_fin = true; + [vector_indices,vector_widths,vector_amplitudes,vector_ini_fin] = ... + Find_Peaks_Or_Valleys(stimuli,coactivity_threshold,join,detect_peaks,... + minimum_width,width,ignore_ini_fin); + experiment.Peaks.DetectPeaks = false; + experiment.Peaks.DetectValleys = false; + experiment.Peaks.DetectStimuli = true; + else + % Find peaks indexes + detect_peaks = get(handles.popPeaksValleys,'value')==1; + ignore_ini_fin = true; + [vector_indices,vector_widths,vector_amplitudes,vector_ini_fin] = ... + Find_Peaks_Or_Valleys(coactivity,coactivity_threshold,join,detect_peaks,... + minimum_width,width,ignore_ini_fin); + experiment.Peaks.DetectPeaks = true; + experiment.Peaks.DetectValleys = false; + experiment.Peaks.DetectStimuli = false; + end + % Write experiment + experiment.Peaks.Widths = vector_widths; + experiment.Peaks.Amplitudes = vector_amplitudes; + experiment.Peaks.IniFin = vector_ini_fin; + else + % Create vector indices + samples=experiment.Raster.Samples; + n=ceil(samples/width); + vector_indices=zeros(samples,1); + for i=1:n + ini=(i-1)*width+1; + fin=i*width; + if(fin>samples) + fin=samples; + end + vector_indices(ini:fin)=i; + end + + % Write experiment + experiment.Peaks.DetectPeaks=false; + experiment.Peaks.DetectValleys = true; + experiment.Peaks.DetectStimuli = false; + end + + if divide_peaks + vector_indices = Divide_Peaks(vector_indices, division_window); + end + + if max(vector_indices)>0 + % Create a matrix with all vector peaks + bin_network = 1; % bin_network = 25; + vectors=Get_Peak_Vectors(data,vector_indices,vector_method,network_method,bin_network); + count=size(vectors,1); + + % Create raster with only peaks + raster_peaks = data(:,vector_indices>0); + raster_no_peaks = data(:,~vector_indices); + + % Write experiment + experiment.Peaks.Indices=vector_indices; + experiment.Peaks.Vectors=vectors; + experiment.Peaks.Count=count; + experiment.Peaks.RasterPeaks=raster_peaks; + experiment.Peaks.RasterNoPeaks=raster_no_peaks; + Write_Experiment(handles,experiment); + + % Disable/Enable buttons + Disable_Buttons(handles,'Similarity') + Enable_Buttons(handles,'Similarity') + if(get(handles.popPeaksValleys,'value')==3) + set(handles.btnGetNetworksByStimuli,'enable','on') + else + set(handles.btnGetNetworksByStimuli,'enable','off') + end + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) + else + warning('No vectors created') + % Color red + set(hObject,'ForeGroundColor',[0.5 0 0]) + end + time=toc; disp(['Done in ' num2str(time) ' s.']) +end + +%% Get similarity +function btnGetSimilarity_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + tic + + % Read experiment + experiment=Read_Experiment(handles); + vectors=experiment.Peaks.Vectors; + similarity_method=experiment.Clustering.SimilarityMethod; + linkage_method=experiment.Clustering.LinkageMethod; + + % Get similarity + similarity = Get_Peaks_Similarity(vectors,similarity_method); + if(~isempty(similarity)) + tree=linkage(squareform(1-similarity,'tovector'),linkage_method); + else + tree=[]; + end + + % Write experiment + experiment.Clustering.Similarity=similarity; + experiment.Clustering.Tree=tree; + Write_Experiment(handles,experiment); + + % Enable buttons + Enable_Buttons(handles,'Clustering') + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) + time=toc; disp(['Done in ' num2str(time) ' s.']) +end + +%% Groups test +function btnDunnTest_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + prompt = {'Enter test type (1=Contrast; 2=Dunn; 3=Davies)'}; + title = 'Enter parameters'; + dims = [1 50]; + default_input = {'1'}; + answer = inputdlg(prompt,title,dims,default_input); + if(~isempty(answer)) + test_method=str2num(answer{1}); + if(isempty(test_method)) + test_method=1; + end + + switch(test_method) + case 1 + test_method='Contrast'; + case 2 + test_method='Dunn'; + case 3 + test_method='Davies'; + otherwise + test_method='Contrast'; + end + + % Read experiment + experiment=Read_Experiment(handles); + name=experiment.Raster.Name; + tree=experiment.Clustering.Tree; + similarity=experiment.Clustering.Similarity; + groups=experiment.Clustering.Groups; + + if(groups>30) + groups=2:ceil(groups/10):groups; + else + groups=2:30; + end + + + % Process + obj=Set_Figure(['Clustering test (' name ')'],[0 0 600 300]); + [~,groups_test]=ClustIdx_JP(tree, similarity,test_method,obj,'hierarchical',... + groups); % 'Connectivity', 'Davies', 'Contrast' + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Clustering test (' test_method ') - ' name]); + end + + % Write experiment + experiment.Clustering.GroupsTest=groups_test; + Write_Experiment(handles,experiment); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Get clusters +function btnGetClusters_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + tic + + % Read experiment + experiment=Read_Experiment(handles); + raster = experiment.Raster.Raster; + indices = experiment.Peaks.Indices; + tree=experiment.Clustering.Tree; + groups=experiment.Clustering.Groups; + + % Process + % Get clusters + group_indices=cluster(tree,'maxclust',groups); + % Get Sequences + [sequence_sorted,sorted_indices] = Get_Peaks_Sequence_Sorted(group_indices); + + % Get raster from states + for i = 1:groups + peaks = find(sequence_sorted==i); + n_peaks = length(peaks); + + raster_state = []; + for j = 1:n_peaks + peak = raster(:,indices==peaks(j)); + raster_state = [raster_state peak]; + end + raster_states{i}=raster_state; + end + + % Write experiment + experiment.Clustering.GroupIndices = group_indices; + experiment.Clustering.SortedIndices = sorted_indices; + experiment.Clustering.SequenceSorted = sequence_sorted; + experiment.Clustering.RasterStates = raster_states; + experiment.Plot.CorrelationState = []; + Write_Experiment(handles,experiment); + + % Enable options on sorting raster/frequencies + labels={'no sorting','activity','activation','jaccard correlation','linear correlation'}; + for i=1:groups + labels{end+1}=['group ' num2str(i)]; + end + set(handles.popSortingNeurons,'string',labels); + set(handles.popSortingNeurons,'value',1); + + % Enable buttons + Enable_Buttons(handles,'PlotStates') + Enable_Buttons(handles,'Network') + + if(experiment.Peaks.DividePeaks) + set(handles.btnPlotSequenceByPeak,'enable','on') + end + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) + time=toc; disp(['Done in ' num2str(time) ' s.']) +end + +%% Get network +function btnGetNetworkRaster_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + tic + + % Read experiment + experiment = Read_Experiment(handles); + raster = experiment.Raster.Raster; + bin = 1; + iterations = 1000; + alpha = 0.05; + network_method = experiment.Peaks.NetworkMethod; + shuffle_method = 'time_shift'; + single_th = true; + + % Get significant network + [whole_network,whole_coactivity] = Get_Significant_Network_From_Raster(... + raster,bin,iterations,alpha,network_method,shuffle_method,single_th); + + % Write experiment + experiment.Network.WholeCoactivity = whole_coactivity; + experiment.Network.WholeNetwork = whole_network; + Write_Experiment(handles,experiment); + + % Enable buttons + set(handles.btnPlotWholeNetwork,'enable','on') + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) + time=toc; disp(['Done in ' num2str(time) ' s.']) +end + +%% Get networks from states +function btnGetNetworks_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + n = experiment.Raster.Neurons; + groups = experiment.Clustering.Groups; + + % clear current networks + experiment.Network.State = {}; + experiment.Network.StateSignificant = {}; + + with_th = true; + if(with_th) + bin = 1; %bin = 25; + iterations = 1000; + alpha = 0.05; + network_method = experiment.Peaks.NetworkMethod; + shuffle_method = 'time_shift'; + single_th = true; + + groups_to_plot = experiment.Clustering.GroupsToPlot; + % + %correlation = experiment.Plot.Correlation; + +% sequence_sorted = experiment.Clustering.SequenceSorted; +% indices = experiment.Peaks.Indices; +% raster = experiment.Raster.Raster; + % Get significant networks of each state + for i=1:groups + if(ismember(i,groups_to_plot)) + + % Get state raster + raster_state = experiment.Clustering.RasterStates{i}; + + % compute correlation + try + correlation = experiment.Plot.CorrelationState(i,:); + catch + correlation = Compute_Correlation_by_State(experiment,i); + experiment.Plot.CorrelationState(i,:)=correlation; + end + + tic + % Add raster with no peks to the raster peaks + %{ + raster_noise = experiment.Peaks.RasterNoPeaks; + n_state=size(raster_state,2); + n_noise=size(raster_noise,2); + if(n_noise>n_state) + id = randperm(n_noise); + id = id(1:n_state); + raster_noise = raster_noise(:,id); + end + raster_state = [raster_state raster_noise]; + %} + % Get raster from states + %{ + extra=500; + peaks = find(sequence_sorted==i); + n_peaks = length(peaks); + raster_state = []; + for j = 1:n_peaks + id = find(indices==peaks(j)); + id = id(1)-extra:id(end)+extra; + peak = raster(:,id); + raster_state = [raster_state peak]; + end + %} + + % Get significant network +% [state_network_th,state_network] = Get_Significant_Network_From_Raster(... +% raster_state,bin,iterations,alpha,network_method,shuffle_method,single_th); + [state_network_th,state_network] = ... + Get_Significant_Network_From_Raster_And_Correlation(raster_state,... + correlation,bin,iterations,alpha,network_method,shuffle_method,single_th); + + % Minimum two coactivations + %state_network_th(state_network < 2) = 0; + + % Write in experiment + experiment.Network.State{i}=state_network; + experiment.Network.StateSignificant{i}=state_network_th; + toc + + end + end + %} + % Get core network +% raster_peaks = experiment.Peaks.RasterPeaks; +% [core_network_th,core_network] = Get_Significant_Network_From_Raster(raster_peaks,... +% bin,iterations,alpha,network_method,shuffle_method,single_th); + + % Network of all networks = union of states and core network = intersection + core_network = ones(n); + network = zeros(n); + for i=1:groups + if(ismember(i,groups_to_plot)) + state_network_th = experiment.Network.StateSignificant{i}; + network = network + state_network_th; + core_network = core_network .* state_network_th; + end + end + core_network_th = core_network; + network_th = logical(network); + else + networks = experiment.Peaks.Vectors; + sequence = experiment.Clustering.SequenceSorted; + % Get significant networks of each state + for i=1:groups + if(ismember(i,groups_to_plot)) + % Get significant network + if(sum(sequence==i)==1) + state_network = squareform(networks(sequence==i,:)>0); + else + state_network = squareform(mean(networks(sequence==i,:)>0)); + end + state_network_th = state_network==1; + + % Write in experiment + experiment.Network.State{i}=state_network; + experiment.Network.StateSignificant{i}=state_network_th; + end + end + + % Network of whole raster = intersection of states + core_network = squareform(mean(networks>0)); + core_network_th = core_network==1; + + % Network of all networks = union of states + network = zeros(n); + for i=1:groups + if(ismember(i,groups_to_plot)) + state_network_th = experiment.Network.StateSignificant{i}; + network = network + state_network_th; + end + end + network_th = logical(network); + end + + % Write experiment + experiment.Network.Network=network; + experiment.Network.Significant=network_th; + experiment.Network.CoreNetwork=core_network; + experiment.Network.CoreSignificant=core_network_th; + Write_Experiment(handles,experiment); + + % Enable buttons + Enable_Buttons(handles,'PlotNetwork') + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) +end + +%% Get networks by stimuli +function btnGetNetworksByStimuli_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + n = experiment.Raster.Neurons; + stimuli = experiment.Peaks.Stimuli; + raster = experiment.Raster.Raster; + + groups = sum(unique(stimuli)>0); + stimuli_sequence = zeros(size(stimuli)); + j=1; + for i = unique(stimuli)' + if(i) + stimuli_sequence(stimuli==i)=j; + j=j+1; + end + end + stimuli_sequence = Delete_Consecutive_Coactivation(stimuli_sequence'); + stimuli_sequence = Get_Peaks_Sequence_Sorted(stimuli_sequence); + stimuli_sequence = stimuli_sequence(stimuli_sequence>0); + + idx = Get_Peak_Or_Valley_Indices(stimuli,0.0001,true); + is = find(idx~=[0; idx(1:numel(idx)-1)+1]); % index of same peak + % number of total peaks + count = numel(is); + if count + for j = 1:count-1 + indices(idx(is(j)):idx(is(j+1)-1),1)=j; % set #peak + end + indices(idx(is(count)):max(idx),1)=count; + end + + % clear current networks + experiment.Network.Stimulus = {}; + experiment.Network.StimulusSignificant = {}; + + bin = 1; %bin = 25; + iterations = 100; + alpha = 0.05; + network_method = experiment.Peaks.NetworkMethod; + shuffle_method = 'time_shift'; + single_th = true; + + % Get significant networks of each stimulus + for i=1:groups + % Get state raster + peaks = find(stimuli_sequence==i); + n_peaks = length(peaks); + + raster_state = []; + for j = 1:n_peaks + peak = raster(:,indices==peaks(j)); + raster_state = [raster_state peak]; + end + raster_state = experiment.Clustering.RasterStates{i}; + + tic + % Get significant network + [state_network_th,state_network] = Get_Significant_Network_From_Raster(... + raster_state,bin,iterations,alpha,network_method,shuffle_method,single_th); +% % Minimum two coactivations + %state_network_th(state_network < 2) = 0; + + % Write in experiment + experiment.Network.Stimulus{i}=state_network; + experiment.Network.StimulusSignificant{i}=state_network_th; + toc + end + %} + % Network of all networks = union of states and core network = intersection + core_network = ones(n); + network = zeros(n); + for i=1:groups + state_network_th = experiment.Network.StimulusSignificant{i}; + network = network + state_network_th; + core_network = core_network .* state_network_th; + end + core_network_th = core_network; + network_th = logical(network); + + % Write experiment + experiment.Network.UnionStimulus=network; + experiment.Network.UnionSignificantStimulus=network_th; + experiment.Network.IntersectionStimulus=core_network; + experiment.Network.IntersectionSignificantStimulus=core_network_th; + Write_Experiment(handles,experiment); + + % Enable buttons + Enable_Buttons(handles,'PlotNetworkByStimulus') + + % Color green + set(hObject,'ForeGroundColor',[0 0.5 0]) +end + +%% --- Callbacks --- +%% Raster +function popWorkspace_Callback(hObject,~,handles) + % Get data + data_num=get(hObject,'Value'); + set(handles.lblRaster,'String','...') + Disable_Buttons(handles,'Raster'); + if data_num>1 + data_strings=get(hObject,'String'); + name=data_strings{data_num}; + % Check if exist + if evalin('base',['exist(''' name ''',''var'')']) + raster=evalin('base',name); + % Evaluate data + set(handles.lblRaster,'ForegroundColor',[0 0.5 0]) + [cells,samples]=size(raster); + if (samples>1 && cells>1 && length(size(raster))==2) + % Data with less than 500 samples or more than 500 cells + if (samplessamples_per_second*samples) + zscore_window=0; + else + zscore_window=round(zscore_window*samples_per_second); + end + experiment.Coactivity.ZScoreWindow=zscore_window; + experiment.Coactivity.ZScoreOnlyMean=only_mean; + end + end + % Write experiment + experiment.Coactivity.ZScore=z_score; + experiment.Coactivity.CoactivityThreshold=coactivity_threshold; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Peaks') + set(handles.btnGetCoactivity,'ForegroundColor','black') +end + +function chkRemoveOscillations_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + remove_oscillations = get(hObject,'value'); + + % Write experiment + experiment.Coactivity.RemoveOscillations=remove_oscillations; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Peaks') + set(handles.btnGetCoactivity,'ForegroundColor','black') +end + +%% Neural vectors + +% Selection +function chkDetectPeaksOrValleys_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + detect_peaks_or_valleys=get(hObject,'value'); + + % Enable/Disable + if(detect_peaks_or_valleys) + % Enable + set(handles.popPeaksValleys,'enable','on') + set(handles.txtCoactivityThreshold,'enable','on') + set(handles.btnShuffleTest,'enable','on') + set(handles.chkFixedWidth,'enable','on') + if(get(handles.chkFixedWidth,'value')) + set(handles.txtFixedWidth,'enable','on') + end + % Set values + coactivity_threshold=str2double(get(handles.txtCoactivityThreshold,'string')); + experiment.Coactivity.CoactivityThreshold=coactivity_threshold; + else + % Disable + set(handles.popPeaksValleys,'enable','off') + set(handles.txtCoactivityThreshold,'enable','off') + set(handles.btnShuffleTest,'enable','off') + set(handles.chkFixedWidth,'enable','off') + set(handles.txtFixedWidth,'enable','on') + % Set values + set(handles.chkFixedWidth,'value',true) + experiment.Peaks.FixedWidth=true; + experiment.Coactivity.CoactivityThreshold=[]; + end + + % Write experiment + experiment.Peaks.DetectPeaksOrValleys=detect_peaks_or_valleys; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Peaks or valleys +function popPeaksValleys_Callback(hObject,~,handles) + + % Enable Stimuli signal if selected + if(get(hObject,'value')==3) + set(handles.popStimuli,'enable','on') + else + set(handles.popStimuli,'enable','off') + end + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Stimuli +function popStimuli_Callback(hObject,~,handles) + set(hObject,'ForegroundColor','black') + + % Read experiment + experiment=Read_Experiment(handles); + + % Get data + data_num=get(hObject,'Value'); + if data_num>1 + data_strings=get(hObject,'String'); + name=data_strings{data_num}; + % Check if exist + if evalin('base',['exist(''' name ''',''var'')']) + stimuli=evalin('base',name); + % Evaluate data + set(handles.lblRaster,'ForegroundColor',[0 0.5 0]) + if(isvector(stimuli)) + if(length(stimuli)~=experiment.Raster.Samples) + set(hObject,'ForegroundColor','red') + end + else + set(hObject,'ForegroundColor','red') + end + else + btnRefreshWorkspace_Callback([],[],handles) + Disable_Buttons(handles,'Raster'); + end + end + + % Write experiment + experiment.Peaks.Stimuli = stimuli; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Coactivity Threshold +function txtCoactivityThreshold_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + z_score=experiment.Coactivity.ZScore; + coactivity=experiment.Coactivity.Coactivity; + + % Process + if(z_score) + maximum=max(floor(coactivity*10)/10); + else + maximum=experiment.Raster.Neurons; + end + coactivity_threshold = User_Double_Entry(hObject,0,0,maximum); + + % Write experiment + experiment.Coactivity.CoactivityThreshold=coactivity_threshold; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Use spikes +function rdbSpikes_Callback(~,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + if(~get(handles.rdbSpikes,'value')) + set(handles.rdbSpikes,'value',true) + end + set(handles.rdbFrequencies,'value',false) + use_spikes=true; + + % Write experiment + experiment.Peaks.UseSpikes=use_spikes; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Use frequencies +function rdbFrequencies_Callback(~,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + if(~get(handles.rdbFrequencies,'value')) + set(handles.rdbFrequencies,'value',true) + end + set(handles.rdbSpikes,'value',false) + use_spikes=false; + + % Write experiment + experiment.Peaks.UseSpikes=use_spikes; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Fixed width +function chkFixedWidth_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + fixed_width=get(hObject,'value'); + + if(fixed_width) + set(handles.txtFixedWidth,'enable','on') + else + set(handles.txtFixedWidth,'enable','off') + end + + % Write experiment + experiment.Peaks.FixedWidth=fixed_width; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Define fixed with +function txtFixedWidth_Callback(hObject,~,handles) + % Read experiment + experiment = Read_Experiment(handles); + samples = experiment.Raster.Samples; + samples_per_second = experiment.Raster.SamplesPerSecond; + detect_peaks_or_valleys = experiment.Peaks.DetectPeaksOrValleys; + + % Process + minimum = 1000/samples_per_second; + maximum = samples*1000/samples_per_second; + multiple = true; + fixed_width_window = round(User_Number_Entry(hObject,minimum,minimum,maximum,multiple,... + detect_peaks_or_valleys)*samples_per_second/1000); + + % Write experiment + experiment.Peaks.FixedWidthWindow=fixed_width_window; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Join peaks +function chkJoin_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + join=get(hObject,'value'); + + if(join) + set(handles.chkDividePeaks,'enable','on') + if(get(handles.chkDividePeaks,'value')) + set(handles.txtDividePeaks,'enable','on') + end + else + set(handles.chkDividePeaks,'enable','off') + set(handles.txtDividePeaks,'enable','off') + end + + % Write experiment + experiment.Peaks.Join=join; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Divide Peaks +function chkDividePeaks_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + + % Process + divide_peaks=get(hObject,'value'); + + if(divide_peaks) + set(handles.txtDividePeaks,'enable','on') + else + set(handles.txtDividePeaks,'enable','off') + set(handles.btnPlotSequenceByPeak,'enable','off') + end + + % Write experiment + experiment.Peaks.DividePeaks=divide_peaks; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +% Define division time +function txtDividePeaks_Callback(hObject,~,handles) + % Read experiment + experiment=Read_Experiment(handles); + samples_per_second=experiment.Raster.SamplesPerSecond; + fixed_width_window=experiment.Peaks.FixedWidthWindow; + + % Process + minimum=1000/samples_per_second; + maximum=abs(fixed_width_window)*1000/samples_per_second; + multiple=true; + division_window=User_Number_Entry(hObject,minimum,minimum,maximum,... + multiple)*samples_per_second/1000; + + % Write experiment + experiment.Peaks.DivisionWindow=division_window; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + + +% Vector method +function popVectorMethod_Callback(hObject,~,handles) + % Get data + data_num=get(hObject,'Value'); + data_strings=get(hObject,'String'); + vector_method=data_strings{data_num}; + + % Disable/enable button + if(data_num<4) + set(handles.popNetworkMethod,'enable','off') + else + set(handles.popNetworkMethod,'enable','on') + end + + % Read experiment + experiment=Read_Experiment(handles); + + % Write experiment + experiment.Peaks.VectorMethod=vector_method; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end +% Network method +function popNetworkMethod_Callback(hObject,~,handles) + % Get data + data_num=get(hObject,'Value'); + data_strings=get(hObject,'String'); + network_method=data_strings{data_num}; + + % Read experiment + experiment=Read_Experiment(handles); + + % Write experiment + experiment.Peaks.NetworkMethod=network_method; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Similarity') + set(handles.btnGetVectors,'ForegroundColor','black') +end + +%% Similarity +% Similarity method +function popSimilarityMethod_Callback(hObject,~,handles) + % Get data + data_num=get(hObject,'Value'); + data_strings=get(hObject,'String'); + similarity_method=data_strings{data_num}; + + % Read experiment + experiment=Read_Experiment(handles); + + % Write experiment + experiment.Clustering.SimilarityMethod=similarity_method; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Clustering') + set(handles.btnGetSimilarity,'ForegroundColor','black') +end +% Linkage method +function popLinkageMethod_Callback(hObject,~,handles) + % Get data + data_num=get(hObject,'Value'); + data_strings=get(hObject,'String'); + linkage_method=data_strings{data_num}; + + % Read experiment + experiment=Read_Experiment(handles); + + % Write experiment + experiment.Clustering.LinkageMethod=linkage_method; + Write_Experiment(handles,experiment); + + % Disable buttons + Disable_Buttons(handles,'Clustering') + set(handles.btnGetSimilarity,'ForegroundColor','black') +end + +%% Clustering +% Groups +function txtGroups_Callback(hObject,~,handles) + % Read experiment + experiment = Read_Experiment(handles); + count = experiment.Peaks.Count; + + % Process + groups = User_Number_Entry(hObject,5,2,count); + groups_string = []; + for i=1:(groups-1) + groups_string = [groups_string num2str(i) ',']; + end + groups_string = [groups_string num2str(groups)]; + set(handles.txtGroupsToPlot,'string',groups_string) + groups_to_plot = 1:groups; + + % Write experiment + experiment.Clustering.Groups = groups; + experiment.Clustering.GroupsToPlot = groups_to_plot; + Write_Experiment(handles,experiment); + + % Disable buttons + set(handles.btnGetClusters,'ForegroundColor','black') +end + +%% Plots +% Sort neurons +function popSortingNeurons_Callback(hObject, ~, handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment=Read_Experiment(handles); + raster = experiment.Raster.Raster; + neurons = experiment.Raster.Neurons; + + % Process + data_num=get(hObject,'Value'); + data_strings=get(hObject,'String'); + sorting_method=data_strings{data_num}; + + % Select sorting method + switch(sorting_method) + case 'no sorting' + cell_indices = 1:neurons; + case 'activity' + activity=experiment.Raster.Activity; + [~,cell_indices]=sort(activity,'descend'); + case 'activation' + [~,cell_indices] = Sort_Raster(raster); + case 'jaccard correlation' + coactivity=experiment.Coactivity.Coactivity; + try + correlation = experiment.Plot.JaccardCorrelation(1,:); + [~, cell_indices]=sort(correlation,'descend'); + catch + disp('Computing correlation...') + tic + for i=1:neurons + D(i)=pdist([coactivity'; raster(i,:)],'jaccard'); + end + correlation=1-D; + [~, cell_indices]=sort(correlation,'descend'); + experiment.Plot.JaccardCorrelation=correlation; + toc + end + case 'linear correlation' + if(get(handles.popPeaksValleys,'value')==3) + stimuli = experiment.Peaks.Stimuli; + + disp('Computing correlation...') + tic + for i=1:neurons + D(i)=pdist([stimuli';raster(i,:)],'correlation'); + end + correlation=1-D; + correlation(isnan(correlation))=0; + correlation(correlation<0)=0; + [~, cell_indices]=sort(correlation,'descend'); + toc + else + try + correlation = experiment.Plot.Correlation(1,:); + [~, cell_indices]=sort(correlation,'descend'); + catch + disp('Computing correlation...') + tic + coactivity=experiment.Coactivity.Coactivity; + for i=1:neurons + D(i)=pdist([coactivity';raster(i,:)],'correlation'); + end + correlation=1-D; + correlation(isnan(correlation))=0; + correlation(correlation<0)=0; + [~, cell_indices]=sort(correlation,'descend'); + toc + end + end + experiment.Plot.Correlation=correlation; + otherwise + by_group=strsplit(sorting_method,' '); + group=str2num(by_group{2}); + try + correlation = experiment.Plot.CorrelationState(group,:); + [~, cell_indices]=sort(correlation,'descend'); + catch + [correlation,cell_indices] = Compute_Correlation_by_State(experiment,group); + experiment.Plot.CorrelationState(group,:)=correlation; + end + end + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = sorting_method; + Write_Experiment(handles,experiment); + + % Color green + set(hObject,'ForeGroundColor',[0 0 0]) +end + +function [correlation,cell_indices] = Compute_Correlation_by_State(experiment,group) + + disp('Computing correlation...') + tic + + coactivity = experiment.Coactivity.Coactivity; + indices = experiment.Peaks.Indices; + group_sequence = experiment.Clustering.SequenceSorted; + neurons = experiment.Raster.Neurons; + raster = experiment.Raster.Raster; + + % find indices of group peaks + idx_group=find(group_sequence==group)'; + idx_indices=zeros(size(indices)); + for i=idx_group + idx_indices(indices==i)=1; + end + coactivity=coactivity.*idx_indices; + + % compute correlation + for i=1:neurons + D(i) = pdist([coactivity';raster(i,:)],'correlation'); + end + correlation = 1-D; + correlation(isnan(correlation)) = 0; + correlation(correlation<0) = 0; + % sort + [~, cell_indices] = sort(correlation,'descend'); + + toc +end + +% Groups to plot +function txtGroupsToPlot_Callback(hObject,~, handles) + % Read experiment + experiment=Read_Experiment(handles); + groups=experiment.Clustering.Groups; + + % Process + % default + default_entry=[]; + for i=1:(groups-1) + default_entry=[default_entry num2str(i) ',']; + end + default_entry=[default_entry num2str(groups)]; + default_groups_to_plot=[1:groups]; + + % validate user entry + user_entry = get(hObject,'string'); + if ~isnan(user_entry) + cell_groups=strsplit(user_entry,','); + n=length(cell_groups); + groups_to_plot=[]; + for i=1:n + group_num=str2num(cell_groups{i}); + if(isempty(group_num)) + user_entry=default_entry; + groups_to_plot=default_groups_to_plot; + break; + end + groups_to_plot=[groups_to_plot group_num]; + end + else + user_entry=default_entry; + groups_to_plot=default_groups_to_plot; + end + set(hObject,'string',num2str(user_entry)); + + % Write experiment + experiment.Clustering.GroupsToPlot=groups_to_plot; + Write_Experiment(handles,experiment); +end + +%% --- Plots --- +%% Plot raster +function btnPlotRaster_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + raster = experiment.Raster.Raster; + name = experiment.Raster.Name; + + try + cell_indices = experiment.Plot.CurrentIndices; + current_sorting = experiment.Plot.CurrentSorting; + catch + neurons = experiment.Raster.Neurons; + cell_indices = 1:neurons; + current_sorting = 'no sorting'; + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = current_sorting; + Write_Experiment(handles,experiment); + end + + % Plot raster + plot_spikes=true; + Plot_Raster(raster(cell_indices,:),name,plot_spikes) + if (strcmp(current_sorting,'no sorting')) + ylabel('neurons') + else + ylabel({'neuorns sorted by'; current_sorting}) + end + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Raster - ' name]); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot frequencies +function btnPlotFrequencies_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + frequencies = experiment.Raster.Frequencies; + name = experiment.Raster.Name; + + try + cell_indices = experiment.Plot.CurrentIndices; + current_sorting = experiment.Plot.CurrentSorting; + catch + neurons = experiment.Raster.Neurons; + cell_indices = 1:neurons; + current_sorting = 'no sorting'; + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = current_sorting; + Write_Experiment(handles,experiment); + end + + % Plot frequencies + plot_spikes=false; + Plot_Raster(frequencies(cell_indices,:),name,plot_spikes) + if (strcmp(current_sorting,'no sorting')) + ylabel('neurons') + else + ylabel({'neuorns sorted by'; current_sorting}) + end + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Frequencies - ' name]); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot coactivity +function btnPlotCoactivity_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment=Read_Experiment(handles); + coactivity_threshold=experiment.Coactivity.CoactivityThreshold; + name=experiment.Raster.Name; + samples_per_second=experiment.Raster.SamplesPerSecond; + smooth_coactivity=experiment.Coactivity.Coactivity; + + + if(get(handles.popPeaksValleys,'value')==3) + coactivity_threshold = []; + end + + % Plot coactivity + Plot_Coactivity(smooth_coactivity,name,coactivity_threshold,samples_per_second) + if(experiment.Coactivity.ZScore) + ylabel({'coactivity';'(z-score)'}) + end + if (experiment.Coactivity.SmoothFilter>1000/samples_per_second) + if(experiment.Coactivity.RemoveOscillations) + title(['smooth filter (' num2str(experiment.Coactivity.SmoothFilter)... + ' ms) - slow oscillations removed']); + else + title(['smooth filter (' num2str(experiment.Coactivity.SmoothFilter) ' ms)']); + end + elseif(experiment.Coactivity.RemoveOscillations) + title('slow oscillations removed'); + else + title(''); + end + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Coactivity - ' name]); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot similarity +function btnPlotSimilarity_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment=Read_Experiment(handles); + similarity=experiment.Clustering.Similarity; + name=experiment.Raster.Name; + tree=experiment.Clustering.Tree; + similarity_method=experiment.Clustering.SimilarityMethod; + linkage_method=experiment.Clustering.LinkageMethod; + + % Plot Similarity + Plot_Similarity(similarity,name,tree,similarity_method,linkage_method) + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Similarity - ' name]); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot peaks +function btnPlotPeaks_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment=Read_Experiment(handles); + name=experiment.Raster.Name; + samples_per_second=experiment.Raster.SamplesPerSecond; + coactivity=experiment.Coactivity.Coactivity; + coactivity_threshold=experiment.Coactivity.CoactivityThreshold; + peak_indices=experiment.Peaks.Indices; + indices=experiment.Clustering.SequenceSorted; + groups_to_plot=experiment.Clustering.GroupsToPlot; + groups=experiment.Clustering.Groups; + + if(get(handles.popPeaksValleys,'value')==3) + coactivity_threshold = []; + end + + % noise_group + noise_group=setdiff(1:groups,groups_to_plot); + + % Plot Peaks + peak_number=false; + Plot_Coactivity(coactivity,name,coactivity_threshold,samples_per_second); + Plot_Peaks_On_Coactivity(coactivity,peak_indices,indices,... + noise_group,samples_per_second,peak_number) + if(experiment.Coactivity.ZScore) + ylabel({'coactivity';'(z-score)'}) + end + if (experiment.Coactivity.SmoothFilter>1000/samples_per_second) + if(experiment.Coactivity.RemoveOscillations) + title(['smooth filter (' num2str(experiment.Coactivity.SmoothFilter)... + ' ms) - slow oscillations removed']); + else + title(['smooth filter (' num2str(experiment.Coactivity.SmoothFilter) ' ms)']); + end + elseif(experiment.Coactivity.RemoveOscillations) + title('slow oscillations removed'); + end + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Coactivity Peaks - ' name]); + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot sequence +function btnPlotSequence_Callback(hObject,~,handles) + + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Ask for division + prompt = {'Enter the seconds of windows to divide the raster: (0=all sequence)'}; + title = 'Enter parameters'; + dims = [1 50]; + default_input = {'0'}; + answer = inputdlg(prompt,title,dims,default_input); + if(~isempty(answer)) + window_sec=str2num(answer{1}); + if(isempty(window_sec)) + window_sec=1; + end + + % Read experiment + experiment=Read_Experiment(handles); + name=experiment.Raster.Name; + groups_to_plot=experiment.Clustering.GroupsToPlot; + sequence=experiment.Clustering.SequenceSorted; + groups=experiment.Clustering.Groups; + + % noise_group + noise_group=setdiff(1:groups,groups_to_plot); + + if(window_sec==0) + % Plot Sequence + Plot_States_Sequence(sequence,noise_group,name); + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Sequence - ' name]); + end + else + % read indices + indices=experiment.Peaks.Indices; + samples=experiment.Raster.Samples; + samples_per_second=experiment.Raster.SamplesPerSecond; + window=window_sec*samples_per_second; + + n_seqs=ceil(samples/window); + indices_seq=zeros(length(sequence),1); + for i=1:n_seqs + if(i*window>samples) + idx=find(indices(((i-1)*window+1):end)>0,1,'first'); + ini=indices((i-1)*window+idx); + fin=max(indices(((i-1)*window+1):end)); + else + idx=find(indices(((i-1)*window+1):i*window)>0,1,'first'); + ini=indices((i-1)*window+idx); + fin=max(indices(((i-1)*window+1):i*window)); + end + + % Plot Sequence + Plot_States_Sequence(sequence(ini:fin),noise_group,... + [name ' (' num2str(i) '-' num2str(n_seqs) ')'],... + Read_Colors(max(sequence))); + + if(i==1) + Hold_Axes('States count'); + ylims = get(gca,'ylim'); + else + Hold_Axes('States count'); + ylim(ylims) + end + + % Save + if(get(handles.chkSavePlot,'value')) + Save_Figure(['Sequence - ' name '_' num2str(i) '-' num2str(n_seqs)]); + end + indices_seq(ini:fin)=i; + end + % Write experiment + experiment.Plot.SecondsForSequences=window_sec; + experiment.Plot.IndicesForSequences=indices_seq; + Write_Experiment(handles,experiment); + end + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot sequence by peak +function btnPlotSequenceByPeak_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment=Read_Experiment(handles); + name=experiment.Raster.Name; + division_window=experiment.Peaks.DivisionWindow; + fixed_width_window=abs(experiment.Peaks.FixedWidthWindow); + sequence=experiment.Clustering.SequenceSorted; + samples_per_second=experiment.Raster.SamplesPerSecond; + + % Peak width + width=ceil(fixed_width_window/division_window); + + % Plot save + save_plot=get(handles.chkSavePlot,'value'); + + % Plot states by width + division_ms=division_window*1000/samples_per_second; + + % Plot state sequences + sequences=Plot_States_By_Width(sequence,width,name,save_plot); + Plot_Sequences(sequences,division_ms,save_plot,'Sequences'); + + % Plot graphs from sequencies + %adjacency_vectors=Plot_Sequence_Graph_By_Width(sequence,width,name,save_plot); + %Plot_Sequences(double(adjacency_vectors>0),division_ms,save_plot,'Adjacencies',... + %flipud(gray(max(adjacency_vectors(:))))); + %Plot_Sequences(adjacency_vectors,division_ms,save_plot,'Adjacencies',... + %flipud(gray(max(adjacency_vectors(:))))); + + % Write experiment + experiment.Plot.Sequences=sequences; + %experiment.Plot.AdjacencyVectors=adjacency_vectors; + Write_Experiment(handles,experiment); + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot whole network +function btnPlotWholeNetwork_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + name = strrep(experiment.Raster.Name,'_','-'); + n = experiment.Raster.Neurons; + coactivity = experiment.Network.WholeCoactivity; + network = experiment.Network.WholeNetwork; + + try + cell_indices = experiment.Plot.CurrentIndices; + catch + neurons = experiment.Raster.Neurons; + cell_indices = 1:neurons; + current_sorting = 'no sorting'; + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = current_sorting; + Write_Experiment(handles,experiment); + end + + % Get coordinates of network +% xy = Get_Force_XY(coactivity); + xy = Get_Circular_XY(n); + + % Plot network + save_plot=get(handles.chkSavePlot,'value'); + + edge_color = [0.5 0.5 0.5]; + node_color = [0.8 0.8 0.8]; + network_plot = coactivity.*network; + Plot_Adjacencies_And_Network(coactivity(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),['All networks (union) - ' name],... + xy,node_color,edge_color,save_plot) + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + + +%% Plot networks +function btnPlotNetworks_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + name = strrep(experiment.Raster.Name,'_','-'); + n = experiment.Raster.Neurons; + groups = experiment.Clustering.Groups; + groups_to_plot=experiment.Clustering.GroupsToPlot; + network = experiment.Network.Network; + network_th = experiment.Network.Significant; + core_network = experiment.Network.CoreNetwork; + core_network_th = experiment.Network.CoreSignificant; + + try + cell_indices = experiment.Plot.CurrentIndices; + catch + neurons = experiment.Raster.Neurons; + cell_indices = 1:neurons; + current_sorting = 'no sorting'; + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = current_sorting; + Write_Experiment(handles,experiment); + end + + % Get coordinates of network +% xy = Get_Force_XY(network); + xy = Get_Circular_XY(n); + + % Plot network + save_plot=get(handles.chkSavePlot,'value'); + + edge_color = [0.5 0.5 0.5]; + node_color = [0.8 0.8 0.8]; + network_plot = network.*network_th; + Plot_Adjacencies_And_Network(network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),['All networks (union) - ' name],... + xy,node_color,edge_color,save_plot) + lims_x = get(gca,'xlim'); + lims_y = get(gca,'ylim'); + + % Plot core network + network_plot = core_network.*core_network_th; + Plot_Adjacencies_And_Network(core_network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),... + ['Core network (intersection) - ' name],... + xy,node_color,edge_color,save_plot) + xlim(lims_x) + ylim(lims_y) + + % Plot network of each state + colors = Read_Colors(groups); + for i=1:groups + if(ismember(i,groups_to_plot)) + state_network = experiment.Network.State{i}; + state_network_th = experiment.Network.StateSignificant{i}; + + network_plot = state_network.*state_network_th; + Plot_Adjacencies_And_Network(state_network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),... + ['State ' num2str(i) ' network - ' name],... + xy,colors(i,:),edge_color,save_plot) + + xlim(lims_x) + ylim(lims_y) + end + end + %btnPlotRandomNetworks_Callback(hObject,[], handles) + + [~, xy_colors, id, structure] = Get_XY_Ensembles(experiment.Network.StateSignificant); + Plot_Ensembles(network_th(id,id),[],xy_colors,structure,name,save_plot); + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% Plot networks by stimulus +function btnPlotStimulusNetworks_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + name = strrep(experiment.Raster.Name,'_','-'); + n = experiment.Raster.Neurons; + stimuli = experiment.Peaks.Stimuli; + groups = sum(unique(stimuli)>0); + + network = experiment.Network.UnionStimulus; + network_th = experiment.Network.UnionSignificantStimulus; + core_network = experiment.Network.IntersectionStimulus; + core_network_th = experiment.Network.IntersectionSignificantStimulus; + + try + cell_indices = experiment.Plot.CurrentIndices; + catch + neurons = experiment.Raster.Neurons; + cell_indices = 1:neurons; + current_sorting = 'no sorting'; + + % Write experiment + experiment.Plot.CurrentIndices = cell_indices; + experiment.Plot.CurrentSorting = current_sorting; + Write_Experiment(handles,experiment); + end + + % Get coordinates of network +% xy = Get_Force_XY(network); + xy = Get_Circular_XY(n); + + % Plot network + save_plot=get(handles.chkSavePlot,'value'); + + edge_color = [0.5 0.5 0.5]; + node_color = [0.8 0.8 0.8]; + network_plot = network.*network_th; + Plot_Adjacencies_And_Network(network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),['All networks (union) - ' name ' - stimulus'],... + xy,node_color,edge_color,save_plot) + lims_x = get(gca,'xlim'); + lims_y = get(gca,'ylim'); + + % Plot core network + network_plot = core_network.*core_network_th; + Plot_Adjacencies_And_Network(core_network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),... + ['Core network (intersection) - ' name ' - stimulus'],... + xy,node_color,edge_color,save_plot) + xlim(lims_x) + ylim(lims_y) + + % Plot network of each state + colors = Read_Colors(groups); + for i=1:groups + state_network = experiment.Network.Stimulus{i}; + state_network_th = experiment.Network.StimulusSignificant{i}; + + network_plot = state_network.*state_network_th; + Plot_Adjacencies_And_Network(state_network(cell_indices,cell_indices),... + network_plot(cell_indices,cell_indices),... + ['State ' num2str(i) ' network - ' name],... + xy,colors(i,:),edge_color,save_plot) + + xlim(lims_x) + ylim(lims_y) + end + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +function btnPlotRandomNetworks_Callback(hObject, ~, handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Read experiment + experiment = Read_Experiment(handles); + name = strrep(experiment.Raster.Name,'_','-'); + width = experiment.Peaks.FixedWidthWindow; + n = experiment.Raster.Neurons; + vector_method = experiment.Peaks.VectorMethod; + network_method = experiment.Peaks.NetworkMethod; + groups = experiment.Clustering.Groups; + raster_states = experiment.Clustering.RasterStates; + + if(isfield(experiment,'Plot')) + cell_indices = experiment.Plot.CellIndices; + else + cell_indices = 1:experiment.Raster.Neurons; + end + + shuffled_joined = zeros(n); + shuffled_core = ones(n); + for i = 1:groups + raster_state = raster_states{i}; + samples=size(raster_state,2); + raster_shuffled = shuffle(raster_state,'time_shift'); + + % Get vectors from state + n=ceil(samples/width); + vector_indices=zeros(samples,1); + for j=1:n + ini=(j-1)*width+1; + fin=j*width; + if(fin>samples) + fin=samples; + end + vector_indices(ini:fin)=j; + end + % Create a matrix with all vector peaks + shuffled_networks=Get_Peak_Vectors(raster_shuffled,vector_indices,... + vector_method,network_method); + state_shuffled = squareform(mean(shuffled_networks>0)); + state_shuffled_th = state_shuffled==1; + + % Get + shuffled_joined = shuffled_joined + state_shuffled_th; + shuffled_core = shuffled_core.*state_shuffled_th; + + % Write in experiment + experiment.Network.StateShuffled{i}=state_shuffled; + experiment.Network.StateSignificantShuffled{i}=state_shuffled_th; + end + shuffled_joined_th = logical(shuffled_joined); + + % Get coordinates of network + xy = Get_Force_XY(shuffled_joined); + xy = xy(cell_indices,:); + + % Plot networks + save_plot=get(handles.chkSavePlot,'value'); + edge_color = [0.5 0.5 0.5]; + node_color = [0.8 0.8 0.8]; + Plot_Adjacencies_And_Network(shuffled_joined(cell_indices,cell_indices),... + shuffled_joined_th(cell_indices,cell_indices),... + ['Shuffled all networks (union) - ' name],... + xy,node_color,edge_color,save_plot) + lims_x = get(gca,'xlim'); + lims_y = get(gca,'ylim'); + + % Plot core network + Plot_Adjacencies_And_Network(shuffled_core(cell_indices,cell_indices),... + shuffled_core(cell_indices,cell_indices),... + ['Shuffled core network (intersection) - ' name],... + xy,node_color,edge_color,save_plot) + xlim(lims_x) + ylim(lims_y) + + % Plot network of each state + colors = Read_Colors(groups); + for i=1:groups + state_shuffled = experiment.Network.StateShuffled{i}; + state_shuffled_th = experiment.Network.StateSignificantShuffled{i}; + + Plot_Adjacencies_And_Network(state_shuffled(cell_indices,cell_indices),... + state_shuffled_th(cell_indices,cell_indices),... + ['Shuffled state ' num2str(i) ' network - ' name],... + xy,colors(i,:),edge_color,save_plot) + + xlim(lims_x) + ylim(lims_y) + end + + % Write experiment + experiment.Network.NetworkShuffled=shuffled_joined; + experiment.Network.ShuffledSignificant=shuffled_joined_th; + experiment.Network.ShuffledCoreSignificant=shuffled_core; + Write_Experiment(handles,experiment); + + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end + +%% --- Save --- + +%% Save raster by x seconds +function btnSaveBySeconds_Callback(hObject,~,handles) + % Color yellow + set(hObject,'ForeGroundColor',[0.5 0.5 0]); pause(0.1); pause on + + % Ask for division + prompt = {'Enter the seconds of windows to divide the raster:'}; + title = 'Enter parameters'; + dims = [1 50]; + default_input = {'60'}; + answer = inputdlg(prompt,title,dims,default_input); + if(~isempty(answer)) + window_sec=str2num(answer{1}); + if(isempty(window_sec)) + window_sec=60; + end + + % Read experiment + experiment=Read_Experiment(handles); + name=experiment.Raster.Name; + samples_per_second=experiment.Raster.SamplesPerSecond; + samples=experiment.Raster.Samples; + final_sec=samples/samples_per_second; + use_spikes=experiment.Peaks.UseSpikes; + + % Check if raster was ploted + h=findobj('name',['Raster (' name ')']); + if isempty(h) + % Plot + if(use_spikes) + btnPlotRaster_Callback([],[],handles); + else + btnPlotFrequencies_Callback([],[],handles); + end + end + + % Check if coactivity was ploted + h=findobj('name',['Coactivity (' name ')']); + i=findobj('tag',['CoactiveAxes' name]); + if (isempty(h) && isempty(i)) + btnPlotPeaks_Callback([],[],handles); + end + + % Save + Save_Raster_By_Windows(name,samples_per_second,window_sec,final_sec) + end + % Color black + set(hObject,'ForeGroundColor',[0 0 0]) +end diff --git a/PeaksVectors_JP_OLD.m b/PeaksVectors_JP_OLD.m new file mode 100644 index 0000000..572451a --- /dev/null +++ b/PeaksVectors_JP_OLD.m @@ -0,0 +1,33 @@ +% Peaks Vectors +% Join the vectors of the same peak. +% +% [XPeaks] = PeaksVectors_JP(X,Xid,mode) +% +% Inputs +% X = data as F x C matrix (F = #frames, C = #cells) +% Xid = Fx1 vector containing the peaks indexes +% sumMode = set 1 if you want to sum the activity or set 0 if you only want +% binary data +% +% Outputs +% XPeaks = data as matrix PxC (P = #peaks) +% +% ..:: by Jesús E. Pérez-Ortega ::.. Jun-2012 +% +% V 2.0 3rd input added: 'sumMode' (binary or sum) jun-2012 +% modified march-2018 + +function [XPeaks] = PeaksVectors_JP_OLD(X,Xid,binary) + +C=size(X,1); +peaks=max(Xid); + +XPeaks=zeros(peaks,C); +for i=1:peaks + peak_n=find(Xid==i); + XPeak=X(:,peak_n); + XPeaks(i,:)=sum(XPeak,2); + if binary + XPeaks(i,:)=XPeaks(i,:)&1; + end +end diff --git a/Plot_Activation_Order.m b/Plot_Activation_Order.m new file mode 100644 index 0000000..dc91963 --- /dev/null +++ b/Plot_Activation_Order.m @@ -0,0 +1,95 @@ +function [id_position,entropy,sequences_sorted] = Plot_Activation_Order(raster,network,window,... + bin,colors,name,save) +% Plot activation order of neurons based on a network +% +% By Pérez-Ortega Jesús, Feb 2019 + +switch(nargin) + case 2 + window = size(raster,2); + bin = 1; + colors = autumn(size(raster,1)); + name = ''; + save = false; + case 3 + bin = 1; + colors = autumn(size(raster,1)); + name = ''; + save = false; + case 4 + colors = autumn(size(raster,1)); + name = ''; + save = false; + case 5 + name = ''; + save = false; + case 6 + save = false; +end + +% Get sequences +n = size(raster,1); +sequences = randperm(n); + +n_seqs = size(raster,2)/window; +for i = 1:n_seqs + ini = (i - 1) * window + 1; + fin = i * window; + r = Reshape_Raster(raster(:,ini:fin),bin); + sequence = Get_Network_Activation_Sequence(r,network); + rest = n-length(sequence); + if (rest>0) + resting = setdiff(1:n,sequence); + resting = resting(randperm(rest)); + sequence = [sequence resting]; + end + sequences(i,1:length(sequence)) = sequence; +end + +% Identify activation position +[sequences_sorted,id_position,probabilities] = Sort_Activation_Position(sequences); + +% Get entropy +for i = 1:n + p = probabilities(:,i); + entropy(i) = Get_Entropy(p); +end +% Normalize entropy +%entropy = entropy/Get_Entropy(ones(1,n+1)); % with "neuron 0" +entropy = entropy/Get_Entropy(ones(1,n)); % without "neuron 0" + +Set_Figure(['Sequences - ' name],[0 0 500 800]); +ax=subplot(6,1,1:4); +imagesc(sequences_sorted); hold on +%colormap(ax,[1 1 1;colors])% with "neuron 0" +colormap(ax,colors) % without "neuron 0" +ylabel('sequence #') +title(name) + +ax=subplot(6,1,5); +imagesc(probabilities,[0 0.5]) +set(gca,'ytick',[1 n+1],'yticklabel',[0 n]) +colormap(ax,flipud(gray(100))) +ylabel('neuron #') + +subplot(6,1,6) +plot(entropy,'o-','color',colors(1,:)) +ylabel('entropy') +xlabel('activation order') +xlim([0.5 n+0.5]) +ylim([0 1]) +if(save) + Save_Figure([name ' - activation order']) +end + +% color neurons +% Set_Figure(['Color neurons - ' name],[0 0 20 800]); +% imagesc((1:n)') +% set(gca,'xtick',[],'ytick',[]) +% colormap(colors) +% for i = 1:n +% text(1,i,num2str(i),'horizontalalignment','center') +% end +% if(save) +% Save_Figure([name ' - color neurons']) +% end \ No newline at end of file diff --git a/Plot_Adjacencies_And_Network.m b/Plot_Adjacencies_And_Network.m new file mode 100644 index 0000000..d0fe538 --- /dev/null +++ b/Plot_Adjacencies_And_Network.m @@ -0,0 +1,55 @@ +% Plot weighted and binary adjacency and its network + +function Plot_Adjacencies_And_Network(adjacency,adjacency_threshold,name,xy,node_color,... + edge_color,save_plot) + + if(nargin==1) + adjacency_threshold = adjacency; + name = 'network'; + xy = []; + node_color = [0.2 0.2 0.2]; + edge_color = [0.5 0.5 0.5]; + save_plot = false; + elseif(nargin==2) + name = 'network'; + xy = []; + node_color = [0.2 0.2 0.2]; + edge_color = [0.5 0.5 0.5]; + save_plot = false; + elseif(nargin==3) + xy = []; + node_color = [0.2 0.2 0.2]; + edge_color = [0.5 0.5 0.5]; + save_plot = false; + elseif(nargin==4) + node_color = [0.2 0.2 0.2]; + edge_color = [0.5 0.5 0.5]; + save_plot = false; + elseif(nargin==5) + edge_color = [0.5 0.5 0.5]; + save_plot = false; + elseif(nargin==6) + save_plot = false; + end + + curved = 0; + size_node = 20; + numbers = false; + + Set_Figure(name,[0 0 1000 300]); + % Plot weighted adjacency matrix + subplot(1,3,1) + imagesc(adjacency); pbaspect([1 1 1]) + title(name) + % Plot significant adjacency matrix + ax=subplot(1,3,2); + imagesc(adjacency_threshold>0); colormap(ax,[1 1 1; 0 0 0]); + pbaspect([1 1 1]) + % Plot weighted network + subplot(1,3,3) + Plot_WU_Network(adjacency_threshold,xy,node_color,curved,edge_color,size_node,numbers) + % Save Plot + if(save_plot) + Save_Figure(name) + end +end \ No newline at end of file diff --git a/Plot_Changes_In_Neurons.m b/Plot_Changes_In_Neurons.m new file mode 100644 index 0000000..9c13c4e --- /dev/null +++ b/Plot_Changes_In_Neurons.m @@ -0,0 +1,19 @@ +% Plot change in each neuron activity +% +% Jesús Pérez-Ortega, nov 2018 + +function Plot_Changes_In_Neurons(change,change_title,name) + if(nargin==2) + name=''; + end + n_neurons=length(change); + plot(find(change>0),change(change>0),'.r','markersize',10); hold on + plot(find(change==0),change(change==0),'.k','markersize',10) + plot(find(change<0),change(change<0),'.b','markersize',10) + plot([0 n_neurons],[mean(change) mean(change)],'--k','markersize',10) + title(name) + xlabel('neuron'); ylabel(change_title) + xlim([1 n_neurons]); %ylim([-7000 7000]) + view([90 90]); + set(gca,'xdir','reverse') +end \ No newline at end of file diff --git a/Plot_Coactivity.m b/Plot_Coactivity.m new file mode 100644 index 0000000..8b26e1e --- /dev/null +++ b/Plot_Coactivity.m @@ -0,0 +1,56 @@ +% Plot Coactivity +% +% P?rez-Ortega Jes?s - March 2018 +% Modified Nov 2018 +function Plot_Coactivity(coactivity,name,threshold,samples_per_second) + if(nargin==1) + name = ''; + threshold = []; + samples_per_second = 1; + elseif(nargin==2) + threshold = []; + samples_per_second = 1; + elseif(nargin==3) + samples_per_second = 1; + end + + h=findobj('name',['Raster (' name ')']); + if isempty(h) + Set_Figure(['Coactivity (' name ')'], [0 0 1220 230]); + Set_Axes(['CoactiveAxes' name],[0 0 1 1]); + else + figure(h); + h=Hold_Axes(['CoactiveAxes' name]); + if(isempty(h)) + Set_Axes(['CoactiveAxes' name],[0 0 1 0.34]); + else + cla; + end + end + F=length(coactivity); + + plot((1:F)/samples_per_second,coactivity,'k');hold on + ylabel('coactivity'); ylim([min(coactivity(3:end-2)) max(coactivity(3:end-2))]) + + if(~isempty(threshold)) + plot([1 F]/samples_per_second,[threshold threshold],'--','color',[0.5 0.5 0.5],'lineWidth',2) + end + + if(F/samples_per_second<30) + set(gca,'box','off','xtick',0:F/samples_per_second,'tag',['CoactiveAxes' name]) + xlabel('Time (s)'); + elseif(F/samples_per_second/60<3) + set(gca,'box','off','xtick',0:10:F/samples_per_second,'tag',['CoactiveAxes' name]) + xlabel('Time (s)'); + else + set(gca,'box','off','xtick',0:60:F/samples_per_second,'xticklabel',0:F/samples_per_second/60,'tag',['CoactiveAxes' name]) + xlabel('Time (min)'); + end + + xlim([0 F/samples_per_second]) + +% ylims=get(gca,'ylim'); +% if(ylims(1)<0) +% ylim([0 ylims(2)]) +% end +end \ No newline at end of file diff --git a/Plot_Degree_Distribution.m b/Plot_Degree_Distribution.m new file mode 100644 index 0000000..8177b69 --- /dev/null +++ b/Plot_Degree_Distribution.m @@ -0,0 +1,76 @@ +% Plot degree distribution +% +% Jesús Pérez-Ortega +% modified nov 2018 + +function [slope,R2,intercept] = Plot_Degree_Distribution(adjacency,name,save) + + if(nargin==2) + save=false; + end + + cells=length(adjacency); + links=sum(adjacency,1); + + if ~links + warning('There are no links.') + return; + end + + % Get links histogram (only neurons with at least one link) + links_1=links(links>0); + N=length(links_1); + nbins = round(log2(N)+1); % Sturges rule + %nbins = 2*iqr(links_1)*N^(-1/3); % Freedman-Diaconis rule for heavy tailed + + [y,x]=hist(links_1,nbins); + y=y/sum(y); + + % Plot power law fit + try + [slope, intercept, R2] = Fit_Power_Law(x',y'); + catch + slope=NaN; + intercept=NaN; + R2=NaN; + return + end + xfit=min(x):(max(x)-min(x))/100:max(x); + yfit=intercept*xfit.^(slope); + + % Plot Links + Set_Figure(['Links - ' name],[0 0 600 200]); + Set_Axes(['Links ' name],[0 0 1 1]) + plot(find(links>0),links_1,'ob'); hold on + plot(find(links==0),links(links==0),'or') + set(gca,'xlim',[0 cells],'ylim',[0 max(links)+1]) + title(name); xlabel('# neuron'); ylabel('Count of links') + + % Save + if(save) + Save_Figure([name ' - Links' ]); + end + + % Plot linear links histogram + Set_Figure(['Distribution - ' name],[0 0 600 200]); + Set_Axes(['Links ' name],[0 0 0.5 1]) + plot(x,y,'ob','markersize',10); hold on + plot(xfit,yfit,'k') + xlabel('k'); ylabel('P(k)'); title(['Degree distribution - ' name]) + + % Write the coefficients + text(max(x)/2,max(y)*0.8,['\lambda=' num2str(-slope,'%0.1f')],'FontSize',14,'FontWeight','bold') + text(max(x)/2,max(y)*0.6,['R^2=' num2str(R2,'%0.3f')],'FontSize',14,'FontWeight','bold') + + % Plot loglog links histogram + Set_Axes(['HistLinksPL ' name],[0.5 0 0.5 1]) + loglog(x,y,'ob','markersize',10); hold on + loglog(xfit,yfit,'k') + xlabel('k'); ylabel('P(k)'); title(['Degree distribution - ' name]) + + % Save + if(save) + Save_Figure([name ' - Distribution']); + end + +end \ No newline at end of file diff --git a/Plot_Edge.m b/Plot_Edge.m new file mode 100644 index 0000000..e5b7860 --- /dev/null +++ b/Plot_Edge.m @@ -0,0 +1,84 @@ +%% Plot edge with or without arrow +% +% Jesús Pérez-Ortega - june 2018 +% Modified nov 2018 + +function [x,y] = Plot_Edge(XY_initial,XY_final,radius,length_arrow,line_width,color) + + if(nargin==5) + color=[0 0 0]; + elseif(nargin==4) + color=[0 0 0]; + line_width=2; + elseif(nargin==3) + color=[0 0 0]; + line_width=2; + length_arrow=3.5; + elseif(nargin==2) + color=[0 0 0]; + line_width=2; + length_arrow=3.5; + radius=0.15; + end + + if(XY_initial(1)>0 && XY_final(1)>0 && XY_initial(2)>0 && XY_final(2)<0) + XY_temporal=XY_initial; + XY_initial=XY_final; + XY_final=XY_temporal; + end + + % If loop + if (XY_initial==XY_final) + r=0.2; + [theta,rho] = cart2pol(XY_initial(1),XY_initial(2)); + rho=rho+r; + [x_center,y_center] = pol2cart(theta,rho); + + % Circle + ang=0:0.01:2*pi; + x=r*cos(ang)+x_center; + y=r*sin(ang)+y_center; + else + if(radius) + % Curve + l=norm(XY_initial-XY_final); % length of line + dx=XY_final(1)-XY_initial(1); + dy=XY_final(2)-XY_initial(2); + + alpha=atan2(dy,dx); % angle of rotation + cosa=cos(alpha); + sina=sin(alpha); + + points=linspace(pi/4,3*pi/4); + a=(0.5-cos(points)/2^0.5)*l; + b=((sin(points)-2^0.5/2)/(1-2^0.5/2))*radius; + + x=a*cosa-b*sina+XY_initial(1); + y=a*sina+b*cosa+XY_initial(2); + else + x=[XY_initial(1) XY_final(1)]; + y=[XY_initial(2) XY_final(2)]; + end + end + plot(x,y,'-','color',color,'linewidth',line_width); hold on + + if(length_arrow) + % Arrow end + xai=x([end end-20]); + yai=y([end end-20]); + xa1=length_arrow*[0 0.1 0.08 0.1 0]'; + ya1=length_arrow*[0 0.03 0 -0.03 0]'; + dx=diff(xai); + dy=diff(yai); + alpha=atan2(dy,dx); % angle of rotation + cosa=cos(alpha); + sina=sin(alpha); + xa=xa1*cosa-ya1*sina+xai(1); + ya=xa1*sina+ya1*cosa+yai(1); + + if (XY_initial~=XY_final) + fill(xa,ya,color) + plot(xa,ya,'-','color',color) + end + end +end diff --git a/Plot_Ensembles.m b/Plot_Ensembles.m new file mode 100644 index 0000000..b658c40 --- /dev/null +++ b/Plot_Ensembles.m @@ -0,0 +1,65 @@ +function xy = Plot_Ensembles(network,xy,xy_colors,structure,name,save) +% Plot ensembles +% +% xy = Plot_Ensembles(network,xy,xy_colors,structure,name) +% +% Jesus Perez-Ortega April-19 + +switch(nargin) + case 4 + name = ''; + save = false; + case 3 + save = false; +end + +% Get data +[n_networks,n_neurons] = size(structure); + +if isempty(xy) + % Identify inactive + n_inactive = length(find(sum(network)==0)); + + % Generate internal circular XY for inactive + xy_1 = Get_Circular_XY(n_neurons-n_inactive); + + % Generate external circular XY for inactive + xy_2 = Get_Circular_XY(n_inactive,1.1); + xy = [xy_1;xy_2]; +end + +% Plot structure +Set_Figure(['Structure - ' name],[0 0 1200 300]); +pcolor([structure; zeros(1,n_neurons)]) +colormap([1 1 1;Read_Colors(n_networks)]) +yticks(1.5:n_networks+0.5) +yticklabels(1:n_networks) +xlabel('neuron #') +ylabel('ensemble #') +if save + Save_Figure(['Structure - ' name]) +end + +% Plot ensembles +Set_Figure(['Network - ' name],[0 0 600 600]); +Plot_Network(network,'undirected',xy,xy_colors) +title(['Network - ' name]) +if save + Save_Figure(['Network - ' name]) +end + +Set_Figure(['Single networks' name],[0 0 900 900]); +rows = ceil(n_networks/2); +for i = 1:n_networks + id = find(structure(i,:)); + subplot(rows,2,i) + Plot_Network(network(id,id),'undirected',xy(id,:),xy_colors(id,:)) + title(['# ' num2str(i)]) + xlim([-1 1]) + ylim([-1 1]) +end +if save + Save_Figure(['Single networks - ' name]) +end + + diff --git a/Plot_Hierarchy.m b/Plot_Hierarchy.m new file mode 100644 index 0000000..ee96400 --- /dev/null +++ b/Plot_Hierarchy.m @@ -0,0 +1,60 @@ +% Plot clustering coefficient in function of degree C(k) vs k +% +% Jesús Pérez-Ortega +% Modified Jan 2019 + +function [slope,R2,intercept,links,clocal] = Plot_Hierarchy(adjacency,name,save) + switch(nargin) + case 1 + name = ''; + save = false; + case 2 + save= false; + end + + % Number of cells + N=length(adjacency); + + if ~N + links=0; + clocal=0; + return; + end + + links=sum(adjacency)'; + clocal=clustering_coef_bu(adjacency); + l=links; + c=clocal; + + % delete zeros clustering coefficients + x=links(clocal>0); + y=clocal(clocal>0); + + Set_Figure([name ' - Hierarchy'],[0 0 600 200]); + + [slope, intercept, R2] = Fit_Power_Law(x,y); + xfit=min(x):(max(x)-min(x))/100:max(x); + yfit=intercept*xfit.^slope; + + % linear plot + Set_Axes(['Axes - C(k) lineal' name],[0 0 0.5 1]); hold on + plot(xfit,yfit,'k','linewidth',2,'markersize',10); hold on + plot(l,c,'or','linewidth',2,'markersize',10) + xlabel('k'); ylabel('C(k)') + title('Local Clustering Coeficient C(k)') + + % Write the coefficients + text(max(x)/2,max(y)*0.8,['\alpha=' num2str(-slope,'%0.1f')],'FontSize',14,'FontWeight','bold') + text(max(x)/2,max(y)*0.6,['R^2=' num2str(R2,'%0.3f')],'FontSize',14,'FontWeight','bold') + + % loglog plot + Set_Axes(['Axes - C(k) loglog' name],[0.5 0 0.5 1]) + loglog(xfit,yfit,'k','linewidth',2,'markersize',10); hold on + loglog(l,c,'or','linewidth',2,'markersize',10) + xlabel('k'), ylabel('C(k)') + title('Local Clustering Coeficient C(k)') + + if(save) + Save_Figure([name ' - Hierarchy']) + end +end \ No newline at end of file diff --git a/Plot_Network.m b/Plot_Network.m new file mode 100644 index 0000000..0442e96 --- /dev/null +++ b/Plot_Network.m @@ -0,0 +1,111 @@ +function Plot_Network(adjacency,type,xy,xy_colors,link_color,node_size,node_number) +% Plot weighted and undirected network with loops +% +% Plot_Network(adjacency,xy,xy_colors,link_color,node_size,node_number) +% +% Pérez-Ortega Jesús - april 2018 +% Modified Jan 2019 +% Modified April 2019 + +n=length(adjacency); +switch (nargin) + case 6 + node_number=false; + case 5 + node_number=false; + node_size=10; + case 4 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + case 3 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + xy_colors = Read_Colors(n); + case 2 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + xy_colors = Read_Colors(n); + xy = []; + case 1 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + xy_colors = Read_Colors(n); + xy = []; + type = 'undirected'; +end + +if(size(xy_colors,1)==1) + xy_colors=repmat(xy_colors,n,1); +end + +lims = []; +if(nargin==1||isempty(xy)) + nodes=length(adjacency); + xy=Get_Circular_XY(nodes); + lims = [-1.5 1.5]; +% xy = Get_Force_XY(adjacency); +end + +C=length(adjacency); + +% Plot edges +curved = 0; +if(sum(adjacency(:))) + line_widths = zeros(C); + line_widths(adjacency>0) = Scale(adjacency(adjacency>0),0.5,5); + hold on + for a=1:C + for b=a:C + if (adjacency(a,b)) + if strcmp(type,'directed') + length_arrow=3+0.5*line_widths(a,b); + else + length_arrow = 0; + end + link_color = mean([xy_colors(a,:);xy_colors(b,:)]); + Plot_Edge(xy(a,:),xy(b,:),curved,length_arrow,line_widths(a,b),... + link_color); + end + end + end +end + +links=sum(adjacency); +if(length(node_size)==1) + if(sum(links) && node_size) + sizes_in=Scale(sum(adjacency),node_size,node_size+40); + else + sizes_in=ones(1,C)*30; + end +else + sizes_in=Scale(node_size,10,50); +end + +% Plot nodes +for i=1:C + if(links(i)) + plot(xy(i,1),xy(i,2),'.k','MarkerSize',sizes_in(i)+5) + plot(xy(i,1),xy(i,2),'.','color',xy_colors(i,:),'MarkerSize',sizes_in(i)) + if(node_number) + %text(xy(i,1)*1.1,xy(i,2)*1.1,num2str(i),'HorizontalAlignment','Center') + text(xy(i,1),xy(i,2),num2str(i),'HorizontalAlignment','left') + end + else + plot(xy(i,1),xy(i,2),'.','color',mean([0.5 0.5 0.5; xy_colors(i,:)]),... + 'MarkerSize',sizes_in(i)*.2) + end +end +set(gca,'xtick',[],'ytick',[],'xcolor',[1 1 1],'ycolor',[1 1 1]) +if(lims) + xlim(lims) + ylim(lims) +else + xlim([min(xy(:,1)) max(xy(:,1))]) + ylim([min(xy(:,2)) max(xy(:,2))]) +end + +pbaspect([1 1 1]) diff --git a/Plot_Peaks_On_Coactivity.m b/Plot_Peaks_On_Coactivity.m new file mode 100644 index 0000000..e3cb86b --- /dev/null +++ b/Plot_Peaks_On_Coactivity.m @@ -0,0 +1,34 @@ +% Plot Peaks +% +% Pérez-Ortega Jesús - March 2018 + +function Plot_Peaks_On_Coactivity(coactivity,peak_indices,group_indices,noise_group,samples_per_second,plot_peak_number,group_colors,width) + n=max(group_indices); + if(nargin==5) + plot_peak_number=false; + width=1; + group_colors=Read_Colors(n); + elseif(nargin==6) + width=1; + group_colors=Read_Colors(n); + elseif(nargin==7) + width=1; + end + + F=length(peak_indices); + peaks=max(peak_indices); + for i=1:peaks + peak=find(peak_indices==i); + group=group_indices(i); + if(~ismember(group,noise_group)) + x=peak(1)-width:peak(end)+width; + x(x<=0)=[]; + x(x>=F)=[]; + plot(x/samples_per_second,coactivity(x),'-','color',group_colors(group,:),'linewidth',1.5) + if(plot_peak_number) + max_peak=max(coactivity(x)); + text(peak(1)/samples_per_second,max_peak,num2str(i)) + end + end + end +end \ No newline at end of file diff --git a/Plot_Raster.m b/Plot_Raster.m new file mode 100644 index 0000000..c4497e2 --- /dev/null +++ b/Plot_Raster.m @@ -0,0 +1,52 @@ +% Plot Raster +% +% Pérez-Ortega Jesús 2018 +% modified Nov 2018 +% modified Mar 2019 +function Plot_Raster(data,name,spikes,reshape) + switch(nargin) + case 1 + name=''; + spikes = true; + reshape = true; + case 2 + spikes = true; + reshape = true; + case 3 + reshape = true; + end + + [C,F]=size(data); + Set_Figure(['Raster (' name ')'],[0 0 1220 460]); + Set_Axes(['RasterAxes' name],[0 0.34 1 0.66]); + axis([0 F 0.5 C+0.5]); box; hold on + if(spikes) + if(reshape) + if(F<6001) + win = 1; + elseif(F<20001) + win = 10; + elseif(F<100001) + win = 20; + else + win = 50; + end + else + win = 1; + end + + if(sum(data(:,1))>floor(sum(data(:,1)))) + imagesc(data); colormap(flipud(gray)) + else + data = Reshape_Raster(data,win); xlim([0 F/win]) + imagesc(data,[0,1]); colormap([1 1 1; 0 0 0]) + end + else +% data=data>0; +% imagesc(data,[0,1]); colormap([1 1 1; 0.8 0 0]) +% imagesc(data,[-0.2,0.2]); Set_Colormap_Blue_White_Red(); + imagesc(data,[-4 4]); Set_Colormap_Blue_White_Red(); + end + set(gca,'XTicklabel','','XTick',[0 C]) + title(strrep(name,'_','-')) +end \ No newline at end of file diff --git a/Plot_Rich_Club.m b/Plot_Rich_Club.m new file mode 100644 index 0000000..ae61149 --- /dev/null +++ b/Plot_Rich_Club.m @@ -0,0 +1,52 @@ +% Plot rich-club ordering +% +% By Jesús Pérez-Ortega, Jan 2019 + +%function [R_index,R_index_rnd] = Plot_Rich_Club(adjacency,name,save) +function [R_index] = Plot_Rich_Club(adjacency,name,save) + + switch(nargin) + case 1 + name = ''; + save = false; + case 2 + save = false; + end + + % Get basic properties + N = length(adjacency); + links = sum(adjacency); + K = sum(links)/2; + K_mean = 2*K/N; + level = round(sqrt(N*K_mean)); + + % Get rich-club index + Rclub = rich_club_bu(adjacency,level); +% Rclub_rnd = rich_club_bu(makerandCIJ_und(N,K),level); + + % 1,000 iterations + for i = 1:1000 + Rclub_rnds(i,:) = rich_club_bu(makerandCIJ_und(N,K),level); + end + Rclub_rnd_avg = nanmean(Rclub_rnds); + + % Get rich-club ordering + R_index = Rclub./Rclub_rnd_avg; + %R_index_rnd = Rclub_rnd./Rclub_rnd_avg; + + Set_Figure([name ' - Rich club'],[0 0 300 200]); + semilogx(R_index,'o','color',[0 0.5 0],'markersize',10); hold on + %semilogx(R_index_rnd,'or','markersize',10) + plot([1 length(R_index)],[1 1],'--k') + %legend({'Real','ER'}) + xlabel('k'), ylabel('\rho_{ran}(k)') + title([ name ' - Rich-club ordering']) + + ylims = get(gca,'ylim'); + ylims(1) = 0; + ylim(ylims) + + if(save) + Save_Figure([ name ' - Rich-club ordering']) + end +end \ No newline at end of file diff --git a/Plot_Sequence_Graph_By_Width.m b/Plot_Sequence_Graph_By_Width.m new file mode 100644 index 0000000..5bd7895 --- /dev/null +++ b/Plot_Sequence_Graph_By_Width.m @@ -0,0 +1,50 @@ +% Plot sequence graph by width +% +% Pérez-Ortega Jesús - April 2018 + +function adjacency_vectors = Plot_Sequence_Graph_By_Width(sequence,width,name,save,group_colors) + n=max(sequence); + if (nargin==4) + group_colors = Read_Colors(n); + elseif(nargin==3) + save=false; + group_colors = Read_Colors(n); + end + + n_seq=length(sequence); + n_div=floor(n_seq/width); + n_nodes=max(sequence); + + sub_fig=1; + for i=1:n_div + if(~mod(i-1,20)) + Set_Figure(['Sequence graph - ' name ' (' num2str(ceil(i/20)) ')' ],[0 0 1400 800]); + sub_fig=1; + end + subplot(4,5,sub_fig) + if(i*width>n_seq) + seq_i=sequence(((i-1)*width+1):end); + else + seq_i=sequence(((i-1)*width+1):i*width); + end + + adjacency = Get_Adjacency_From_Sequence(seq_i); + Plot_WD_Network(adjacency,[],group_colors); + title(['Peak ' num2str(i)]) + sub_fig=sub_fig+1; + + % Convert directed adjacency matrix to vector + adjacency_complete=zeros(n_nodes); + adjacency_complete(1:length(adjacency),1:length(adjacency))=adjacency; + adjacency_vector=adjacency_complete(:); + adjacency_vectors(i,:)=adjacency_vector; + end + + if(save) + n_win=ceil(n_div/20); + for i=1:n_win + Hold_Figure(['Sequence graph - ' name ' (' num2str(i) ')' ]); + Save_Figure(['Sequence graph - ' name ' (' num2str(i) ').png' ]); + end + end +end diff --git a/Plot_Sequences.m b/Plot_Sequences.m new file mode 100644 index 0000000..b82f17a --- /dev/null +++ b/Plot_Sequences.m @@ -0,0 +1,71 @@ +% Plot all sequences in vertical way +% +% Pérez-Ortega Jesús - May 2018 + +function Plot_Sequences(sequences,division_ms,save,name,colors) + n_colors=max(sequences(:)); + default_colors= Read_Colors(n_colors); + if(nargin==2) + save=false; + name='Sequences'; + colors=default_colors; + elseif(nargin==3) + name='Sequences'; + colors=default_colors; + elseif(nargin==4) + colors=default_colors; + end + + [n,n_in_seq]=size(sequences); + + if(division_ms*n_in_seq>=3000) + times=(division_ms:division_ms:division_ms*n_in_seq)/1000; + elseif(division_ms==0) + times=1:n_in_seq; + else + times=division_ms:division_ms:division_ms*n_in_seq; + end + + Set_Figure(name,[0 0 900 200]); + map=winter(n); + for i=1:n + plot(times,sequences(i,:)+1*i,'color',map(i,:));hold on + end + + sequence_mode=mode(sequences); + errors=0; + for i=1:n_in_seq + errors=errors+length(find(sequences(:,i)~=sequence_mode(i))); + end + + if(division_ms*n_in_seq>=3000) + xlabel('time (s)') + elseif(division_ms==0) + xlabel('peak # (t)') + else + xlabel('time (ms)') + end + ylabel('sequence #') + + errors_percetage=errors/(n*n_in_seq)*100; + + title([name ' - ' num2str(errors) ' errors from the mode - '... + num2str(errors_percetage) '%']) + + % to save + if(save) + Save_Figure(name); + end + + % Sequences in image + Set_Figure([name ' - image'],[0 0 1000 800]); + imagesc(sequences) + colormap(colors) + title([name ' - ' num2str(errors) ' errors from the mode - '... + num2str(errors_percetage) '%']) + + % to save + if(save) + Save_Figure([name ' - image']); + end +end \ No newline at end of file diff --git a/Plot_Similarity.m b/Plot_Similarity.m new file mode 100644 index 0000000..d141ca9 --- /dev/null +++ b/Plot_Similarity.m @@ -0,0 +1,31 @@ +% Plot Similarity +% +% Pérez-Ortega Jesús - March 2018 +function Plot_Similarity(sim,name,tree,sim_method,linkage_method) + if(nargin==3) + sim_method=''; + linkage_method=''; + end + + Set_Figure(['Clustering (' name ')'],[0 0 900 300]); + + % Plot similarity in time function + Set_Axes(['SimAxes' name],[0 0 0.33 1]); + imagesc(sim) + set(gca,'YDir','normal') + title([sim_method ' similarity']) + xlabel('# peak (t)') + + % Plot dendrogram + Set_Axes(['TreeAxes' name],[0.66 0 0.33 1]); + dendrogram(tree,0,'orientation','rigth','ColorThreshold','default'); + Tid = str2num(get(gca,'YTicklabel')); + set(gca,'xtick',[],'ytick',[]) + title([linkage_method ' linkage']) + + % Plot similarity sort by dendrogram + Set_Axes(['SimSortAxes' name],[0.33 0 0.33 1]); + imagesc(sim(Tid,Tid)) + set(gca,'YDir','normal','xtick',[],'ytick',[]) + title('Sort by similarity') +end \ No newline at end of file diff --git a/Plot_Similarity_With_Dendrogram.m b/Plot_Similarity_With_Dendrogram.m new file mode 100644 index 0000000..467b629 --- /dev/null +++ b/Plot_Similarity_With_Dendrogram.m @@ -0,0 +1,39 @@ +% Plot Similarity +% +% Pérez-Ortega Jesús - Nov 2018 +function Plot_Similarity_With_Dendrogram(data,name,sim_method,linkage_method) + if(nargin==1) + name=''; + sim_method='euclidean'; + linkage_method='ward'; + end + + [similarity, distance]= Get_Peaks_Similarity(data,sim_method); + if(~isempty(similarity)) + tree=linkage(squareform(distance,'tovector'),linkage_method); + else + error('Similarity matrix could not be created.') + end + + Set_Figure(['Clustering (' name ')'],[0 0 900 300]); + + % Plot similarity in time function + Set_Axes(['SimAxes' name],[0 0 0.33 1]); + imagesc(similarity) + set(gca,'YDir','normal') + title([sim_method ' similarity']) + xlabel('# peak (t)') + + % Plot dendrogram + Set_Axes(['TreeAxes' name],[0.66 0 0.33 1]); + dendrogram(tree,0,'orientation','rigth','ColorThreshold','default'); + Tid=str2num(get(gca,'YTicklabel')); + %set(gca,'xtick',[],'ytick',[]) + title([linkage_method ' linkage']) + + % Plot similarity sort by dendrogram + Set_Axes(['SimSortAxes' name],[0.33 0 0.33 1]); + imagesc(similarity(Tid,Tid)) + set(gca,'YDir','normal','xtick',[],'ytick',[]) + title('Sort by similarity') +end \ No newline at end of file diff --git a/Plot_Spikes.m b/Plot_Spikes.m new file mode 100644 index 0000000..8a96713 --- /dev/null +++ b/Plot_Spikes.m @@ -0,0 +1,35 @@ +% Plot spikes +% +% Jesús Pérez-Ortega, Nov 2018 + +function Plot_Spikes(spikes,value,color) + switch(nargin) + case 1 + color = [0 0 0]; + value = 1; + case 2 + color = [0 0 0]; + end + + samples = length(spikes); + + id = find(spikes); + n_spikes = length(id); + + time=repmat(id',1,2); + + spikes_1=ones(n_spikes,1)*value-1; + spikes_2=ones(n_spikes,1)*value; + spikes=[spikes_1 spikes_2]; + + plot(time',spikes','color',color) + if(samples<1000) + label = [num2str(samples) ' ms']; + elseif(samples<=60000) + label = [num2str(samples/1000) ' s']; + else + label = [num2str(samples/60000) ' min']; + end + + set(gca,'ytick',[],'xtick',[0 samples],'xticklabel',{'0',label}) +end \ No newline at end of file diff --git a/Plot_Spikes_From_ISI.m b/Plot_Spikes_From_ISI.m new file mode 100644 index 0000000..fa5b14d --- /dev/null +++ b/Plot_Spikes_From_ISI.m @@ -0,0 +1,24 @@ +% Plot spikes from ISI +% +% Jesús Pérez-Ortega, Nov 2018 + +function Plot_Spikes_From_ISI(isi) + + n_spikes = length(isi); + + % Get spikes from ISI + if(size(isi,2)==1) + time=repmat(cumsum(isi),1,2); + else + time=repmat(cumsum(isi)',1,2); + end + + spikes_1=zeros(n_spikes,1); + spikes_2=ones(n_spikes,1); + spikes=[spikes_1 spikes_2]; + + plot(time',spikes','k') +% title(['Spikes = ' num2str(n_spikes)]) +% xlabel('time [s]') + set(gca,'ytick',[]) +end \ No newline at end of file diff --git a/Plot_States_By_Width.m b/Plot_States_By_Width.m new file mode 100644 index 0000000..a4ebdd3 --- /dev/null +++ b/Plot_States_By_Width.m @@ -0,0 +1,53 @@ +% Plot Peak states +% +% Pérez-Ortega Jesús - March 2018 + +function seqs = Plot_States_By_Width(sequence,width,name,save,group_colors) + n=max(sequence); + if (nargin==4) + group_colors = Read_Colors(n); + elseif(nargin==3) + save=false; + group_colors = Read_Colors(n); + end + + states=max(sequence); + n_seq=length(sequence); + n_div=floor(n_seq/width); + + sub_fig=1; + for i=1:n_div + if(~mod(i-1,20)) + Set_Figure(['Sequences by width - ' name ' (' num2str(ceil(i/20)) ')' ],[0 0 1400 800]); + sub_fig=1; + end + subplot(4,5,sub_fig) + if(i*width>n_seq) + seq_i=sequence(((i-1)*width+1):end); + seqs(i,1:length(seq_i))=seq_i; + else + seq_i=sequence(((i-1)*width+1):i*width); + seqs(i,:)=seq_i; + end + + plot(seq_i,'-k');hold on + for j=1:states + idx=find(seq_i==j); + ns=length(idx); + plot(idx,repmat(j,ns,1),'o','color',group_colors(j,:),'markersize',10,... + 'linewidth',2) + end + ylim([1 states]) + title(['Sequence ' num2str(i)]) + + sub_fig=sub_fig+1; + end + + if(save) + n_win=ceil(n_div/20); + for i=1:n_win + Hold_Figure(['Sequences by width - ' name ' (' num2str(i) ')']); + Save_Figure(['Sequences by width - ' name ' (' num2str(i) ')']); + end + end +end diff --git a/Plot_States_Sequence.m b/Plot_States_Sequence.m new file mode 100644 index 0000000..047b769 --- /dev/null +++ b/Plot_States_Sequence.m @@ -0,0 +1,54 @@ +% Plot sequence +% +% P?rez-Ortega Jes?s - March 2018 +% modified Aug-2018 +function count_states = Plot_States_Sequence(sequence,noise_group,name,group_colors) + n=max(sequence); + default_colors= Read_Colors(n); + switch(nargin) + case 1 + noise_group=0; + name='untitled'; + group_colors=default_colors; + case 2 + name='untitled'; + group_colors=default_colors; + case 3 + group_colors=default_colors; + end + groups=max(sequence); + + Set_Figure(['States Sequence - ' name],[0 0 1220 200]); + Set_Axes('States sequence',[-.05 0 .85 1]); hold on + for i=noise_group + sequence=sequence(sequence~=i); + end + for i=1:max(sequence) + idx=find(sequence==i); + ns=length(idx); + count_states(i)=ns; + plot(idx,repmat(i,ns,1),'o','color',group_colors(i,:),'markersize',10,... + 'linewidth',2) + end + plot(sequence,'-k') + ylim([0 groups+1]); ylabel('State') + l=length(sequence); + xlim([0 l+1]); xlabel('# peak') + title(['States sequence - ' strrep(name,'_','-')]) + + Set_Axes('States count',[0.71 0.1 .15 .85]); hold on + for i=1:max(sequence) + bar(i,count_states(i),'FaceColor',group_colors(i,:)); hold on + end + set(gca,'xtick',[]); ylabel('count'); + view(90,-90) + + n=length(sequence)-1; + transitions=sum(diff(sequence)~=0)/n; + same=1-transitions; + Set_Axes('transitions',[0.86 0 .14 1]); hold on + bar([1 2],[same transitions]); hold on + set(gca,'xtick',1:2,'xticklabel',{'same','between'}); + xlim([.5 2.5]); ylim([0 1]) + title('transitions'); ylabel('fraction'); +end \ No newline at end of file diff --git a/Plot_WD_Network.m b/Plot_WD_Network.m new file mode 100644 index 0000000..f26da66 --- /dev/null +++ b/Plot_WD_Network.m @@ -0,0 +1,70 @@ +% Plot weighted and directed network with loops +% +% Pérez-Ortega Jesús - april 2018 +% modified june 2018 + +function Plot_WD_Network(adjacency,xy,xy_colors,node_size,node_number) + n=length(adjacency); + if (nargin==4) + node_number=false; + elseif (nargin==3) + node_number=false; + node_size=35; + elseif (nargin==2) + node_number=false; + node_size=35; + xy_colors = Read_Colors(n); + elseif (nargin==1) + node_number=false; + node_size=35; + xy_colors = Read_Colors(n); + end + + if(nargin==1||isempty(xy)) + nodes=length(adjacency); + xy=Get_Circular_XY(nodes); + end + + % Plot edges with arrow + C=length(adjacency); + size_factor=5/sum(adjacency(:)); + radius=.15; % Arrow property + hold on + for a=1:C + for b=a:C + if (adjacency(a,b)) + line_width=adjacency(a,b)*size_factor+.5; + length_arrow=3+0.5*line_width; + Plot_Edge(xy(a,:),xy(b,:),radius,length_arrow,line_width); + end + end + end + for a=1:C-1 + for b=a+1:C + if (adjacency(b,a)) + line_width=adjacency(b,a)*size_factor+.5; + length_arrow=3+0.5*line_width; + Plot_Edge(xy(b,:),xy(a,:),radius,length_arrow,line_width); + end + end + end + if(sum(adjacency(:))) + sizes_in=sum(adjacency)./sum(adjacency(:)).*35+node_size; + else + sizes_in=ones(1,C)*.35+node_size; + end + + % Plot nodes + for i=1:C + plot(xy(i,1),xy(i,2),'.k','MarkerSize',sizes_in(i)) + plot(xy(i,1),xy(i,2),'.','color',xy_colors(i,:),'MarkerSize',sizes_in(i)*.7) + if(node_number) + text(xy(i,1)*1.1,xy(i,2)*1.1,num2str(i),'HorizontalAlignment','Center') + end + end + set(gca,'xtick',[],'ytick',[]) + xlim([-1.5 1.5]) + ylim([-1.5 1.5]) + box on + pbaspect([1 1 1]) +end \ No newline at end of file diff --git a/Plot_WU_Network.m b/Plot_WU_Network.m new file mode 100644 index 0000000..438308b --- /dev/null +++ b/Plot_WU_Network.m @@ -0,0 +1,105 @@ +function Plot_WU_Network(adjacency,xy,xy_colors,curved,link_color,node_size,node_number) +% Plot weighted and undirected network with loops +% +% Plot_WU_Network(adjacency,curved,xy,xy_colors,link_color,node_size,node_number) +% +% Pérez-Ortega Jesús - april 2018 +% Modified Jan 2019 +% Modified April 2019 + +n=length(adjacency); +switch (nargin) + case 6 + node_number=false; + case 5 + node_number=false; + node_size=10; + case 4 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + case 3 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + curved=0; + case 2 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + curved=0; + xy_colors = Read_Colors(n); + case 1 + node_number=false; + node_size=10; + link_color=[0.5 0.5 0.5]; + curved=0; + xy_colors = Read_Colors(n); + xy = []; +end + +if(size(xy_colors,1)==1) + xy_colors=repmat(xy_colors,n,1); +end + +lims = []; +if(nargin==1||isempty(xy)) + nodes=length(adjacency); + xy=Get_Circular_XY(nodes); + lims = [-1.5 1.5]; +% xy = Get_Force_XY(adjacency); +end + +C=length(adjacency); + +% Plot edges +if(sum(adjacency(:))) + line_widths = zeros(C); + line_widths(adjacency>0) = Scale(adjacency(adjacency>0),0.5,5); + hold on + for a=1:C + for b=a:C + if (adjacency(a,b)) + length_arrow=0; + Plot_Edge(xy(a,:),xy(b,:),curved,length_arrow,line_widths(a,b),... + link_color); + end + end + end +end + +links=sum(adjacency); +if(length(node_size)==1) + if(sum(links) && node_size) + sizes_in=Scale(sum(adjacency),node_size,node_size+40); + else + sizes_in=ones(1,C)*30; + end +else + sizes_in=Scale(node_size,10,50); +end + +% Plot nodes +for i=1:C + if(links(i)) + plot(xy(i,1),xy(i,2),'.k','MarkerSize',sizes_in(i)+5) + plot(xy(i,1),xy(i,2),'.','color',xy_colors(i,:),'MarkerSize',sizes_in(i)) + if(node_number) + %text(xy(i,1)*1.1,xy(i,2)*1.1,num2str(i),'HorizontalAlignment','Center') + text(xy(i,1),xy(i,2),num2str(i),'HorizontalAlignment','left') + end + else + plot(xy(i,1),xy(i,2),'.','color',mean([0.5 0.5 0.5; xy_colors(i,:)]),... + 'MarkerSize',sizes_in(i)*.2) + end +end +set(gca,'xtick',[],'ytick',[],'xcolor',[1 1 1],'ycolor',[1 1 1]) +if(lims) + xlim(lims) + ylim(lims) +else + xlim([min(xy(:,1)) max(xy(:,1))]) + ylim([min(xy(:,2)) max(xy(:,2))]) +end + +pbaspect([1 1 1]) diff --git a/RV_coefficient.m b/RV_coefficient.m new file mode 100644 index 0000000..6928d62 --- /dev/null +++ b/RV_coefficient.m @@ -0,0 +1,12 @@ +% RV coefficient +% +% Compare the correlation between matrices of differents column size. +% tr(XX'YY')/sqrt(tr((XX')^2)*tr((YY')^2)) +% +% Pérez-Ortega Jesús march-2018 + +function RV = RV_coefficient(X,Y) + XX=X*X'; + YY=Y*Y'; + RV=trace(XX*YY)/sqrt(trace(abs(XX^2))*trace(abs(YY^2))); +end \ No newline at end of file diff --git a/Read_Colors.m b/Read_Colors.m new file mode 100644 index 0000000..8c1fd71 --- /dev/null +++ b/Read_Colors.m @@ -0,0 +1,45 @@ +% Return colors +% +% Pérez-Ortega Jesús - May 2018 + +function colors = Read_Colors(num_colors) + if(nargin==0) + num_colors=10; + end + if(num_colors<=10) + colors=[0.9 0.0 0.0;... % red + 0.5 0.8 0.0;... % green + 0.0 0.5 1.0;... % sky blue + 0.9 0.9 0.0;... % yellow + 0.9 0.0 0.9;... % magenta + 0.0 0.9 0.9;... % cyan + 1.0 0.5 0.0;... % orange + 0.5 0.0 1.0;... % purple + 1.0 0.7 0.7;... % rose + 0.6 0.3 0.0;... % brown + 0.95 0.5 0.5;... % red light + 0.75 0.9 0.5;... % green light + 0.5 0.75 1.0;... % sky blue light + 0.95 0.95 0.5;... % yellow light + 0.95 0.5 0.95;... % magenta light + 0.5 0.95 0.95;... % cyan light + 1.0 0.75 0.5;... % orange light + 0.75 0.5 1.0;... % purple light + 1.0 0.85 0.85;... % rose light + 0.8 0.65 0.5;... % brown light + 0.4 0.0 0.0;... % red dark + 0.0 0.3 0.0;... % green dark + 0.0 0.0 0.5;... % sky blue dark + 0.4 0.4 0.0;... % yellow dark + 0.4 0.0 0.4;... % magenta dark + 0.0 0.4 0.4;... % cyan dark + 0.5 0.0 0.0;... % orange dark + 0.0 0.0 0.5;... % purple dark + 0.5 0.2 0.2;... % rose dark + 0.2 0.1 0.0]; % brown dark + colors=colors(1:num_colors,:); + else + colors=hsv(num_colors); + end + +end \ No newline at end of file diff --git a/Remove_Oscillations.m b/Remove_Oscillations.m new file mode 100644 index 0000000..d1475fe --- /dev/null +++ b/Remove_Oscillations.m @@ -0,0 +1,15 @@ +% Remove smoothed signal from original +% +% Remove the smoothed loess signal from the original +% +% By Jes?s P?rez-Ortega april-2018 + +function [smoothed,removed]= Remove_Oscillations(coactivity,percentage) + if(nargin==1) + %percentage=0.2; + percentage=0.01; % 15 min at 1 kH --> 9 s + end + + removed=smooth(coactivity,percentage,'loess'); % quadratic fit + smoothed=coactivity-removed; +end \ No newline at end of file diff --git a/Reshape_Raster.m b/Reshape_Raster.m new file mode 100644 index 0000000..9674454 --- /dev/null +++ b/Reshape_Raster.m @@ -0,0 +1,21 @@ +% Reshape raster +% +% Binarize the raster by a given window +% +% Pérez-Ortega Jesús - May 2018 + +function new_raster=Reshape_Raster(raster,window) + [c,n]=size(raster); + + if(window==1) + new_raster = raster; + else + new_n=floor(n/window); + new_raster=zeros(c,new_n); + for i=1:new_n + ini=(i-1)*window+1; + fin=i*window; + new_raster(:,i)=logical(sum(raster(:,ini:fin),2)); + end + end +end \ No newline at end of file diff --git a/Save_Figure.m b/Save_Figure.m new file mode 100644 index 0000000..c9f19e5 --- /dev/null +++ b/Save_Figure.m @@ -0,0 +1,43 @@ +% Save current figure with default style +% +% Pérez-Ortega Jesús - June 2018 +% modified Feb 2019 +function Save_Figure(name,format) + + if (nargin==1) + s.Format = 'png'; + else + s.Format = format; + end + s.Version='1'; + s.Preview='none'; + s.Width='auto'; + s.Height='auto'; + s.Units='inches'; + s.Color='rgb'; + s.Background='w'; + s.FixedFontSize='14'; + s.ScaledFontSize='auto'; + s.FontMode='fixed'; + s.FontSizeMin='8'; + s.FixedLineWidth='1'; + s.ScaledLineWidth='auto'; + s.LineMode='none'; + s.LineWidthMin='0.5'; + s.FontName='auto'; + s.FontWeight='auto'; + s.FontAngle='auto'; + s.FontEncoding='latin1'; + s.PSLevel='3'; + s.Renderer='auto'; + s.Resolution='auto'; + s.LineStyleMap='none'; + s.ApplyStyle='0'; + s.Bounds='loose'; + s.LockAxes='on'; + s.LockAxesTicks='off'; + s.ShowUI='on'; + s.SeparateText='off'; + + hgexport(gcf,[name '.' s.Format ],s); +end \ No newline at end of file diff --git a/Save_Raster_By_Windows.m b/Save_Raster_By_Windows.m new file mode 100644 index 0000000..968399d --- /dev/null +++ b/Save_Raster_By_Windows.m @@ -0,0 +1,20 @@ +% Save figures second by second from raster +% +% Pérez-Ortega Jesús - May 2018 + +function Save_Raster_By_Windows(name,samples_per_second,window_sec,final_sec) + + Hold_Axes(['CoactiveAxes' name]); + set(gca,'xtick',0:window_sec:final_sec) + set(gca,'xticklabel',0:window_sec:final_sec) + xlabel('time (s)') + for i=1:window_sec:final_sec + Hold_Axes(['RasterAxes' name]); + xlim([i-1 i+window_sec-1]*samples_per_second) + Hold_Axes(['CoactiveAxes' name]); + xlim([i-1 i+window_sec-1]) + + % Configure and save image + Save_Figure([name '_' num2str(i-1,'%.2f') '-' num2str(i+window_sec-1,'%.2f') 'sec']); + end +end diff --git a/Save_Raster_Min_By_Min.m b/Save_Raster_Min_By_Min.m new file mode 100644 index 0000000..09380e8 --- /dev/null +++ b/Save_Raster_Min_By_Min.m @@ -0,0 +1,16 @@ +% Save figures minute by minute from raster +% +% Pérez-Ortega Jesús - March 2018 + +function Save_Raster_Min_By_Min(data_name,samples_per_minute,initial_min,final_min) + initial_min=initial_min+1; + for i=initial_min:final_min + Hold_Axes(['RasterAxes' data_name]); + xlim([(i-1)*samples_per_minute+1 i*samples_per_minute]) + Hold_Axes(['CoactiveAxes' data_name]); + xlim([(i-1)*60 i*60]) + + % Configure and save image + Save_Figure([data_name '_' num2str(i-1,'%.2i') '-' num2str(i,'%.2i') 'min']); + end +end diff --git a/Scale.m b/Scale.m new file mode 100644 index 0000000..3cfab2b --- /dev/null +++ b/Scale.m @@ -0,0 +1,36 @@ +function data_out = Scale(data,min_value,max_value) +% Scale data between 0 and 1 +% +% data_out = Scale(data,min_value,max_value) +% +% Jesus Perez-Ortega April-19 + +if nargin==1 + min_value = 0; + max_value = 1; +end + +% Change to double +data_out = double(data); + +% Substract minimum +min_data = min(data_out(:)); +data_out = data_out-min_data; + +% Divide by maximum +max_data = max(data_out(:)); +if max_data + data_out = data_out./max_data; + + % Multiply by factor + factor = max_value-min_value; + data_out = data_out.*factor; + + % Add minimum + data_out = data_out+min_value; +else + data_out = min_value*ones(size(data)); +end + + + diff --git a/Set_Axes.m b/Set_Axes.m new file mode 100644 index 0000000..e23082b --- /dev/null +++ b/Set_Axes.m @@ -0,0 +1,5 @@ +%% Set Axes +function Set_Axes(AxesName,Position) + axes('outerposition',Position) + set(gca,'Tag',AxesName) +end \ No newline at end of file diff --git a/Set_Colormap_Blue_White_Red.m b/Set_Colormap_Blue_White_Red.m new file mode 100644 index 0000000..81b8da5 --- /dev/null +++ b/Set_Colormap_Blue_White_Red.m @@ -0,0 +1,11 @@ +% Set colormap blue-white-red +% +% By Jesús Pérez-Ortega jan-2018 + +function Set_Colormap_Blue_White_Red() + bluemap=colormap(gray(32))+repmat([0 0 1],32,1); + bluemap(bluemap>1)=1; + redmap=colormap(flipud(gray(32)))+repmat([1 0 0],32,1); + redmap(redmap>1)=1; + colormap(gca,[bluemap;redmap]) +end \ No newline at end of file diff --git a/Set_Figure.m b/Set_Figure.m new file mode 100644 index 0000000..5c82403 --- /dev/null +++ b/Set_Figure.m @@ -0,0 +1,23 @@ +function fig = Set_Figure(title_name,position) +%% Set Figure +% +% Create a figure with basic properties: title, size and position. If the +% figure already exist, it is only cleaned. +% +% fig = Set_Figure(title_name,position) +% +% position = [x y width height]) +% +% Jesus Perez-Ortega modififed April-19 + +h = findobj('name',title_name); +if isempty(h) + h = figure('name',title_name,'numbertitle','off','position',position); +else + figure(h); +end +clf + +if nargout + fig = h; +end diff --git a/Set_Label_Time.m b/Set_Label_Time.m new file mode 100644 index 0000000..3eb76d5 --- /dev/null +++ b/Set_Label_Time.m @@ -0,0 +1,29 @@ +function Set_Label_Time(samples,sample_frequency) +% Set convenient label of time +% +% Set_Label_Time(samples,sample_frequency) +% +% Jesus Perez-Ortega April-19 + +% less than 30 seconds +if(samples/sample_frequency<10) + step = 1; + xlabel('Time (s)'); +elseif(samples/sample_frequency<30) + step = 3; + xlabel('Time (s)'); +elseif(samples/sample_frequency/60<3) + step = 10; + xlabel('Time (s)'); +elseif(samples/sample_frequency/60<60) + step = 60; + xlabel('Time (min)'); +else + step = 60*60; + xlabel('Time (h)'); +end + +set(gca,'box','off','xtick',0:step*sample_frequency:samples,... + 'xticklabel',0:samples/sample_frequency/step) + + diff --git a/Shuffle_Test.m b/Shuffle_Test.m new file mode 100644 index 0000000..5b1c467 --- /dev/null +++ b/Shuffle_Test.m @@ -0,0 +1,113 @@ +% Test for significant number of coactive neurons +% +% P?rez Ortega Jes?s E. - March 2018 +% Modified April 2018 + +function Th = Shuffle_Test(raster,smooth_window,n,shuffle_method,alpha,... + integer_interval,remove_oscillations,z_score,only_mean,zscore_window,plot_shuffled_figures) + + if(nargin==7) + plot_shuffled_figures=false; + elseif(nargin==6) + plot_shuffled_figures=false; + remove_oscillations=false; + elseif(nargin==5) + plot_shuffled_figures=false; + remove_oscillations=false; + integer_interval=true; + elseif(nargin==4) + plot_shuffled_figures=false; + remove_oscillations=false; + integer_interval=true; + alpha=0.05; + elseif(nargin==3) + plot_shuffled_figures=false; + remove_oscillations=false; + integer_interval=true; + alpha=0.05; + shuffle_method='time_shift'; + end + + [c,f]=size(raster); + smooth_coactivity = Get_And_Filter_Coactivity(raster,smooth_window); + + % Remove Oscillations + if(remove_oscillations) + smooth_coactivity = Remove_Oscillations(smooth_coactivity); + end + + % Z-score + if(z_score) + smooth_coactivity=Z_Score_Coactivity(smooth_coactivity,zscore_window,only_mean); + end + + if(integer_interval) + interval=1:c; + PS=zeros(n,c); + else + interval=0:.1:max(smooth_coactivity); + PS=zeros(n,length(interval)); + end + + % Plot only if necessary + if(plot_shuffled_figures) + Set_Figure('Raster | real VS shuffled',[0 0 1000 800]); + subplot(5,1,1) + imagesc(raster) + title('Observed') + colormap([1 1 1; 0 0 0]) + Set_Figure('Coactivity | real VS shuffled',[0 0 1000 800]); + subplot(5,1,1) + plot(smooth_coactivity) + title('Observed') + y_limits=get(gca,'ylim'); + end + + % SHUFFLED data + for i=1:n + R_shuffled = shuffle(raster,shuffle_method); % Get raster shuffled + smooth_co = Get_And_Filter_Coactivity(R_shuffled,smooth_window); % Get coactivity + % Remove Oscillations + if(remove_oscillations) + smooth_co = Remove_Oscillations(smooth_co); + end + + % Z-score + if(z_score) + smooth_co=Z_Score_Coactivity(smooth_co,0,only_mean); + end + + HS = histc(smooth_co,interval); % Histogram of coactive cells + %PS(i,:) = cumsum(HS)/f; % Synchrony probability + PS(i,:) = cumsum(HS)/max(cumsum(HS)); % Synchrony probability + + % Plot only if necessary + if(plot_shuffled_figures && i<5) + Hold_Figure('Raster | real VS shuffled'); + subplot(5,1,i+1) + imagesc(R_shuffled) + title(['Shuffled ' num2str(i)]) + Hold_Figure('Coactivity | real VS shuffled') + subplot(5,1,i+1) + plot(smooth_co) + title(['Shuffled ' num2str(i)]) + ylim(y_limits) + end + end + PSmean=mean(PS); % Mean of Probability + + % Set threshold from Probability distribution < alpha + th_idx=find(PSmean>=(1-alpha),1,'first'); + Th=interval(th_idx); + + % Plot distribution shuffled + Set_Figure('Shuffled test',[0 0 600 300]); + plot(interval,PSmean,'o-b'); hold on + title({['Cumulative distribution of raster shuffled (' num2str(n) ' iterations; method '... + strrep(shuffle_method,'_','-') ')'];['th = ' num2str(Th) ' at alpha=' num2str(alpha)]}) + ylabel('P(x)') + xlabel('x') + if(~isnan(Th)) + xlim([0 Th*3]) + end +end \ No newline at end of file diff --git a/Shuffle_Test_For_Every_Link.m b/Shuffle_Test_For_Every_Link.m new file mode 100644 index 0000000..2765be1 --- /dev/null +++ b/Shuffle_Test_For_Every_Link.m @@ -0,0 +1,46 @@ +% Weight threshold +% +% Montecarlo for determining weight threshold in weighted matrix. +% +% Threshold is determined when the edges reach the 1-alpha percentage in +% its randomized versions (N times). +% +% Adjacency matrix is determined by coactivity, weight of connection means +% the times which are two nodes coactive. +% +% w_ths = Shuffle_Test_For_Every_Link(raster,shuffle_method,connectivity_method,iterations,alpha) +% +% Jesus E. Perez-Ortega - Sep 2018 +% modified june 2018 +% modified nov 2018 + +function w_ths = Shuffle_Test_For_Every_Link(raster,shuffle_method,connectivity_method,iterations,alpha) + + % Data size + n = size(raster,1); + adjacency_shuffle=zeros(iterations,n,n); + + for i=1:iterations + % Shuffled version + raster_shuffled = shuffle(raster,shuffle_method); + + % Adjacent from shuffled version + adjacency_shuffle(i,:,:)=Get_Adjacency_From_Raster(raster_shuffled,connectivity_method); + end + + % Cumulative distribution of individual link + w_ths = zeros(n); + for j=1:n-1 + for k=j+1:n + try + y=hist(adjacency_shuffle(:,j,k),0:max(adjacency_shuffle(:,j,k))); + cum=cumsum(y)/max(cumsum(y)); + th=find(cum>(1-alpha),1,'first')-1; + w_ths(j,k)=th; + w_ths(k,j)=w_ths(j,k); + catch + disp(th) + end + end + end +end \ No newline at end of file diff --git a/Shuffle_Test_For_Weighted_Matrix.m b/Shuffle_Test_For_Weighted_Matrix.m new file mode 100644 index 0000000..5807894 --- /dev/null +++ b/Shuffle_Test_For_Weighted_Matrix.m @@ -0,0 +1,67 @@ +% Weight threshold +% +% Montecarlo for determining weight threshold in weighted matrix. +% +% Threshold is determined when the edges reach the 1-alpha percentage in +% its randomized versions (N times). +% +% Adjacency matrix is determined by coactivity, weight of connection means +% the times which are two nodes coactive. +% +% W_Th = WeightTh(Data, N, alpha) +% +% ..:: by Jes?s E. P?rez-Ortega ::.. Jun-2013 +% modified june 2018 + +function w_threshold = Shuffle_Test_For_Weighted_Matrix(raster,shuffle_method,connectivity_method,iterations,alpha) + + % Data size + samples=size(raster,2); + + for i=1:iterations + % Shuffled version + raster_shuffled = shuffle(raster,shuffle_method); % Get raster shuffled + + % Adjacent from shuffled version + adjacency_shuffle=Get_Adjacency_From_Raster(raster_shuffled,connectivity_method); + + % Number of edges at each threshold + edges_th_shuffled=zeros(1,100); + if(strcmp(connectivity_method,'jaccard')) + interval=0:0.001:1; + else + interval=0:samples; + end + j=1; + for th=interval + edges_th_shuffled(j)=sum(sum(adjacency_shuffle>th)); + if(~edges_th_shuffled(j)) + break; + end + j=j+1; + end + + % Coactivation probability + p(i,1:length(edges_th_shuffled)) = cumsum(edges_th_shuffled)/max(cumsum(edges_th_shuffled)); + end + p(p==0)=1; + p_mean=mean(p); + interval=interval(1:length(p_mean)); + % Weight threshold + idx=find(p_mean>(1-alpha),1,'first'); + w_threshold=interval(idx); + + % Plot distribution shuffled + Set_Figure('Shuffled weigthed matrix test',[0 0 600 300]); + %plot(interval/samples,p_mean,'o-b'); + plot(interval,p_mean,'o-b'); + title({['Cumulative distribution of raster shuffled (' num2str(iterations) ' iterations; method '... + strrep(shuffle_method,'_','-') ')'];['th = ' num2str(w_threshold) ' at alpha=' num2str(alpha)]}) + ylabel('P(x)') + xlabel('x') + ylim([0 1.05]) + if(~isnan(w_threshold) && w_threshold>0) + xlim([0 (w_threshold*2)]) + end + %w_threshold=w_threshold/100; +end \ No newline at end of file diff --git a/Sort_Activation_Position.m b/Sort_Activation_Position.m new file mode 100644 index 0000000..4eab137 --- /dev/null +++ b/Sort_Activation_Position.m @@ -0,0 +1,63 @@ +% Sort activation position +% +% By Jesús Pérez-Ortega, Jan 2019 + +function [sequences_sorted,position,probabilities] = Sort_Activation_Position(sequences) + [trials,n] = size(sequences); + + % without "neuron 0" + % get the probability of activation + for i = 1:n + p = sum(sequences==i)/trials; + probabilities(i,:) = p; + end + + % Set the activation position + % C + [~,position] = Sort_Raster(probabilities); + + % sort the sequence + sequences_sorted = zeros(trials,n); + for i = 1:n + sequences_sorted(sequences==position(i))=i; + end + + % sort preference + probabilities = probabilities(position,:); + +% % set activation position (other methods) +% % A +% remaining = 1:n; +% for i = 1:n +% [~,id] = max(preferences(remaining,i)); +% position(i) = remaining(id); +% remaining = setdiff(remaining,remaining(id)); +% end +% +% % B +% for i = 1:n +% [~,position(i)] = max(preferences(i,:)); +% end +% [~,position] = sort(position); +end + +% % with "neuron 0" probability +% % get the probability of activation +% for i = 0:n +% p = sum(sequences==i)/trials; +% probabilities(i+1,:) = p; +% end +% +% % Set the activation position +% [~,position] = Sort_Raster(probabilities(2:end,:)); +% position = [1 position+1]; +% +% % sort the sequence +% sequences_sorted = zeros(trials,n); +% for i = 0:n +% sequences_sorted(sequences==position(i+1)-1)=i; +% end +% +% % sort preference +% probabilities = probabilities(position,:); +% position = position(2:end)-1; \ No newline at end of file diff --git a/Sort_Raster.m b/Sort_Raster.m new file mode 100644 index 0000000..d13418b --- /dev/null +++ b/Sort_Raster.m @@ -0,0 +1,21 @@ +function [sorted,sorted_id] = Sort_Raster(raster,direction) +% Sort raster +% +% [sorted,sorted_id] = Sort_Raster(raster,direction) +% +% Jesús Pérez-Ortega - Dic 2018 +% Modified Jan 2019 + + if(nargin==1) + direction = 'descend'; + end + + sorted = raster; + sorted_id = 1:size(raster,1); + n_seq = size(sorted,2); + for i = n_seq:-1:1 + [~,id] = sort(sorted(:,i),direction); + sorted = sorted(id,:); + sorted_id = sorted_id(id); + end +end \ No newline at end of file diff --git a/Z_Score_Coactivity.m b/Z_Score_Coactivity.m new file mode 100644 index 0000000..bfb1a40 --- /dev/null +++ b/Z_Score_Coactivity.m @@ -0,0 +1,51 @@ +% Get Z-score of coactivity with an specific time window +% +% +% By Jes?s P?rez-Ortega jan-2018 +% modified april-2018 +% modified august-2018 + +function z_score_coactivity = Z_Score_Coactivity(coactivity,zscore_window,only_mean) + if(nargin<2) + zscore_window=length(coactivity); + only_mean=false; + end + + if(zscore_window==0) + zscore_window=length(coactivity); + end + + z_score_coactivity=zeros(size(coactivity)); + F=length(coactivity); + if(F>zscore_window) + n_final=round(F/zscore_window); + for i=1:n_final + inicio=zscore_window*(i-1)+1; + fin=inicio+zscore_window-1; + if(fin>F) + fin=F; + end + if(only_mean) + z_score_coactivity(inicio:fin)=coactivity(inicio:fin)-mean(coactivity(inicio:fin)); + else + z_score_coactivity(inicio:fin)=(coactivity(inicio:fin)-mean(coactivity(inicio:fin)))/std(coactivity(inicio:fin)); + end + end + n_Smooth=length(z_score_coactivity); + if(fin=2 %degree must be at least 2 + S=G(V,V); + C(u)=sum(S(:))/(k^2-k); + end +end \ No newline at end of file diff --git a/density_und.m b/density_und.m new file mode 100644 index 0000000..87debf4 --- /dev/null +++ b/density_und.m @@ -0,0 +1,28 @@ +function [kden,N,K] = density_und(CIJ) +% DENSITY_UND Density +% +% kden = density_und(CIJ); +% [kden,N,K] = density_und(CIJ); +% +% Density is the fraction of present connections to possible connections. +% +% Input: CIJ, undirected (weighted/binary) connection matrix +% +% Output: kden, density +% N, number of vertices +% K, number of edges +% +% Notes: Assumes CIJ is undirected and has no self-connections. +% Weight information is discarded. +% +% +% Olaf Sporns, Indiana University, 2002/2007/2008 + + +% Modification history: +% 2009-10: K fixed to sum over one half of CIJ [Tony Herdman, SFU] + +N = size(CIJ,1); +K = nnz(triu(CIJ)); +kden = K/((N^2-N)/2); + diff --git a/dist_corr.m b/dist_corr.m new file mode 100644 index 0000000..9774871 --- /dev/null +++ b/dist_corr.m @@ -0,0 +1,6 @@ +function d = dist_corr(x,y) + A=x-mean(x); + B=y-mean(y); + + d = 1 - sum(A.*B)/sqrt(sum(A.*A).*sum(B.*B)); +end \ No newline at end of file diff --git a/distance_bin.m b/distance_bin.m new file mode 100644 index 0000000..5210b78 --- /dev/null +++ b/distance_bin.m @@ -0,0 +1,45 @@ +function D=distance_bin(A) +%DISTANCE_BIN Distance matrix +% +% D = distance_bin(A); +% +% The distance matrix contains lengths of shortest paths between all +% pairs of nodes. An entry (u,v) represents the length of shortest path +% from node u to node v. The average shortest path length is the +% characteristic path length of the network. +% +% Input: A, binary directed/undirected connection matrix +% +% Output: D, distance matrix +% +% Notes: +% Lengths between disconnected nodes are set to Inf. +% Lengths on the main diagonal are set to 0. +% +% Algorithm: Algebraic shortest paths. +% +% +% Mika Rubinov, U Cambridge +% Jonathan Clayden, UCL +% 2007-2013 + +% Modification history: +% 2007: Original (MR) +% 2013: Bug fix, enforce zero distance for self-connections (JC) + +A=double(A~=0); %binarize and convert to double format + +l=1; %path length +Lpath=A; %matrix of paths l +D=A; %distance matrix + +Idx=true; +while any(Idx(:)) + l=l+1; + Lpath=Lpath*A; + Idx=(Lpath~=0)&(D==0); + D(Idx)=l; +end + +D(~D)=inf; %assign inf to disconnected nodes +D(1:length(A)+1:end)=0; %clear diagonal \ No newline at end of file diff --git a/distcorr.m b/distcorr.m new file mode 100644 index 0000000..ff89017 --- /dev/null +++ b/distcorr.m @@ -0,0 +1,35 @@ +function dcor = distcorr(x,y) + % This function calculates the distance correlation between x and y. + % Reference: http://en.wikipedia.org/wiki/Distance_correlation + % Date: 18 Jan, 2013 + % Author: Shen Liu (shen.liu@hotmail.com.au) + % Check if the sizes of the inputs match + if size(x,1) ~= size(y,1) + error('Inputs must have the same number of rows') + end + % Delete rows containing unobserved values + N = any([isnan(x) isnan(y)],2); + x(N,:) = []; + y(N,:) = []; + % Calculate doubly centered distance matrices for x and y + a = pdist2(x,x); + mcol = mean(a); + mrow = mean(a,2); + ajbar = ones(size(mrow))*mcol; + akbar = mrow*ones(size(mcol)); + abar = mean(mean(a))*ones(size(a)); + A = a - ajbar - akbar + abar; + b = pdist2(y,y); + mcol = mean(b); + mrow = mean(b,2); + bjbar = ones(size(mrow))*mcol; + bkbar = mrow*ones(size(mcol)); + bbar = mean(mean(b))*ones(size(b)); + B = b - bjbar - bkbar + bbar; + % Calculate squared sample distance covariance and variances + dcov = sum(sum(A.*B))/(size(mrow,1)^2); + dvarx = sum(sum(A.*A))/(size(mrow,1)^2); + dvary = sum(sum(B.*B))/(size(mrow,1)^2); + % Calculate the distance correlation + dcor = sqrt(dcov/sqrt(dvarx*dvary)); +end \ No newline at end of file diff --git a/makerandCIJ_und.m b/makerandCIJ_und.m new file mode 100644 index 0000000..e03dbf1 --- /dev/null +++ b/makerandCIJ_und.m @@ -0,0 +1,25 @@ +function [CIJ] = makerandCIJ_und(N,K) +%MAKERANDCIJ_UND Synthetic directed random network +% +% CIJ = makerandCIJ_und(N,K); +% +% This function generates an undirected random network +% +% Inputs: N, number of vertices +% K, number of edges +% +% Output: CIJ, undirected random connection matrix +% +% Note: no connections are placed on the main diagonal. +% +% +% Olaf Sporns, Indiana University, 2007/2008 + +ind = triu(~eye(N)); +i = find(ind); +rp = randperm(length(i)); +irp = i(rp); + +CIJ = zeros(N); +CIJ(irp(1:K)) = 1; +CIJ = CIJ+CIJ'; % symmetrize diff --git a/rich_club_bu.m b/rich_club_bu.m new file mode 100644 index 0000000..e7e7651 --- /dev/null +++ b/rich_club_bu.m @@ -0,0 +1,49 @@ +function [R,Nk,Ek] = rich_club_bu(CIJ,varargin) +%RICH_CLUB_BU Rich club coefficients (binary undirected graph) +% +% R = rich_club_bu(CIJ) +% [R,Nk,Ek] = rich_club_bu(CIJ,klevel) +% +% The rich club coefficient, R, at level k is the fraction of edges that +% connect nodes of degree k or higher out of the maximum number of edges +% that such nodes might share. +% +% Input: CIJ, connection matrix, binary and undirected +% klevel, optional input argument. klevel sets the +% maximum level at which the rich club +% coefficient will be calculated. If klevel is +% not included the the maximum level will be +% set to the maximum degree of CIJ. +% +% Output: R, vector of rich-club coefficients for levels +% 1 to klevel. +% Nk, number of nodes with degree>k +% Ek, number of edges remaining in subgraph with +% degree>k +% +% Reference: Colizza et al. (2006) Nat. Phys. 2:110. +% +% Martijn van den Heuvel, University Medical Center Utrecht, 2011 + +Degree = sum(CIJ); %compute degree of each node + +if nargin == 1 + klevel = max(Degree); +elseif nargin == 2 + klevel = varargin{1}; +elseif nargin > 2 + error('number of inputs incorrect. Should be [CIJ], or [CIJ, klevel]') +end + +R = zeros(1,klevel); +Nk = zeros(1,klevel); +Ek = zeros(1,klevel); +for k = 1:klevel + SmallNodes=find(Degree<=k); %get 'small nodes' with degree <=k + subCIJ=CIJ; %extract subnetwork of nodes >k by removing nodes <=k of CIJ + subCIJ(SmallNodes,:)=[]; %remove rows + subCIJ(:,SmallNodes)=[]; %remove columns + Nk(k)=size(subCIJ,2); %number of nodes with degree >k + Ek(k)=sum(subCIJ(:)); %total number of connections in subgraph + R(k)=Ek(k)/(Nk(k)*(Nk(k)-1)); %unweighted rich-club coefficient +end \ No newline at end of file diff --git a/shuffle.m b/shuffle.m new file mode 100644 index 0000000..ff12568 --- /dev/null +++ b/shuffle.m @@ -0,0 +1,116 @@ +function [shuffled,rand_id] = shuffle(x,method) + +%shuffle Shuffles raster data using various different methods +% Shuffles spike data (0 or 1) using three differnt methods +% assumes rows are individual cells and columns are time frames +% +% 'frames' - shuffles the frames in time, maintains activity pattern of +% each frame +% +% 'time' - shuffles the activity of each individual cell in time +% each cell maintains its total level of activity +% +% 'time_shift' - shifts the time trace of each cell by a random amount +% each cell maintains its pattern of activity +% +% Methods fom synfire chains paper +% +% 'isi' - Inter-Spike Interval shuffling within cells +% each cell maintains in level of activity +% +% 'cell' - shuffles the activity at a given time between cells +% each frame maintains the number of active cells +% +% 'exchange' - exchange pairs of spikes across cells +% slow, but each cell and frame maintains level of activity +% +% jzaremba 01/2012 +% +% modified Perez-Ortega Jesus - Aug 2018 +% modified - Feb 2019 + +if nargin < 2 + method = 'frames'; + warning('Method frames was selected.') +end + +if ~any(strcmp(method, {'frames','time','time_shift','isi','cell','exchange'})) + method = 'frames'; + warning('Method frames was selected.') +end + +rand_id =[]; +shuffled=x; + +switch method + case 'frames' + n = size(x,2); + randp = randperm(n); + shuffled = sortrows([randp;x]')'; + shuffled = shuffled(2:end,:); + + case 'time' + n = size(x,2); + for i = 1:size(x,1) + randp = randperm(n); + temp = sortrows([randp; x(i,:)]')'; + shuffled(i,:) = temp(2,:); + end + + case 'time_shift' + n = size(x,2); + for i = 1:size(x,1) + randp = randi(n); + shuffled(i,:) = [ x(i,n-randp+1:n) x(i,1:n-randp) ]; + rand_id(i) = randp; + end + + case 'isi' + n = size(x,2); + shuffled = zeros(size(x,1),n); + + for i = 1:size(x,1) + % Pull out indices of spikes, get ISIs, buffer at start and end + isi = diff(find([1 x(i,:) 1])); + isi = isi(randperm(length(isi))); % Randomize ISIs + + temp = cumsum(isi); + temp = temp(1:end-1); % Removes the end spikes that were added + % Put the spikes back + shuffled(i,temp) = true; + end + + + case 'cell' + [n,len] = size(x); + for i = 1:len + randp = randperm(n); + temp = sortrows([randp' x(:,i)]); + shuffled(:,i) = temp(:,2); + end + + case 'exchange' + n = sum(x(:)); + for i = 1:2*n + randp = randi(n,1,2); + [r,c] = find(shuffled); + + % Make sure that the swap will actually do something + while randp(1)==randp(2) || r(randp(1))==r(randp(2)) || c(randp(1))==c(randp(2)) ||... + shuffled(r(randp(2)),c(randp(1)))==true ||... + shuffled(r(randp(1)),c(randp(2)))==true + randp = randi(n,1,2); + end + + % Swap + shuffled(r(randp(2)),c(randp(1))) = true; + shuffled(r(randp(1)),c(randp(1))) = false; + + shuffled(r(randp(1)),c(randp(2))) = true; + shuffled(r(randp(2)),c(randp(2))) = false; + + end + +end + + \ No newline at end of file