Home > FMAToolbox > Analyses > PhasePrecession.m

PhasePrecession

PURPOSE ^

PhasePrecession - Compute spike phase precession.

SYNOPSIS ^

function [data,stats] = PhasePrecession(positions,spikes,phases,varargin)

DESCRIPTION ^

PhasePrecession - Compute spike phase precession.

 Compute spike phase precession using the methods of O'Keefe and Recce (1993),
 i.e. spike phase vs position, and Harris et al. (2002), i.e. spike phase vs
 spike rate. Single-lap phase precession is also computed.

  USAGE

    [data,stats] = PhasePrecession(positions,spikes,phases,<options>)

    positions      linearized position <a href="matlab:help samples">samples</a> (normalized to [0..1])
    spikes         spike timestamps
    phases         phase <a href="matlab:help samples">samples</a> (see <a href="matlab:help Phase">Phase</a>)
    <options>      optional list of property-value pairs (see table below)

    =========================================================================
     Properties    Values
    -------------------------------------------------------------------------
     'maxGap'      time gaps between successive position samples exceeding
                   this threshold (e.g. undetects) will not be interpolated
                   (default = 100 ms)
     'boundaries'  onset and offset for single-lap phase precession can be
                   determined either automatically based on spike count
                   ('count', default) or using explicit firing field
                   boundaries ([Xstart Xstop], where each X is in [0..1])
     'slope'       search start value for slope (default = 0)
    =========================================================================

  NOTE

    To compute only phase vs rate, positions are not required and can be left
    empty, e.g. PhasePrecession([],spikes,phases).

  OUTPUT

    data.x                position samples

    data.position.ok      spikes occurring at valid coordinates (logical)
    data.position.t       spike times (only for valid coordinates)
    data.position.x       x coordinate for each spike
    data.position.phase   spike phase for each spike (in radians)
    data.position.lap     lap number for each spike

    data.rate.t           spike times
    data.rate.r           spike rate for each spike
    data.rate.phase       spike phase for each spike (in radians)
    data.rate.lap         lap number for each spike

    Additional statistics computed using phase vs position data:

    stats.slope           phase precession slope (via circular regression)
    stats.intercept       phase precession intercept (via circular regression)
    stats.r2              coefficient of determination for circular regression
    stats.p               p-value for circular regression
    stats.x               center x for early, middle and late subfields
    stats.mean            mean phase for early, middle and late subfields
    stats.var             phase variance for early, middle and late subfields
    stats.std             phase standard deviation for early, middle and late subfields
    stats.conf            95% confidence intervals
    stats.all             cell array containing all phases for each subfield
                          (useful for population analyses)

    stats.lap.slope       phase precession slope (via circular regression)
    stats.lap.intercept   phase precession intercept (via circular regression)
    stats.lap.r2          coefficient of determination for circular regression
    stats.lap.p           p-value for circular regression

    Additional statistics computed using phase vs rate data:

    stats.rate.mean       mean phase for each spike rate
    stats.rate.var        phase variance for each spike rate
    stats.rate.conf       phase 95% confidence intervals for each spike rate

    stats.boundaries      field boundaries

  SEE

    See also Phase, PlotPhasePrecession.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [data,stats] = PhasePrecession(positions,spikes,phases,varargin)
