Skip to content

Phase precession

There are basically two different types of phase precession analysis currently possible but I've attempted to capture both in one class, phasePrecessionND.

This design decision is reflected in the way that data is extracted from firing rate maps and the runs through them in the fieldprops function and the FieldProps and RunProps classes that function creates

However, it still makes sense to separate out 1D and 2D phase precession analysis to some degree as they are quite different. That is why there is a separate module for dealing with linear track data; the functions in the plotting sub-module of the phase_precession module are concerned with plotting linear track data not open-field 2D data.

The 2D phase precession analysis is heavily indebted to the following paper:

Reference

Jeewajee A, Barry C, Douchamps V, Manson D, Lever C, Burgess N. Theta phase precession of grid and place cell firing in open environments. Philos Trans R Soc Lond B Biol Sci. 2013 Dec 23;369(1635):20120532. doi: 10.1098/rstb.2012.0532.

As with the analysis of replay data, phase precession analyses are heavily contingent on how things like firing rate maps are extracted, what the window sizes are for binning up spike trains, what your inclusion/ exclusion criteria are for things like good runs/ good firing fields etc. Whilst not infintely flexible, there are many options available when you run these analyses. Look at the documentation but also the source code and raise an issue(s) on github if any mistakes are found/ improvements can be made (they definitely can!)

Many of the settings used in the analysis can be changed by altering the values in the following dictionaries that are used as input to the phase precession classes:

ephysiopy.phase_precession.config.phase_precession_config = {'pos_sample_rate': 50, 'lfp_sample_rate': 250, 'cms_per_bin': 1, 'ppm': 445, 'field_smoothing_kernel_len': 7, 'field_smoothing_kernel_sigma': 5, 'field_threshold': 0.5, 'field_threshold_percent': 20, 'area_threshold': 0.01, 'bins_per_cm': 1, 'convert_xy_2_cm': True, 'allowed_min_spike_phase': np.pi, 'min_power_percent_threshold': 0, 'min_theta': 6, 'max_theta': 12, 'speed_smoothing_window_len': 15, 'minimum_allowed_run_speed': 0.5, 'minimum_allowed_run_duration': 1, 'min_spikes': 1, 'ifr_smoothing_constant': 1.0 / 3, 'spatial_lowpass_cutoff': 3, 'ifr_kernel_len': 1, 'ifr_kernel_sigma': 0.5, 'bins_per_second': 50} module-attribute

A list of the regressors that can be used in the phase precession analysis

ephysiopy.phase_precession.config.all_regressors = ['spk_numWithinRun', 'pos_exptdRate_cum', 'pos_instFR', 'pos_timeInRun', 'pos_d_cum', 'pos_d_meanDir', 'pos_d_currentdir', 'spk_thetaBatchLabelInRun'] module-attribute

ephysiopy.phase_precession.phase_precession.phasePrecessionND(T: AxonaTrial, cluster: int, channel: int, pp_config: dict = phase_precession_config, regressors=None, **kwargs)

Bases: object

Performs phase precession analysis for single unit data

Mostly a rip-off of code written by Ali Jeewajee for his paper on 2D phase precession in place and grid cells [1]_

.. [1] Jeewajee A, Barry C, Douchamps V, Manson D, Lever C, Burgess N. Theta phase precession of grid and place cell firing in open environments. Philos Trans R Soc Lond B Biol Sci. 2013 Dec 23;369(1635):20120532. doi: 10.1098/rstb.2012.0532.

Parameters:

Name Type Description Default
T AxonaTrial (or OpenEphysBase eventually)

The trial object holding position, LFP, spiking and ratemap stuff

required
cluster int

the cluster to examine

required
channel int

The channel the cluster was recorded on

required
pp_config dict

Contains parameters for running the analysis. See phase_precession_config dict in ephysiopy.common.eegcalcs

phase_precession_config
regressors list

A list of the regressors to use in the analysis

None

Attributes:

