Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -942,34 +942,123 @@ def _trial_of(times, _gc=gc, _n=n_tr):
y_main_top = params["curve_top"]

# FIP channels (single-session only), normalised and stacked above the behavior panel
fip_unclipped_ids = []
fip_clipped_ids = []
if df_fip is not None and len(sessions) == 1 and len(fip) > 0:
fip_channels = fip
present = set(df_fip["event"].unique())
band = 0
band = 0.0
fip_gap = 0.5
for channel in fip_channels:
if channel not in present:
continue
bottom = params["curve_top"] + 0.1 + band
C = df_fip.query("event == @channel").copy()
d = C["data"].values - np.nanmin(C["data"].values)
d = d / np.nanmax(d) + bottom
vals = C["data"].astype(float).to_numpy()
if vals.size == 0 or np.all(np.isnan(vals)):
continue
vmin = np.nanmin(vals)
vmax = np.nanmax(vals)
span = vmax - vmin
# avoid zero-span stacking problems for constant signals
if np.isnan(span) or span == 0:
span = 1.0

# place this channel so its minimum maps to `base`, preserving original scale
base = params["curve_top"] + 0.1 + band
offset = base - vmin
d = vals + offset

color = get_fip_color(channel)
custom = np.stack([C.timestamps.values, vals], axis=-1)
hover = f"%{{customdata[0]:.2f}}s %{{customdata[1]:.3f}} <extra>{channel}</extra>"

fip_unclipped_ids.append(len(fig.data))
fig.add_trace(
go.Scattergl(
x=C.timestamps.values + last_off,
y=d,
customdata=custom,
mode="lines",
hovertemplate=hover,
line=dict(color=color),
name=channel,
),
row=1,
col=1,
)

# clipped version: clip raw values to 1st–99th percentile, same y-offset
p01, p99 = np.nanpercentile(vals, 1), np.nanpercentile(vals, 99)
vals_clipped = np.clip(vals, p01, p99)
d_clipped = vals_clipped + offset
fip_clipped_ids.append(len(fig.data))
fig.add_trace(
go.Scattergl(
x=C.timestamps.values + last_off,
y=d_clipped,
customdata=custom,
mode="lines",
hovertemplate=hover,
line=dict(color=color),
name=channel,
visible=False,
showlegend=False,
),
row=1,
col=1,
)
yticks.append(bottom + 0.5)
ylabels.append(channel)
band += 1
y_main_top = bottom + 1.0

# use three ticks: bottom (vmin), center (channel name), top (vmax)
yticks.extend([base - vmin, base + span / 2.0, base + span])
ylabels.extend(
[
"0.0",
f"{channel.split('_dff')[0]}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",
f"{vmax:.2f}",
]
)

# advance band by the display span (not raw span) plus a small gap to avoid overlap
band += span + fip_gap
y_main_top = base + span + 0.25

# Toggle button to switch FIP traces between full range and 1–99% clipped
if fip_unclipped_ids:
n_all = len(fig.data)
vis_full = [True] * n_all
vis_clip = [True] * n_all
for idx in fip_clipped_ids:
vis_full[idx] = False
for idx in fip_unclipped_ids:
vis_clip[idx] = False
for idx in fip_clipped_ids:
vis_clip[idx] = True

fig.update_layout(
updatemenus=[
dict(
type="buttons",
direction="right",
x=1.0,
y=1.0,
xanchor="right",
yanchor="top",
showactive=True,
buttons=[
dict(
label="FIP: full range",
method="restyle",
args=[{"visible": vis_full}],
),
dict(
label="FIP: clip 1–99%",
method="restyle",
args=[{"visible": vis_clip}],
),
],
)
]
)
# Thick vertical lines marking session boundaries (both panels)
for b in boundaries:
for row in (1, 2):
Expand Down Expand Up @@ -1012,7 +1101,7 @@ def _trial_of(times, _gc=gc, _n=n_tr):
# Title pinned to the very top-left so it clears the legend below it.
title=dict(text=title or "Session Scroller", x=0.0, xanchor="left", y=0.98, yanchor="top"),
showlegend=True,
height=620,
height=620 + (50 * len(fip) if df_fip is not None and len(fip) > 0 else 0),
width=1000,
template="simple_white",
# Legend outside, top-left, horizontal, compact entries (narrow box).
Expand Down
Loading