Figure 1#
A closed-loop virtual-reality active sensing task for mice.#
For reference, here is the full figure

and corresponding supplemental figure

cd "/app/"
%run env.py
%run run.py connect
from vr4mice.schema.base_analysis import DataFrame
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from vr4mice.analysis import plotting
from vr4mice.schema import base_analysis, dlc
from vr4mice.analysis.analysis import style
from vr4mice.schema.latency_tests import AllLatencies, SignalsPhotodiodeAligned
from vr4mice.analysis.latency_testing import find_rising_edges, get_latency
from matplotlib.colors import ListedColormap
style()
save_fig_path = "notebooks/Paper_figures/Figure_output/"
Behavioral traces#
key = {"dataset": "Oribi_2024-08-14_1"}
df = DataFrame().get_data(
key=key, columns=["dataset", "reward", "x", "y", "trial", "aperture", "iti"]
)
box_df = base_analysis.BoxDataFrame().get_data(key=key)
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
plotting.plot_session(
df=df[df.iti == 0.0], box_df=box_df, per_aperture=False, per_side=False, ax=ax
)
sns.despine(offset=10)
plt.savefig(
save_fig_path + "figure1_example_session_trajectory_plot.svg", transparent=True
)
key = {"dataset": "Pheasant_2024-08-21_1"}
traces_to_plot = ["head_center_x", "head_center_y", "heading_dir", "head_angle"]
dlc_df = dlc.OfflineKinematics().get_data(key=key, columns=traces_to_plot)
df = DataFrame().get_data(
key=key,
columns=["dataset", "reward", "x", "y", "trial", "trial_step", "aperture", "iti"],
)
df = dlc_df.join(df)
df = df[df.iti == 0.0]
fig, ax = plt.subplots(5, 1, figsize=(10, 5), sharex=True, constrained_layout=True)
trial = df[df.trial.isin(range(10, 20))].copy().reset_index()
trial["trial_length"] = trial.groupby(["trial"], as_index=False)[
"trial_step"
].transform(lambda x: x / x.max())
ax[0].plot(trial.x, c="black")
ax[0].set_ylim(-26, 26)
ax[0].set_xticks([])
ax[1].plot(trial.y, c="black")
ax[1].set_ylim(-30, 30)
ax[2].plot(trial.heading_dir, c="black")
ax[2].set_ylim(-180, 180)
ax[3].plot(trial.head_angle, c="black")
ax[4].plot(trial.trial_length, c="black")
plt.rc("axes.spines", top=False, bottom=False, left=False, right=False)
for a in ax:
a.yaxis.tick_right() # Moves the ticks to the right
a.yaxis.set_label_position("right") # Moves the label to the right
a.spines["right"].set_position(("outward", 0)) # Adjust the right spine outward
a.spines["left"].set_visible(False) # Hide the left spine
a.spines["right"].set_visible(True)
print(traces_to_plot, key)
plt.savefig(save_fig_path + "figure1_behavioural_var_traces.svg", transparent=True)
Latency tests#
# fetch latency data and transform into dataframe
mathis = (AllLatencies() & 'dataset LIKE "%Latencytest1%"').fetch( as_dict=True)
mathis_df = pd.concat([pd.DataFrame(d) for d in mathis])
mathis_df ["lab_id"] = "Mathis"
tolias = (AllLatencies() & 'dataset LIKE "%Latencytest2%"').fetch(as_dict=True)
tolias_df = pd.concat([pd.DataFrame(d) for d in tolias])
tolias_df ["lab_id"] = "Tolias"
niell = (AllLatencies() & 'dataset LIKE "%Latencytest3%"').fetch(as_dict=True)
niell_df = pd.concat([pd.DataFrame(d) for d in niell])
niell_df ["lab_id"] = "Niell"
latencies = pd.concat([mathis_df, tolias_df, niell_df])
mean_latencies = latencies.groupby(["dataset", "lab_id"], as_index=False).mean()
mean_latencies [["dataset", "time_diff"]]
fig, ax = plt.subplots(1,3, figsize = (15,5), constrained_layout=True)
for i, lab in enumerate(latencies.lab_id.unique()):
lab_latencies = latencies [latencies.lab_id == lab]
g = sns.kdeplot(data=lab_latencies, x= lab_latencies.time_diff*1000, hue="dataset", palette=ListedColormap(['black']).colors, alpha=0.5, legend=False, ax=ax[i])
ax[i].set_xlim(0,125)
ax[i].set_xlabel("Latency (ms)")
ax[i].axvline(np.mean(lab_latencies.time_diff*1000), c="red", linestyle="dashed", alpha = 0.7)
print(f"lab = {lab} mean (ms): ", np.mean( lab_latencies.time_diff*1000), "std:", np.std( lab_latencies.time_diff*1000))
sns.despine(offset=10)
ax[i].set_title(lab)
plt.savefig(save_fig_path + "figure1_roundtrip_latency.svg")
fig, ax = plt.subplots(1,3, figsize = (15,5), constrained_layout=True)
for i, lab in enumerate(mean_latencies.lab_id.unique()):
lab_latencies = mean_latencies [mean_latencies.lab_id == lab]
sns.violinplot(x=np.repeat("DLClive", len(lab_latencies ["time_diff"])), y=abs(lab_latencies ["frame_to_socket"])*1000, color="cornflowerblue", ax=ax[i])
sns.violinplot(x=np.repeat("Unity", len(lab_latencies ["time_diff"])), y=(abs(lab_latencies ["time_diff"])*1000) - (abs(lab_latencies ["frame_to_socket"])*1000), color="purple", ax=ax[i])
sns.violinplot(x=np.repeat("Total", len(lab_latencies ["time_diff"])), y=abs(lab_latencies ["time_diff"])*1000, color="black", alpha=0.7,ax=ax[i])
ax[i].set_ylabel("Latency (ms)")
ax[i].set_ylim(0,120)
sns.despine(offset=10)
ax[i].set_title(lab)
plt.savefig(save_fig_path + "figure1_mean_latencies.svg")
# This fecth call takes some time as the photodide is sampled at 1000 hz
df = pd.DataFrame((SignalsPhotodiodeAligned() & 'dataset = "Latencytest1_2024-10-31_2"').fetch(as_dict=True)[0])
rising_edges_signal = find_rising_edges(df.time_stamp, df.signal_read)
rising_edges_photodiode = find_rising_edges(df.time_stamp, df.photodiode_read)
latencies = get_latency(rising_edges_signal, rising_edges_photodiode)
start_time = df.time_stamp[np.where(df.signal_read == 1)[0][0]]
print("start_time", start_time)
fig, ax = plt.subplots(3,1,figsize=(10,5))
ax[0].plot(df.time_stamp, df.signal_read,c="black",linewidth=2)
ax[1].plot(df.time_stamp, df.photodiode_raw_scaled, c="#AE18D3",linewidth=2)
ax[2].plot(df.time_stamp, df.signal_read, c="black",linewidth=2,alpha=0.8)
ax[2].plot(df.time_stamp, df.photodiode_read, c="#AA1BCE",linewidth=2,alpha=0.8)
for ft, td in zip(latencies.frame_time, latencies.time_diff):
ax[2].annotate(round(td*1000, 2), (ft, 1.1))
ax[1].set_ylim(-0.2,1.3)
ax[1].axhline(0.3,c="red",linestyle="dashed",alpha=0.5, linewidth=3)
ax[2].set_ylim(-0.2,1.3)
sns.despine(offset=10)
for a in ax:
a.axis('off')
a.set_xlim(start_time-1,start_time+3)
plt.savefig(save_fig_path + "figure1_signal.svg")
mean_latencies
fig, ax = plt.subplots(1,1, figsize = (5,5), constrained_layout=True)
sns.violinplot(data=mean_latencies, x="lab_id", y=mean_latencies.time_diff*1000, color="black", ax=ax, alpha=0.5)
ax.set_ylabel("Latency (ms)")
ax.set_xlabel("Lab")
ax.set_ylim(0,120)
sns.despine(offset=10)
plt.savefig(save_fig_path + "figure1_mean_latencies_lab.svg")
mean_latencies.groupby("lab_id")["time_diff"].mean() * 1000, mean_latencies.groupby("lab_id")["time_diff"].sem() * 1000