Name Type Description
orig_xy ndarray

The original position data

spike_ts ndarray

The spike timestamps

regressors dict

A dictionary containing the regressors and their values

alpha float

The alpha value for hypothesis testing

hyp int

The hypothesis to test

conf bool

Whether to calculate confidence intervals

eeg ndarray

The EEG signal

min_theta int

The minimum theta frequency

max_theta int

The maximum theta frequency

filteredEEG ndarray

The filtered EEG signal

phase ndarray

The phase of the EEG signal

phaseAdj MaskedArray

The adjusted phase of the EEG signal as a masked array

spike_times_in_pos_samples ndarray

The spike times in position samples (vector with length = npos)

spk_weights ndarray

The spike weights (vector with length = npos)

**kwargs

Most important kwarg here is run_aggregation which determines how runs are pulled out of the data. It has two options, "field" and "clump_runs" - see ephysiopy.common.fieldproperties.fieldprops for more details but essentially field is best for 2D open-field data and clump_runs is better on linear track data

Methods:

Name Description
do_correlation

Do the regression(s) for each regressor in the phase_regressors dict,

do_regression

Wrapper function for doing the actual regression which has multiple

get_phase_reg_per_field

Extracts the phase and all regressors for all runs through each

get_pos_props

Uses the output of fancy_partition and returns vectors the same

get_theta_props

Processes the LFP data and inserts into each run within each field

plot_regressor

Plot the regressor against the phase

update_config

Update the relevant pp_config values

do_correlation(phase_regressors: dict, **kwargs) -> list[RegressionResults]

Do the regression(s) for each regressor in the phase_regressors dict, optionally plotting the results of the regression

Parameters:

Name Type Description Default
phase_regressors dict

Dictionary with keys as field label (1,2 etc), each key contains a dictionary with keys 'phase' and optional nummbers of regressors

required
Notes

This collapses across fields and does the regression for all phase and regressor values

do_regression(**kwargs)

Wrapper function for doing the actual regression which has multiple stages.

Specifically here we partition fields into sub-fields, get a bunch of information about the position, spiking and theta data and then do the actual regression.

**kwargs do_plot : bool whether to plot the results of the regression(s) ax : matplotlib.mat The axes to plot into

get_phase_reg_per_field(fp: list[FieldProps], **kwargs) -> dict

Extracts the phase and all regressors for all runs through each field separately

Parameters:

Name Type Description Default
fp list

A list of FieldProps instances

required

Returns:

Type Description
dict

two-level dictionary holding regression results per field first level keys are field number second level are the regressors (current_dir etc) items in the second dict are the regression results

get_pos_props(binned_data: BinnedData = None, var_type: VariableToBin = VariableToBin.XY, **kwargs) -> list

Uses the output of fancy_partition and returns vectors the same length as pos.

Parameters:

Name Type Description Default
binned_data BinnedData

optional BinnedData instance. Will be calculated here if not given

None
var_type VariableToBin

defines if we are dealing with 1- or 2D data essentially

XY
**kwargs

valid kwargs: field_threshold - see fancy_partition() field_threshold_percent - see fancy_partition() area_threshold - see fancy_partition()

{}

Returns:

Type Description
list of FieldProps

A list of FieldProps instances (see ephysiopy.common.fieldcalcs.FieldProps)

get_theta_props(field_props: list[FieldProps])

Processes the LFP data and inserts into each run within each field a segment of LFP data that has had its phase and amplitude extracted as well as some other data

Parameters:

Name Type Description Default
field_props list[FieldProps]

A list of FieldProps instances

required

Returns:

Type Description
list of FieldProps

The amended list with LFP data added to each run for each field

plot_regressor(regressor: str, vals: np.ndarray, pha: np.ndarray, result: CircStatsResults, ax=None)

Plot the regressor against the phase

Parameters:

Name Type Description Default
regressor str

The regressor to plot

required
ax Axes

The axes to plot on

None

Returns:

Name Type Description
ax Axes

The axes with the plot

update_config(pp_config)

Update the relevant pp_config values

Linear track phase precession

ephysiopy.phase_precession.linear_track.run_phase_analysis(trial: AxonaTrial, cluster: int, channel: int, **kwargs) -> list[RegressionResults]

Run the phase analysis on a linear track trial.

Parameters:

Name Type Description Default
trial AxonaTrial
required
cluster int
required
channel int
required
kwargs
{}

Returns:

Type Description
list[RegressionResults] - list of RegressionResults

ordered by the field id and the regression results for that field

ephysiopy.phase_precession.linear_track.fieldprops_phase_precession(P: phasePrecessionND, **kwargs)

Run the phase analysis on a linear track trial.

Parameters:

Name Type Description Default
P phasePrecessionND
                trial, cluster and channel information
required
kwargs
{}

Returns:

Type Description
tuple - (phase_pos, f_props, run_direction, n_fields, P) where:
phase_pos (np.ndarray) - the position of each spike in the phase precession

analysis, normalised to the field limits and the run direction (east or west)

f_props (list[FieldProps]) - the field properties for each field in the linear

track trial, with the LFPSegment instance attached

run_direction (str) - the run direction (east or west) for the linear track trial
n_fields (int) - the number of fields in the linear track trial
P (phasePrecessionND) - the phasePrecessionND instance containing the

trial, cluster and channel information, with the field properties and phase positions attached

ephysiopy.phase_precession.linear_track.get_field_props_for_linear_track(P: phasePrecessionND, var_type=VariableToBin.X, **kwargs) -> list[FieldProps]

Get the field properties for a linear track trial.

Filters the linear track data based on speed, direction (east or west; larger ranges than the usual 90degs are used), and position (masks the start and end 12cm of the track)

Paraemters

trial (AxonaTrial) - the trial cluster (int) - the cluster id channel (int) - the channel id

kwargs (dict) - additional parameters to pass to the get_rate_map() function and additionally apply filtering to the result of that (a BinnedData instance). Rationale is that sometimes we might want to limit the field extent according to different criteria, e.g. field size in bins, field rate threshold (mean, peak, etc.), etc.

ephysiopy.phase_precession.linear_track.apply_linear_track_filter(T: AxonaTrial, run_direction: str, var_type: VariableToBin = VariableToBin.X, **kws)

ephysiopy.phase_precession.linear_track.get_run_direction(run: RunProps)

ephysiopy.phase_precession.linear_track.add_normalised_run_position(f_props: list[FieldProps]) -> list[FieldProps]

Adds the normalised run position to each run through a field in field_props where the run x position is normalised with respect to the field x position limits and the run direction (east or west)

ephysiopy.phase_precession.linear_track.plot_linear_runs(f_props: list[FieldProps], var: str = 'speed', **kwargs)

Plots the runs through the field(s) on a linear track as a sort of raster plot with each run as a separate line on the y-axis with ticks for each spike occurring on each run. For each run the height of

Plotting phase precssion results

ephysiopy.phase_precession.plotting.plot_phase_precession(phase, normalised_position, slope, intercept, ax=None, **kwargs)

Plot the phase precession of spikes in a field.

Parameters:

Name Type Description Default
phase ndarray

The phase of the LFP signal at each spike time.

required
normalised_position ndarray

The normalised position of the animal at each spike time.

required
slope float

The slope of the phase precession.

required
intercept float

The intercept of the phase precession.

required
ax Axes

Axes to plot on. If None, a new figure and axes will be created.

None
**kwargs dict

Additional keyword arguments for plotting.

{}

ephysiopy.phase_precession.plotting.plot_runs_and_precession(trial: AxonaTrial, cluster: int, channel: int, field_props: list[FieldProps])

Plot runs versus time where the colour of the line indicates directional heading. The field limits are also plotted and spikes are overlaid on the runs. Boxes delineate the runs that have been identified in field_props. Also plots phase precession for each field.

