Skip to content

Commit 0579275

Browse files
missed df_sess_fm and also added enrich_df_sess
1 parent f458246 commit 0579275

2 files changed

Lines changed: 167 additions & 32 deletions

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
2+
import pandas as pd
3+
import numpy as np
4+
5+
6+
7+
8+
9+
10+
11+
12+
13+
def add_slope_to_df_sess(df_sess, df_slope, slope_col_name, channel_name,
14+
session_date_col='session_date', channel_col='channel',
15+
new_col_name=None):
16+
"""
17+
Add a slope column from df_slope into df_sess by matching session_date.
18+
- df_sess: sessions dataframe
19+
- df_slope: slopes dataframe (must contain channel_col and session_date_col)
20+
- slope_col_name: name of the slope column in df_slope to pull (e.g. 'slope (RPE >= 0)')
21+
- channel_name: channel string to filter df_slope by
22+
- new_col_name: optional name for the added column in df_sess (defaults to slope_col_name)
23+
Returns a new dataframe (does not modify inputs).
24+
"""
25+
if new_col_name is None:
26+
new_col_name = channel_name.split('dff')[0] + slope_col_name
27+
28+
df_slope = df_slope.rename(columns={'date': session_date_col})
29+
# filter slope table to the requested channel and keep only date + slope column
30+
slope_filtered = (df_slope
31+
.loc[df_slope[channel_col] == channel_name, [session_date_col, slope_col_name]]
32+
.copy())
33+
34+
# if there are duplicate session_date rows for the same channel take the mean (simple resolution)
35+
slope_filtered = slope_filtered.groupby(session_date_col, as_index=False).mean()
36+
37+
# merge into sessions on session date
38+
merged = df_sess.merge(slope_filtered, on=session_date_col, how='left')
39+
40+
# if original slope column name collides with other columns, rename to new_col_name
41+
if slope_col_name != new_col_name and slope_col_name in merged.columns:
42+
merged = merged.rename(columns={slope_col_name: new_col_name})
43+
44+
return merged
45+
46+
def add_all_slopes_to_df_sess(df_sess, df_slope, slope_type,
47+
session_date_col='session_date',
48+
channel_col='channel'):
49+
50+
51+
df_sess_slope = df_sess.copy()
52+
53+
if isinstance(slope_type, str):
54+
slope_type = [slope_type]
55+
slope_cols = []
56+
slope_col_names = {'slope (RPE >= 0)':'slope_pos', 'slope (RPE < 0)':'slope_neg'}
57+
if 'pos' in slope_type or 'positive' in slope_type or'slope (RPE >= 0)' in slope_type:
58+
slope_cols.append('slope (RPE >= 0)')
59+
if 'neg' in slope_type or 'negative' in slope_type or'slope (RPE < 0)' in slope_type:
60+
slope_cols.append('slope (RPE < 0)')
61+
if 'both' in slope_type or 'all' in slope_type:
62+
slope_cols.append(['slope (RPE >= 0)', 'slope (RPE < 0)'])
63+
64+
for channel in df_slope[channel_col].unique():
65+
for slope_col in slope_cols:
66+
df_sess_slope = add_slope_to_df_sess(df_sess_slope, df_slope, slope_col, channel,
67+
session_date_col=session_date_col,
68+
channel_col=channel_col,
69+
new_col_name=f'{channel.split("dff")[0]}{slope_col_names[slope_col]}')
70+
return df_sess_slope
71+
72+
def enrich_df_sess_from_nwbs(nwb_list, df_sess, extractor_func, new_col_name):
73+
"""
74+
Call extractor_func for each nwb in nwb_list and merge results into df_sess.
75+
extractor_func may return:
76+
- (session_date, value)
77+
- dict {session_date: value}
78+
- pandas.Series indexed by session_date
79+
- scalar (in which case the function will try to obtain session_date from the nwb)
80+
The merged column will be named new_col_name.
81+
Returns a new dataframe (does not modify inputs).
82+
"""
83+
rows = []
84+
for nwb in nwb_list:
85+
res = extractor_func(nwb)
86+
session_date = nwb.session_id.split('_')[1]
87+
rows.append({'session_date': str(session_date) if session_date is not None else None, new_col_name: res})
88+
89+
df_new = pd.DataFrame(rows).dropna(subset=['session_date'])
90+
91+
# align column name for merge
92+
merged = df_sess.merge(df_new, on='session_date', how='left')
93+
return merged
94+
95+
# a bunch of extractor functions
96+
97+
98+
def get_max_side_bias(nwb):
99+
100+
return nwb.df_trials['side_bias'].max()
101+
102+
def get_mean_side_bias(nwb):
103+
104+
return nwb.df_trials['side_bias'].mean()
105+
106+
def get_baited_rate(nwb):
107+
df_trials = nwb.df_trials
108+
mask = ((df_trials['bait_left'] == True) & (df_trials['choice'] == 0.0)) | ((df_trials['bait_right'] == True) & (df_trials['choice'] == 1.0))
109+
return float(mask.sum()) / float(len(df_trials))
110+
111+
def get_left_choice_rate(nwb):
112+
df_trials = nwb.df_trials
113+
left_count = (df_trials['choice'] == 0.0).sum()
114+
return float(left_count) / float(len(df_trials))
115+
116+
def get_ignore_choice_rate(nwb):
117+
df_trials = nwb.df_trials
118+
ignore_count = (df_trials['choice'] == 2.0).sum()
119+
return float(ignore_count) / float(len(df_trials))
120+
121+
def get_left_right_diff(nwb):
122+
df = getattr(nwb, 'df_trials', None)
123+
if df is None or len(df) == 0:
124+
return float('nan')
125+
left = (df['choice'] == 0.0).sum()
126+
right = (df['choice'] == 1.0).sum()
127+
return float(left - right) / float(len(df)) # signed difference normalized by total trials
128+
129+
def get_left_right_abs_diff(nwb):
130+
v = get_left_right_diff(nwb)
131+
return abs(v) if not np.isnan(v) else v # magnitude of bias (0..1)
132+
133+
def enrich_df_sess_with_all_getters(nwb_list, df_sess):
134+
"""
135+
Enrich df_sess by calling enrich_df_sess_from_nwbs for each get_* extractor
136+
defined in this module. Returns a new dataframe with added columns:
137+
138+
- 'max_side_bias'
139+
- 'mean_side_bias'
140+
- 'baited_rate'
141+
- 'left_choice_rate'
142+
- 'ignore_choice_rate'
143+
- 'left_right_diff'
144+
- 'left_right_abs_diff'
145+
146+
Parameters:
147+
- nwb_list: iterable of nwb objects
148+
- df_sess: sessions dataframe
149+
150+
This function does not modify the inputs; it returns an enriched copy.
151+
"""
152+
enrichments = [
153+
('max_side_bias', get_max_side_bias),
154+
('mean_side_bias', get_mean_side_bias),
155+
('baited_rate', get_baited_rate),
156+
('left_choice_rate', get_left_choice_rate),
157+
('ignore_choice_rate', get_ignore_choice_rate),
158+
('left_right_diff', get_left_right_diff),
159+
('left_right_abs_diff', get_left_right_abs_diff),
160+
]
161+
162+
df_out = df_sess.copy()
163+
for col_name, extractor in enrichments:
164+
df_out = enrich_df_sess_from_nwbs(nwb_list, df_out, extractor, col_name)
165+
166+
return df_out