0002 
0003 %PhasePrecession - Compute spike phase precession.
0004 %
0005 % Compute spike phase precession using the methods of O'Keefe and Recce (1993),
0006 % i.e. spike phase vs position, and Harris et al. (2002), i.e. spike phase vs
0007 % spike rate. Single-lap phase precession is also computed.
0008 %
0009 %  USAGE
0010 %
0011 %    [data,stats] = PhasePrecession(positions,spikes,phases,<options>)
0012 %
0013 %    positions      linearized position <a href="matlab:help samples">samples</a> (normalized to [0..1])
0014 %    spikes         spike timestamps
0015 %    phases         phase <a href="matlab:help samples">samples</a> (see <a href="matlab:help Phase">Phase</a>)
0016 %    <options>      optional list of property-value pairs (see table below)
0017 %
0018 %    =========================================================================
0019 %     Properties    Values
0020 %    -------------------------------------------------------------------------
0021 %     'maxGap'      time gaps between successive position samples exceeding
0022 %                   this threshold (e.g. undetects) will not be interpolated
0023 %                   (default = 100 ms)
0024 %     'boundaries'  onset and offset for single-lap phase precession can be
0025 %                   determined either automatically based on spike count
0026 %                   ('count', default) or using explicit firing field
0027 %                   boundaries ([Xstart Xstop], where each X is in [0..1])
0028 %     'slope'       search start value for slope (default = 0)
0029 %    =========================================================================
0030 %
0031 %  NOTE
0032 %
0033 %    To compute only phase vs rate, positions are not required and can be left
0034 %    empty, e.g. PhasePrecession([],spikes,phases).
0035 %
0036 %  OUTPUT
0037 %
0038 %    data.x                position samples
0039 %
0040 %    data.position.ok      spikes occurring at valid coordinates (logical)
0041 %    data.position.t       spike times (only for valid coordinates)
0042 %    data.position.x       x coordinate for each spike
0043 %    data.position.phase   spike phase for each spike (in radians)
0044 %    data.position.lap     lap number for each spike
0045 %
0046 %    data.rate.t           spike times
0047 %    data.rate.r           spike rate for each spike
0048 %    data.rate.phase       spike phase for each spike (in radians)
0049 %    data.rate.lap         lap number for each spike
0050 %
0051 %    Additional statistics computed using phase vs position data:
0052 %
0053 %    stats.slope           phase precession slope (via circular regression)
0054 %    stats.intercept       phase precession intercept (via circular regression)
0055 %    stats.r2              coefficient of determination for circular regression
0056 %    stats.p               p-value for circular regression
0057 %    stats.x               center x for early, middle and late subfields
0058 %    stats.mean            mean phase for early, middle and late subfields
0059 %    stats.var             phase variance for early, middle and late subfields
0060 %    stats.std             phase standard deviation for early, middle and late subfields
0061 %    stats.conf            95% confidence intervals
0062 %    stats.all             cell array containing all phases for each subfield
0063 %                          (useful for population analyses)
0064 %
0065 %    stats.lap.slope       phase precession slope (via circular regression)
0066 %    stats.lap.intercept   phase precession intercept (via circular regression)
0067 %    stats.lap.r2          coefficient of determination for circular regression
0068 %    stats.lap.p           p-value for circular regression
0069 %
0070 %    Additional statistics computed using phase vs rate data:
0071 %
0072 %    stats.rate.mean       mean phase for each spike rate
0073 %    stats.rate.var        phase variance for each spike rate
0074 %    stats.rate.conf       phase 95% confidence intervals for each spike rate
0075 %
0076 %    stats.boundaries      field boundaries
0077 %
0078 %  SEE
0079 %
0080 %    See also Phase, PlotPhasePrecession.
0081 
0082 % Copyright (C) 2004-2012 by Michaƫl Zugaro
0083 %
0084 % This program is free software; you can redistribute it and/or modify
0085 % it under the terms of the GNU General Public License as published by
0086 % the Free Software Foundation; either version 3 of the License, or
0087 % (at your option) any later version.
0088 
0089 % Default values
0090 maxGap = 0.1;
0091 boundaries = 'count';
0092 slope = 0;
0093 
0094 % Check number of parameters
0095 if nargin < 3 | mod(length(varargin),2) ~= 0,
0096   error('Incorrect number of parameters (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0097 end
0098 
0099 % Check parameter sizes
0100 if ~isempty(positions) && size(positions,2) < 2,
0101     error('Parameter ''positions'' should have at least 2 columns (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0102 end
0103 if ~isdvector(spikes),
0104     error('Parameter ''spikes'' is not a vector (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0105 end
0106 if size(phases,2) ~= 2,
0107     error('Parameter ''phases'' is not a Nx2 matrix (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0108 end
0109 isradians(phases(:,2));
0110 
0111 % Parse options
0112 for i = 1:2:length(varargin),
0113     if ~ischar(varargin{i}),
0114         error(['Parameter ' num2str(i+2) ' is not a property (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).']);
0115     end
0116     switch(lower(varargin{i})),
0117         case 'boundaries',
0118             boundaries = lower(varargin{i+1});
0119             if ~isdvector(boundaries,'#2','>=0','<=1') && ~isastring(boundaries,'count'),
0120                 error('Incorrect value for property ''boundaries'' (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0121             end
0122         case 'maxgap',
0123             maxGap = varargin{i+1};
0124             if ~isdscalar(maxGap,'>=0'),
0125                 error('Incorrect value for property ''maxGap'' (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).');
0126             end
0127         case 'slope',
0128             slope = varargin{i+1};
0129             if ~isdscalar(slope),
0130                 error('Incorrect value for property ''slope'' (type ''help <a href="matlab:help CircularRegression">PhasePrecession</a>'' for details).');
0131             end
0132         otherwise,
0133             error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help <a href="matlab:help PhasePrecession">PhasePrecession</a>'' for details).']);
0134     end
0135 end
0136 
0137 % Default values
0138 data.x = positions;
0139 data.position.t = [];
0140 data.position.x = [];
0141 data.position.phase = [];
0142 data.position.lap = [];
0143 data.rate.t = [];
0144 data.rate.r = [];
0145 data.rate.phase = [];
0146 data.rate.lap = [];
0147 stats.slope = nan;
0148 stats.intercept = nan;
0149 stats.r2 = nan;
0150 stats.p = nan;
0151 stats.x = nan(1,3);
0152 stats.mean = nan(1,3);
0153 stats.var = nan(1,3);
0154 stats.std = nan(1,3);
0155 stats.conf = nan(2,3);
0156 stats.all = {nan nan nan};
0157 stats.rate.mean = nan(1,16);
0158 stats.rate.conf = nan(2,16);
0159 stats.boundaries = [];
0160 stats.lap.slope = [];
0161 stats.lap.intercept = [];
0162 stats.lap.r2 = [];
0163 stats.lap.p = [];
0164 
0165 % Compute spike phases
0166 if isempty(spikes), return; end
0167 spikePhases = Interpolate(phases,spikes,'trim','off','type','circular');
0168 if isempty(spikePhases), return; end
0169 
0170 % Interpolate positions at spike times
0171 if ~isempty(positions),
0172     % Make sure positions are normalized
0173     if max(positions(:,2)) > 1 || min(positions(:,2)) < 0,
0174         positions(:,2) = ZeroToOne(positions(:,2));
0175         warning('Parameter ''positions'' should contain values in [0 1]. The data will now be transformed accordingly.');
0176     end
0177     [x,ignored] = Interpolate(positions,spikes,'trim','off','maxGap',maxGap);
0178     data.position.ok = ~ignored;
0179     data.position.t = spikes(~ignored);
0180     data.position.x = x(:,2);
0181     data.position.phase = spikePhases(~ignored,2);
0182 end
0183 
0184 % Compute unwrapped spike phases
0185 unwrapped = [phases(:,1) unwrap(phases(:,2))];
0186 [spikeUnwrappedPhases,ignored] = Interpolate(unwrapped,spikes,'trim','off');
0187 % Get beginning and end unwrapped phases of two cycles surrounding each spike
0188 startUnwrappedPhases = spikeUnwrappedPhases(:,2)-2*pi;
0189 stopUnwrappedPhases = spikeUnwrappedPhases(:,2)+2*pi;
0190 
0191 %%%%
0192 %  figure;PlotXY(unwrapped);hold on;
0193 %  PlotHVLines(spikes,'v','k');PlotXY(spikeUnwrappedPhases,'r+');PlotHVLines(startUnwrappedPhases,'h','g');PlotHVLines(stopUnwrappedPhases,'h','r');
0194 %  figure;PlotXY(phases);hold on;
0195 %  PlotTicks(spikes,pi,'k');
0196 %%%%
0197 
0198 % Compute instantaneous rate at spike times
0199 data.rate.r = CountInIntervals(spikeUnwrappedPhases(:,2),[startUnwrappedPhases stopUnwrappedPhases]);
0200 data.rate.t = spikes;
0201 data.rate.phase = spikePhases(:,2);
0202 
0203 
0204 % Determine lap number for each spike (for single-lap phase precession)
0205 
0206 if isdvector(boundaries,'#2') && ~isempty(positions),
0207 
0208     stats.boundaries = boundaries;
0209 
0210     % Firing curve boundaries
0211     fieldStart = boundaries(1);
0212     fieldStop = boundaries(2);
0213     fieldSize = abs(fieldStart-fieldStop);
0214 
0215     % Let x1 be current position, x0 be previous position and x2 be next position
0216     x0 = [positions(1,2);positions(1:end-1,2)];
0217     x1 = positions(:,2);
0218     x2 = [positions(2:end,2);positions(end,2)];
0219 
0220     % Determine when the animal enters/leaves the field:
0221     % 1) the animal enters the field when x1 is inside and x0 is not inside and near x1
0222     % 2) the animal leaves the field when x1 is inside and x2 is not inside and near x1
0223     % There are two additional cases:
0224     % 1) the animal enters the field in the first lap when x1 is inside and close to the entrance
0225     % 1) the animal leaves the field in the last lap when x1 is inside and close to the exit
0226     if fieldStart < fieldStop,
0227         x0_inside = x0 >= fieldStart & x0 <= fieldStop;
0228         x1_inside = x1 >= fieldStart & x1 <= fieldStop;
0229         x2_inside = x2 >= fieldStart & x2 <= fieldStop;
0230         x0_near_x1 = abs(x0-x1)<0.1*fieldSize;
0231         x2_near_x1 = abs(x2-x1)<0.1*fieldSize;
0232     else
0233         x0_inside = x0 >= fieldStart | x0 <= fieldStop;
0234         x1_inside = x1 >= fieldStart | x1 <= fieldStop;
0235         x2_inside = x2 >= fieldStart | x2 <= fieldStop;
0236         x0_near_x1 = abs(x0-x1)<0.1*fieldSize | 1-abs(x0-x1)<0.1*fieldSize;
0237         x2_near_x1 = abs(x2-x1)<0.1*fieldSize | 1-abs(x2-x1)<0.1*fieldSize;
0238     end
0239     start = x1_inside & ~(x0_inside & x0_near_x1);
0240     start(1) = x1_inside(1) & abs(x1(1)-fieldStart)<0.1*fieldSize;
0241     stop = x1_inside & ~(x2_inside & x2_near_x1);
0242     stop(end) = x1_inside(end) & abs(x1(end)-fieldStop)<0.1*fieldSize;
0243 
0244     if any(start) && any(stop),
0245         startT = positions(start,1);
0246         stopT = positions(stop,1);
0247         % Get rid of duplicate events (e.g. the rat enters twice before it exits)
0248         s = [startT ones(size(startT));stopT 2*ones(size(stopT))];
0249         s = sortrows(s);
0250         ds = [1;diff(s(:,2))];
0251         s = s(ds~=0,:);
0252         startT = s(s(:,2)==1);
0253         stopT = s(s(:,2)==2);
0254         % Drop incomplete traversals of the field
0255         if startT(1) > stopT(1), stopT(1) = []; end
0256         if ~isempty(stopT),
0257             if startT(end) > stopT(end), startT(end) = []; end
0258             traversals = [startT stopT];
0259 
0260             % Show laps (debugging)
0261             %figure;hold on;
0262             %nn = positions(:,1);
0263             %plot(nn,x1);
0264             %PlotHVLines([fieldStart fieldStop],'h',':k');
0265             %PlotHVLines(startT,'v','g');
0266             %PlotHVLines(stopT,'v','r');
0267 
0268             % Determine lap number
0269             [~,lap] = InIntervals(data.position.t,traversals);
0270             data.position.lap = lap;
0271             [~,lap] = InIntervals(data.rate.t,traversals);
0272             data.rate.lap = lap;
0273         end
0274     end
0275 
0276 elseif strcmp(boundaries,'count'),
0277 
0278     % Get beginning and end unwrapped phases of eight cycles before each spike
0279     startUnwrappedPhases = spikeUnwrappedPhases(:,2)-8*2*pi;
0280     stopUnwrappedPhases = spikeUnwrappedPhases(:,2);
0281     rateBefore = CountInIntervals(spikeUnwrappedPhases,[startUnwrappedPhases stopUnwrappedPhases]);
0282     % Get beginning and end unwrapped phases of eight cycles after each spike
0283     startUnwrappedPhases = spikeUnwrappedPhases(:,2);
0284     stopUnwrappedPhases = spikeUnwrappedPhases(:,2)+8*2*pi;
0285     rateAfter = CountInIntervals(spikeUnwrappedPhases,[startUnwrappedPhases stopUnwrappedPhases]);
0286     % Find periods of minimal (resp. maximal) firing rates
0287     minimalBefore = rateBefore <= 4;
0288     maximalAfter = rateAfter >= 16;
0289     maximalBefore = rateBefore >= 16;
0290     minimalAfter = rateAfter <= 4;
0291     start = minimalBefore & maximalAfter;
0292     stop = maximalBefore & minimalAfter;
0293     % Drop incomplete traversals of the field
0294     if any(start) && any(stop),
0295         if start(1) >= stop(1), stop(1) = []; end
0296         if start(end) >= stop(end), start(end) = []; end
0297         traversals = [start stop];
0298         % Determine lap number
0299         [~,lap] = InIntervals(data.position.t,traversals);
0300         data.position.lap = lap;
0301         [~,lap] = InIntervals(data.rate.t,traversals);
0302         data.rate.lap = lap;
0303     end
0304 
0305 end
0306 
0307 % Statistics: regression + circular means and variances for early, middle and late subfields
0308 if ~isempty(positions) && nargout >= 2,
0309     % For circular/circular regression, shift the field leftward along x (if necessary)
0310     % to treat as circular-linear regression - as a result, the intercept corresponds to
0311     % the right portion of the field
0312     x = data.position.x;
0313     dx = 0;
0314     if isdvector(boundaries,'#2'),
0315         if fieldStart > fieldStop,
0316             dx = fieldStop; % shift by this amount
0317             left = x < fieldStart;
0318             x(left) = x(left) + 1 - dx;
0319             x(~left) = x(~left) - dx;
0320         end
0321     end
0322     % Circular regression for average data
0323     ok = ~isnan(x);
0324     [beta,r2,p] = CircularRegression(x(ok),data.position.phase(ok),'slope',slope);
0325     stats.slope = beta(1);
0326     stats.intercept = beta(2) + stats.slope*dx; % Correction for circular/circular regression (see above)
0327     stats.r2 = r2;
0328     stats.p = p;
0329     % Circular regression for each lap separately
0330     for lap = 1:max(data.rate.lap),
0331         thisLap = data.position.lap == lap;
0332         [beta,r2,p] = CircularRegression(x(thisLap&ok),data.position.phase(thisLap&ok),'slope',stats.slope);
0333         stats.lap.slope(lap,1) = beta(1);
0334         stats.lap.intercept(lap,1) = beta(2) + stats.lap.slope(lap,1)*dx;
0335         stats.lap.r2(lap,1) = r2;
0336         stats.lap.p(lap,1) = p;
0337     end
0338     % Mean phase for each subfield
0339     x0 = min(data.position.x);
0340     x1 = max(data.position.x);
0341     dx = x1-x0;
0342     for i = 1:3,
0343         ok = data.position.x > x0+dx/3*(i-1) & data.position.x < x0+dx/3*i;
0344         if sum(ok) == 0, continue;    end
0345         stats.all{i} = data.position.phase(ok);
0346         stats.x(i) = x0+1.5*dx/3*(i-1);
0347         [stats.var(i),stats.std(i)] = CircularVariance(data.position.phase(ok));
0348         [stats.mean(i),stats.conf(:,i)] = CircularConfidenceIntervals(data.position.phase(ok));
0349     end
0350 end
0351 
0352 % Statistics: phase vs rate
0353 if nargout >= 2,
0354     for rate = 1:max(data.rate.r),
0355         ok = data.rate.r==rate;
0356         if any(ok),
0357             [stats.rate.mean(rate),stats.rate.conf(:,rate)] = CircularConfidenceIntervals(data.rate.phase(ok));
0358         else
0359             stats.rate.mean(rate) = NaN;
0360             stats.rate.conf(1:2,rate) = NaN;
0361         end
0362     end
0363 end

Generated on Fri 16-Mar-2018 13:00:20 by m2html © 2005