ephysiopy.phase_precession.plotting.plot_field_and_runs(trial: AxonaTrial, field_props: list[FieldProps])

Parameters:

Name Type Description Default
trial AxonaTrial

The trial containing the position and spike data.

required
field_props list[FieldProps]

List of FieldProps objects containing run and LFP data.

required
Notes

Plot runs versus time where the colour of the line indicates directional heading. The field limits are also plotted and spikes are overlaid on the runs. Boxes delineate the runs that have been identified in field_props

ephysiopy.phase_precession.plotting.plot_phase_v_position(field_props: list[FieldProps], ax=None, **kwargs)

Plot the phase of the LFP signal at each position in the field.

Parameters:

Name Type Description Default
field_props list[FieldProps]

List of FieldProps objects containing run and LFP data.

required
ax Axes

Axes to plot on. If None, a new figure and axes will be created.

None
**kwargs dict

Additional keyword arguments for plotting.

{}

Returns:

Type Description
Axes

The axes with the plot.

ephysiopy.phase_precession.plotting.ratemap_line_graph(binned_data: BinnedData, ax=None, **kwargs) -> plt.Axes

Plot a line graph of the rate map.

Parameters:

Name Type Description Default
binned_data BinnedData

The binned data containing the rate map.

required
**kwargs dict

Additional keyword arguments for plotting.

{}

Returns:

Type Description
Axes

The axes with the plot.

ephysiopy.phase_precession.plotting.add_fields_to_line_graph(f_props: list[FieldProps], ax=None) -> plt.Axes

Add field boundaries to a line graph of the rate map.

Parameters:

Name Type Description Default
f_props list[FieldProps]

List of FieldProps containing field information.

required

Returns:

Type Description
Axes

The axes with the field boundaries added.

ephysiopy.phase_precession.plotting.plot_lfp_and_spikes_per_run(f_props: list[FieldProps]) -> plt.Axes

Plot the LFP and spikes per run.

Parameters:

Name Type Description Default
f_props list[FieldProps]

List of FieldProps containing field information.

required

Returns:

Type Description
Axes

The axes with the plot.

ephysiopy.phase_precession.plotting.plot_lfp_segment(field: FieldProps, lfp_sample_rate: int = 250)

Plot the lfp segments for a series of runs through a field including the spikes emitted by the cell.

ephysiopy.phase_precession.plotting.plot_lfp_run(run: RunProps, cycle_labels: np.ndarray = None, lfp_sample_rate: int = 250, **kwargs)

Plot the lfp segment for a single run through a field including the spikes emitted by the cell.

Notes

There are very small inaccuracies here due to the way the timebase is being created from the slice belonging to the run and the way the indexing is being done by repeating the indices (repeat_ind) of the spike counts binned wrt the LFP sample rate. This shouldn;t matter for purposes of plotting - it's only when you zoom in a lot that you can see the diffferences between this and the actual spike times etc (if you can be arsed to plot them)

ephysiopy.phase_precession.plotting.plot_spikes_in_runs_per_field(field_label: np.ndarray, run_starts: np.ndarray, run_ends: np.ndarray, spikes_in_time: np.ndarray, ttls_in_time: np.ndarray | None = None, **kwargs)

Debug plotting to show spikes per run per field found in the ratemap as a raster plot

Parameters:

Name Type Description Default
field_label ndarray

The field labels for each position bin a vector

required
run_starts ndarray

The start indices of each run (vectors)

required
run_ends ndarray
The stop indices of each run (vectors)
required
spikes_in_time ndarray

The number of spikes in each position bin (vector)

required
ttls_in_time ndarray

TTL occurences in time (vector)

None
**kwargs

separate_plots : bool If True then each field will be plotted in a separate figure single_axes : bool If True will plot all the runs/ spikes in a single axis with fields delimited by horizontal lines

{}

Returns:

Type Description
fig, axes : tuple

The figure and axes objects