src/rachel_analysis_utils/nwb_utils.py

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -255,38 +255,7 @@ def load_nwb_list(plot_loc):
255255

256256
return nwbs, df_sess, df_slope
257257

258-
def add_slope_to_sess(df_sess, df_slope, slope_col_name, channel_name,
259-
session_date_col='session_date', channel_col='channel',
260-
new_col_name=None):
261-
"""
262-
Add a slope column from df_slope into df_sess by matching session_date.
263-
- df_sess: sessions dataframe
264-
- df_slope: slopes dataframe (must contain channel_col and session_date_col)
265-
- slope_col_name: name of the slope column in df_slope to pull (e.g. 'slope (RPE >= 0)')
266-
- channel_name: channel string to filter df_slope by
267-
- new_col_name: optional name for the added column in df_sess (defaults to slope_col_name)
268-
Returns a new dataframe (does not modify inputs).
269-
"""
270-
if new_col_name is None:
271-
new_col_name = channel_name.split('dff')[0] + slope_col_name
272-
273-
df_slope = df_slope.rename(columns={'date': session_date_col})
274-
# filter slope table to the requested channel and keep only date + slope column
275-
slope_filtered = (df_slope
276-
.loc[df_slope[channel_col] == channel_name, [session_date_col, slope_col_name]]
277-
.copy())
278-
279-
# if there are duplicate session_date rows for the same channel take the mean (simple resolution)
280-
slope_filtered = slope_filtered.groupby(session_date_col, as_index=False).mean()
281-
282-
# merge into sessions on session date
283-
merged = df_sess.merge(slope_filtered, on=session_date_col, how='left')
284-
285-
# if original slope column name collides with other columns, rename to new_col_name
286-
if slope_col_name != new_col_name and slope_col_name in merged.columns:
287-
merged = merged.rename(columns={slope_col_name: new_col_name})
288258

289-
return merged
290259

291260
def get_dummy_nwbs(df_trials, df_events, df_fip):
292261
ses_idx_list = df_trials.ses_idx.unique()
@@ -444,4 +413,4 @@ def get_nwb_processed(file_locations, **parameters) -> None:
444413
df_trials_final = df_trials
445414

446415
# return all dataframes
447-
return (df_sess, df_trials_final, df_events, df_fip_final)
416+
return (df_sess_fm, df_trials_final, df_events, df_fip_final)

0 commit comments

Comments
 (0)