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
|
|
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
|
|
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 |