Skip to content

added get_average_signal_window.#69

Merged
rachelstephlee merged 15 commits into
mainfrom
avg-signal-window
Aug 28, 2025
Merged

added get_average_signal_window.#69
rachelstephlee merged 15 commits into
mainfrom
avg-signal-window

Conversation

@rachelstephlee

Copy link
Copy Markdown
Collaborator

get_average_signal_window takes in an nwb and takes the average window of signal around an alignment event that occurs once per trial.

@rachelstephlee rachelstephlee requested a review from alexpiet July 3, 2025 00:40
@rachelstephlee rachelstephlee marked this pull request as draft July 3, 2025 02:32
Comment thread src/aind_dynamic_foraging_basic_analysis/metrics/trial_metrics.py
@rachelstephlee rachelstephlee marked this pull request as ready for review August 15, 2025 03:06
@rachelstephlee

Copy link
Copy Markdown
Collaborator Author

Checked for bugs, added check for nwb.df_trials.

The bug was subtle but important.

when Censor = True, the alignment ocde sorts the event_times. If Nan values are in event_times (for example, if aligned to choice_time, choice_time = Nan for trials in which the mice made no choice), the Nan values are automatically sorted to the end.

Since the code assumes alignment_times should be in df_trials, so therefore should be ordered in trials not by time. If Nan values are shuffled to the end, the ordering of averaged times --> trials will be messed up.

I didn't want to change alignment.py because this is the correct behavior for any event_times. Let me know if you feel differently-- we can check for nan values in event_times and then order them according to their original position (but would that even make sense?).

@alexpiet

Copy link
Copy Markdown
Collaborator

I think the better solution is to remove NaNs in the first place:

  1. copy df_trials
  2. remove trials with NaNs (like mouse made no choice)
  3. Compute ETR, with censoring

I would also call alignment.event_triggered_response directly, rather than import plot_fip just to call a wrapper function

@rachelstephlee

rachelstephlee commented Aug 23, 2025

Copy link
Copy Markdown
Collaborator Author

I think the better solution is to remove NaNs in the first place:

  1. copy df_trials
  2. remove trials with NaNs (like mouse made no choice)

both done (dropping na helped with that)

  1. Compute ETR, with censoring

done. added censoring as an option.

I would also call alignment.event_triggered_response directly, rather than import plot_fip just to call a wrapper function

Done. this was really helpful and made it much cleaner.

@rachelstephlee

Copy link
Copy Markdown
Collaborator Author

Addressed comments and checked functionality.

I added a wrapper function for multiple NWBs as well.. I was working on it concurrently, so I figured we can put it in as well.


if not hasattr(nwb, "df_trials"):
raise ValueError("You need to compute df_trials: nwb_utils.create_trials_df(nwb)")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add a check here that df_fip has been computed, and reference the code to do it:
if not hasattr(nwb, "df_fip"):
raise AttributeError("You need to compute df_fip: nwb_utils.create_fib_df(nwb)")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

df_trials = nwb.df_trials.dropna(subset=alignment_event, inplace=False)
df_trials = df_trials.sort_values(by=alignment_event)

data = nwb.df_fip.query("event == @channel")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check here that data is not empty. This could happen if the channel isn't in the df

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. added a check that the channel is in the df_fip.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should i just return an empty df_trials if the channel isn't present or give a value error if channel isn't in the df_fip?

just thinking of cases when i am running this for multiple NWB's, i might have some channels that exist for a particular NWB vs not. In that case, i might want to skip over that call rather than return an error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would either:

  • generate an error
  • return the unchanged original df_trials, and then generate a warning about the missing channel

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or, I guess I see that in the multiple session version you are concatenating the df_trials, so an empty dataframe is fine

# Check alignment_event is in df_trials columns
if alignment_event not in nwb.df_trials.columns:
raise ValueError(f"alignment_event '{alignment_event}' not found in df_trials columns.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a check that data_column is in the dataframe

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

output_col = (
f"{data_column}_{channel}_{offsets[0]}_"
f"{offsets[1]}_{alignment_event.replace('_in_session','')}"
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you add a check that the output column isn't already in the dataframe?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if user provides an output_col "Average_signal" but uses different data_columns, channels, and offsets, we should allow them to update it.

I would rather keep this as is if it's OK

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, thats fine

@alexpiet alexpiet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add additional checks for input

@alexpiet alexpiet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for the fixes

@rachelstephlee rachelstephlee merged commit 73f94a9 into main Aug 28, 2025
3 checks passed
@alexpiet alexpiet deleted the avg-signal-window branch September 4, 2025 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants