# ~/analyseur/cbgtc/visual/rate.py
#
# Documentation by Lungsi 30 Oct 2025
#
# This contains function for SpikingStats
#
"""
=============
Rate Plotting
=============
+------------------------------------------------------+
| Functions |
+======================================================+
| :func:`plot_mean_rate_isi` |
+------------------------------------------------------+
| :func:`plot_mean_rate_isi_in_ax` |
+------------------------------------------------------+
| :func:`plot_pool_avg_inst_rates` |
+------------------------------------------------------+
| :func:`plot_pool_avg_inst_rates_in_ax` |
+------------------------------------------------------+
| :func:`plot_true_avg_inst_rate` |
+------------------------------------------------------+
| :func:`plot_true_avg_inst_rate_in_ax` |
+------------------------------------------------------+
| :func:`plot_mean_rate_spikecounts_in_ax` |
+------------------------------------------------------+
| :func:`plot_mean_rate_all_neurons_across_time_in_ax` |
+------------------------------------------------------+
--------------
Plot Mean Rate
--------------
1. Pre-requisites
=================
1.1. Import Modules
-------------------
::
from analyseur.cbgtc.loader import LoadSpikeTimes
from analyseur.cbgtc.visual.rate import plot_mean_rate_isi
1.2. Load file and get spike times
----------------------------------
::
loadST = LoadSpikeTimes("spikes_GPi.csv")
spiketimes_superset = loadST.get_spiketimes_superset()
2. Cases
========
2.1. Visualize Mean Rate with default setting
---------------------------------------------
::
[fig, ax] = plot_mean_rate_isi(spiketimes_superset)
2.2. Visualize Mean Rate in portrait mode
-----------------------------------------
::
[fig, ax] = plot_mean_rate_isi(spiketimes_superset, mode="portrait")
2.3. Visualize Mean Rate in portrait mode with nucleus name in title
--------------------------------------------------------------------
::
[fig, ax] = plot_mean_rate_isi(spiketimes_superset, mode="portrait", nucleus="GPi")
2.4. Create the plot for customization
--------------------------------------
This is for power users who for instance want to insert the Mean Rate plot in their
collage of subplots.
::
import matplotlib.pyplot as plt
from analyseur.cbgtc.visual.rate import plot_mean_rate_isi_in_ax
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.suptitle('Horizontally stacked subplots')
ax1 = plot_mean_rate_isi_in_ax(ax1, spiketimes_superset)
ax2 = plot_mean_rate_isi_in_ax(ax2, spiketimes_superset)
plt.show()
NOTE: This example shows :func:`plot_mean_rate_isi_in_ax` in default setting but this function works like
:func:`plot_mean_rate_isi` therefore all the cases 2.1, 2.2 and 2.3 are applicable for :func:`plot_mean_rate_isi_in_ax`.
-------------------------------
Plot Average Instantaneous Rate
-------------------------------
Similar as documented above for plotting Mean Rate but using the function
:func:`plot_avg_inst_rate` and :func:`plot_avg_inst_rate_in_ax` with the
additional OPTIONAL argument for `binsz` (otherwise it picks a default value).
This is imported as
::
from analyseur.cbgtc.visual.rate import plot_avg_inst_rate, plot_avg_inst_rate_in_ax
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
import numbers
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde, alpha
import re
from analyseur.cbgtc.curate import get_desired_spiketimes_subset
from analyseur.cbgtc.stats.rate import Rate
from analyseur.cbgtc.stats.isi import InterSpikeInterval
from analyseur.cbgtc.parameters import SignalAnalysisParams, SimulationParams
__siganal = SignalAnalysisParams()
__simparams = SimulationParams()
##########################################################################
# PLOT Mean Rate Based on Spike Counts
##########################################################################
[docs]
def plot_mean_rate_spikecounts_in_ax(ax, spiketimes_set, window=None, binsz=None,
nucleus=None, mode=None):
"""
.. code-block:: text
Mean Rate (1/s)
^
| █ █ █ █ █ █ █ █
| ███ ██ ████ ██ ██ ███ ██
| █████████████████████████
|
+---------------------------------------------> Neurons
0 50 100 150 200
Each bar represents the mean firing rate of one neuron
computed from spike counts within the analysis window.
Draws the Mean Rate (1/s) on the given
`matplotlib.pyplot.axis <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axis.html>`_
:param ax: object `matplotlib.pyplot.axis``
:param spiketimes_set: Dictionary returned using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_superset`
or using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_subset`
OPTIONAL parameters
- :param nucleus: string; name of the nucleus
- :param mode: "portrait" or None/landscape [default]
- :return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
# ============== DEFAULT Parameters ==============
if window is None:
window = __siganal.window
if binsz is None:
binsz = __siganal.binsz_100perbin
n_neurons = len(spiketimes_set)
match mode:
case "portrait":
orient = "horizontal"
case _:
orient = "landscape"
get_axis = lambda orient: "x" if orient=="horizontal" else "y"
mu_rate_vec, _ = Rate.mean_rate(spiketimes_set=spiketimes_set, window=window,
binsz=binsz, neurons="all", across="times")
if orient == "horizontal":
ax.barh(range(len(mu_rate_vec)), mu_rate_vec, color="steelblue", edgecolor="black")
ax.set_ylabel("Neurons")
ax.set_xlabel("Mean Rate (1/s)")
ax.margins(y=0)
else:
ax.bar(range(len(mu_rate_vec)), mu_rate_vec, color="steelblue", edgecolor="black")
ax.set_ylabel("Mean Rate (1/s)")
ax.set_xlabel("Neurons")
ax.grid(True, alpha=0.3, axis=get_axis(orient))
nucname = "" if nucleus is None else " in " + nucleus
ax.set_title("Mean Rate (Counts) Distribution of " + str(n_neurons) + " neurons" + nucname)
return ax
##########################################################################
# PLOT Mean Rate Based on ISI
##########################################################################
[docs]
def plot_mean_rate_isi_in_ax(ax, spiketimes_set, nucleus=None, mode=None):
"""
.. code-block:: text
Mean Rate (1/s)
^
| █ █ █ █ █ █ █ █
| ███ ██ ████ ██ ██ ███ ██
| █████████████████████████
|
+---------------------------------------------> Neurons
0 50 100 150 200
Each bar represents the mean firing rate of one neuron
computed from its inter-spike intervals (ISI).
Draws the Mean Rate (1/s) on the given
`matplotlib.pyplot.axis <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axis.html>`_
:param ax: object `matplotlib.pyplot.axis``
:param spiketimes_set: Dictionary returned using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_superset`
or using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_subset`
OPTIONAL parameters
- :param nucleus: string; name of the nucleus
- :param mode: "portrait" or None/landscape [default]
- :return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
n_neurons = len(spiketimes_set)
match mode:
case "portrait":
orient = "horizontal"
case _:
orient = "landscape"
get_axis = lambda orient: "x" if orient=="horizontal" else "y"
[all_isi, _] = InterSpikeInterval.compute(spiketimes_set)
mu_arr = InterSpikeInterval.mean_freqs(all_isi)
if len(mu_arr)==0:
vec_mu = [0]
else:
vec_mu = mu_arr.values()
if orient == "horizontal":
ax.barh(range(len(vec_mu)), vec_mu, color="steelblue", edgecolor="black")
ax.set_ylabel("Neurons")
ax.set_xlabel("Mean Rate (1/s)")
ax.margins(y=0)
else:
ax.bar(range(len(vec_mu)), vec_mu, color="steelblue", edgecolor="black")
ax.set_ylabel("Mean Rate (1/s)")
ax.set_xlabel("Neurons")
ax.grid(True, alpha=0.3, axis=get_axis(orient))
nucname = "" if nucleus is None else " in " + nucleus
ax.set_title("Mean Rate (ISI) Distribution of " + str(n_neurons) + " neurons" + nucname)
return ax
[docs]
def plot_mean_rate_isi(spiketimes_superset, nucleus=None, mode=None):
"""
Visualize Mean Rate (1/s) of the given neuron population using :py:func:`plot_mean_rate_isi_in_ax`.
:param spiketimes_superset: Dictionary returned using :meth:`analyseur.cbgtc.stats.isi.InterSpikeInterval.compute`
OPTIONAL parameters
- :param nucleus: string; name of the nucleus
- :param mode: "portrait" or None/landscape [default]
- :return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
if mode=="portrait":
fig, ax = plt.subplots(figsize=(6, 10))
else:
fig, ax = plt.subplots(figsize=(10, 6))
ax = plot_mean_rate_isi_in_ax(ax, spiketimes_superset, nucleus=nucleus, mode=mode)
plt.show()
return fig, ax
##########################################################################
# PLOT Average Instantaneous Rate
##########################################################################
[docs]
def plot_pool_avg_inst_rates_in_ax(ax, spiketimes_superset, binsz=None, nucleus=None, mode=None):
"""
.. code-block:: text
Average Inst. Rate (Hz)
^
| ███ ████ ███ ████ ███ ███
| █████████████████████████
| ██████████████████████████
|
+--------------------------------------------------> Time (s)
0 2 4 6 8 10
Each bar represents the average instantaneous firing rate
of the neuron population within a time bin.
Draws the Mean Rate (1/s) on the given
`matplotlib.pyplot.axis <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axis.html>`_
:param ax: object `matplotlib.pyplot.axis``
:param spiketimes_superset: Dictionary returned using :meth:`analyseur.cbgtc.stats.isi.InterSpikeInterval.compute`
OPTIONAL parameters
- :param binsz: 0.01 [default]
- :param nucleus: string; name of the nucleus
- :param mode: "portrait" or None/landscape [default]
- :return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
# ============== DEFAULT Parameters ==============
if binsz is None:
binsz = __siganal.binsz_100perbin
n_neurons = len(spiketimes_superset)
match mode:
case "portrait":
orient = "horizontal"
case _:
orient = "landscape"
get_axis = lambda orient: "x" if orient=="horizontal" else "y"
[all_isi, all_times] = InterSpikeInterval.compute(spiketimes_superset)
all_inst = InterSpikeInterval.inst_rates(all_isi)
[avg_rates, bin_centers, bin_counts] = InterSpikeInterval.pool_avg_inst_rates(inst_rates_set=all_inst,
tbins_set=all_times,
binsz=binsz)
if orient == "horizontal":
ax.barh(bin_centers, avg_rates, height=binsz*0.8, linewidth=0.5,
alpha=0.7, color="steelblue", edgecolor="black")
ax.set_ylabel("Time (s)")
ax.set_xlabel("Average Inst. Rate (Hz)")
else:
# Base bar
ax.bar(bin_centers, avg_rates, width=binsz*0.8, linewidth=0.5,
alpha=0.7, color="steelblue", edgecolor="black")
# ax.plot(bin_centers, avg_rates, "o-", linewidth=2, markersize=6) # Plot
ax.set_ylabel("Average Inst. Rate (Hz)")
ax.set_xlabel("Time (s)")
ax.grid(True, alpha=0.3, axis=get_axis(orient))
nucname = "" if nucleus is None else " in " + nucleus
ax.set_title("Pooled Average Inst. Rates of " + str(n_neurons) + " neurons" + nucname)
return ax
[docs]
def plot_pool_avg_inst_rates(spiketimes_superset, binsz=None, nucleus=None, mode=None):
"""
Visualize Mean Rate (1/s) of the given neuron population using :py:func:`plot_pool_avg_inst_rates_in_ax`.
:param spiketimes_superset: Dictionary returned using :class:`~analyseur.cbgtc.loader.LoadSpikeTimes`
OPTIONAL parameters
- :param binsz: 0.01 [default]
- :param nucleus: string; name of the nucleus
- :param mode: "portrait" or None/landscape [default]
- :return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
if mode=="portrait":
fig, ax = plt.subplots(figsize=(6, 10))
else:
fig, ax = plt.subplots(figsize=(10, 6))
ax = plot_pool_avg_inst_rates_in_ax(ax, spiketimes_superset, binsz=binsz, nucleus=nucleus, mode=mode)
plt.show()
return fig, ax
##########################################################################
# PLOT Instantaneous Rate
##########################################################################
[docs]
def plot_true_avg_inst_rate_in_ax(ax, spiketimes_set, nucleus=None, mode=None):
"""
.. code-block:: text
Avg. Inst. Rate (Hz)
^
| █ █ █ █ █ █ █ █
| ███ ██ ████ ██ ██ ███ ██
| █████████████████████████
|
+---------------------------------------------> Neurons
0 50 100 150 200
Each bar represents the average instantaneous firing rate
of an individual neuron computed from its ISI sequence.
Draws the Instantaneuous Rate (1/s) on the given
`matplotlib.pyplot.axis <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axis.html>`_
:param ax: object `matplotlib.pyplot.axis``
:param spiketimes_set: Dictionary returned using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_superset`
or using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_subset`
OPTIONAL parameters
:param neurons: `"all"` or `scalar` or `range(a, b)` or list of neuron ids like `[2, 3, 6, 7]`
- `"all"` means subset = superset
- `N` (a scalar) means subset of first N neurons in the superset
- `range(a, b)` or `[2, 3, 6, 7]` means subset of selected neurons
:param nucleus: string; name of the nucleus
:param mode: "portrait" or None/landscape [default]
:return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
n_neurons = len(spiketimes_set)
match mode:
case "portrait":
orient = "horizontal"
case _:
orient = "landscape"
get_axis = lambda orient: "x" if orient == "horizontal" else "y"
[all_isi, _] = InterSpikeInterval.compute(spiketimes_set)
all_inst_rates = InterSpikeInterval.inst_rates(isi_set=all_isi)
Instarr = InterSpikeInterval.true_avg_inst_rates(inst_rates_set=all_inst_rates)
vec_IR = Instarr.values()
#print(f"Inst. Rate Array {Instarr}")
if orient == "horizontal":
ax.barh(range(len(vec_IR)), vec_IR, color="steelblue", edgecolor="black")
ax.set_ylabel("Neurons")
ax.set_xlabel("Avg. Ins. Rate (1/s)")
ax.margins(y=0)
else:
ax.bar(range(len(vec_IR)), vec_IR, color="steelblue", edgecolor="black")
ax.set_ylabel("Avg. Ins. Rate (1/s)")
ax.set_xlabel("Neurons")
ax.grid(True, alpha=0.3, axis=get_axis(orient))
nucname = "" if nucleus is None else " in " + nucleus
ax.set_title("Avg. Inst. Rate Distribution of " + str(n_neurons) + " neurons" + nucname)
return ax
[docs]
def plot_true_avg_inst_rate(spiketimes_set, nucleus=None, mode=None):
"""
Visualize Instantaneuous Rate (1/s) of the given neuron population using :py:func:`plot_true_avg_inst_rate_in_ax`.
:param spiketimes_set: Dictionary returned using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_superset`
or using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_subset`
OPTIONAL parameters
:param neurons: `"all"` or `scalar` or `range(a, b)` or list of neuron ids like `[2, 3, 6, 7]`
- `"all"` means subset = superset
- `N` (a scalar) means subset of first N neurons in the superset
- `range(a, b)` or `[2, 3, 6, 7]` means subset of selected neurons
:param nucleus: string; name of the nucleus
:param mode: "portrait" or None/landscape [default]
:return: object `ax` with Rate Distribution plotting done into it
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
if mode=="portrait":
fig, ax = plt.subplots(figsize=(6, 10))
else:
fig, ax = plt.subplots(figsize=(10, 6))
ax = plot_true_avg_inst_rate_in_ax(ax, spiketimes_set, nucleus=nucleus, mode=mode)
plt.show()
return fig, ax
[docs]
def plot_mean_rate_all_neurons_across_time_in_ax(ax, spiketimes_set, window=None,
binsz=None, nucleus=None,):
"""
.. code-block:: text
Mean Population Firing Rate Over Time
Firing Rate (Hz)
^
| ~~~ ~~~ ~~~ ~~~
| ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
| ~ ~~ ~~ ~~ ~
|
+--------------------------------------------------> Time (s)
0 5 10 15 20
Line shows the mean firing rate of the neuron population
as it evolves over time.
Draws the Mean Firing Rate (1/s) over time on the given
`matplotlib.pyplot.axis <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axis.html>`_
:param ax: object `matplotlib.pyplot.axis``
:param spiketimes_set: Dictionary returned using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_superset`
or using :meth:`~analyseur.cbgtc.loader.LoadSpikeTimes.get_spiketimes_subset`
OPTIONAL parameters
:param window: 2-tuple; (0, 10) [default]
:param binsz: 0.01 [default]
:param nucleus: string; name of the nucleus
:return: object `ax` with plotting of the temporal dynamics of the mean firing rate across neurons
.. raw:: html
<hr style="border: 2px solid red; margin: 20px 0;">
"""
# ============== DEFAULT Parameters ==============
if window is None:
window = __siganal.window
if binsz is None:
binsz = __siganal.binsz_100perbin
n_neurons = len(spiketimes_set)
mu_rate_vec, time_bins = Rate.mean_rate(spiketimes_set=spiketimes_set, window=window,
binsz=binsz, neurons="all", across="times")
# Plot
ax.plot(np.arange(len(mu_rate_vec)) / (window[1] - window[0]),
mu_rate_vec, "b-", linewidth=1)
ax.grid(True, alpha=0.3)
ax.set_xlabel("Time (seconds)")
ax.set_ylabel("Firing Rate (Hz)")
nucname = "" if nucleus is None else " in " + nucleus
ax.set_title("Firing Rate Over Time of " + str(n_neurons) + " neurons" + nucname)
return ax