labdata 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {labdata-0.1.1 → labdata-0.1.2}/PKG-INFO +1 -1
- {labdata-0.1.1 → labdata-0.1.2}/labdata/__init__.py +1 -1
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/caiman.py +24 -27
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/decisiontask.py +98 -81
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/segmentation.py +248 -23
- labdata-0.1.2/labdata/schema/twophoton.py +368 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/PKG-INFO +1 -1
- labdata-0.1.1/labdata/schema/twophoton.py +0 -203
- {labdata-0.1.1 → labdata-0.1.2}/LICENSE +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/README.md +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/cli.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/__init__.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/ec2.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/ephys.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/pose.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/schedulers.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/singularity.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/suite2p.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/compute/utils.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/copy.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/compute.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/console.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/explorer/__init__.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/explorer/cellsegmentation.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/explorer/spikesorting.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/index.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/notes.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/procedures.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/sorting.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/dashboard/video.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/export.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/rules/__init__.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/rules/ephys.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/rules/imaging.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/rules/utils.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/s3.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/__init__.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/ephys.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/general.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/histology.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/onephoton.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/procedures.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/tasks.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/utils.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/schema/video.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/stacks.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/utils.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata/widgets.py +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/SOURCES.txt +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/entry_points.txt +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/requires.txt +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/pyproject.toml +0 -0
- {labdata-0.1.1 → labdata-0.1.2}/setup.cfg +0 -0
|
@@ -22,7 +22,7 @@ class CaimanCompute(Suite2pCompute):
|
|
|
22
22
|
# default parameters
|
|
23
23
|
self.parameters = dict(algorithm_name = 'caiman')
|
|
24
24
|
# will only store these in CellSegmentationParams
|
|
25
|
-
self.parameter_keys = ['pw_rigid','p','gSig','gSiz','merge_thr','rf','stride','tsub',
|
|
25
|
+
self.parameter_keys = ['pw_rigid','p','gSig','gSig_filt','max_shift','gSiz','merge_thr','rf','stride','tsub',
|
|
26
26
|
'ssub','nb','min_corr','min_pnr','ssub_B','ring_size_factor',
|
|
27
27
|
'min_SNR', 'rval_thr', 'use_cnn', 'detrendWin','quantileMin','denoise_dff']
|
|
28
28
|
|
|
@@ -50,12 +50,18 @@ class CaimanCompute(Suite2pCompute):
|
|
|
50
50
|
parser.add_argument('-m','--pwrigid',
|
|
51
51
|
action='store_true', default=False,
|
|
52
52
|
help = "Piecewise-rigid registration")
|
|
53
|
+
parser.add_argument('--max-allowed-shift',
|
|
54
|
+
action='store', default=[20,20], type = int, nargs = 2,
|
|
55
|
+
help = "maximum allowed shift for motion correction")
|
|
53
56
|
parser.add_argument('-p',
|
|
54
57
|
action='store', default=1, type = int,
|
|
55
58
|
help = "Order of the autoregressive system")
|
|
56
59
|
parser.add_argument('-g','--gsig',
|
|
57
60
|
action='store', default=[6,6], type = int, nargs = 2,
|
|
58
61
|
help = "Expected halfwidth of the neurons in pixels")
|
|
62
|
+
parser.add_argument('--gsig-filt',
|
|
63
|
+
action='store', default=None, type = int, nargs = 2,
|
|
64
|
+
help = "Filter for 1photon data")
|
|
59
65
|
parser.add_argument('--nb',
|
|
60
66
|
action='store', default=0, type = int,
|
|
61
67
|
help = "number of background components (rank) if positive, set to 0 for CNMFE")
|
|
@@ -113,8 +119,10 @@ class CaimanCompute(Suite2pCompute):
|
|
|
113
119
|
args = parser.parse_args(arguments[1:])
|
|
114
120
|
|
|
115
121
|
params = dict(pw_rigid = args.pwrigid,
|
|
122
|
+
max_shifts = args.max_allowed_shifts,
|
|
116
123
|
p = int(args.p),
|
|
117
124
|
gSig = [int(a) for a in args.gsig],
|
|
125
|
+
gSig_filt = [int(a) for a in args.gsig_filt],
|
|
118
126
|
gSiz = [int(a) for a in 2*np.array(args.gsig) + 1],
|
|
119
127
|
merge_thr = float(args.merge_thr),
|
|
120
128
|
rf = int(args.rf),
|
|
@@ -143,18 +151,18 @@ class CaimanCompute(Suite2pCompute):
|
|
|
143
151
|
temporary_folder = Path(prefs['scratch_path'])/f'caiman_temporary_{rand}'
|
|
144
152
|
dset = (self.schema.Miniscope() & self.dataset_key)
|
|
145
153
|
cnmfparams = {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
'motion_correct' : True,
|
|
155
|
+
'method_init': 'corr_pnr', # use this for 1 photon
|
|
156
|
+
'K': None, # for 1p
|
|
157
|
+
'nb': 0, # number of background components (rank) if positive, set to 0 for CNMFE
|
|
158
|
+
'nb_patch': 0,
|
|
159
|
+
'low_rank_background': None, # for 1p
|
|
160
|
+
'update_background_components': True, # sometimes setting to False improve the results
|
|
161
|
+
'del_duplicates': True, # whether to remove duplicates from initialization
|
|
162
|
+
'normalize_init': False, # just leave as is
|
|
163
|
+
'center_psf': True, # True for 1p
|
|
164
|
+
'only_init': True, # set it to True to run CNMF-E
|
|
165
|
+
'method_deconvolution': 'oasis'} # could use 'cvxpy' alternatively
|
|
158
166
|
|
|
159
167
|
if not len(dset):
|
|
160
168
|
dset = (self.schema.TwoPhoton() & self.dataset_key)
|
|
@@ -244,7 +252,7 @@ class CaimanCompute(Suite2pCompute):
|
|
|
244
252
|
print(f"Number accepted: {len(cnmfe_model.estimates.idx_components)}")
|
|
245
253
|
print(f"Number rejected: {len(cnmfe_model.estimates.idx_components_bad)}")
|
|
246
254
|
cluster.terminate()
|
|
247
|
-
from ..stacks import compute_projections
|
|
255
|
+
from ..stacks import compute_projections,get_roi_pixels,get_roi_contour
|
|
248
256
|
mean_proj,std_proj,max_proj,corr_proj = compute_projections(images)
|
|
249
257
|
print('Projections computed.')
|
|
250
258
|
import caiman
|
|
@@ -330,19 +338,8 @@ class CaimanCompute(Suite2pCompute):
|
|
|
330
338
|
os.unlink(f)
|
|
331
339
|
else:
|
|
332
340
|
print(f'[{self.name} job] Kept the temporary folder {temporary_folder}.')
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
from skimage.measure import find_contours
|
|
336
|
-
# find_contours?
|
|
337
|
-
level = np.percentile(roi_mask[roi_mask>0], percentile_threshold)
|
|
338
|
-
C = find_contours(roi_mask,level = level) # there should be only one contour, this takes the first one
|
|
339
|
-
return C[0]
|
|
340
|
-
|
|
341
|
-
def get_roi_pixels(roi_mask):
|
|
342
|
-
ii = np.ravel_multi_index(np.where(roi_mask!=0), roi_mask.shape)
|
|
343
|
-
return ii, np.take(roi_mask,ii)
|
|
344
|
-
|
|
345
|
-
|
|
341
|
+
CellSegmentationMetrics().populate(planekey, display_progress = True)
|
|
342
|
+
|
|
346
343
|
def setup_cluster(n_cpus,cluster = None):
|
|
347
344
|
from caiman import stop_server, cluster
|
|
348
345
|
#%% start a cluster for parallel processing (if a cluster already exists it will be closed and a new session will be opened)
|
|
@@ -46,7 +46,7 @@ axis = alt.Axis(domain=True, domainColor='black', domainCap='butt',
|
|
|
46
46
|
|
|
47
47
|
def compute_psychometric(trialset_rows):
|
|
48
48
|
"""Proportion of rightward responses per condition per trialset with Wilson CI.
|
|
49
|
-
response_values convention:
|
|
49
|
+
response_values convention: side1=1, no response=0, side2=-1.
|
|
50
50
|
"""
|
|
51
51
|
from scipy.stats import binomtest
|
|
52
52
|
rows = []
|
|
@@ -73,44 +73,58 @@ def compute_psychometric(trialset_rows):
|
|
|
73
73
|
trialset_description=desc))
|
|
74
74
|
return pd.DataFrame(rows)
|
|
75
75
|
|
|
76
|
-
|
|
77
76
|
def decisionmaking_tab(schema = None):
|
|
78
77
|
@st.cache_data
|
|
79
|
-
def
|
|
78
|
+
def get_max_date():
|
|
79
|
+
try:
|
|
80
|
+
return pd.to_datetime(
|
|
81
|
+
(schema.DecisionTask.TrialSet() * schema.Session()).fetch('session_datetime')
|
|
82
|
+
).max().date()
|
|
83
|
+
except:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
@st.cache_data
|
|
87
|
+
def get_subjects_last_ran(end_date):
|
|
88
|
+
date_str = end_date.strftime('%Y-%m-%d')
|
|
80
89
|
subs = schema.Subject & schema.DecisionTask
|
|
81
90
|
try:
|
|
82
|
-
|
|
91
|
+
tt = (schema.DecisionTask() * schema.Session() &
|
|
92
|
+
f'session_datetime > (DATE("{date_str}") - INTERVAL 3 MONTH)' &
|
|
93
|
+
f'DATE(session_datetime) <= "{date_str}"')
|
|
94
|
+
subjects = (schema.Subject() & tt).fetch(format='frame').reset_index()
|
|
95
|
+
subjects = np.unique(subjects.subject_name.values)
|
|
83
96
|
except:
|
|
84
|
-
print('There are no sessions
|
|
85
|
-
return [],[]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
subjects = (schema.Subject() & tt).fetch(format = 'frame').reset_index()
|
|
89
|
-
subjects = np.unique(subjects.subject_name.values)
|
|
90
|
-
keys = [dict(subject_name = s) for s in subjects]
|
|
91
|
-
return subs.fetch('subject_name'), keys, max_date
|
|
97
|
+
print('There are no sessions.')
|
|
98
|
+
return [], []
|
|
99
|
+
keys = [dict(subject_name=s) for s in subjects]
|
|
100
|
+
return subs.fetch('subject_name'), keys
|
|
92
101
|
|
|
93
102
|
@st.cache_data
|
|
94
|
-
def get_summary_data(selected_indices,
|
|
95
|
-
keys = [dict(subject_name
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
from datetime import timedelta
|
|
103
|
+
def get_summary_data(selected_indices, end_date):
|
|
104
|
+
keys = [dict(subject_name=i) for i in selected_indices]
|
|
105
|
+
date_str = end_date.strftime('%Y-%m-%d')
|
|
106
|
+
date_filter = (f'session_datetime > (DATE("{date_str}") - INTERVAL 3 MONTH) '
|
|
107
|
+
f'AND DATE(session_datetime) <= "{date_str}"')
|
|
100
108
|
summarysource = pd.DataFrame((schema.Session*schema.DecisionTask.TrialSet() & keys & 'trialset_description NOT LIKE "%opto%"'
|
|
101
|
-
&
|
|
109
|
+
& date_filter).fetch())
|
|
102
110
|
if not len(summarysource):
|
|
103
111
|
return []
|
|
104
|
-
days = np.array([(summarysource.session_datetime.max() - timedelta(days=d)).date() for d in range(40,-2,-1)])
|
|
105
112
|
summarysource['date'] = summarysource.session_datetime.map(
|
|
106
113
|
lambda x: x.strftime("%y-%m-%d"))
|
|
107
114
|
return summarysource
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
ctrl_col, date_col, _ = st.columns([1, 2, 6])
|
|
117
|
+
with ctrl_col:
|
|
118
|
+
if st.button('Refresh', icon=':material/refresh:'):
|
|
119
|
+
get_max_date.clear()
|
|
120
|
+
get_subjects_last_ran.clear()
|
|
121
|
+
get_summary_data.clear()
|
|
122
|
+
with date_col:
|
|
123
|
+
end_date = st.date_input('End date', value=get_max_date(), label_visibility='collapsed')
|
|
112
124
|
|
|
113
|
-
|
|
125
|
+
if end_date is None:
|
|
126
|
+
return
|
|
127
|
+
all_subjects, subjects = get_subjects_last_ran(end_date)
|
|
114
128
|
if not len(subjects):
|
|
115
129
|
st.write('There are no sessions.')
|
|
116
130
|
return
|
|
@@ -118,10 +132,12 @@ def decisionmaking_tab(schema = None):
|
|
|
118
132
|
selected_indices = st.multiselect('Select subjects:',all_subjects,default = [s['subject_name'] for s in subjects])
|
|
119
133
|
if len(selected_indices):
|
|
120
134
|
# cache data to run faster
|
|
121
|
-
summarysource = get_summary_data(selected_indices,
|
|
135
|
+
summarysource = get_summary_data(selected_indices, end_date)
|
|
122
136
|
if not len(summarysource):
|
|
123
137
|
st.write('Could not find sessions for the specified dates..')
|
|
124
138
|
return
|
|
139
|
+
|
|
140
|
+
# Build heatmap chart
|
|
125
141
|
point_selector = alt.selection_point(on="click")
|
|
126
142
|
chart = alt.Chart(summarysource).mark_rect().encode(
|
|
127
143
|
x=alt.X('session_datetime:T',timeUnit = 'yearmonthdate',
|
|
@@ -130,9 +146,13 @@ def decisionmaking_tab(schema = None):
|
|
|
130
146
|
color=alt.Color('performance_easy:Q',
|
|
131
147
|
scale=alt.Scale(scheme='spectral', reverse=True, domain=[0.5,1], clamp=True)).title('Perf easy'),
|
|
132
148
|
tooltip = ['subject_name:N','session_name:N','session_datetime:T','performance_easy:Q','performance:Q','n_trials:Q']
|
|
149
|
+
).properties(title ='Behavioral performance').add_params(point_selector)
|
|
150
|
+
|
|
151
|
+
# Heatmap (session selector) and psychometric curve side-by-side
|
|
152
|
+
col_map, col_psych = st.columns([3, 2])
|
|
133
153
|
|
|
134
|
-
|
|
135
|
-
|
|
154
|
+
with col_map:
|
|
155
|
+
event_data = st.altair_chart(chart.interactive(), on_select='rerun', width='stretch')
|
|
136
156
|
|
|
137
157
|
if len(event_data["selection"]["param_1"]):
|
|
138
158
|
subject_name = event_data["selection"]["param_1"][0]["subject_name"]
|
|
@@ -148,7 +168,6 @@ def decisionmaking_tab(schema = None):
|
|
|
148
168
|
st.session_state['decision_last_clicked_session'] = new_session
|
|
149
169
|
except Exception:
|
|
150
170
|
pass
|
|
151
|
-
st.write(f'### _{subject_name}_')
|
|
152
171
|
|
|
153
172
|
@st.cache_resource
|
|
154
173
|
def get_subject_data(subject_name):
|
|
@@ -164,10 +183,6 @@ def decisionmaking_tab(schema = None):
|
|
|
164
183
|
& dict(subject_name=subject_name, session_name=session_name)
|
|
165
184
|
).fetch(*cols, as_dict=True)
|
|
166
185
|
|
|
167
|
-
if st.button('Refresh sessions', icon=':material/refresh:'):
|
|
168
|
-
get_subject_data.clear()
|
|
169
|
-
get_session_trials.clear()
|
|
170
|
-
|
|
171
186
|
trialset, weights, watering = get_subject_data(subject_name)
|
|
172
187
|
sessions = sorted(trialset['session_datetime'].unique())
|
|
173
188
|
|
|
@@ -182,7 +197,7 @@ def decisionmaking_tab(schema = None):
|
|
|
182
197
|
except Exception:
|
|
183
198
|
pass
|
|
184
199
|
|
|
185
|
-
# resolve selected session before
|
|
200
|
+
# resolve selected session before rendering so selected_dt is available everywhere
|
|
186
201
|
session_names = trialset[['session_datetime','session_name']].drop_duplicates('session_name').sort_values('session_datetime')
|
|
187
202
|
snames = list(session_names['session_name'])
|
|
188
203
|
clicked_session = st.session_state.pop('decision_selected_session', None)
|
|
@@ -196,46 +211,19 @@ def decisionmaking_tab(schema = None):
|
|
|
196
211
|
selected_session = snames[session_idx]
|
|
197
212
|
selected_dt = session_names.loc[session_names['session_name'] == selected_session, 'session_datetime'].iloc[0]
|
|
198
213
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
perf_val = trialset.dropna(subset=['performance_easy','performance']).sort_values('session_datetime').drop_duplicates('session_datetime').iloc[[-1]]
|
|
204
|
-
x_enc = alt.X('session_datetime:T', axis=axis).title('Session date')
|
|
205
|
-
perf_chart = (
|
|
206
|
-
alt.Chart(trialset).mark_line(color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q', axis=axis).title('Performance')).properties(width=1000) +
|
|
207
|
-
alt.Chart(trialset).mark_point(color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q'), tooltip=['session_name:N','session_datetime:T','performance_easy:Q','performance:Q']) +
|
|
208
|
-
alt.Chart(perf_val).mark_text(align='left', dx=6, dy=100, color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q'), text=alt.value('easy')) +
|
|
209
|
-
alt.Chart(trialset).mark_line(color=orange).encode(x=x_enc, y=alt.Y('performance:Q', axis=axis)) +
|
|
210
|
-
alt.Chart(trialset).mark_point(color=orange).encode(x=x_enc, y=alt.Y('performance:Q'),
|
|
211
|
-
tooltip=['session_name:N','session_datetime:T','performance_easy:Q','performance:Q']) +
|
|
212
|
-
alt.Chart(perf_val).mark_text(align='left', dx=6, dy=100, color=orange).encode(x=x_enc, y=alt.Y('performance:Q'), text=alt.value('all trials'))
|
|
213
|
-
)
|
|
214
|
-
ruler_df = pd.DataFrame({'t': [selected_dt]})
|
|
215
|
-
def make_ruler(x_field):
|
|
216
|
-
return alt.Chart(ruler_df).mark_rule(
|
|
217
|
-
color='grey', strokeDash=[10, 10], strokeWidth=1.5
|
|
218
|
-
).encode(x=alt.X(f't:T'))
|
|
219
|
-
|
|
220
|
-
chartw = line_point_plot(weights,
|
|
221
|
-
x=alt.X('weighing_datetime:T',axis=axis).title(''),
|
|
222
|
-
y=alt.Y('weight:Q',axis=axis).title('Weight (g)'),
|
|
223
|
-
tooltip=['weighing_datetime', 'weight'],
|
|
224
|
-
color = colors[0])
|
|
225
|
-
charth2o = line_point_plot(watering,
|
|
226
|
-
x=alt.X('watering_datetime:T',axis=axis).title('Session date'),
|
|
227
|
-
y=alt.Y('water_volume:Q',axis=axis).title('Water volume (mL)'),
|
|
228
|
-
tooltip=['watering_datetime', 'water_volume'],
|
|
229
|
-
color = colors[2])
|
|
230
|
-
linked = alt.vconcat(
|
|
231
|
-
perf_chart + make_ruler('session_datetime'),
|
|
232
|
-
chartw + make_ruler('weighing_datetime'),
|
|
233
|
-
charth2o + make_ruler('watering_datetime'),
|
|
234
|
-
).resolve_scale(x='shared')
|
|
235
|
-
st.altair_chart(linked.interactive(), width="stretch")
|
|
214
|
+
with col_map:
|
|
215
|
+
if st.button('Refresh sessions', icon=':material/refresh:'):
|
|
216
|
+
get_subject_data.clear()
|
|
217
|
+
get_session_trials.clear()
|
|
236
218
|
|
|
219
|
+
# Psychometric curve and reaction time in right column
|
|
237
220
|
with col_psych:
|
|
238
|
-
|
|
221
|
+
name_col, slider_col = st.columns([1, 3])
|
|
222
|
+
name_col.write(f'### _{subject_name}_')
|
|
223
|
+
if len(snames) > 1:
|
|
224
|
+
with slider_col:
|
|
225
|
+
st.slider('Session', min_value=0, max_value=len(snames)-1,
|
|
226
|
+
key='decision_session_slider')
|
|
239
227
|
trial_rows = get_session_trials(subject_name, selected_session)
|
|
240
228
|
psych_df = compute_psychometric(trial_rows)
|
|
241
229
|
if not psych_df.empty:
|
|
@@ -258,8 +246,8 @@ def decisionmaking_tab(schema = None):
|
|
|
258
246
|
)
|
|
259
247
|
lines = base.mark_line().encode(y=alt.Y('proportion:Q'))
|
|
260
248
|
st.altair_chart((lines + errorbars + pts).properties(
|
|
261
|
-
width=400, height=
|
|
262
|
-
).interactive(), width=
|
|
249
|
+
width=400, height=400, title=f'{pd.Timestamp(selected_dt).strftime("%Y-%m-%d %H:%M")}'
|
|
250
|
+
).interactive(), width='stretch')
|
|
263
251
|
|
|
264
252
|
# reaction time per condition
|
|
265
253
|
rt_rows = []
|
|
@@ -298,15 +286,11 @@ def decisionmaking_tab(schema = None):
|
|
|
298
286
|
)
|
|
299
287
|
rt_lines = rt_base.mark_line().encode(y=alt.Y('median_rt:Q'))
|
|
300
288
|
st.altair_chart((rt_lines + rt_err + rt_pts).properties(
|
|
301
|
-
width=400, height=
|
|
302
|
-
).interactive(), width=
|
|
289
|
+
width=400, height=400, title='Reaction time per condition'
|
|
290
|
+
).interactive(), width='stretch')
|
|
303
291
|
else:
|
|
304
292
|
st.error('No data available for this session.')
|
|
305
293
|
|
|
306
|
-
if len(snames) > 1:
|
|
307
|
-
st.slider('Session', min_value=0, max_value=len(snames)-1,
|
|
308
|
-
key='decision_session_slider')
|
|
309
|
-
|
|
310
294
|
@st.cache_data
|
|
311
295
|
def get_session_notes(subject_name, session_name):
|
|
312
296
|
rows = (schema.Dataset * schema.Note
|
|
@@ -320,11 +304,44 @@ def decisionmaking_tab(schema = None):
|
|
|
320
304
|
st.caption(f"*{n['notetaker']} — {pd.Timestamp(n['note_datetime']).strftime('%Y-%m-%d %H:%M')}*")
|
|
321
305
|
st.write(n['notes'])
|
|
322
306
|
|
|
323
|
-
|
|
307
|
+
# Performance and watering charts under the colormap in the left column (80% width)
|
|
308
|
+
with col_map:
|
|
309
|
+
perf_col, _ = st.columns([4, 1])
|
|
310
|
+
black, orange = '#000000','#ff7f0e'
|
|
311
|
+
perf_val = trialset.dropna(subset=['performance_easy','performance']).sort_values('session_datetime').drop_duplicates('session_datetime').iloc[[-1]]
|
|
312
|
+
x_enc = alt.X('session_datetime:T', axis=axis).title('Session date')
|
|
313
|
+
perf_chart = (
|
|
314
|
+
alt.Chart(trialset).mark_line(color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q', axis=axis).title('Performance')).properties(width=400, height=150) +
|
|
315
|
+
alt.Chart(trialset).mark_point(color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q'), tooltip=['session_name:N','session_datetime:T','performance_easy:Q','performance:Q']) +
|
|
316
|
+
alt.Chart(perf_val).mark_text(align='left', dx=6, dy=100, color=black).encode(x=x_enc, y=alt.Y('performance_easy:Q'), text=alt.value('easy')) +
|
|
317
|
+
alt.Chart(trialset).mark_line(color=orange).encode(x=x_enc, y=alt.Y('performance:Q', axis=axis)) +
|
|
318
|
+
alt.Chart(trialset).mark_point(color=orange).encode(x=x_enc, y=alt.Y('performance:Q'),
|
|
319
|
+
tooltip=['session_name:N','session_datetime:T','performance_easy:Q','performance:Q']) +
|
|
320
|
+
alt.Chart(perf_val).mark_text(align='left', dx=6, dy=100, color=orange).encode(x=x_enc, y=alt.Y('performance:Q'), text=alt.value('all trials'))
|
|
321
|
+
)
|
|
322
|
+
chartw = line_point_plot(weights,
|
|
323
|
+
x=alt.X('weighing_datetime:T',axis=axis).title(''),
|
|
324
|
+
y=alt.Y('weight:Q',axis=axis).title('Weight (g)'),
|
|
325
|
+
tooltip=['weighing_datetime', 'weight'],
|
|
326
|
+
color=colors[0], height=150)
|
|
327
|
+
charth2o = line_point_plot(watering,
|
|
328
|
+
x=alt.X('watering_datetime:T',axis=axis).title('Session date'),
|
|
329
|
+
y=alt.Y('water_volume:Q',axis=axis).title('Water volume (mL)'),
|
|
330
|
+
tooltip=['watering_datetime', 'water_volume'],
|
|
331
|
+
color=colors[2], height=150)
|
|
332
|
+
linked = alt.vconcat(
|
|
333
|
+
perf_chart,
|
|
334
|
+
chartw,
|
|
335
|
+
charth2o,
|
|
336
|
+
).resolve_scale(x='shared')
|
|
337
|
+
with perf_col:
|
|
338
|
+
st.altair_chart(linked.interactive(), width='stretch')
|
|
339
|
+
|
|
340
|
+
def line_point_plot(data, x, y, tooltip, color, height=150):
|
|
324
341
|
if data is None or not len(data):
|
|
325
342
|
return alt.Chart(pd.DataFrame()).mark_point()
|
|
326
343
|
scatter = alt.Chart(data).mark_point(color=color).encode(
|
|
327
344
|
x=x, y=y, tooltip=tooltip)
|
|
328
345
|
line = alt.Chart(data).mark_line(color=color).encode(
|
|
329
|
-
x=x, y=y).properties(width=
|
|
346
|
+
x=x, y=y).properties(width=400, height=height)
|
|
330
347
|
return scatter + line
|