psth.m 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. function [R,t,E] = psth(data,sig,plt,T,err,t)
  2. % function to plot trial averaged rate smoothed by
  3. % a Gaussian kernel - visual check on stationarity
  4. % Usage: [R,t,E] = psth(data,sig,plt,T,err,t)
  5. %
  6. % Inputs:
  7. % Note that all times have to be consistent. If data
  8. % is in seconds, so must be sig and t. If data is in
  9. % samples, so must sig and t. The default is seconds.
  10. % data structural array of spike times
  11. % sig std dev of Gaussian (default 50ms)
  12. % (minus indicates adaptive with
  13. % approx width equal to mod sig)
  14. % plt = 'n'|'r' etc (default 'r')
  15. % T is the time interval (default all)
  16. % err - 0 = none
  17. % 1 = Poisson
  18. % 2 = Bootstrap over trials (default)
  19. % (both are based on 2* std err rather than 95%)
  20. % t = times to evaluate psth at
  21. %
  22. % The adaptive estimate works by first estimating
  23. % the psth with a fixed kernel width (-sig) it
  24. % then alters the kernel width so that the number
  25. % of spikes falling under the kernel is the same on
  26. % average but is time dependent. Reagions rich
  27. % in data therefore have their kernel width reduced
  28. %
  29. % Outputs:
  30. %
  31. % R = rate
  32. % t = times
  33. % E = errors (standard error)
  34. if nargin <1 ; error('I need data!');end
  35. [data]=padNaN(data); % create a zero padded data matrix from input structural array
  36. sz=size(data);
  37. % transposes data so that the longer dimension is assumed to be time
  38. % Procedure used to bring input data into form compatible with that required
  39. % for Murray's routine
  40. if sz(1)>sz(2); data=data';end;
  41. if nargin < 2; sig = 0.05;end
  42. if nargin < 3; plt = 'r';end
  43. if nargin < 5; err = 2; end
  44. if isempty(sig); sig = 0.05;end
  45. if isempty(plt); plt = 'r';end
  46. if isempty(err); err = 2; end
  47. adapt = 0;
  48. if sig < 0; adapt = 1; sig = -sig; end
  49. % to avoid end effects increase time interval by the width
  50. % of the kernel (otherwise rate is too low near edges)
  51. if nargin < 4;
  52. T(1) = min(data(:,1));
  53. T(2) = max(max(data));
  54. else
  55. T(1) = T(1)-4*sig;
  56. T(2) = T(2)+4*sig;
  57. end
  58. % calculate NT and ND and filter out times of interest
  59. NT = length(data(:,1));
  60. if NT < 4 && err == 2
  61. disp('Using Poisson errorbars as number of trials is too small for bootstrap')
  62. err = 1;
  63. end
  64. m = 1;
  65. D = zeros(size(data));
  66. ND=zeros(1,NT);
  67. for n=1:NT
  68. indx = find(~isnan(data(n,:)) & data(n,:)>=T(1) & data(n,:)<=T(2));
  69. ND(n) = length(indx);
  70. D(n,1:ND(n)) = data(n,indx);
  71. m = m + ND(n);
  72. end
  73. N_tot = m;
  74. N_max = max(ND);
  75. D = D(:,1:N_max);
  76. % if the kernel density is such that there are on average
  77. % one or less spikes under the kernel then the units are probably wrong
  78. L = N_tot/(NT*(T(2)-T(1)));
  79. if 2*L*NT*sig < 1 || L < 0.1
  80. disp('Spikes very low density: are the units right? is the kernel width sensible?')
  81. disp(['Total events: ' num2str(fix(100*N_tot)/100) ' sig: ' ...
  82. num2str(fix(1000*sig)) 'ms T: ' num2str(fix(100*T)/100) ' events/sig: ' ...
  83. num2str(fix(100*N_tot*sig/(T(2)-T(1)))/100)])
  84. end
  85. % Smear each spike out
  86. % std dev is sqrt(rate*(integral over kernal^2)/trials)
  87. % for Gaussian integral over Kernal^2 is 1/(2*sig*srqt(pi))
  88. if nargin < 6
  89. N_pts = fix(5*(T(2)-T(1))/sig);
  90. t = linspace(T(1),T(2),N_pts);
  91. else
  92. N_pts = length(t);
  93. end
  94. RR = zeros(NT,N_pts);
  95. f = 1/(2*sig^2);
  96. for n=1:NT
  97. for m=1:ND(n)
  98. RR(n,:) = RR(n,:) + exp(-f*(t-D(n,m)).^2);
  99. end
  100. end
  101. RR = RR*(1/sqrt(2*pi*sig^2));
  102. if NT > 1; R = mean(RR); else R = RR;end
  103. if err == 1
  104. E = sqrt(R/(2*NT*sig*sqrt(pi)));
  105. elseif err == 2
  106. Nboot = 10;
  107. mE = 0;
  108. sE = 0;
  109. for b=1:Nboot
  110. indx = floor(NT*rand(1,NT)) + 1;
  111. mtmp = mean(RR(indx,:));
  112. mE = mE + mtmp;
  113. sE = sE + mtmp.^2;
  114. end
  115. E = sqrt((sE/Nboot - mE.^2/Nboot^2));
  116. end
  117. % if adaptive warp sig so that on average the number of spikes
  118. % under the kernel is the same but regions where there is
  119. % more data have a smaller kernel
  120. if adapt
  121. sigt = mean(R)*sig./R;
  122. RR = zeros(NT,N_pts);
  123. f = 1./(2*sigt.^2);
  124. for n=1:NT
  125. for m=1:ND(n)
  126. RR(n,:) = RR(n,:) + exp(-f.*(t-D(n,m)).^2);
  127. end
  128. RR(n,:) = RR(n,:).*(1./sqrt(2*pi*sigt.^2));
  129. end
  130. if NT > 1; R = mean(RR); else R = RR;end
  131. if err == 1
  132. E = sqrt(R./(2*NT*sigt*sqrt(pi)));
  133. elseif err == 2
  134. Nboot = 10;
  135. mE = 0;
  136. sE = 0;
  137. for b=1:Nboot
  138. indx = floor(NT*rand(1,NT)) + 1;
  139. mtmp = mean(RR(indx,:));
  140. mE = mE + mtmp;
  141. sE = sE + mtmp.^2;
  142. end
  143. E = sqrt((sE/Nboot - mE.^2/Nboot^2));
  144. end
  145. end
  146. if plt == 'n';return;end
  147. plot(t,R,plt)
  148. hold on
  149. if err > 0
  150. plot(t,R+2*E,'g')
  151. plot(t,R-2*E,'g')
  152. end
  153. %axis([T(1)+(4*sig) T(2)-(4*sig) 0 1.25*max(R)])
  154. axis([T(1)+(4*sig) T(2)-(4*sig) 0 max(R+2*E)+10])
  155. xlabel('time (s)')
  156. ylabel('rate (Hz)')
  157. title(['Trial averaged rate : Gaussian Kernel :' ...
  158. ' sigma = ' num2str(1000*sig) 'ms'])
  159. hold off