From 3b7041724173aaefd9e0a67af9f569e6d8fa3618 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 18 Oct 2021 21:29:18 -0700 Subject: [PATCH 001/187] use cell_roi_id when no cell_specimen_id --- visual_behavior/ophys/response_analysis/response_analysis.py | 2 +- visual_behavior/ophys/response_analysis/response_processing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/ophys/response_analysis/response_analysis.py b/visual_behavior/ophys/response_analysis/response_analysis.py index d4afe403f..246a9bf16 100644 --- a/visual_behavior/ophys/response_analysis/response_analysis.py +++ b/visual_behavior/ophys/response_analysis/response_analysis.py @@ -210,7 +210,6 @@ def get_response_df(self, df_name='trials_response_df', df_format=None): self.save_response_df(df, df_name) else: # default behavior - create the df df = self.get_df_for_df_name(df_name, df_format if df_format is not None else self.dataframe_format) - if 'trials' in df_name: trials = self.dataset.trials trials = trials.rename( @@ -254,6 +253,7 @@ def get_response_df(self, df_name='trials_response_df', df_format=None): stimulus_presentations = self.dataset.stimulus_presentations.copy() df = df.merge(stimulus_presentations, right_on='stimulus_presentations_id', left_on='stimulus_presentations_id') + return df def get_trials_response_df(self, df_format=None): diff --git a/visual_behavior/ophys/response_analysis/response_processing.py b/visual_behavior/ophys/response_analysis/response_processing.py index c6dbae274..2c655920d 100644 --- a/visual_behavior/ophys/response_analysis/response_processing.py +++ b/visual_behavior/ophys/response_analysis/response_processing.py @@ -490,7 +490,7 @@ def get_stimulus_response_xr(dataset, use_events=False, filter_events=True, fram traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.index.values + trace_ids = dataset.dff_traces.cell_roi_id.values timestamps = dataset.ophys_timestamps event_times = dataset.stimulus_presentations['start_time'].values event_ids = dataset.stimulus_presentations.index.values From 7f488c29e130c1e9c56b72d93c4ae4eea4b515fb Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 19 Oct 2021 12:59:58 -0700 Subject: [PATCH 002/187] use cell_roi_ids for all dfs --- .../ophys/response_analysis/response_processing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/visual_behavior/ophys/response_analysis/response_processing.py b/visual_behavior/ophys/response_analysis/response_processing.py index 2c655920d..5032cc13b 100644 --- a/visual_behavior/ophys/response_analysis/response_processing.py +++ b/visual_behavior/ophys/response_analysis/response_processing.py @@ -452,7 +452,7 @@ def get_trials_response_xr(dataset, use_events=False, filter_events=False, frame traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.index.values + trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids timestamps = dataset.ophys_timestamps change_trials = dataset.trials[~pd.isnull(dataset.trials['change_time'])] # [:-1] # last trial can get cut off event_times = change_trials['change_time'].values @@ -490,7 +490,7 @@ def get_stimulus_response_xr(dataset, use_events=False, filter_events=True, fram traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.cell_roi_id.values + trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids timestamps = dataset.ophys_timestamps event_times = dataset.stimulus_presentations['start_time'].values event_ids = dataset.stimulus_presentations.index.values @@ -527,7 +527,7 @@ def get_omission_response_xr(dataset, use_events=False, filter_events=False, fra traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.index.values + trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids timestamps = dataset.ophys_timestamps stimuli = dataset.stimulus_presentations omission_presentations = stimuli[stimuli.image_name == 'omitted'] From 9f35907248cea5d221dccc1b714255594dbd1687 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 19 Oct 2021 13:00:12 -0700 Subject: [PATCH 003/187] revise metrics_mask plotting --- .../visualization/ophys/experiment_summary_figures.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/visual_behavior/visualization/ophys/experiment_summary_figures.py b/visual_behavior/visualization/ophys/experiment_summary_figures.py index cf230b38d..3efb9a5b7 100644 --- a/visual_behavior/visualization/ophys/experiment_summary_figures.py +++ b/visual_behavior/visualization/ophys/experiment_summary_figures.py @@ -362,10 +362,10 @@ def format_table_data(dataset): return table_data -def plot_metrics_mask(dataset, metrics, cell_list, metric_name, max_image=True, cmap='RdBu', ax=None, save=False, +def plot_metrics_mask(roi_mask_array, metrics, cell_list, metric_name, max_projection=None, cmap='RdBu', ax=None, save=False, colorbar=False): # roi_dict = dataset.roi_dict.copy() - roi_mask_array = dataset.roi_mask_array.copy() + # roi_mask_array = dataset.roi_mask_array.copy() if cmap == 'hls': from matplotlib.colors import ListedColormap cmap = ListedColormap(sns.color_palette('hls', 8)) @@ -377,8 +377,8 @@ def plot_metrics_mask(dataset, metrics, cell_list, metric_name, max_image=True, if ax is None: figsize = (10, 10) fig, ax = plt.subplots(figsize=figsize) - if max_image is True: - ax.imshow(dataset.max_projection, cmap='gray', vmin=0, vmax=np.amax(dataset.max_projection)) + if max_projection is not None: + ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection)) for roi in cell_list: tmp = roi_mask_array[roi, :, :].copy() mask = np.empty(tmp.shape, dtype=np.float) From be88e5f2056814dd4425512710c2140450ea0116 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 19 Oct 2021 13:17:10 -0700 Subject: [PATCH 004/187] multi session dfs for learning project mouse --- scripts/create_multi_session_df.py | 57 +++++++++++++++++++++----- scripts/run_create_multi_session_df.py | 14 ++++--- visual_behavior/data_access/loading.py | 2 + 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 196549834..b9805c2d6 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -19,21 +19,40 @@ print(project_code, session_number) - # omissions + # stimulus response - df_name = 'omission_response_df' - conditions = ['cell_specimen_id'] + df_name = 'stimulus_response_df' + conditions = ['cell_specimen_id', 'is_change', 'image_name'] print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) - df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, + df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + filter_events=False, + use_extended_stimulus_presentations=False) + + + df_name = 'stimulus_response_df' + conditions = ['cell_specimen_id', 'image_name'] + + print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + filter_events=False, use_extended_stimulus_presentations=False) + # omissions + df_name = 'omission_response_df' - conditions = ['cell_specimen_id', 'epoch'] + conditions = ['cell_specimen_id'] print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, - use_extended_stimulus_presentations=True) + use_extended_stimulus_presentations=False) + + # df_name = 'omission_response_df' + # conditions = ['cell_specimen_id', 'epoch'] + # + # print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + # df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, + # use_extended_stimulus_presentations=True) # trials @@ -43,21 +62,39 @@ print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, use_extended_stimulus_presentations=False) + # + # df_name = 'trials_response_df' + # conditions = ['cell_specimen_id', 'stimulus_change', 'epoch'] + # + # print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + # df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, + # use_extended_stimulus_presentations=True) df_name = 'trials_response_df' - conditions = ['cell_specimen_id', 'stimulus_change', 'epoch'] + conditions = ['cell_specimen_id', 'stimulus_change', 'change_image_name'] print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, - use_extended_stimulus_presentations=True) + use_extended_stimulus_presentations=False) + df_name = 'trials_response_df' - conditions = ['cell_specimen_id', 'stimulus_change', 'change_image_name'] + conditions = ['cell_specimen_id', 'stimulus_change', 'hit'] print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) - df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, filter_events=False, + df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + filter_events=False, use_extended_stimulus_presentations=False) + + df_name = 'trials_response_df' + conditions = ['cell_specimen_id', 'stimulus_change', 'engaged'] + + print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + filter_events=False, + use_extended_stimulus_presentations=True) + print('done') # diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 70b2cf5a2..47b9636c9 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -24,12 +24,14 @@ # define the job record output folder stdout_location = r'/allen/programs/braintv/workgroups/nc-ophys/Marina/ClusterJobs/JobRecords' -cache_dir = loading.get_platform_analysis_cache_dir() -cache = bpc.from_s3_cache(cache_dir=cache_dir) -print(cache_dir) - -experiments_table = cache.get_ophys_experiment_table() - +# cache_dir = loading.get_platform_analysis_cache_dir() +# cache = bpc.from_s3_cache(cache_dir=cache_dir) +# print(cache_dir) +# experiments_table = cache.get_ophys_experiment_table() + +cache = VisualBehaviorOphysProjectCache.from_lims() +experiments_table = cache.get_ophys_experiment_table(passed_only=False) +experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 02cd91739..42eccba78 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -70,6 +70,8 @@ def get_platform_analysis_cache_dir(): This cache contains NWB files downloaded directly from AWS """ return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_cache' + # return r'//allen/programs/braintv/workgroups/nc-ophys/learning/analysis_files' + def get_production_cache_dir(): From 56148666044187eee4218e902ee3950a162d1ef1 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:21:44 -0700 Subject: [PATCH 005/187] change directory --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 42eccba78..c182bdf50 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -76,7 +76,7 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/2020_cache/production_cache' + cache_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/learning_mFISH' return cache_dir From cd82874883c0768771f1dbf0f1cf6aa5054fbbc7 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:24:43 -0700 Subject: [PATCH 006/187] fix cache usage --- scripts/run_create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 47b9636c9..aff249ba9 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -2,7 +2,7 @@ from simple_slurm import Slurm import visual_behavior.data_access.loading as loading -from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache as bpc +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache # python file to execute on cluster python_file = r"/home/marinag/visual_behavior_analysis/scripts/create_multi_session_df.py" @@ -25,7 +25,7 @@ stdout_location = r'/allen/programs/braintv/workgroups/nc-ophys/Marina/ClusterJobs/JobRecords' # cache_dir = loading.get_platform_analysis_cache_dir() -# cache = bpc.from_s3_cache(cache_dir=cache_dir) +# cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir=cache_dir) # print(cache_dir) # experiments_table = cache.get_ophys_experiment_table() From 34522445b6a54b5c14947f94f8932baf4a6f0b00 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:34:13 -0700 Subject: [PATCH 007/187] load from lims not s3 --- scripts/run_create_multi_session_df.py | 3 ++- visual_behavior/ophys/io/create_multi_session_df.py | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index aff249ba9..36c43d3ba 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -32,11 +32,12 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] +experiments_table = experiments_table[experiments_table.session_type!='OPHYS_7_receptive_field_mapping'] # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): print(project_code) - for session_number in experiments_table.session_number.unique(): + for session_number in experiments_table.session_type.unique(): # instantiate a Slurm object slurm = Slurm( diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 686d169e6..d85182979 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -30,10 +30,8 @@ def get_multi_session_df(project_code, session_number, df_name, conditions, use_ # experiments_table = loading.get_filtered_ophys_experiment_table(release_data_only=True) - cache_dir = loading.get_platform_analysis_cache_dir() - cache = bpc.from_s3_cache(cache_dir=cache_dir) - print(cache_dir) - experiments_table = cache.get_ophys_experiment_table() + cache = bpc.from_lims() + experiments = cache.get_ophys_experiment_table(passed_only=False) session_number = float(session_number) experiments = experiments_table[(experiments_table.project_code == project_code) & From 6a705e80ebeccc7ad17b0e5d9445680cde8dee62 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:37:18 -0700 Subject: [PATCH 008/187] use session number --- scripts/run_create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 36c43d3ba..6536cff9a 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -37,7 +37,7 @@ # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): print(project_code) - for session_number in experiments_table.session_type.unique(): + for session_number in experiments_table.session_number.unique(): # instantiate a Slurm object slurm = Slurm( From ee01ee03b2c2e47078138c331f5f84a654234e49 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:39:34 -0700 Subject: [PATCH 009/187] typo --- visual_behavior/ophys/io/create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index d85182979..0660b7259 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -31,7 +31,7 @@ def get_multi_session_df(project_code, session_number, df_name, conditions, use_ # experiments_table = loading.get_filtered_ophys_experiment_table(release_data_only=True) cache = bpc.from_lims() - experiments = cache.get_ophys_experiment_table(passed_only=False) + experiments_table = cache.get_ophys_experiment_table(passed_only=False) session_number = float(session_number) experiments = experiments_table[(experiments_table.project_code == project_code) & From d16d4e0c89c195ab91ceb75b63a0f20cfd1ef7f1 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 17:49:09 -0700 Subject: [PATCH 010/187] load from lims --- visual_behavior/ophys/io/create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 0660b7259..cf84cca43 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -43,7 +43,7 @@ def get_multi_session_df(project_code, session_number, df_name, conditions, use_ for experiment_id in experiments.index: try: print(experiment_id) - dataset = loading.get_ophys_dataset(experiment_id) + dataset = loading.get_ophys_dataset(experiment_id, load_from_lims=True, load_from_nwb=False) analysis = ResponseAnalysis(dataset, use_events=use_events, filter_events=filter_events, use_extended_stimulus_presentations=use_extended_stimulus_presentations) df = analysis.get_response_df(df_name) From b54e6f97a90f47557a6c7c966e62d52ddb79c1c0 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 18:50:01 -0700 Subject: [PATCH 011/187] change default loading behavior to include dev data --- visual_behavior/data_access/loading.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index c182bdf50..30288b296 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -309,7 +309,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_ return experiments -def get_filtered_ophys_session_table(release_data_only=True, include_failed_data=False): +def get_filtered_ophys_session_table(release_data_only=False, include_failed_data=True): """Get ophys sessions table from SDK, and add container_id and container_workflow_state to table, add session_workflow_state to table (defined as >1 experiment within session passing), and return only sessions where container and session workflow states are 'passed'. @@ -625,7 +625,7 @@ def get_cell_specimen_id_for_cell_roi_id(self, cell_roi_id): return cell_specimen_id -def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from_lims=False, load_from_nwb=True, +def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from_lims=True, load_from_nwb=False, get_extended_stimulus_presentations=True, get_behavior_movie_timestamps=False): """ Gets behavior + ophys data for one experiment (single imaging plane), either using the SDK LIMS API, From 0dc377af501dc312f415a3067192fb68029de750 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 18:50:09 -0700 Subject: [PATCH 012/187] use learning project cache dir --- visual_behavior/data_access/loading.py | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 30288b296..35e98a1c9 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -69,59 +69,57 @@ def get_platform_analysis_cache_dir(): This is the cache directory to use for all platform paper analysis This cache contains NWB files downloaded directly from AWS """ - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_cache' - # return r'//allen/programs/braintv/workgroups/nc-ophys/learning/analysis_files' - + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/learning_mFISH' + cache_dir = r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' return cache_dir def get_qc_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' def get_super_container_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/super_container_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/super_container_plots' def get_container_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/container_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/container_plots' def get_session_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/session_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/session_plots' def get_experiment_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/experiment_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/experiment_plots' def get_single_cell_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/single_cell_plots' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/single_cell_plots' def get_analysis_cache_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/visual_behavior_production_analysis' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_events_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/event_detection' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/event_detection' def get_behavior_model_outputs_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/behavior_model_output' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/behavior_model_output' def get_decoding_analysis_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/decoding' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/decoding' def get_ophys_glm_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/ophys_glm' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/ophys_glm' def get_manifest_path(): From 8fcde976a8baacc5d1c37cabee53bab56be01712 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 18:50:23 -0700 Subject: [PATCH 013/187] get learning proj experiments --- .../visualization/qc/run_save_all_container_plots.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 213f5a8fa..7ad45099a 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -1,7 +1,9 @@ import os import sys import argparse -from visual_behavior.data_access import loading as loading +# from visual_behavior.data_access import loading as loading +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache + sys.path.append('/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/src/') from pbstools import pbstools # NOQA E402 @@ -21,7 +23,11 @@ } -container_ids = loading.get_ophys_container_ids() +# container_ids = loading.get_ophys_container_ids() +cache = VisualBehaviorOphysProjectCache.from_lims() +experiments = cache.get_ophys_experiment_table(passed_only=False) +experiments = experiments[experiments.project_code=='LearningmFISHDevelopment'] +container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From 6ce84b17df6179c2bcdb0a21921d744e9b44b3f7 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 18:52:21 -0700 Subject: [PATCH 014/187] set default env --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 7ad45099a..62965a172 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -10,7 +10,7 @@ parser = argparse.ArgumentParser(description='run container qc plot generation functions on the cluster') -parser.add_argument('--env', type=str, default='', metavar='name of conda environment to use') +parser.add_argument('--env', type=str, default='visual_behavior_sdk', metavar='name of conda environment to use') parser.add_argument('--scriptname', type=str, default='save_all_container_plots.py', metavar='name of script to run (must be in same folder)') parser.add_argument("--plots", type=str, default=None, metavar='plot name to generate') From d8eec017acf47178d95c65160010f70c2189ef5c Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 18:59:06 -0700 Subject: [PATCH 015/187] update for slurm --- .../qc/run_save_all_container_plots.py | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 62965a172..9f603519d 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -1,12 +1,8 @@ import os -import sys -import argparse -# from visual_behavior.data_access import loading as loading -from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache - +from simple_slurm import Slurm -sys.path.append('/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/src/') -from pbstools import pbstools # NOQA E402 +import visual_behavior.data_access.loading as loading +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache parser = argparse.ArgumentParser(description='run container qc plot generation functions on the cluster') @@ -14,14 +10,22 @@ parser.add_argument('--scriptname', type=str, default='save_all_container_plots.py', metavar='name of script to run (must be in same folder)') parser.add_argument("--plots", type=str, default=None, metavar='plot name to generate') -job_dir = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/vba_qc_plots" +stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/vba_qc_plots" -job_settings = {'queue': 'braintv', - 'mem': '60g', - 'walltime': '3:00:00', - 'ppn': 1, - } +# python file to execute on cluster +python_file = r"/home/marinag/visual_behavior_analysis/visual_behavior/visualization/qc/save_all_container_plots.py" +# +# build the python path +# this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' +python_path = os.path.join( + os.path.expanduser("~"), + 'anaconda2', + 'envs', + conda_environment, + 'bin', + 'python' +) # container_ids = loading.get_ophys_container_ids() cache = VisualBehaviorOphysProjectCache.from_lims() @@ -32,22 +36,21 @@ if __name__ == "__main__": args = parser.parse_args() - # python_executable = "{}/.conda/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) python_file = os.path.join(os.getcwd(), args.scriptname) for ii, container_id in enumerate(container_ids): - if args.plots is None: - args_to_pass = '--container-id {}'.format(container_id) - else: - args_to_pass = '--container-id {} --plots {}'.format(container_id, args.plots) - print('container ID = {}, number {} of {}'.format(container_id, ii + 1, len(container_ids))) - job_title = 'container_{}'.format(container_id) - pbstools.PythonJob( - python_file, - python_executable, - python_args=args_to_pass, - jobname=job_title, - jobdir=job_dir, - **job_settings - ).run(dryrun=False) + # instantiate a Slurm object + slurm = Slurm( + mem='100g', + cpus_per_task=1, + time='60:00:00', + partition='braintv', + job_name='container_' + str(container_id), + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', + ) + + slurm.sbatch(python_path + ' ' + python_file + ' --container_id ' + str(container_id)) + + + From 93ab52366533337213f338b1540ff51550ae1daa Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 19:10:54 -0700 Subject: [PATCH 016/187] fix import --- visual_behavior/visualization/qc/run_save_all_container_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 9f603519d..4fefc10f9 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -1,4 +1,5 @@ import os +import argparse from simple_slurm import Slurm import visual_behavior.data_access.loading as loading From ddf2063350fd140e05ed128b89f4731548623a7a Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 19:17:26 -0700 Subject: [PATCH 017/187] add env --- visual_behavior/visualization/qc/run_save_all_container_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 4fefc10f9..a5ca0794a 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -16,6 +16,7 @@ # python file to execute on cluster python_file = r"/home/marinag/visual_behavior_analysis/visual_behavior/visualization/qc/save_all_container_plots.py" +conda_environment = 'visual_behavior_sdk' # # build the python path # this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' From afa6d8babc7d8f9a183d9707ed7b3d49a9b87a80 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 19:22:54 -0700 Subject: [PATCH 018/187] typo --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index a5ca0794a..9c051e7e6 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -52,7 +52,7 @@ output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_path + ' ' + python_file + ' --container_id ' + str(container_id)) + slurm.sbatch(python_path + ' ' + python_file + ' --container-id ' + str(container_id)) From 3313e1c9acce7cde45143f13fbaefb8141843833 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 20 Oct 2021 20:04:19 -0700 Subject: [PATCH 019/187] dont filter out expts we want --- visual_behavior/data_access/loading.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 35e98a1c9..59f93d724 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -219,7 +219,7 @@ def get_platform_paper_experiment_table(add_extra_columns=True): # -def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_only=True, exclude_ai94=True, +def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_only=False, exclude_ai94=False, add_extra_columns=False, from_cached_file=False, overwrite_cached_file=False): """ Loads a list of available ophys experiments FROM LIMS (not S3 cache) and adds additional useful columns to the table. @@ -270,7 +270,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_ cache = bpc.from_lims() experiments = cache.get_ophys_experiment_table(passed_only=False) # limit to the 4 VisualBehavior project codes - experiments = filtering.limit_to_production_project_codes(experiments) + # experiments = filtering.limit_to_production_project_codes(experiments) if add_extra_columns: print('adding extra columns') print('NOTE: this is slow. set from_cached_file to True to load cached version of experiments_table at:') @@ -288,7 +288,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_ print('limiting to sessions that start with OPHYS') experiments = filtering.limit_to_valid_ophys_session_types(experiments) if experiments.index.name != 'ophys_experiment_id': - experiments = experiments.drop_duplicates(subset='ophys_experiment_id') + # experiments = experiments.drop_duplicates(subset='ophys_experiment_id') experiments = experiments.set_index('ophys_experiment_id') if exclude_ai94: print('excluding Ai94 data') @@ -296,10 +296,10 @@ def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_ if 'cre_line' not in experiments.keys(): experiments['cre_line'] = [full_genotype.split('/')[0] for full_genotype in experiments.full_genotype.values] # filter one more time on load to restrict to Visual Behavior project experiments ### - experiments = filtering.limit_to_production_project_codes(experiments) + # experiments = filtering.limit_to_production_project_codes(experiments) # add new columns for conditions to analyze for platform paper ### - experiments = utilities.add_cell_type_column(experiments) + # experiments = utilities.add_cell_type_column(experiments) if overwrite_cached_file == True: print('overwriting pre-saved experiments table file') @@ -359,7 +359,7 @@ def get_filtered_ophys_session_table(release_data_only=False, include_failed_dat return sessions -def get_filtered_behavior_session_table(release_data_only=True): +def get_filtered_behavior_session_table(release_data_only=False): """ Loads list of behavior sessions from SDK BehaviorProjectCache, and does some basic filtering and addition of columns, such as changing mouse_id from str to int and adding project code. From 5ddb7da4ddc1658848687579ad7c7702eb9c7b47 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 18:08:25 -0700 Subject: [PATCH 020/187] hack --- visual_behavior/data_access/loading.py | 6 +++++- .../visualization/qc/run_save_all_container_plots.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 59f93d724..e2f8be321 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -288,7 +288,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o print('limiting to sessions that start with OPHYS') experiments = filtering.limit_to_valid_ophys_session_types(experiments) if experiments.index.name != 'ophys_experiment_id': - # experiments = experiments.drop_duplicates(subset='ophys_experiment_id') + # experiments = experiments.drop_duplicates(subset=['ophys_experiment_id', 'ophys_container_id']) experiments = experiments.set_index('ophys_experiment_id') if exclude_ai94: print('excluding Ai94 data') @@ -301,6 +301,10 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o # add new columns for conditions to analyze for platform paper ### # experiments = utilities.add_cell_type_column(experiments) + + ### hack because of problem container + experiments = experiments[experiments.ophys_container_id!=1132424700] + if overwrite_cached_file == True: print('overwriting pre-saved experiments table file') experiments.to_csv(os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv')) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 9c051e7e6..fc9a06e96 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -46,7 +46,7 @@ slurm = Slurm( mem='100g', cpus_per_task=1, - time='60:00:00', + time='120:00:00', partition='braintv', job_name='container_' + str(container_id), output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', From 4710ec9493b3a568b7f97ec72a733d93355252c1 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 18:32:06 -0700 Subject: [PATCH 021/187] filter session types --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index e2f8be321..f6eeaa2d5 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -286,7 +286,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o experiments = filtering.remove_failed_containers(experiments) # container_workflow_state can be anything other than 'failed' # limit to sessions that start with OPHYS print('limiting to sessions that start with OPHYS') - experiments = filtering.limit_to_valid_ophys_session_types(experiments) + experiments = filtering.limit_to_valid_ophys_session_types(experiments) if experiments.index.name != 'ophys_experiment_id': # experiments = experiments.drop_duplicates(subset=['ophys_experiment_id', 'ophys_container_id']) experiments = experiments.set_index('ophys_experiment_id') From 449624fd734b6181e2ce4971e2c7ee57d19ca3ea Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 19:22:59 -0700 Subject: [PATCH 022/187] fix metadata_string --- visual_behavior/visualization/qc/container_plots.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 9dd80bc6d..664a9385e 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -878,9 +878,8 @@ def get_metadata_string(ophys_container_id): """ ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) - title = dataset.analysis_folder - m = title.split('_') # dataset.analysis_folder.split('_') - metadata_string = str(ophys_container_id) + '_' + m[1] + '_' + m[2] + '_' + m[3] + '_' + m[4] + '_' + m[5] + '_' + m[6] + m = dataset.metadata.copy() + metadata_string = str(m['mouse_id']) + '_' str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string @@ -896,7 +895,8 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, df :return: """ dataset = loading.get_ophys_dataset(container_df.ophys_experiment_id.unique()[0]) - title = dataset.metadata_string + # title = dataset.metadata_string + title = get_metadata_string(ophys_container_id) # frame_rate = dataset.metadata['ophys_frame_rate'] if omitted: figsize = (12, 5) From d6c97b3932fc8e0018ec543b491fef5219ec807f Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 19:38:30 -0700 Subject: [PATCH 023/187] fix metadata_string again --- visual_behavior/visualization/qc/container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 664a9385e..3d370d111 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -879,7 +879,7 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string From 451c5a8d393ee666244e7fdfd6c0e16684d80f58 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 20:13:33 -0700 Subject: [PATCH 024/187] standardize filename for saving --- .../visualization/qc/container_plots.py | 89 +++++++++++-------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 3d370d111..02dc57334 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -77,7 +77,7 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): fig.subplots_adjust(top=0.9) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'ophys_session_sequence', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) # OPHYS @@ -108,7 +108,7 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_movie_max_projection_images_for_container(ophys_container_id, save_figure=True): @@ -137,7 +137,7 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection_movies', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): @@ -166,7 +166,7 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_images', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_movie_average_images_for_container(ophys_container_id, save_figure=True): @@ -195,7 +195,7 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_images_movies', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): @@ -215,7 +215,7 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'eyetracking_sample_frames', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) return fig, axes @@ -233,7 +233,7 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_masks', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figure=True): @@ -271,7 +271,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur print('error: {}'.format(e)) if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', get_file_name_for_container(ophys_container_id)) def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True): @@ -301,7 +301,7 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'roi_filtering_metrics_all_rois', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, save_figure=True): @@ -331,7 +331,7 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'roi_filtering_metrics_valid_rois', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): @@ -359,7 +359,7 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): ax=ax[i + (n * 4)]) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'filtered_roi_masks', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True): @@ -376,7 +376,7 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'dff_traces_heatmaps', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_average_intensity_timeseries_for_container(ophys_container_id, save_figure=True): @@ -403,7 +403,7 @@ def plot_average_intensity_timeseries_for_container(ophys_container_id, save_fig fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_intensity_timeseries', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_pmt_for_container(ophys_container_id, save_figure=True): @@ -432,7 +432,7 @@ def plot_pmt_for_container(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pmt_settings', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_average_intensity_for_container(ophys_container_id, save_figure=True): @@ -466,7 +466,7 @@ def plot_average_intensity_for_container(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'FOV_average_intensity', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_average_intensity_by_pmt_for_container(ophys_container_id, save_figure=True): @@ -502,7 +502,7 @@ def plot_average_intensity_by_pmt_for_container(ophys_container_id, save_figure= fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_intensity_by_pmt', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_snr_by_pmt_gain_and_intensity_for_container(ophys_container_id, save_figure=True): @@ -521,7 +521,7 @@ def plot_snr_by_pmt_gain_and_intensity_for_container(ophys_container_id, save_fi plt.title("container: " + str(ophys_container_id)) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'snr_by_pmt_and_intensity', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_snr_by_pmt_for_container(ophys_container_id, save_figure=True): @@ -557,7 +557,7 @@ def plot_snr_by_pmt_for_container(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'snr_by_pmt', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_cell_snr_for_container(ophys_container_id, save_figure=True): @@ -590,7 +590,7 @@ def plot_cell_snr_for_container(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_snr_by_experiment', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_number_segmented_rois_for_container(ophys_container_id, save_figure=True): @@ -635,7 +635,7 @@ def plot_number_segmented_rois_for_container(ophys_container_id, save_figure=Tru fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmented_rois_by_experiment', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_number_matched_cells_for_container(ophys_container_id, save_figure=True): @@ -656,7 +656,7 @@ def plot_number_matched_cells_for_container(ophys_container_id, save_figure=True fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'number_matched_cells', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_fraction_matched_cells_for_container(ophys_container_id, save_figure=True): @@ -677,7 +677,7 @@ def plot_fraction_matched_cells_for_container(ophys_container_id, save_figure=Tr fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'fraction_matched_cells', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_cell_matching_registration_overlay_grid(ophys_container_id, save_figure=True): @@ -745,7 +745,7 @@ def plot_cell_matching_registration_overlay_grid(ophys_container_id, save_figure fig.suptitle(get_metadata_string(ophys_container_id), x=0.5, y=1.02, horizontalalignment='center', fontsize=14) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_matching_registration_overlay_grid', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_cell_matching_registration_output(ophys_container_id, save_figure=True): @@ -816,7 +816,7 @@ def plot_cell_matching_registration_output(ophys_container_id, save_figure=True) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_matching_registration_output', - 'container_' + str(ophys_container_id) + '_' + str(x)) + get_file_name_for_container(ophys_container_id) + '_' + str(x)) def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figure=True): @@ -833,7 +833,7 @@ def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figur fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'motion_correction_xy_shift', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=False, alpha=0.15, facecolor='gray'): @@ -879,10 +879,23 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string +def get_file_name_for_container(ophys_container_id): + """ + gets standardized filename for saving figures + format "container_id_"+str(ophys_container_id) is necessary for files to be able to be viewed in Dougs QC viewer + using get_metadata_string(ophys_container_id) gives a more interpretable filename with cre line, area, etc + :param ophys_container_id: + :return: + """ + # filename = "container_id_"+str(ophys_container_id) + filename = get_metadata_string(ophys_container_id) + return filename + + def plot_population_average_across_sessions(container_df, ophys_container_id, df_name, trials=False, omitted=False, save_figure=True): """ Plots population average response across all sessions within a container @@ -944,7 +957,7 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, df if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'population_average_by_session_' + df_name.split('_')[0], - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_omission_population_average_across_sessions(ophys_container_id, save_figure=True): @@ -1017,7 +1030,7 @@ def plot_stimulus_population_average_across_sessions(ophys_container_id, save_fi # fig.tight_layout() # if save_figure: # ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'fov_ave_intensity_by_pmt', -# 'container_' + str(ophys_container_id)) +# get_file_name_for_container(ophys_container_id)) # BEHAVIOR @@ -1035,7 +1048,7 @@ def plot_running_speed_for_container(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'running_speed', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): @@ -1053,7 +1066,7 @@ def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'lick_rasters', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_pupil_area_sdk(ophys_container_id, save_figure=True): @@ -1078,7 +1091,7 @@ def plot_pupil_area_sdk(ophys_container_id, save_figure=True): if save_figure: print('saving') if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time_sdk', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time_sdk', get_file_name_for_container(ophys_container_id)) return fig, axes @@ -1105,7 +1118,7 @@ def plot_pupil_area(ophys_container_id, save_figure=True): if save_figure: print('saving') if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time', get_file_name_for_container(ophys_container_id)) return fig, axes @@ -1131,7 +1144,7 @@ def plot_pupil_position(ophys_container_id, save_figure=True): if save_figure: if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_position_vs_time', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_position_vs_time', get_file_name_for_container(ophys_container_id)) return fig, axes @@ -1238,7 +1251,7 @@ def plot_behavior_summary(ophys_container_id, save_figure=True): if save_figure: if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'behavior_metric_summary', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_event_detection_for_container(ophys_container_id, save_figure=True): @@ -1347,7 +1360,7 @@ def plot_OphysRegistrationSummaryImage(ophys_container_id, save_figure=True): if save_figure: if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'OphysRegistrationSummaryImage', - 'container_' + str(ophys_container_id)) + get_file_name_for_container(ophys_container_id)) def plot_nway_match_fraction(ophys_container_id, save_figure=True): @@ -1366,7 +1379,7 @@ def plot_nway_match_fraction(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_match_fraction', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_match_fraction', get_file_name_for_container(ophys_container_id)) def plot_nway_warp_overlay(ophys_container_id, save_figure=True): @@ -1385,7 +1398,7 @@ def plot_nway_warp_overlay(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_overlay', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_overlay', get_file_name_for_container(ophys_container_id)) def plot_nway_warp_summary(ophys_container_id, save_figure=True): @@ -1404,7 +1417,7 @@ def plot_nway_warp_summary(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_summary', 'container_' + str(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_summary', get_file_name_for_container(ophys_container_id)) def plot_experiment_summary_figure_for_container(ophys_container_id, save_figure=True): From bf35b70832ec63a1300c6ec1abb543d14c16e686 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 20:51:40 -0700 Subject: [PATCH 025/187] use csids --- visual_behavior/data_access/loading.py | 5 +++-- .../ophys/response_analysis/response_processing.py | 14 ++++++++++---- .../visualization/qc/container_plots.py | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index f6eeaa2d5..bc6d99ecc 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2617,8 +2617,9 @@ def get_multi_session_df(cache_dir, df_name, conditions, experiments_table, remo """ cache_dir = get_platform_analysis_cache_dir() - cache = bpc.from_s3_cache(cache_dir=cache_dir) - experiments_table = cache.get_ophys_experiment_table() + # cache = bpc.from_s3_cache(cache_dir=cache_dir) + # experiments_table = cache.get_ophys_experiment_table() + experiments_table = get_filtered_ophys_experiment_table() project_codes = experiments_table.project_code.unique() multi_session_df = pd.DataFrame() diff --git a/visual_behavior/ophys/response_analysis/response_processing.py b/visual_behavior/ophys/response_analysis/response_processing.py index 5032cc13b..5c252abd2 100644 --- a/visual_behavior/ophys/response_analysis/response_processing.py +++ b/visual_behavior/ophys/response_analysis/response_processing.py @@ -19,7 +19,7 @@ def get_default_trial_response_params(): (dict) dict of response window params for computing trial_response_xr ''' trial_response_params = { - "window_around_timepoint_seconds": [-5, 5], + "window_around_timepoint_seconds": [-2, 2], "response_window_duration_seconds": 0.25, "baseline_window_duration_seconds": 0.25 } @@ -452,7 +452,9 @@ def get_trials_response_xr(dataset, use_events=False, filter_events=False, frame traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids + # trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids when no cell_specimen_ids + trace_ids = dataset.dff_traces.index.values + timestamps = dataset.ophys_timestamps change_trials = dataset.trials[~pd.isnull(dataset.trials['change_time'])] # [:-1] # last trial can get cut off event_times = change_trials['change_time'].values @@ -490,7 +492,9 @@ def get_stimulus_response_xr(dataset, use_events=False, filter_events=True, fram traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids + # trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids when no cell_specimen_ids + trace_ids = dataset.dff_traces.index.values + timestamps = dataset.ophys_timestamps event_times = dataset.stimulus_presentations['start_time'].values event_ids = dataset.stimulus_presentations.index.values @@ -527,7 +531,9 @@ def get_omission_response_xr(dataset, use_events=False, filter_events=False, fra traces = np.stack(dataset.events['events'].values) else: traces = np.stack(dataset.dff_traces['dff'].values) - trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids because no cell_specimen_ids + # trace_ids = dataset.dff_traces.cell_roi_id.values # use cell_roi_ids when no cell_specimen_ids + trace_ids = dataset.dff_traces.index.values + timestamps = dataset.ophys_timestamps stimuli = dataset.stimulus_presentations omission_presentations = stimuli[stimuli.image_name == 'omitted'] diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 02dc57334..88bc5d1e8 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -879,7 +879,7 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string From 863886e4fb142eccc40389855a5cdd54996746fb Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 20:54:43 -0700 Subject: [PATCH 026/187] change omission response window --- visual_behavior/ophys/response_analysis/response_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/response_analysis/response_processing.py b/visual_behavior/ophys/response_analysis/response_processing.py index 5c252abd2..6e436eba1 100644 --- a/visual_behavior/ophys/response_analysis/response_processing.py +++ b/visual_behavior/ophys/response_analysis/response_processing.py @@ -57,7 +57,7 @@ def get_default_omission_response_params(): (dict) dict of response window params for computing omission_response_xr ''' omission_response_params = { - "window_around_timepoint_seconds": [-5, 5], + "window_around_timepoint_seconds": [-1, 2], "response_window_duration_seconds": 0.75, "baseline_window_duration_seconds": 0.25 } From e4272317a37031f91502f9e14438697bdc711f1d Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 20:55:55 -0700 Subject: [PATCH 027/187] process omissions --- scripts/create_multi_session_df.py | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index b9805c2d6..116ca1a3c 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -21,22 +21,22 @@ # stimulus response - df_name = 'stimulus_response_df' - conditions = ['cell_specimen_id', 'is_change', 'image_name'] - - print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) - df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, - filter_events=False, - use_extended_stimulus_presentations=False) - - - df_name = 'stimulus_response_df' - conditions = ['cell_specimen_id', 'image_name'] - - print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) - df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, - filter_events=False, - use_extended_stimulus_presentations=False) + # df_name = 'stimulus_response_df' + # conditions = ['cell_specimen_id', 'is_change', 'image_name'] + # + # print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + # df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + # filter_events=False, + # use_extended_stimulus_presentations=False) + # + # + # df_name = 'stimulus_response_df' + # conditions = ['cell_specimen_id', 'image_name'] + # + # print('creating multi_session_df for', df_name, ', ', project_code, ', session number', session_number, conditions) + # df = io.get_multi_session_df(project_code, session_number, df_name, conditions, use_events=False, + # filter_events=False, + # use_extended_stimulus_presentations=False) # omissions From d874db381e48ce4413f8b165227a61a0d06be212 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 21:13:14 -0700 Subject: [PATCH 028/187] longer walltime --- scripts/run_create_cell_metrics_table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_create_cell_metrics_table.py b/scripts/run_create_cell_metrics_table.py index 9a33429a6..a8861840b 100644 --- a/scripts/run_create_cell_metrics_table.py +++ b/scripts/run_create_cell_metrics_table.py @@ -29,7 +29,7 @@ slurm = Slurm( mem='60g', # '24g' cpus_per_task=10, - time='6:00:00', + time='60:00:00', partition='braintv', job_name='metrics_table', output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', From be03955087689bec842efc34deda1f8a333662af Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 21 Oct 2021 22:00:11 -0700 Subject: [PATCH 029/187] fix pop avgs plot --- visual_behavior/visualization/qc/container_plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 88bc5d1e8..3a78dcd0b 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -933,9 +933,9 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, df traces = df.trace.values mean_trace = df.trace.mean() timestamps = df.trace_timestamps.mean() - ax.plot(timestamps, mean_trace, color=colors[session_number - 1], label=session_number) + ax.plot(timestamps, mean_trace, color=colors[int(session_number - 1)], label=session_number) sem = (traces.std()) / np.sqrt(float(len(traces))) - ax.fill_between(timestamps, mean_trace + sem, mean_trace - sem, alpha=0.5, color=colors[session_number - 1]) + ax.fill_between(timestamps, mean_trace + sem, mean_trace - sem, alpha=0.5, color=colors[int(session_number - 1)]) if omitted: ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=True, alpha=0.2, facecolor='gray') From f3cc6937eadbabb5c116346575de0198f3b6a112 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 11 Feb 2022 15:41:36 -0800 Subject: [PATCH 030/187] prep to produce QC plots --- visual_behavior/data_access/loading.py | 2 +- .../qc/run_save_all_container_plots.py | 24 ++++++------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index d424c0837..a4c526439 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -242,7 +242,7 @@ def get_platform_paper_behavior_session_table(): return behavior_sessions -def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_only=False, exclude_ai94=False, +def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_only=False, exclude_ai94=True, add_extra_columns=False, from_cached_file=False, overwrite_cached_file=False): """ Loads a list of available ophys experiments FROM LIMS (not S3 cache) and adds additional useful columns to the table. diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index fc9a06e96..d813f50e1 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -11,31 +11,21 @@ parser.add_argument('--scriptname', type=str, default='save_all_container_plots.py', metavar='name of script to run (must be in same folder)') parser.add_argument("--plots", type=str, default=None, metavar='plot name to generate') -stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/vba_qc_plots" +# job_dir = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/vba_qc_plots" +job_dir = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/qc_plots" # python file to execute on cluster python_file = r"/home/marinag/visual_behavior_analysis/visual_behavior/visualization/qc/save_all_container_plots.py" -conda_environment = 'visual_behavior_sdk' -# -# build the python path -# this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' -python_path = os.path.join( - os.path.expanduser("~"), - 'anaconda2', - 'envs', - conda_environment, - 'bin', - 'python' -) - # container_ids = loading.get_ophys_container_ids() + +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache + cache = VisualBehaviorOphysProjectCache.from_lims() -experiments = cache.get_ophys_experiment_table(passed_only=False) -experiments = experiments[experiments.project_code=='LearningmFISHDevelopment'] +experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +experiments_table = cache.get_ophys_experiment_table(passed_only=False) container_ids = experiments.ophys_container_id.unique() - if __name__ == "__main__": args = parser.parse_args() python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) From 1641f9d8064b47c50c9eb8fc0042c955a8ec69f7 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 11 Feb 2022 20:56:43 -0800 Subject: [PATCH 031/187] updates for container qc plots --- visual_behavior/data_access/loading.py | 57 ++++++------ visual_behavior/data_access/reformat.py | 4 +- .../visualization/qc/container_plots.py | 87 ++++++++++++------- .../qc/run_save_all_container_plots.py | 2 +- 4 files changed, 92 insertions(+), 58 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 6115fd62a..2a593b02e 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -68,57 +68,57 @@ def get_platform_analysis_cache_dir(): This is the cache directory to use for all platform paper analysis This cache contains NWB files downloaded directly from AWS """ - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' return cache_dir def get_qc_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' def get_super_container_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/super_container_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/super_container_plots' def get_container_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/container_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/container_plots' def get_session_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/session_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/session_plots' def get_experiment_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/experiment_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/experiment_plots' def get_single_cell_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/single_cell_plots' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/single_cell_plots' def get_analysis_cache_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_events_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/event_detection' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/event_detection' def get_behavior_model_outputs_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/behavior_model_output' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/behavior_model_output' def get_decoding_analysis_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/decoding' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/decoding' def get_ophys_glm_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/ophys_glm' + return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/ophys_glm' def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): @@ -275,7 +275,7 @@ def get_platform_paper_behavior_session_table(): return behavior_sessions -def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_only=False, exclude_ai94=True, +def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_only=False, exclude_ai94=True, add_extra_columns=False, from_cached_file=False, overwrite_cached_file=False): """ Loads a list of available ophys experiments FROM LIMS (not S3 cache) and adds additional useful columns to the table. @@ -342,7 +342,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=False, release_data_ experiments = filtering.remove_failed_containers(experiments) # container_workflow_state can be anything other than 'failed' # limit to sessions that start with OPHYS print('limiting to sessions that start with OPHYS') - experiments = filtering.limit_to_valid_ophys_session_types(experiments) + # experiments = filtering.limit_to_valid_ophys_session_types(experiments) if experiments.index.name != 'ophys_experiment_id': # experiments = experiments.drop_duplicates(subset=['ophys_experiment_id', 'ophys_container_id']) experiments = experiments.set_index('ophys_experiment_id') @@ -568,6 +568,8 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o output_sampling_rate: sampling rate for interpolation, only used if interpolate is True data_type: which timeseries to get event triggered responses for options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate' + event_type: how to filter stimulus presentations before creating table + options: 'all', 'omissions', 'changes' """ import mindscope_utilities.visual_behavior_ophys.data_formatting as vb_ophys # load stimulus response df from file if it exists otherwise generate it @@ -1007,7 +1009,8 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id): ophys_experiment_ids -- list of ophys_experiment_ids that meet filtering criteria """ experiments = get_filtered_ophys_experiment_table() - ophys_experiment_ids = np.sort(experiments[(experiments.ophys_container_id == ophys_container_id)].index.values) + experiments = experiments.sort_values(by='date_of_acquisition') + ophys_experiment_ids = experiments[(experiments.ophys_container_id == ophys_container_id)].index.values return ophys_experiment_ids @@ -2988,22 +2991,26 @@ def get_cell_info(cell_specimen_ids=None, ophys_experiment_ids=None): return db.lims_query(query.format(search_key, search_vals)) -def get_container_response_df(ophys_container_id, df_name='omission_response_df', use_events=False): +def get_container_response_df(ophys_container_id, data_type='dff', event_type='all'): """ - get concatenated dataframe of response_df type specificied by df_name, across all experiments from a container, + get concatenated dataframe of stimulus_response_df with stimulus aligned traces across all experiments in a container + data_type: can be 'dff', 'events', 'filtered_events', using the ResponseAnalysis class to build event locked response dataframes """ - from visual_behavior.ophys.response_analysis.response_analysis import ResponseAnalysis experiments_table = get_filtered_ophys_experiment_table() container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id] container_df = pd.DataFrame() for ophys_experiment_id in container_expts.index.values: - dataset = get_ophys_dataset(ophys_experiment_id) - analysis = ResponseAnalysis(dataset, use_events) - odf = analysis.get_response_df(df_name=df_name) - odf['ophys_experiment_id'] = ophys_experiment_id - odf['session_number'] = experiments_table.loc[ophys_experiment_id].session_number - container_df = pd.concat([container_df, odf]) + print(ophys_experiment_id) + try: + dataset = get_ophys_dataset(ophys_experiment_id) + sdf = get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, + data_type=data_type, event_type=event_type, load_from_file=False) + sdf['ophys_experiment_id'] = ophys_experiment_id + sdf['session_number'] = experiments_table.loc[ophys_experiment_id].session_number + container_df = pd.concat([container_df, sdf]) + except: + print('problem for', ophys_experiment_id) return container_df diff --git a/visual_behavior/data_access/reformat.py b/visual_behavior/data_access/reformat.py index 252974823..2b790851d 100644 --- a/visual_behavior/data_access/reformat.py +++ b/visual_behavior/data_access/reformat.py @@ -250,8 +250,8 @@ def reformat_experiments_table(experiments): experiments = experiments[experiments.cre_line != 'Cux2-CreERT2'] # why is this in the VB dataset? # replace session types that are NaN with string None experiments.at[experiments[experiments.session_type.isnull()].index.values, 'session_type'] = 'None' - experiments = add_mouse_seeks_fail_tags_to_experiments_table(experiments) - experiments = add_model_outputs_availability_to_table(experiments) + # experiments = add_mouse_seeks_fail_tags_to_experiments_table(experiments) + # experiments = add_model_outputs_availability_to_table(experiments) if 'level_0' in experiments.columns: experiments = experiments.drop(columns='level_0') if 'index' in experiments.columns: diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 6526966d1..71b0ad25b 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -42,13 +42,13 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): n_expts = len(expts) img = np.empty((n_expts, 1, 3)) fail_x = [] - fail_tags = [] + # fail_tags = [] for expt_ind, expt_id in enumerate(experiment_ids): this_expt = expts.loc[expt_id] img[expt_ind, 0, :] = session_type_color_map[this_expt['session_type']] if this_expt['experiment_workflow_state'] == 'failed': fail_x.append(expt_ind) - fail_tags.append(this_expt['failure_tags']) + # fail_tags.append(this_expt['failure_tags']) # create plot with expt colors image figsize = (20, n_expts) @@ -67,8 +67,8 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): # add X for fails and list fail tags for ind_fail, x in enumerate(fail_x): ax.text(x=0, y=x, s='X', ha='center', va='center', fontsize=60) - fail_string = 'Failure: ' + str(fail_tags[ind_fail]) - ax.text(x=8.5, y=x, s=fail_string, ha='left', va='center', fontsize=20) + # fail_string = 'Failure: ' + str(fail_tags[ind_fail]) + # ax.text(x=8.5, y=x, s=fail_string, ha='left', va='center', fontsize=20) plt.suptitle('specimen_id: {}'.format(specimen_id) + ', ophys_container_id: {}'.format(ophys_container_id), fontsize=25, ha='left', x=0.06, y=.97) @@ -101,10 +101,12 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) - # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] - ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) + try: + ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] + ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection', @@ -130,7 +132,10 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -159,7 +164,10 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -188,7 +196,10 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -211,7 +222,10 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): for ii, ophys_experiment_id in enumerate(ophys_experiment_ids): print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) axes.append(vbp.placeAxesOnGrid(fig, dim=(3, 10), xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) - axes[-1] = ep.make_eye_matrix_plot(ophys_experiment_id, axes[-1]) + try: + axes[-1] = ep.make_eye_matrix_plot(ophys_experiment_id, axes[-1]) + except: + pass if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'eyetracking_sample_frames', @@ -227,7 +241,10 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -245,7 +262,10 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur ax = ax.ravel() for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -260,8 +280,11 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur # except: # print('cant plot valid masks for', ophys_experiment_id) - ax[i + (n * 2)] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i + (n * 2)]) - ax[i + (n * 2)].set_title('red=valid, blue=invalid, \ngreen=crosstalk, cyan=both') + try: + ax[i + (n * 2)] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i + (n * 2)]) + ax[i + (n * 2)].set_title('red=valid, blue=invalid, \ngreen=crosstalk, cyan=both') + except: + pass try: ax[i + (n * 3)] = ep.plot_remaining_decrosstalk_masks_for_experiment(ophys_experiment_id, ax=ax[i + (n * 3)]) @@ -369,7 +392,10 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): - ax[i] = ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=ax[i]) + try: + ax[i] = ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=ax[i]) + except: + pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) ax[i].set_title(str(ophys_experiment_id) + ' - ' + session_type) @@ -396,7 +422,10 @@ def plot_average_intensity_timeseries_for_container(ophys_container_id, save_fig figsize = (9, 5) fig, ax = plt.subplots(figsize=figsize) for i, ophys_experiment_id in enumerate(container_df["ophys_experiment_id"].unique()): - ax = ep.plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=ax) + try: + ax = ep.plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=ax) + except: + pass ax.legend(exp_order_and_stage["stage_name_lims"], fontsize='xx-small', title='stage name', title_fontsize='xx-small', bbox_to_anchor=(1.01, 1), loc=2) ax.set_title('full field average fluorescence intensity over time') @@ -896,7 +925,7 @@ def get_file_name_for_container(ophys_container_id): return filename -def plot_population_average_across_sessions(container_df, ophys_container_id, df_name, trials=False, omitted=False, save_figure=True): +def plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='all', save_figure=True): """ Plots population average response across all sessions within a container :param container_df: response dataframe for all sessions in a container, can be stimulus_response_df, omission_response_df, etc @@ -936,12 +965,12 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, df ax.plot(timestamps, mean_trace, color=colors[int(session_number - 1)], label=session_number) sem = (traces.std()) / np.sqrt(float(len(traces))) ax.fill_between(timestamps, mean_trace + sem, mean_trace - sem, alpha=0.5, color=colors[int(session_number - 1)]) - if omitted: + if event_type == 'omissions': ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=True, alpha=0.2, facecolor='gray') ax.axvline(x=0, ymin=0, ymax=1, linestyle='--', color='gray') ax.set_xlabel('time relative to omission (sec)') - elif trials: + elif event_type == 'changes': ax = plot_flashes_on_trace(ax, timestamps, trial_type='go', omitted=False, alpha=0.2, facecolor='gray') ax.set_xlabel('time relative to change (sec)') @@ -967,9 +996,8 @@ def plot_omission_population_average_across_sessions(ophys_container_id, save_fi :param save_figure: :return: """ - df_name = 'omission_response_df' - container_df = loading.get_container_response_df(ophys_container_id, df_name, use_events=False) - plot_population_average_across_sessions(container_df, ophys_container_id, df_name, trials=False, omitted=True, + container_df = loading.get_container_response_df(ophys_container_id, data_type='dff', event_type='all') + plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='all', save_figure=save_figure) @@ -980,9 +1008,8 @@ def plot_trials_population_average_across_sessions(ophys_container_id, save_figu :param save_figure: :return: """ - df_name = 'trials_response_df' - container_df = loading.get_container_response_df(ophys_container_id, df_name, use_events=False) - plot_population_average_across_sessions(container_df, ophys_container_id, df_name, trials=True, omitted=False, + container_df = loading.get_container_response_df(ophys_container_id, data_type='dff', event_type='changes') + plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='changes', save_figure=save_figure) @@ -993,9 +1020,9 @@ def plot_stimulus_population_average_across_sessions(ophys_container_id, save_fi :param save_figure: :return: """ - df_name = 'stimulus_response_df' - container_df = loading.get_container_response_df(ophys_container_id, df_name, use_events=False) - plot_population_average_across_sessions(container_df, ophys_container_id, df_name, trials=False, omitted=False, + container_df = loading.get_container_response_df(ophys_container_id, data_type='dff', event_type='all') + container_df = container_df[container_df.omitted == False] + plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='all', save_figure=save_figure) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index d813f50e1..86e76f954 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -22,8 +22,8 @@ from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache cache = VisualBehaviorOphysProjectCache.from_lims() -experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] experiments_table = cache.get_ophys_experiment_table(passed_only=False) +experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From e229b2886f3ba84fee477785e618cbacb26e6e14 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 11 Feb 2022 21:48:46 -0800 Subject: [PATCH 032/187] stdout --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 86e76f954..7cb02b64c 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -12,7 +12,7 @@ parser.add_argument("--plots", type=str, default=None, metavar='plot name to generate') # job_dir = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/vba_qc_plots" -job_dir = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/qc_plots" +stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/qc_plots" # python file to execute on cluster python_file = r"/home/marinag/visual_behavior_analysis/visual_behavior/visualization/qc/save_all_container_plots.py" From bc32c3b642bd4ae33a4bc7c5fdd23d824ff6484f Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 11 Feb 2022 22:39:45 -0800 Subject: [PATCH 033/187] typo --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 7cb02b64c..bf12bbfb9 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -42,7 +42,7 @@ output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_path + ' ' + python_file + ' --container-id ' + str(container_id)) + slurm.sbatch(python_executable + ' ' + python_file + ' --container-id ' + str(container_id)) From bbca5d275aa44ac116f2a1367cb51b50e2d4565c Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 14 Feb 2022 12:29:47 -0800 Subject: [PATCH 034/187] typo --- visual_behavior/visualization/qc/save_all_container_plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 47b26a6b5..dd48691bf 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,8 +4,8 @@ def main(): possible_plots = { - "pupil_timeseries": plot_pupil_timeseries_for_container, - "event_triggered_averages":plot_event_triggered_averages_for_container, + "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + "event_triggered_averages": cp.plot_event_triggered_averages_for_container, "ophys_session_sequence": cp.plot_container_session_sequence, "max_projection_images": cp.plot_sdk_max_projection_images_for_container, "average_images": cp.plot_sdk_average_images_for_container, From 314ef281947f0e3f513a6df9f7eb7c2966d4d4b7 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 14 Feb 2022 14:27:14 -0800 Subject: [PATCH 035/187] use new directories --- visual_behavior/data_access/loading.py | 31 ++++++++++--------- .../visualization/qc/container_plots.py | 8 ++--- visual_behavior/visualization/utils.py | 9 ++++++ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 2a593b02e..17420e1d9 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -68,61 +68,64 @@ def get_platform_analysis_cache_dir(): This is the cache directory to use for all platform paper analysis This cache contains NWB files downloaded directly from AWS """ - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + return r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + return cache_dir def get_qc_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' + return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + # return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' def get_super_container_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/super_container_plots' + return os.path.join(get_qc_plots_dir(), 'super_container_plots') def get_container_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/container_plots' + return os.path.join(get_qc_plots_dir(), 'container_plots') def get_session_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/session_plots' + return os.path.join(get_qc_plots_dir(), 'session_plots') def get_experiment_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/experiment_plots' + return os.path.join(get_qc_plots_dir(), 'experiment_plots') def get_single_cell_plots_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots/single_cell_plots' + return os.path.join(get_qc_plots_dir(), 'single_cell_plots') def get_analysis_cache_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_events_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/event_detection' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/event_detection' def get_behavior_model_outputs_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/behavior_model_output' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/behavior_model_output' def get_decoding_analysis_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/decoding' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/decoding' def get_ophys_glm_dir(): - return r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/ophys_glm' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/ophys_glm' def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): - base_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_cache/stimulus_response_dfs' + base_dir = os.path.join(get_production_cache_dir, 'stimulus_response_dfs') if interpolate: save_dir = os.path.join(base_dir, event_type, 'interpolate_' + str(output_sampling_rate) + 'Hz') else: diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 71b0ad25b..1ea34e737 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -35,7 +35,7 @@ def ax_to_array(ax): def plot_container_session_sequence(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table(include_failed_data=True) expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values('date_of_acquisition') - specimen_id = expts.specimen_id.unique()[0] + mouse_id = expts.mouse_id.unique()[0] experiment_ids = expts.index.values session_type_color_map = ut.get_session_type_color_map() @@ -70,7 +70,7 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): # fail_string = 'Failure: ' + str(fail_tags[ind_fail]) # ax.text(x=8.5, y=x, s=fail_string, ha='left', va='center', fontsize=20) - plt.suptitle('specimen_id: {}'.format(specimen_id) + ', ophys_container_id: {}'.format(ophys_container_id), + plt.suptitle('mouse_id: {}'.format(mouse_id) + ', ophys_container_id: {}'.format(ophys_container_id), fontsize=25, ha='left', x=0.06, y=.97) fig.subplots_adjust(left=0.05) fig.subplots_adjust(right=0.1) @@ -940,11 +940,11 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, da # title = dataset.metadata_string title = get_metadata_string(ophys_container_id) # frame_rate = dataset.metadata['ophys_frame_rate'] - if omitted: + if event_type == 'omissions': figsize = (12, 5) m = title.split('_') # dataset.analysis_folder.split('_') title = str(ophys_container_id) + '_' + m[1] + '_' + m[2] + '_' + m[3] + '_' + m[4] + '_' + m[5] + '_' + m[6] - elif trials: + elif event_type == 'changes': figsize = (12, 5) container_df = container_df[container_df.go == True] m = title.split('_') # dataset.analysis_folder.split('_') diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index 7c8435454..e3df40ba1 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -79,6 +79,15 @@ def get_session_type_color_map(): black = np.array([0, 0, 0]).astype(np.uint8) session_type_color_map = { + + 'TRAINING_0_gratings_autorewards_15min': lighter(black, 0.1), + 'TRAINING_1_gratings': lighter(black, 0.2), + 'TRAINING_2_gratings_flashed': lighter(black, 0.3), + 'TRAINING_3_images_A_10uL_reward': lighter(black, 0.4), + 'TRAINING_4_images_A_training': lighter(black, 0.5), + 'TRAINING_5_images_A_epilogue': lighter(black, 0.6), + 'TRAINING_5_images_A_handoff_ready': lighter(black, 0.7), + 'OPHYS_0_images_A_habituation': lighter(colors[0, :], 0.8), 'OPHYS_1_images_A': colors[0, :], 'OPHYS_2_images_A_passive': colors[1, :], From ac2886dcfe958fafd84e667e6fb9b2e3e2d4ecac Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 14 Feb 2022 18:18:46 -0800 Subject: [PATCH 036/187] filename with container id not expt --- visual_behavior/visualization/qc/container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 1ea34e737..f2b0ba4af 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -908,7 +908,7 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string From 81af976d802acb377fc11e02ec47e649d6c331a4 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 14 Feb 2022 19:22:01 -0800 Subject: [PATCH 037/187] dont reload expts table all the time --- visual_behavior/data_access/loading.py | 38 +++-- .../visualization/qc/container_plots.py | 133 +++++++++++------- 2 files changed, 103 insertions(+), 68 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 17420e1d9..5b8e26f79 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -987,7 +987,7 @@ def get_ophys_container_ids(platform_paper_only=False, add_extra_columns=True): return container_ids -def get_ophys_session_ids_for_ophys_container_id(ophys_container_id): +def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments=None): """Get ophys_session_ids belonging to a given ophys_container_id. Ophys session must pass QC. Arguments: @@ -996,12 +996,13 @@ def get_ophys_session_ids_for_ophys_container_id(ophys_container_id): Returns: ophys_session_ids -- list of ophys_session_ids that meet filtering criteria """ - experiments = get_filtered_ophys_experiment_table() + if experiments is None: + experiments = get_filtered_ophys_experiment_table() ophys_session_ids = np.sort(experiments[(experiments.ophys_container_id == ophys_container_id)].ophys_session_id.unique()) return ophys_session_ids -def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id): +def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments=None): """Get ophys_experiment_ids belonging to a given ophys_container_id. ophys container must meet the criteria in sdk_utils.get_filtered_session_table() @@ -1011,38 +1012,45 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id): Returns: ophys_experiment_ids -- list of ophys_experiment_ids that meet filtering criteria """ - experiments = get_filtered_ophys_experiment_table() + if experiments is None: + experiments = get_filtered_ophys_experiment_table() experiments = experiments.sort_values(by='date_of_acquisition') ophys_experiment_ids = experiments[(experiments.ophys_container_id == ophys_container_id)].index.values return ophys_experiment_ids -def get_session_type_for_ophys_experiment_id(ophys_experiment_id): - experiments = get_filtered_ophys_experiment_table() +def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments=None): + if experiments == None: + print('getting table! ') + experiments = get_filtered_ophys_experiment_table() session_type = experiments.loc[ophys_experiment_id].session_type return session_type -def get_session_type_for_ophys_session_id(ophys_session_id): - sessions = get_filtered_ophys_session_table() - session_type = sessions.loc[ophys_session_id].session_type +def get_session_type_for_ophys_session_id(ophys_session_id, experiments=None): + if experiments == None: + experiments = get_filtered_ophys_experiment_table() + session_type = experiments[experiments.ophys_session_id==ophys_session_id].session_type return session_type -def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id): - experiments = get_filtered_ophys_experiment_table() +def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments=None): + if experiments == None: + experiments = get_filtered_ophys_experiment_table() ophys_experiment_id = experiments[(experiments.ophys_session_id == ophys_session_id)].index.values[0] return ophys_experiment_id -def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id): - experiments = get_filtered_ophys_experiment_table() +def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments=None): + if experiments == None: + experiments = get_filtered_ophys_experiment_table() ophys_session_id = experiments.loc[ophys_experiment_id].ophys_session_id return ophys_session_id -def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id): - experiments = get_filtered_ophys_experiment_table(include_failed_data=True) +def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments=None): + if experiments == None: + experiments = get_filtered_ophys_experiment_table(include_failed_data=True) behavior_session_id = experiments.loc[ophys_experiment_id].behavior_session_id return behavior_session_id diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index f2b0ba4af..6b359c2e5 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -95,7 +95,8 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -104,9 +105,10 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure try: ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) except: - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) - # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] - ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) + pass + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments=experiments) + # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] + ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection', @@ -126,7 +128,8 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -136,7 +139,7 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu ax[i] = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -158,7 +161,8 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -168,7 +172,7 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): ax[i] = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -190,7 +194,8 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -200,7 +205,7 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True ax[i] = ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -235,7 +240,8 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -245,7 +251,7 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): ax[i] = ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) if save_figure: @@ -254,7 +260,8 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 18) n = len(ophys_experiment_ids) @@ -266,7 +273,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) try: @@ -297,8 +304,9 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', get_file_name_for_container(ophys_container_id)) -def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) +def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 22) n = len(ophys_experiment_ids) @@ -307,7 +315,7 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type + '\nred = valid, blue = invalid') metric = 'area' @@ -328,7 +336,8 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 22) n = len(ophys_experiment_ids) @@ -337,7 +346,7 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) metric = 'area' @@ -358,7 +367,8 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 20) n = len(ophys_experiment_ids) @@ -366,7 +376,7 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): ax = ax.ravel() for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type + '\nred = valid, blue = invalid') ax[i + n] = ep.plot_filtered_masks_for_experiment(ophys_experiment_id, include_invalid_rois=True, ax=ax[i + n]) @@ -386,7 +396,8 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 20) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) @@ -396,7 +407,7 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) ax[i] = ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + ' - ' + session_type) fig.tight_layout() @@ -849,7 +860,8 @@ def plot_cell_matching_registration_output(ophys_container_id, save_figure=True) def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figure=True): - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 20) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) @@ -857,7 +869,7 @@ def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figur for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) fig.tight_layout() if save_figure: @@ -905,7 +917,8 @@ def get_metadata_string(ophys_container_id): :param ophys_container_id: :return: """ - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] @@ -1063,14 +1076,15 @@ def plot_stimulus_population_average_across_sessions(ophys_container_id, save_fi # BEHAVIOR def plot_running_speed_for_container(ophys_container_id, save_figure=True): - ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 15) fig, ax = plt.subplots(len(ophys_session_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_session_id in enumerate(ophys_session_ids): ax[i] = sp.plot_running_speed(ophys_session_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id) + session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments) ax[i].set_title(str(ophys_session_id) + '\n' + session_type) fig.tight_layout() if save_figure: @@ -1079,7 +1093,8 @@ def plot_running_speed_for_container(ophys_container_id, save_figure=True): def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): - ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id) + experiments = loading.get_filtered_ophys_experiment_table() + ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 7) fig, ax = plt.subplots(1, len(ophys_session_ids), figsize=figsize) @@ -1087,7 +1102,7 @@ def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): for i, ophys_session_id in enumerate(ophys_session_ids): ax[i] = sp.plot_lick_raster(ophys_session_id, ax=ax[i]) ax[i].invert_yaxis() - session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id) + session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments) ax[i].set_title(str(ophys_session_id) + '\n' + session_type) # plt.gca().invert_yaxis() @@ -1107,13 +1122,16 @@ def plot_pupil_area_sdk(ophys_container_id, save_figure=True): nplots = len(ophys_experiment_ids) buffer = 0.075 for ii, ophys_experiment_id in enumerate(ophys_experiment_ids): - print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) - axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) - if ii + 1 == len(ophys_experiment_ids): - label_x = True - else: - label_x = False - axes[-1] = ep.make_pupil_area_plot_sdk(ophys_experiment_id, axes[-1], label_x=label_x) + try: + print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) + axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) + if ii + 1 == len(ophys_experiment_ids): + label_x = True + else: + label_x = False + axes[-1] = ep.make_pupil_area_plot_sdk(ophys_experiment_id, axes[-1], label_x=label_x) + except: + pass if save_figure: print('saving') @@ -1134,13 +1152,16 @@ def plot_pupil_area(ophys_container_id, save_figure=True): nplots = len(ophys_experiment_ids) buffer = 0.075 for ii, ophys_experiment_id in enumerate(ophys_experiment_ids): - print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) - axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) - if ii + 1 == len(ophys_experiment_ids): - label_x = True - else: - label_x = False - axes[-1] = ep.make_pupil_area_plot(ophys_experiment_id, axes[-1], label_x=label_x) + try: + print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) + axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) + if ii + 1 == len(ophys_experiment_ids): + label_x = True + else: + label_x = False + axes[-1] = ep.make_pupil_area_plot(ophys_experiment_id, axes[-1], label_x=label_x) + except: + pass if save_figure: print('saving') @@ -1161,13 +1182,16 @@ def plot_pupil_position(ophys_container_id, save_figure=True): nplots = len(ophys_experiment_ids) buffer = 0.075 for ii, ophys_experiment_id in enumerate(ophys_experiment_ids): - print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) - axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) - if ii + 1 == len(ophys_experiment_ids): - label_x = True - else: - label_x = False - axes[-1] = ep.make_pupil_position_plot(ophys_experiment_id, axes[-1], label_x=label_x) + try: + print('on ophys_experiment_id {}, #{} of {}'.format(ophys_experiment_id, ii + 1, nplots)) + axes.append(vbp.placeAxesOnGrid(fig, xspan=(0, 1), yspan=(ii / nplots + buffer, (ii + 1) / nplots))) + if ii + 1 == len(ophys_experiment_ids): + label_x = True + else: + label_x = False + axes[-1] = ep.make_pupil_position_plot(ophys_experiment_id, axes[-1], label_x=label_x) + except: + pass if save_figure: if save_figure: @@ -1319,8 +1343,11 @@ def plot_dff_trace_and_behavior_for_container(ophys_container_id, save_figure=Tr ophys_experiment_ids = ophys_experiments.index.values for ophys_experiment_id in ophys_experiment_ids: - ep.plot_population_activity_and_behavior_for_experiment(ophys_experiment_id, save_figure=save_figure) - ep.plot_dff_trace_and_behavior_for_experiment(ophys_experiment_id, save_figure=save_figure) + try: + ep.plot_population_activity_and_behavior_for_experiment(ophys_experiment_id, save_figure=save_figure) + ep.plot_dff_trace_and_behavior_for_experiment(ophys_experiment_id, save_figure=save_figure) + except: + pass def plot_classifier_validation_for_container(ophys_container_id, save_figure=True): From fdc0a855c59e21b7ca528bbb2fd036f3b8525bff Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 11:12:38 -0800 Subject: [PATCH 038/187] update cluster scripts for learning project data --- scripts/create_multi_session_df.py | 26 +++++----- scripts/run_create_multi_session_df.py | 25 +++++---- scripts/run_save_stimulus_response_dfs.py | 14 ++--- scripts/save_stimulus_response_dfs.py | 51 +++++++++---------- .../ophys/io/create_multi_session_df.py | 21 ++++---- 5 files changed, 67 insertions(+), 70 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index ba3542389..f48e57caf 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -12,12 +12,12 @@ # define args parser = argparse.ArgumentParser() parser.add_argument('--project_code', type=str, help='project code to use') - parser.add_argument('--session_number', type=str, help='session number to use') + parser.add_argument('--session_type', type=str, help='session type to use') args = parser.parse_args() project_code = args.project_code - session_number = float(args.session_number) + session_type = args.session_type - print(project_code, session_number) + print(project_code, session_type) # params for stim response df creation time_window = [-3, 3.1] @@ -27,12 +27,12 @@ use_extended_stimulus_presentations = False # set up conditions to make multi session dfs for - physio_data_types = ['filtered_events', 'events', 'dff'] + physio_data_types = ['dff', 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] physio_conditions = [['cell_specimen_id', 'is_change'], - ['cell_specimen_id', 'is_change', 'epoch'], ['cell_specimen_id', 'omitted'], + ['cell_specimen_id', 'is_change', 'epoch'], ['cell_specimen_id', 'omitted', 'epoch'], ['cell_specimen_id', 'is_change', 'image_name'], ['cell_specimen_id', 'is_change', 'image_name', 'epoch'], @@ -42,8 +42,8 @@ ['cell_specimen_id', 'omitted', 'pre_omitted'],] behavior_conditions = [['ophys_experiment_id', 'is_change'], - ['ophys_experiment_id', 'is_change', 'epoch'], - ['ophys_experiment_id', 'omitted'], + ['ophys_experiment_id', 'omitted'], + ['ophys_experiment_id', 'is_change', 'epoch'], ['ophys_experiment_id', 'omitted', 'epoch'], ['ophys_experiment_id', 'is_change', 'image_name'], ['ophys_experiment_id', 'is_change', 'image_name', 'epoch'], @@ -54,8 +54,8 @@ # event types corresponding to the above physio and behavior conditions - must be in same sequential order - event_types_for_conditions = ['changes', 'changes', - 'omissions', 'omissions', + event_types_for_conditions = ['changes', 'omissions', + 'changes', 'omissions', 'changes', 'changes', 'changes', 'all', 'all', 'all'] @@ -67,7 +67,7 @@ # create dfs for all data types and conditions for physio data for data_type in physio_data_types: # data_type = 'events' - for i, conditions in enumerate(physio_conditions[-1:]): + for i, conditions in enumerate(physio_conditions): print(conditions) event_type = event_types_for_conditions[i] if event_type == 'omissions': @@ -76,11 +76,11 @@ response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(project_code, session_number, conditions, data_type, event_type, + df = io.get_multi_session_df(project_code, session_type, conditions, data_type, event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, - overwrite=False) + overwrite=True) except Exception as e: print('failed to create multi_session_df for', data_type, event_type, conditions) print(e) @@ -96,7 +96,7 @@ # response_window_duration_seconds = 0.5 # print('creating multi_session_df for', data_type, event_type, conditions) # try: # use try except so that it skips over any conditions that fail to generate for some reason - # df = io.get_multi_session_df(project_code, session_number, conditions, data_type, event_type, + # df = io.get_multi_session_df(project_code, session_type, conditions, data_type, event_type, # time_window=time_window, interpolate=interpolate, # output_sampling_rate=output_sampling_rate, # response_window_duration_seconds=response_window_duration_seconds, diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 9a0e577bd..43bcb44e0 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -22,33 +22,32 @@ ) # define the job record output folder -stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/multi_session_dfs" +stdout_location = r"/allen/programs/mindscope/workgroups/learning/cluster_jobs/multi_session_dfs" -# cache_dir = loading.get_platform_analysis_cache_dir() -# cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir=cache_dir) -# print(cache_dir) -# experiments_table = cache.get_ophys_experiment_table() -cache = VisualBehaviorOphysProjectCache.from_lims() -experiments_table = cache.get_ophys_experiment_table(passed_only=False) -experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] -experiments_table = experiments_table[experiments_table.session_type!='OPHYS_7_receptive_field_mapping'] +# cache = VisualBehaviorOphysProjectCache.from_lims() +# experiments_table = cache.get_ophys_experiment_table(passed_only=False) +# experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] +# experiments_table = experiments_table[experiments_table.session_type!='OPHYS_7_receptive_field_mapping'] + +experiments_table = loading.get_filtered_ophys_experiment_table() +experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): print(project_code) - for session_number in experiments_table.session_number.unique(): + for session_type in experiments_table.session_type.unique(): # instantiate a Slurm object slurm = Slurm( mem='120g', # '24g' cpus_per_task=1, - time='60:00:00', + time='20:00:00', partition='braintv', - job_name='multi_session_df_'+project_code+'_'+str(session_number), + job_name='multi_session_df_'+project_code+'_'+session_type, output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_path+' '+python_file+' --project_code '+str(project_code)+' --session_number'+' '+str(session_number)) + slurm.sbatch(python_path+' '+python_file+' --project_code '+str(project_code)+' --session_type'+' '+str(session_type)) diff --git a/scripts/run_save_stimulus_response_dfs.py b/scripts/run_save_stimulus_response_dfs.py index 1b6b3c0b6..93b94ad12 100644 --- a/scripts/run_save_stimulus_response_dfs.py +++ b/scripts/run_save_stimulus_response_dfs.py @@ -10,13 +10,8 @@ parser.add_argument('--scriptname', type=str, default='save_stimulus_response_dfs.py', metavar='name of script to run (must be in same folder)') -experiments_table = loading.get_platform_paper_experiment_table() -ophys_experiment_ids = experiments_table.index.values - -# import pandas as pd -# import numpy as np -# df = pd.read_csv(os.path.join(loading.get_stimulus_response_df_dir(), 'expts_to_reprocess.csv')) -# ophys_experiment_ids = np.unique(df.ophys_experiment_id.values) +experiments_table = loading.get_filtered_ophys_experiment_table() +ophys_experiment_ids = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'].index.values if __name__ == "__main__": @@ -25,12 +20,13 @@ python_file = os.path.join(os.getcwd(), args.scriptname) # define the job record output folder - stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/stimulus_response_dfs" + stdout_location = r"/allen/programs/mindscope/workgroups/learning/cluster_jobs/stimulus_response_dfs" + # instantiate a Slurm object slurm = Slurm( mem='60g', # '24g' - cpus_per_task=10, + cpus_per_task=1, time='10:00:00', partition='braintv', job_name='stim_response_dfs', diff --git a/scripts/save_stimulus_response_dfs.py b/scripts/save_stimulus_response_dfs.py index e792e4509..feb233b65 100644 --- a/scripts/save_stimulus_response_dfs.py +++ b/scripts/save_stimulus_response_dfs.py @@ -21,31 +21,30 @@ # load cache and dataset cache_dir = loading.get_platform_analysis_cache_dir() - cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir) + cache = VisualBehaviorOphysProjectCache.from_lims(cache_dir) dataset = cache.get_behavior_ophys_experiment(ophys_experiment_id) # create and save stimulus response df for all data types - event_type = 'omissions' - for data_type in ['events', 'filtered_events', 'dff', 'running_speed', 'pupil_width', 'lick_rate']: - # for event_type in ['all', 'omissions', 'changes']: - # set up save folder - save_dir = loading.get_stimulus_response_df_dir(interpolate, int(output_sampling_rate), event_type) - if event_type == 'omissions': - response_window_duration = 0.75 - else: - response_window_duration = 0.5 - try: - sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, - time_window=time_window, interpolate=interpolate, - output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration) - # if file already exists, overwrite it - filepath = loading.get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_type, event_type, - interpolate=interpolate, output_sampling_rate=output_sampling_rate) - if os.path.exists(filepath): - os.remove(filepath) - print('h5 file exists for', ophys_experiment_id, ' - overwriting') - sdf.to_hdf(filepath, key='df') - print('saved response df for', data_type) - except Exception as e: - print('could not save stimulus_response_df for', ophys_experiment_id, data_type, event_type) - print(e) + for data_type in ['dff', 'filtered_events', 'events', 'running_speed', 'pupil_width', 'lick_rate']: + for event_type in ['omissions', 'changes', 'all']: + # set up save folder + save_dir = loading.get_stimulus_response_df_dir(interpolate, int(output_sampling_rate), event_type) + if event_type == 'omissions': + response_window_duration = 0.75 + else: + response_window_duration = 0.5 + try: + sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, + time_window=time_window, interpolate=interpolate, + output_sampling_rate=output_sampling_rate, + response_window_duration=response_window_duration) + # if file already exists, overwrite it + filepath = loading.get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_type, event_type, + interpolate=interpolate, output_sampling_rate=output_sampling_rate) + if os.path.exists(filepath): + os.remove(filepath) + print('h5 file exists for', ophys_experiment_id, ' - overwriting') + sdf.to_hdf(filepath, key='df') + print('saved response df for', data_type) + except Exception as e: + print('could not save stimulus_response_df for', ophys_experiment_id, data_type, event_type) + print(e) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 39abbe283..4e446e1ca 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -6,7 +6,7 @@ from visual_behavior.data_access import loading -def get_multi_session_df(project_code, session_number, conditions, data_type, event_type, +def get_multi_session_df(project_code, session_type, conditions, data_type, event_type, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5, use_extended_stimulus_presentations=False, overwrite=False): """ @@ -30,16 +30,19 @@ def get_multi_session_df(project_code, session_number, conditions, data_type, ev get_pref_stim = False print('get_pref_stim', get_pref_stim) - cache_dir = loading.get_platform_analysis_cache_dir() - cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir=cache_dir) - print(cache_dir) - experiments_table = cache.get_ophys_experiment_table() - # dont include Ai94 experiments because they makes things too slow - experiments_table = experiments_table[(experiments_table.reporter_line != 'Ai94(TITL-GCaMP6s)')] + # cache_dir = loading.get_platform_analysis_cache_dir() + # cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir=cache_dir) + # print(cache_dir) + # experiments_table = cache.get_ophys_experiment_table() + # # dont include Ai94 experiments because they makes things too slow + # experiments_table = experiments_table[(experiments_table.reporter_line != 'Ai94(TITL-GCaMP6s)')] - session_number = float(session_number) + experiments_table = loading.get_filtered_ophys_experiment_table() + experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + + # session_type = float(session_type) experiments = experiments_table[(experiments_table.project_code == project_code) & - (experiments_table.session_number == session_number)].copy() + (experiments_table.session_type == session_type)].copy() print('session_types:', experiments.session_type.unique(), ' - there should only be one session_type per session_number') session_type = experiments.session_type.unique()[0] From ffaae4cacac0d347ccafe68d5d9c9fc86b855630 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 12:10:53 -0800 Subject: [PATCH 039/187] typo --- visual_behavior/visualization/qc/container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 6b359c2e5..885f79c94 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -304,7 +304,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', get_file_name_for_container(ophys_container_id)) -def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True) +def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True): experiments = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) From 3548113273d33cde9e2f6a326fe22be59248fb21 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 12:16:53 -0800 Subject: [PATCH 040/187] test --- visual_behavior/data_access/loading.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 5b8e26f79..832b42924 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,13 +74,12 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' return cache_dir def get_qc_plots_dir(): - return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' # return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' From f2c5d2d3e95b5c21d7f51ee85cec9e0df90236f7 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 12:38:30 -0800 Subject: [PATCH 041/187] require expts table to be passed --- visual_behavior/data_access/loading.py | 44 +++++++++---------- .../qc/save_all_container_plots.py | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 832b42924..543de3e04 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -986,7 +986,7 @@ def get_ophys_container_ids(platform_paper_only=False, add_extra_columns=True): return container_ids -def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments=None): +def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments): """Get ophys_session_ids belonging to a given ophys_container_id. Ophys session must pass QC. Arguments: @@ -995,13 +995,13 @@ def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments Returns: ophys_session_ids -- list of ophys_session_ids that meet filtering criteria """ - if experiments is None: - experiments = get_filtered_ophys_experiment_table() + # if experiments is None: + # experiments = get_filtered_ophys_experiment_table() ophys_session_ids = np.sort(experiments[(experiments.ophys_container_id == ophys_container_id)].ophys_session_id.unique()) return ophys_session_ids -def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments=None): +def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments): """Get ophys_experiment_ids belonging to a given ophys_container_id. ophys container must meet the criteria in sdk_utils.get_filtered_session_table() @@ -1011,45 +1011,45 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experime Returns: ophys_experiment_ids -- list of ophys_experiment_ids that meet filtering criteria """ - if experiments is None: - experiments = get_filtered_ophys_experiment_table() + # if experiments is None: + # experiments = get_filtered_ophys_experiment_table() experiments = experiments.sort_values(by='date_of_acquisition') ophys_experiment_ids = experiments[(experiments.ophys_container_id == ophys_container_id)].index.values return ophys_experiment_ids -def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments=None): - if experiments == None: - print('getting table! ') - experiments = get_filtered_ophys_experiment_table() +def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments): + # if experiments == None: + # print('getting table! ') + # experiments = get_filtered_ophys_experiment_table() session_type = experiments.loc[ophys_experiment_id].session_type return session_type -def get_session_type_for_ophys_session_id(ophys_session_id, experiments=None): - if experiments == None: - experiments = get_filtered_ophys_experiment_table() +def get_session_type_for_ophys_session_id(ophys_session_id, experiments): + # if experiments == None: + # experiments = get_filtered_ophys_experiment_table() session_type = experiments[experiments.ophys_session_id==ophys_session_id].session_type return session_type -def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments=None): - if experiments == None: - experiments = get_filtered_ophys_experiment_table() +def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments): + # if experiments == None: + # experiments = get_filtered_ophys_experiment_table() ophys_experiment_id = experiments[(experiments.ophys_session_id == ophys_session_id)].index.values[0] return ophys_experiment_id -def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments=None): - if experiments == None: - experiments = get_filtered_ophys_experiment_table() +def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments): + # if experiments == None: + # experiments = get_filtered_ophys_experiment_table() ophys_session_id = experiments.loc[ophys_experiment_id].ophys_session_id return ophys_session_id -def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments=None): - if experiments == None: - experiments = get_filtered_ophys_experiment_table(include_failed_data=True) +def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments): + # if experiments == None: + # experiments = get_filtered_ophys_experiment_table(include_failed_data=True) behavior_session_id = experiments.loc[ophys_experiment_id].behavior_session_id return behavior_session_id diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index dd48691bf..99f94d6e4 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -5,7 +5,7 @@ def main(): possible_plots = { "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, "ophys_session_sequence": cp.plot_container_session_sequence, "max_projection_images": cp.plot_sdk_max_projection_images_for_container, "average_images": cp.plot_sdk_average_images_for_container, From 82384ef26870bd76b60aa38fd3546fdbc6cf0639 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 18:26:34 -0800 Subject: [PATCH 042/187] cleanup --- visual_behavior/data_access/loading.py | 62 ++++++++++++-------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 543de3e04..0194b6ce8 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -986,71 +986,65 @@ def get_ophys_container_ids(platform_paper_only=False, add_extra_columns=True): return container_ids -def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments): +def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table): """Get ophys_session_ids belonging to a given ophys_container_id. Ophys session must pass QC. Arguments: ophys_container_id -- must be in get_ophys_container_ids() + experiments_table -- table of experimental metadata Returns: ophys_session_ids -- list of ophys_session_ids that meet filtering criteria """ - # if experiments is None: - # experiments = get_filtered_ophys_experiment_table() - ophys_session_ids = np.sort(experiments[(experiments.ophys_container_id == ophys_container_id)].ophys_session_id.unique()) + ophys_session_ids = np.sort(experiments_table[(experiments_table.ophys_container_id == ophys_container_id)].ophys_session_id.unique()) return ophys_session_ids -def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments): - """Get ophys_experiment_ids belonging to a given ophys_container_id. ophys container must meet the criteria in - sdk_utils.get_filtered_session_table() +def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table): + """Get ophys_experiment_ids belonging to a given ophys_container_id. o Arguments: - ophys_container_id -- must be in get_filtered_ophys_container_ids() + ophys_container_id -- must be in experiments_table + experiments_table -- table of experimental metadata Returns: ophys_experiment_ids -- list of ophys_experiment_ids that meet filtering criteria - """ - # if experiments is None: - # experiments = get_filtered_ophys_experiment_table() - experiments = experiments.sort_values(by='date_of_acquisition') - ophys_experiment_ids = experiments[(experiments.ophys_container_id == ophys_container_id)].index.values + + + example of how to create experiments_table using allenSDK: + + from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache + cache = VisualBehaviorOphysProjectCache.from_lims() + experiments_table = cache.get_ophys_experiment_table(passed_only=False) + + """ + experiments_table = experiments_table.sort_values(by='date_of_acquisition') + ophys_experiment_ids = experiments_table[(experiments_table.ophys_container_id == ophys_container_id)].index.values return ophys_experiment_ids -def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments): - # if experiments == None: - # print('getting table! ') - # experiments = get_filtered_ophys_experiment_table() - session_type = experiments.loc[ophys_experiment_id].session_type +def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table): + session_type = experiments_table.loc[ophys_experiment_id].session_type return session_type -def get_session_type_for_ophys_session_id(ophys_session_id, experiments): - # if experiments == None: - # experiments = get_filtered_ophys_experiment_table() - session_type = experiments[experiments.ophys_session_id==ophys_session_id].session_type +def get_session_type_for_ophys_session_id(ophys_session_id, experiments_table): + session_type = experiments_table[experiments_table.ophys_session_id==ophys_session_id].session_type return session_type -def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments): - # if experiments == None: - # experiments = get_filtered_ophys_experiment_table() - ophys_experiment_id = experiments[(experiments.ophys_session_id == ophys_session_id)].index.values[0] +def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments_table): + ophys_experiment_id = experiments_table[(experiments_table.ophys_session_id == ophys_session_id)].index.values[0] return ophys_experiment_id -def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments): - # if experiments == None: - # experiments = get_filtered_ophys_experiment_table() - ophys_session_id = experiments.loc[ophys_experiment_id].ophys_session_id +def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table): + ophys_session_id = experiments_table.loc[ophys_experiment_id].ophys_session_id return ophys_session_id -def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments): - # if experiments == None: - # experiments = get_filtered_ophys_experiment_table(include_failed_data=True) - behavior_session_id = experiments.loc[ophys_experiment_id].behavior_session_id +def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table): + behavior_session_id = experiments_table.loc[ophys_experiment_id].behavior_session_id return behavior_session_id From f2e70765ab704536758966ba78cf3e16d9507fc3 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 18:59:13 -0800 Subject: [PATCH 043/187] generate expt_table if not provided --- visual_behavior/data_access/loading.py | 38 ++++++--- .../visualization/qc/container_plots.py | 80 +++++++++---------- 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 0194b6ce8..5211e5e96 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -986,7 +986,7 @@ def get_ophys_container_ids(platform_paper_only=False, add_extra_columns=True): return container_ids -def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table): +def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table=pd.DataFrame()): """Get ophys_session_ids belonging to a given ophys_container_id. Ophys session must pass QC. Arguments: @@ -996,11 +996,13 @@ def get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments Returns: ophys_session_ids -- list of ophys_session_ids that meet filtering criteria """ + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() ophys_session_ids = np.sort(experiments_table[(experiments_table.ophys_container_id == ophys_container_id)].ophys_session_id.unique()) return ophys_session_ids -def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table): +def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table=pd.DataFrame()): """Get ophys_experiment_ids belonging to a given ophys_container_id. o Arguments: @@ -1018,32 +1020,44 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experime experiments_table = cache.get_ophys_experiment_table(passed_only=False) """ + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() experiments_table = experiments_table.sort_values(by='date_of_acquisition') ophys_experiment_ids = experiments_table[(experiments_table.ophys_container_id == ophys_container_id)].index.values return ophys_experiment_ids -def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table): +def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table=pd.DataFrame()): + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() session_type = experiments_table.loc[ophys_experiment_id].session_type return session_type -def get_session_type_for_ophys_session_id(ophys_session_id, experiments_table): +def get_session_type_for_ophys_session_id(ophys_session_id, experiments_table=pd.DataFrame()): + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() session_type = experiments_table[experiments_table.ophys_session_id==ophys_session_id].session_type return session_type -def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments_table): +def get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments_table=pd.DataFrame()): + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_id = experiments_table[(experiments_table.ophys_session_id == ophys_session_id)].index.values[0] return ophys_experiment_id -def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table): +def get_ophys_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table=pd.DataFrame()): + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() ophys_session_id = experiments_table.loc[ophys_experiment_id].ophys_session_id return ophys_session_id -def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table): +def get_behavior_session_id_for_ophys_experiment_id(ophys_experiment_id, experiments_table=pd.DataFrame()): + if len(experiments_table)==0: + experiments_table = loading.get_filtered_ophys_experiment_table() behavior_session_id = experiments_table.loc[ophys_experiment_id].behavior_session_id return behavior_session_id @@ -1087,7 +1101,7 @@ def get_extended_stimulus_presentations_for_session(session): licks=session.licks, rewards=session.rewards, change_times=change_times, - running_speed_df=session.running_data_df, + running_speed_df=session.running_speed, pupil_area=session.pupil_area ) return extended_stimulus_presentations @@ -1364,13 +1378,15 @@ def get_sdk_dff_traces_array(ophys_experiment_id): def get_sdk_running_speed(ophys_session_id): - session = get_ophys_dataset(get_ophys_experiment_id_for_ophys_session_id(ophys_session_id)) - running_speed = session.running_data_df['speed'] + experiments_table = get_filtered_ophys_experiment_table() + session = get_ophys_dataset(get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments_table)) + running_speed = session.running_speed['speed'] return running_speed def get_sdk_trials(ophys_session_id): - session = get_ophys_dataset(get_ophys_experiment_id_for_ophys_session_id(ophys_session_id)) + experiments_table = get_filtered_ophys_experiment_table() + session = get_ophys_dataset(get_ophys_experiment_id_for_ophys_session_id(ophys_session_id, experiments_table)) trials = session.trials.reset_index() return trials diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 885f79c94..8d75d6f4a 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -95,8 +95,8 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -106,7 +106,7 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments=experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -128,8 +128,8 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -139,7 +139,7 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu ax[i] = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -161,8 +161,8 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -172,7 +172,7 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): ax[i] = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -194,8 +194,8 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True """ # exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) # ophys_experiment_ids = list(exp_order_and_stage["ophys_experiment_id"]) - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -205,7 +205,7 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True ax[i] = ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) # exp_stage_name = exp_order_and_stage.loc[exp_order_and_stage["ophys_experiment_id"]== ophys_experiment_id, "stage_name_lims"].reset_index(drop=True)[0] ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) @@ -215,9 +215,9 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): - table = loading.get_filtered_ophys_experiment_table() - table = table.reset_index() - ophys_experiment_ids = table.query('ophys_container_id == {}'.format(ophys_container_id)).sort_values(by='date_of_acquisition')['ophys_experiment_id'] + experiments_table = loading.get_filtered_ophys_experiment_table() + experiments_table = experiments_table.reset_index() + ophys_experiment_ids = experiments_table.query('ophys_container_id == {}'.format(ophys_container_id)).sort_values(by='date_of_acquisition')['ophys_experiment_id'] figsize = (16, 5 * len(ophys_experiment_ids)) fig = plt.figure(figsize=figsize) @@ -240,7 +240,7 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() + experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) figsize = (25, 5) @@ -251,7 +251,7 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): ax[i] = ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) if save_figure: @@ -260,8 +260,8 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 18) n = len(ophys_experiment_ids) @@ -273,7 +273,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) try: @@ -305,8 +305,8 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 22) n = len(ophys_experiment_ids) @@ -315,7 +315,7 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type + '\nred = valid, blue = invalid') metric = 'area' @@ -336,8 +336,8 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 22) n = len(ophys_experiment_ids) @@ -346,7 +346,7 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) metric = 'area' @@ -367,8 +367,8 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 20) n = len(ophys_experiment_ids) @@ -376,7 +376,7 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): ax = ax.ravel() for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type + '\nred = valid, blue = invalid') ax[i + n] = ep.plot_filtered_masks_for_experiment(ophys_experiment_id, include_invalid_rois=True, ax=ax[i + n]) @@ -396,8 +396,8 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 20) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) @@ -407,7 +407,7 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) ax[i] = ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=ax[i]) except: pass - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + ' - ' + session_type) fig.tight_layout() @@ -860,8 +860,8 @@ def plot_cell_matching_registration_output(ophys_container_id, save_figure=True) def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 20) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) @@ -869,7 +869,7 @@ def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figur for i, ophys_experiment_id in enumerate(ophys_experiment_ids): ax[i] = ep.plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments) + session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) fig.tight_layout() if save_figure: @@ -917,8 +917,8 @@ def get_metadata_string(ophys_container_id): :param ophys_container_id: :return: """ - experiments = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] @@ -1076,15 +1076,15 @@ def plot_stimulus_population_average_across_sessions(ophys_container_id, save_fi # BEHAVIOR def plot_running_speed_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 15) fig, ax = plt.subplots(len(ophys_session_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_session_id in enumerate(ophys_session_ids): ax[i] = sp.plot_running_speed(ophys_session_id, ax=ax[i]) - session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments) + session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments_table) ax[i].set_title(str(ophys_session_id) + '\n' + session_type) fig.tight_layout() if save_figure: From de3ea4de68cad1e1c1bb01bace40a1eb1936c78a Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 15 Feb 2022 19:20:33 -0800 Subject: [PATCH 044/187] use session type instead of session number --- visual_behavior/data_access/loading.py | 14 +++++++----- .../visualization/qc/container_plots.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 5211e5e96..88c885269 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,12 +74,12 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' return cache_dir def get_qc_plots_dir(): - return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots' # return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' @@ -1030,14 +1030,14 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experime def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table=pd.DataFrame()): if len(experiments_table)==0: experiments_table = loading.get_filtered_ophys_experiment_table() - session_type = experiments_table.loc[ophys_experiment_id].session_type + session_type = experiments_table.loc[ophys_experiment_id].session_type.values[0] return session_type def get_session_type_for_ophys_session_id(ophys_session_id, experiments_table=pd.DataFrame()): if len(experiments_table)==0: experiments_table = loading.get_filtered_ophys_experiment_table() - session_type = experiments_table[experiments_table.ophys_session_id==ophys_session_id].session_type + session_type = experiments_table[experiments_table.ophys_session_id==ophys_session_id].session_type.values[0] return session_type @@ -3023,14 +3023,16 @@ def get_container_response_df(ophys_container_id, data_type='dff', event_type='a for ophys_experiment_id in container_expts.index.values: print(ophys_experiment_id) try: + print('getting stimulus response df for', ophys_experiment_id) dataset = get_ophys_dataset(ophys_experiment_id) sdf = get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, data_type=data_type, event_type=event_type, load_from_file=False) sdf['ophys_experiment_id'] = ophys_experiment_id - sdf['session_number'] = experiments_table.loc[ophys_experiment_id].session_number + sdf['session_type'] = experiments_table.loc[ophys_experiment_id].session_type container_df = pd.concat([container_df, sdf]) - except: + except Exception as e: print('problem for', ophys_experiment_id) + print(e) return container_df diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 8d75d6f4a..344bd9747 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -921,7 +921,7 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string @@ -967,34 +967,32 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, da container_df = container_df[container_df.image_name != 'omitted'] title = str(ophys_container_id) fig, ax = plt.subplots(figsize=figsize) - colors = ut.get_colors_for_session_numbers() + # colors = ut.get_colors_for_session_numbers() + colors = ut.get_session_type_color_map() for i, ophys_experiment_id in enumerate(container_df.ophys_experiment_id.unique()): df = container_df[container_df.ophys_experiment_id == ophys_experiment_id].copy() - session_number = df.session_number.unique()[0] + session_type = df.session_type.unique()[0] traces = df.trace.values mean_trace = df.trace.mean() timestamps = df.trace_timestamps.mean() - ax.plot(timestamps, mean_trace, color=colors[int(session_number - 1)], label=session_number) + ax.plot(timestamps, mean_trace, color=colors[session_type], label=session_type) sem = (traces.std()) / np.sqrt(float(len(traces))) - ax.fill_between(timestamps, mean_trace + sem, mean_trace - sem, alpha=0.5, color=colors[int(session_number - 1)]) + ax.fill_between(timestamps, mean_trace + sem, mean_trace - sem, alpha=0.5, color=colors[session_type]) if event_type == 'omissions': - ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=True, alpha=0.2, - facecolor='gray') + ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=True, alpha=0.2, facecolor='gray') ax.axvline(x=0, ymin=0, ymax=1, linestyle='--', color='gray') ax.set_xlabel('time relative to omission (sec)') elif event_type == 'changes': - ax = plot_flashes_on_trace(ax, timestamps, trial_type='go', omitted=False, alpha=0.2, - facecolor='gray') + ax = plot_flashes_on_trace(ax, timestamps, trial_type='go', omitted=False, alpha=0.2, facecolor='gray') ax.set_xlabel('time relative to change (sec)') else: - ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=False, alpha=0.2, - facecolor='gray') + ax = plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=False, alpha=0.2, facecolor='gray') ax.set_xlabel('time (sec)') ax.set_ylabel('dF/F') ax.set_xlim(timestamps[0], timestamps[-1]) ax.set_title(title) - ax.legend(bbox_to_anchor=(1, 1), title='session_number', fontsize='small', title_fontsize='x-small') + ax.legend(bbox_to_anchor=(1, 1), title='session_type', fontsize='small', title_fontsize='x-small') fig.tight_layout() if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), From 63fd5e958e3ca81e0185ff6f1346dc24b3f2e164 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 16 Feb 2022 18:45:48 -0800 Subject: [PATCH 045/187] minor updates --- visual_behavior/data_access/loading.py | 29 ++++++------------- .../visualization/qc/container_plots.py | 8 ++--- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 88c885269..bcf926d25 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,13 +74,13 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' return cache_dir def get_qc_plots_dir(): - return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots' - # return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/qc_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + # return r'\\allen\programs\mindscope\workgroups\learning\ophys\qc_plots' def get_super_container_plots_dir(): @@ -124,7 +124,7 @@ def get_ophys_glm_dir(): def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): - base_dir = os.path.join(get_production_cache_dir, 'stimulus_response_dfs') + base_dir = os.path.join(get_production_cache_dir(), 'stimulus_response_dfs') if interpolate: save_dir = os.path.join(base_dir, event_type, 'interpolate_' + str(output_sampling_rate) + 'Hz') else: @@ -134,16 +134,6 @@ def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, even return save_dir -# def get_multi_session_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): -# base_dir = get_platform_analysis_cache_dir() -# if interpolate: -# save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', 'interpolate_' + str(output_sampling_rate) + 'Hz') -# else: -# save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', 'original_frame_rate') -# if not os.path.exists(save_dir): -# os.mkdir(save_dir) -# return save_dir - def get_multi_session_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): base_dir = get_platform_analysis_cache_dir() if interpolate: @@ -197,9 +187,7 @@ def get_released_ophys_experiment_table(exclude_ai94=True): experiment_table -- returns a dataframe with ophys_experiment_id as the index and metadata as columns. ''' print('getting experiment table from lims, NOT AWS') - cache = bpc.from_lims(data_release_date=['2021-03-25', '2021-08-12']) - experiment_table = cache.get_ophys_experiment_table() if exclude_ai94: @@ -758,8 +746,7 @@ def behavior_movie_predictions(self): ophys_session_id = from_lims.get_ophys_session_id_for_ophys_experiment_id(self.ophys_experiment_id) movie_predictions = get_behavior_movie_predictions_for_session(ophys_session_id) movie_predictions.index.name = 'frame_index' - movie_predictions['timestamps'] = self.behavior_movie_timestamps[:len( - movie_predictions)] # length check will trim off spurious timestamps at the end + movie_predictions['timestamps'] = self.behavior_movie_timestamps[:len(movie_predictions)] # length check will trim off spurious timestamps at the end self._behavior_movie_predictions = movie_predictions return self._behavior_movie_predictions @@ -806,7 +793,9 @@ def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from assert id_type == 'ophys_experiment_id', "The passed ID type is {}. It must be an ophys_experiment_id".format(id_type) if load_from_lims: - dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id)) + cache = bpc.from_lims() + dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id)) + # dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id)) elif load_from_nwb: cache_dir = get_platform_analysis_cache_dir() cache = bpc.from_s3_cache(cache_dir=cache_dir) @@ -1030,7 +1019,7 @@ def get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experime def get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table=pd.DataFrame()): if len(experiments_table)==0: experiments_table = loading.get_filtered_ophys_experiment_table() - session_type = experiments_table.loc[ophys_experiment_id].session_type.values[0] + session_type = experiments_table.loc[ophys_experiment_id].session_type return session_type diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 344bd9747..bca0e743c 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -241,7 +241,7 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments) + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) @@ -1091,8 +1091,8 @@ def plot_running_speed_for_container(ophys_container_id, save_figure=True): def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): - experiments = loading.get_filtered_ophys_experiment_table() - ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments) + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table) figsize = (25, 7) fig, ax = plt.subplots(1, len(ophys_session_ids), figsize=figsize) @@ -1100,7 +1100,7 @@ def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): for i, ophys_session_id in enumerate(ophys_session_ids): ax[i] = sp.plot_lick_raster(ophys_session_id, ax=ax[i]) ax[i].invert_yaxis() - session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments) + session_type = loading.get_session_type_for_ophys_session_id(ophys_session_id, experiments_table) ax[i].set_title(str(ophys_session_id) + '\n' + session_type) # plt.gca().invert_yaxis() From db552ec82a217a2e6e379c5f159acee04ad0fc51 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 16 Feb 2022 19:01:13 -0800 Subject: [PATCH 046/187] save plots to 'all' folder --- .../visualization/qc/container_plots.py | 138 ++++++++++++------ .../qc/save_all_container_plots.py | 42 +++--- 2 files changed, 113 insertions(+), 67 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index bca0e743c..2b7921af1 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -78,6 +78,8 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'ophys_session_sequence', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id)+'_ophys_session_sequence') # OPHYS @@ -113,6 +115,8 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_max_intensity_projection') def plot_movie_max_projection_images_for_container(ophys_container_id, save_figure=True): @@ -146,6 +150,8 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection_movies', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_max_intensity_projection_movies') def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): @@ -179,6 +185,8 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_images', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_average_images') def plot_movie_average_images_for_container(ophys_container_id, save_figure=True): @@ -212,6 +220,8 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_images_movies', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_average_images_movies') def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): @@ -235,6 +245,8 @@ def plot_eye_tracking_sample_frames(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'eyetracking_sample_frames', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_eyetracking_sample_frames') return fig, axes @@ -257,6 +269,8 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_masks', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_segmentation_masks') def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figure=True): @@ -301,7 +315,10 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur print('error: {}'.format(e)) if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id)+'_segmentation_mask_overlays') def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, save_figure=True): @@ -333,6 +350,8 @@ def plot_roi_filtering_metrics_for_all_rois_for_container(ophys_container_id, sa if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'roi_filtering_metrics_all_rois', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_roi_filtering_metrics_all_rois') def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, save_figure=True): @@ -364,6 +383,8 @@ def plot_roi_filtering_metrics_for_valid_rois_for_container(ophys_container_id, if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'roi_filtering_metrics_valid_rois', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_roi_filtering_metrics_valid_rois') def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): @@ -393,6 +414,8 @@ def plot_filtered_roi_masks_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'filtered_roi_masks', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_filtered_roi_masks') def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True): @@ -414,6 +437,8 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'dff_traces_heatmaps', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_dff_traces_heatmaps') def plot_average_intensity_timeseries_for_container(ophys_container_id, save_figure=True): @@ -444,6 +469,8 @@ def plot_average_intensity_timeseries_for_container(ophys_container_id, save_fig if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_intensity_timeseries', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_average_intensity_timeseries') def plot_pmt_for_container(ophys_container_id, save_figure=True): @@ -473,6 +500,8 @@ def plot_pmt_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pmt_settings', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_pmt_settings') def plot_average_intensity_for_container(ophys_container_id, save_figure=True): @@ -507,6 +536,8 @@ def plot_average_intensity_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'FOV_average_intensity', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_FOV_average_intensity') def plot_average_intensity_by_pmt_for_container(ophys_container_id, save_figure=True): @@ -543,6 +574,8 @@ def plot_average_intensity_by_pmt_for_container(ophys_container_id, save_figure= if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'average_intensity_by_pmt', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_average_intensity_by_pmt') def plot_snr_by_pmt_gain_and_intensity_for_container(ophys_container_id, save_figure=True): @@ -562,6 +595,8 @@ def plot_snr_by_pmt_gain_and_intensity_for_container(ophys_container_id, save_fi if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'snr_by_pmt_and_intensity', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_snr_by_pmt_and_intensity') def plot_snr_by_pmt_for_container(ophys_container_id, save_figure=True): @@ -598,6 +633,8 @@ def plot_snr_by_pmt_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'snr_by_pmt', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_snr_by_pmt') def plot_cell_snr_for_container(ophys_container_id, save_figure=True): @@ -631,6 +668,8 @@ def plot_cell_snr_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_snr_by_experiment', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_snr_by_experiment') def plot_number_segmented_rois_for_container(ophys_container_id, save_figure=True): @@ -676,6 +715,8 @@ def plot_number_segmented_rois_for_container(ophys_container_id, save_figure=Tru if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmented_rois_by_experiment', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_segmented_rois_by_experiment') def plot_number_matched_cells_for_container(ophys_container_id, save_figure=True): @@ -697,6 +738,8 @@ def plot_number_matched_cells_for_container(ophys_container_id, save_figure=True if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'number_matched_cells', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_number_matched_cells') def plot_fraction_matched_cells_for_container(ophys_container_id, save_figure=True): @@ -718,6 +761,8 @@ def plot_fraction_matched_cells_for_container(ophys_container_id, save_figure=Tr if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'fraction_matched_cells', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_fraction_matched_cells') def plot_cell_matching_registration_overlay_grid(ophys_container_id, save_figure=True): @@ -786,6 +831,8 @@ def plot_cell_matching_registration_overlay_grid(ophys_container_id, save_figure if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_matching_registration_overlay_grid', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_cell_matching_registration_overlay_grid') def plot_cell_matching_registration_output(ophys_container_id, save_figure=True): @@ -857,6 +904,8 @@ def plot_cell_matching_registration_output(ophys_container_id, save_figure=True) if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'cell_matching_registration_output', get_file_name_for_container(ophys_container_id) + '_' + str(x)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_cell_matching_registration_output') def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figure=True): @@ -875,6 +924,8 @@ def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figur if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'motion_correction_xy_shift', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_motion_correction_xy_shift') def plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=False, alpha=0.15, facecolor='gray'): @@ -998,6 +1049,8 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, da ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'population_average_by_session_' + df_name.split('_')[0], get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_population_average_by_session_' + df_name.split('_')[0]) def plot_omission_population_average_across_sessions(ophys_container_id, save_figure=True): @@ -1037,40 +1090,6 @@ def plot_stimulus_population_average_across_sessions(ophys_container_id, save_fi save_figure=save_figure) -# def plot_average_intensity_by_pmt_for_experiments(ophys_container_id, save_figure=True): -# """seaborn scatter plot where x = pmt gain, y= fov intensity mean -# and each point is a passed experiment in the container. -# The points are colored by their stage name and the legend is stage names -# displayed in order of acquisition date. - -# Arguments: -# ophys_container_id {[type]} -- [description] - -# Keyword Arguments: -# save_figure {bool} -- [description] (default: {True}) -# """ -# container_pmt_settings = processing.container_pmt_settings(ophys_container_id) -# container_intensity_info = processing.container_intensity_mean_and_std(ophys_container_id) -# df = pd.merge(container_pmt_settings, container_intensity_info, how="left", on=["ophys_experiment_id", "ophys_container_id"]) -# exp_order_and_stage = processing.experiment_order_and_stage_for_container(ophys_container_id) -# df = pd.merge(df, exp_order_and_stage, how="left", on="ophys_experiment_id") -# stage_color_dict = pu.gen_ophys_stage_name_colors_dict() - -# figsize = (9, 5.5) -# fig, ax = plt.subplots(figsize=figsize) -# ax = sns.scatterplot(x="pmt_gain", y="intensity_mean", data=df, -# hue="stage_name_lims", palette=stage_color_dict) -# ax.legend(exp_order_and_stage["stage_name_lims"], fontsize='xx-small', title='stage name', title_fontsize='xx-small', -# bbox_to_anchor=(1.01, 1), loc=2) -# plt.xlabel('pmt gain') -# plt.ylabel('FOV intensity mean') -# plt.title("fov intensity mean by pmt gain") -# fig.tight_layout() -# if save_figure: -# ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'fov_ave_intensity_by_pmt', -# get_file_name_for_container(ophys_container_id)) - - # BEHAVIOR def plot_running_speed_for_container(ophys_container_id, save_figure=True): @@ -1088,6 +1107,8 @@ def plot_running_speed_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'running_speed', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_running_speed') def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): @@ -1107,6 +1128,8 @@ def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'lick_rasters', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_lick_rasters') def plot_pupil_area_sdk(ophys_container_id, save_figure=True): @@ -1134,8 +1157,10 @@ def plot_pupil_area_sdk(ophys_container_id, save_figure=True): if save_figure: print('saving') if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time_sdk', get_file_name_for_container(ophys_container_id)) - + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time_sdk', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_pupil_area_vs_time_sdk') return fig, axes @@ -1164,7 +1189,10 @@ def plot_pupil_area(ophys_container_id, save_figure=True): if save_figure: print('saving') if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_area_vs_time', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_pupil_area_vs_time') return fig, axes @@ -1193,7 +1221,10 @@ def plot_pupil_position(ophys_container_id, save_figure=True): if save_figure: if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_position_vs_time', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'pupil_position_vs_time', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_pupil_position_vs_time') return fig, axes @@ -1301,6 +1332,8 @@ def plot_behavior_summary(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'behavior_metric_summary', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_behavior_metric_summary') def plot_event_detection_for_container(ophys_container_id, save_figure=True): @@ -1413,6 +1446,8 @@ def plot_OphysRegistrationSummaryImage(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'OphysRegistrationSummaryImage', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_OphysRegistrationSummaryImage') def plot_nway_match_fraction(ophys_container_id, save_figure=True): @@ -1431,7 +1466,10 @@ def plot_nway_match_fraction(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_match_fraction', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_match_fraction', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_nway_match_fraction') def plot_nway_warp_overlay(ophys_container_id, save_figure=True): @@ -1450,7 +1488,10 @@ def plot_nway_warp_overlay(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_overlay', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_overlay', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_nway_warp_overlay') def plot_nway_warp_summary(ophys_container_id, save_figure=True): @@ -1469,7 +1510,10 @@ def plot_nway_warp_summary(ophys_container_id, save_figure=True): fig.tight_layout() if save_figure: - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_summary', get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'nway_warp_summary', + get_file_name_for_container(ophys_container_id)) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_nway_warp_summary') def plot_experiment_summary_figure_for_container(ophys_container_id, save_figure=True): @@ -1478,10 +1522,10 @@ def plot_experiment_summary_figure_for_container(ophys_container_id, save_figure ophys_experiments = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values( by='date_of_acquisition') for ophys_experiment_id in ophys_experiments.index.values: - # try: - es.plot_experiment_summary_figure(ophys_experiment_id, save_figure=save_figure) - # except: - # print('could not plot experiment summary for', ophys_experiment_id) + try: + es.plot_experiment_summary_figure(ophys_experiment_id, save_figure=save_figure) + except: + print('could not plot experiment summary for', ophys_experiment_id) def generate_snr_metrics_df_for_container(ophys_container_id): @@ -1664,3 +1708,5 @@ def plot_pupil_timeseries_for_container(ophys_container_id, save_figure=True): if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'behavior_timeseries', metadata_string + '_pupil_timeseries_' + str(xlim[0])) + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + get_file_name_for_container(ophys_container_id) + '_behavior_timeseries') diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 99f94d6e4..052c47548 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,46 +4,46 @@ def main(): possible_plots = { - "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, "ophys_session_sequence": cp.plot_container_session_sequence, "max_projection_images": cp.plot_sdk_max_projection_images_for_container, "average_images": cp.plot_sdk_average_images_for_container, + "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + "average_images_movies": cp.plot_movie_average_images_for_container, + "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + "segmentation_masks": cp.plot_segmentation_masks_for_container, + "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - "running_speed": cp.plot_running_speed_for_container, - "lick_rasters": cp.plot_lick_rasters_for_container, - "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + "nway_match_fraction": cp.plot_nway_match_fraction, + "nway_warp_overlay": cp.plot_nway_warp_overlay, + "nway_warp_summary": cp.plot_nway_warp_summary, "number_matched_cells": cp.plot_number_matched_cells_for_container, "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - "nway_match_fraction": cp.plot_nway_match_fraction, - "nway_warp_overlay": cp.plot_nway_warp_overlay, - "nway_warp_summary": cp.plot_nway_warp_summary, - "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - "segmentation_masks": cp.plot_segmentation_masks_for_container, - "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - "average_images_movies": cp.plot_movie_average_images_for_container, - "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + "experiment_summary": cp.plot_experiment_summary_figure_for_container, + "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + "running_speed": cp.plot_running_speed_for_container, + "lick_rasters": cp.plot_lick_rasters_for_container, + "behavior_summary": cp.plot_behavior_summary, + "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, "pupil_area_sdk": cp.plot_pupil_area_sdk, "pupil_area": cp.plot_pupil_area, "pupil_position": cp.plot_pupil_position, "FOV_average_intensity": cp.plot_average_intensity_for_container, + "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, "pmt_settings": cp.plot_pmt_for_container, "snr_by_pmt": cp.plot_snr_by_pmt_for_container, "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - "experiment_summary": cp.plot_experiment_summary_figure_for_container, - "behavior_summary": cp.plot_behavior_summary, - "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + "cell_snr_by_experiment": cp.plot_cell_snr_for_container, "event_detection": cp.plot_event_detection_for_container, "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From 596f04a6b5caa9ec6bb90a32d2c3f38841d22cf0 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 17 Feb 2022 13:26:04 -0800 Subject: [PATCH 047/187] expt plots for max projection and segmentation --- scripts/create_experiment_plots.py | 35 ++++++++++++++----- scripts/run_create_experiment_plots.py | 18 ++++++---- .../visualization/qc/experiment_plots.py | 5 ++- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/scripts/create_experiment_plots.py b/scripts/create_experiment_plots.py index 155a66167..621837d52 100644 --- a/scripts/create_experiment_plots.py +++ b/scripts/create_experiment_plots.py @@ -1,7 +1,10 @@ import argparse -import visual_behavior.data_access.loading as loading -import visual_behavior.visualization.ophys.platform_paper_figures as ppf +# import visual_behavior.data_access.loading as loading +# import visual_behavior.visualization.ophys.platform_paper_figures as ppf + +import visual_behavior.visualization.utils as ut +import visual_behavior.visualization.qc.experiment_plots as ep if __name__ == '__main__': @@ -11,12 +14,28 @@ args = parser.parse_args() ophys_experiment_id = args.ophys_experiment_id - dataset = loading.get_ophys_dataset(ophys_experiment_id) - trials = dataset.trials.copy() - start_times = trials[trials.stimulus_change].start_time.values + save_dir = r'/allen/aibs/informatics/danielsf/mfish_learning/segmentation_220216' + + try: + figsize=(5,5) + fig, ax = plt.subplots(figsize=figsize) + ax = ep.plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax=ax) + ut.save_figure(fig, figsize, save_dir, 'plots', str(ophys_experiment_id) + '_legacy_segmentation') + except: + figsize = (5, 5) + fig, ax = plt.subplots(figsize=figsize) + ax = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax) + ut.save_figure(fig, figsize, save_dir, 'plots', str(ophys_experiment_id) + '_legacy_segmentation_failed') + + - save_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_plots' + # dataset = loading.get_ophys_dataset(ophys_experiment_id) - for start_time in start_times[:20]: - ppf.plot_behavior_timeseries_stacked(dataset, start_time, duration_seconds=20, save_dir=save_dir, ax=None) + # trials = dataset.trials.copy() + # start_times = trials[trials.stimulus_change].start_time.values + # + # save_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_plots' + # + # for start_time in start_times[:20]: + # ppf.plot_behavior_timeseries_stacked(dataset, start_time, duration_seconds=20, save_dir=save_dir, ax=None) diff --git a/scripts/run_create_experiment_plots.py b/scripts/run_create_experiment_plots.py index 6ecebcb06..1a7380f44 100644 --- a/scripts/run_create_experiment_plots.py +++ b/scripts/run_create_experiment_plots.py @@ -17,9 +17,15 @@ # experiments_table = cache.get_ophys_experiment_table() # print(cache_dir) -experiments_table = loading.get_platform_paper_experiment_table() -experiments_table = utilities.limit_to_last_familiar_second_novel_active(experiments_table) -ophys_experiment_ids = experiments_table.index.values +# experiments_table = loading.get_platform_paper_experiment_table() +# experiments_table = utilities.limit_to_last_familiar_second_novel_active(experiments_table) +# ophys_experiment_ids = experiments_table.index.values + +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache +cache = VisualBehaviorOphysProjectCache.from_lims() +experiments_table = cache.get_ophys_experiment_table(passed_only=False) +ophys_experiment_ids = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'].index.values + if __name__ == "__main__": args = parser.parse_args() @@ -27,13 +33,13 @@ python_file = os.path.join(os.getcwd(), args.scriptname) # define the job record output folder - stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/paper_figures" + stdout_location = r"/allen/programs/mindscope/workgroups/learning\ophys/cluster_jobs/qc_plots" # instantiate a Slurm object slurm = Slurm( mem='40g', # '24g' - cpus_per_task=10, - time='20:00:00', + cpus_per_task=1, + time='5:00:00', partition='braintv', job_name='experiment_plots', output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index 5c4925139..c79c2c1f8 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -27,6 +27,7 @@ def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): dataset = loading.get_ophys_dataset(ophys_experiment_id) max_projection = dataset.max_projection.data ax.imshow(max_projection, cmap='gray', vmax=np.percentile(max_projection, 99)) + ax.set_title(str(ophys_experiment_id)) ax.axis('off') return ax @@ -55,6 +56,7 @@ def plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=None fig, ax = plt.subplots() max_image = processing.experiment_max_FOV_from_motion_corrected_movie(ophys_experiment_id) ax.imshow(max_image, cmap='gray', vmin=0, vmax=8000) + ax.set_title(str(ophys_experiment_id)) ax.axis('off') return ax @@ -109,6 +111,7 @@ def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax mask = np.zeros(segmentation_mask[0].shape) mask[segmentation_mask[0] == 1] = 1 ax.contour(mask, levels=0, colors=['red'], linewidths=[0.6]) + ax.set_title(str(ophys_experiment_id)) ax.axis('off') return ax @@ -123,7 +126,7 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime for cell_roi_id in cell_specimen_table.cell_roi_id.values: mask = cell_specimen_table[cell_specimen_table.cell_roi_id == cell_roi_id].roi_mask.values[0] ax.contour(mask, levels=0, colors=['red'], linewidths=[0.6]) - ax.set_title('valid ROI outlines\n n = ' + str(len(cell_specimen_table.cell_roi_id.values))) + ax.set_title(str(ophys_experiment_id)+'\nn valid ROIs = ' + str(len(cell_specimen_table.cell_roi_id.values))) ax.axis('off') return ax From fda32038c77557817c55d4f843a5396f18b1da45 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 17 Feb 2022 13:45:48 -0800 Subject: [PATCH 048/187] typo --- scripts/run_create_experiment_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_create_experiment_plots.py b/scripts/run_create_experiment_plots.py index 1a7380f44..c899bc102 100644 --- a/scripts/run_create_experiment_plots.py +++ b/scripts/run_create_experiment_plots.py @@ -33,7 +33,7 @@ python_file = os.path.join(os.getcwd(), args.scriptname) # define the job record output folder - stdout_location = r"/allen/programs/mindscope/workgroups/learning\ophys/cluster_jobs/qc_plots" + stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/qc_plots" # instantiate a Slurm object slurm = Slurm( From 5ce5dd825621201b05e8e4a6d4913249de5b70fc Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 17 Feb 2022 13:47:27 -0800 Subject: [PATCH 049/187] missing import --- scripts/create_experiment_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/create_experiment_plots.py b/scripts/create_experiment_plots.py index 621837d52..2e29706d4 100644 --- a/scripts/create_experiment_plots.py +++ b/scripts/create_experiment_plots.py @@ -3,6 +3,7 @@ # import visual_behavior.data_access.loading as loading # import visual_behavior.visualization.ophys.platform_paper_figures as ppf +import matplotlib.pyplot as plt import visual_behavior.visualization.utils as ut import visual_behavior.visualization.qc.experiment_plots as ep From 6700fbe4f1bb9d17df17000272ff45ea4da0e8ec Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 17 Feb 2022 14:05:11 -0800 Subject: [PATCH 050/187] change save_dir --- scripts/create_experiment_plots.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/create_experiment_plots.py b/scripts/create_experiment_plots.py index 2e29706d4..c487b1ec9 100644 --- a/scripts/create_experiment_plots.py +++ b/scripts/create_experiment_plots.py @@ -16,18 +16,18 @@ ophys_experiment_id = args.ophys_experiment_id - save_dir = r'/allen/aibs/informatics/danielsf/mfish_learning/segmentation_220216' + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/experiment_plots' try: figsize=(5,5) fig, ax = plt.subplots(figsize=figsize) ax = ep.plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax=ax) - ut.save_figure(fig, figsize, save_dir, 'plots', str(ophys_experiment_id) + '_legacy_segmentation') + ut.save_figure(fig, figsize, save_dir, 'legacy_segmentation', str(ophys_experiment_id) + '_legacy_segmentation') except: figsize = (5, 5) fig, ax = plt.subplots(figsize=figsize) ax = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax) - ut.save_figure(fig, figsize, save_dir, 'plots', str(ophys_experiment_id) + '_legacy_segmentation_failed') + ut.save_figure(fig, figsize, save_dir, 'legacy_segmentation', str(ophys_experiment_id) + '_legacy_segmentation_failed') From b004cb46a78f78b542ba4e0c40fa2c5db01d9782 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 21 Feb 2022 20:30:26 -0800 Subject: [PATCH 051/187] test qc plots --- .../qc/save_all_container_plots.py | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 052c47548..191e7a380 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,46 +4,46 @@ def main(): possible_plots = { - "ophys_session_sequence": cp.plot_container_session_sequence, + # "ophys_session_sequence": cp.plot_container_session_sequence, "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - "average_images": cp.plot_sdk_average_images_for_container, - "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - "average_images_movies": cp.plot_movie_average_images_for_container, - "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - "segmentation_masks": cp.plot_segmentation_masks_for_container, + # "average_images": cp.plot_sdk_average_images_for_container, + # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + # "average_images_movies": cp.plot_movie_average_images_for_container, + # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + # "segmentation_masks": cp.plot_segmentation_masks_for_container, "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - "nway_match_fraction": cp.plot_nway_match_fraction, - "nway_warp_overlay": cp.plot_nway_warp_overlay, - "nway_warp_summary": cp.plot_nway_warp_summary, - "number_matched_cells": cp.plot_number_matched_cells_for_container, - "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - "experiment_summary": cp.plot_experiment_summary_figure_for_container, - "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - "running_speed": cp.plot_running_speed_for_container, - "lick_rasters": cp.plot_lick_rasters_for_container, - "behavior_summary": cp.plot_behavior_summary, - "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - "pupil_area_sdk": cp.plot_pupil_area_sdk, - "pupil_area": cp.plot_pupil_area, - "pupil_position": cp.plot_pupil_position, - "FOV_average_intensity": cp.plot_average_intensity_for_container, - "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - "pmt_settings": cp.plot_pmt_for_container, - "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - "event_detection": cp.plot_event_detection_for_container, - "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + # "nway_match_fraction": cp.plot_nway_match_fraction, + # "nway_warp_overlay": cp.plot_nway_warp_overlay, + # "nway_warp_summary": cp.plot_nway_warp_summary, + # "number_matched_cells": cp.plot_number_matched_cells_for_container, + # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + # "experiment_summary": cp.plot_experiment_summary_figure_for_container, + # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + # "running_speed": cp.plot_running_speed_for_container, + # "lick_rasters": cp.plot_lick_rasters_for_container, + # "behavior_summary": cp.plot_behavior_summary, + # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + # "pupil_area_sdk": cp.plot_pupil_area_sdk, + # "pupil_area": cp.plot_pupil_area, + # "pupil_position": cp.plot_pupil_position, + # "FOV_average_intensity": cp.plot_average_intensity_for_container, + # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + # "pmt_settings": cp.plot_pmt_for_container, + # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + # "event_detection": cp.plot_event_detection_for_container, + # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From 367ece4068493cfde090df68da09cf98df9470d0 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 17:40:24 -0700 Subject: [PATCH 052/187] run container plots again --- .../qc/save_all_container_plots.py | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 191e7a380..052c47548 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,46 +4,46 @@ def main(): possible_plots = { - # "ophys_session_sequence": cp.plot_container_session_sequence, + "ophys_session_sequence": cp.plot_container_session_sequence, "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - # "average_images": cp.plot_sdk_average_images_for_container, - # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - # "average_images_movies": cp.plot_movie_average_images_for_container, - # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - # "segmentation_masks": cp.plot_segmentation_masks_for_container, + "average_images": cp.plot_sdk_average_images_for_container, + "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + "average_images_movies": cp.plot_movie_average_images_for_container, + "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + "segmentation_masks": cp.plot_segmentation_masks_for_container, "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - # "nway_match_fraction": cp.plot_nway_match_fraction, - # "nway_warp_overlay": cp.plot_nway_warp_overlay, - # "nway_warp_summary": cp.plot_nway_warp_summary, - # "number_matched_cells": cp.plot_number_matched_cells_for_container, - # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - # "experiment_summary": cp.plot_experiment_summary_figure_for_container, - # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - # "running_speed": cp.plot_running_speed_for_container, - # "lick_rasters": cp.plot_lick_rasters_for_container, - # "behavior_summary": cp.plot_behavior_summary, - # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - # "pupil_area_sdk": cp.plot_pupil_area_sdk, - # "pupil_area": cp.plot_pupil_area, - # "pupil_position": cp.plot_pupil_position, - # "FOV_average_intensity": cp.plot_average_intensity_for_container, - # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - # "pmt_settings": cp.plot_pmt_for_container, - # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - # "event_detection": cp.plot_event_detection_for_container, - # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + "nway_match_fraction": cp.plot_nway_match_fraction, + "nway_warp_overlay": cp.plot_nway_warp_overlay, + "nway_warp_summary": cp.plot_nway_warp_summary, + "number_matched_cells": cp.plot_number_matched_cells_for_container, + "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + "experiment_summary": cp.plot_experiment_summary_figure_for_container, + "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + "running_speed": cp.plot_running_speed_for_container, + "lick_rasters": cp.plot_lick_rasters_for_container, + "behavior_summary": cp.plot_behavior_summary, + "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + "pupil_area_sdk": cp.plot_pupil_area_sdk, + "pupil_area": cp.plot_pupil_area, + "pupil_position": cp.plot_pupil_position, + "FOV_average_intensity": cp.plot_average_intensity_for_container, + "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + "pmt_settings": cp.plot_pmt_for_container, + "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + "event_detection": cp.plot_event_detection_for_container, + "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From a5bd1a6d1120747763715764f0e38c7811bb93b1 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 18:31:48 -0700 Subject: [PATCH 053/187] multi session dfs --- scripts/create_multi_session_df.py | 4 ++-- visual_behavior/data_access/loading.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index f48e57caf..71c61bda4 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -65,8 +65,8 @@ # behavior_conditions[i].insert(1, 'engagement_state') # create dfs for all data types and conditions for physio data - for data_type in physio_data_types: - # data_type = 'events' + # for data_type in physio_data_types: + data_type = 'dff' for i, conditions in enumerate(physio_conditions): print(conditions) event_type = event_types_for_conditions[i] diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index bcf926d25..1377da00a 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,7 +74,8 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir @@ -104,7 +105,7 @@ def get_single_cell_plots_dir(): def get_analysis_cache_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' + return r'\\allen\programs\braintv\workgroups\nc-ophys\learning_mFISH\learning_project_cache' def get_events_dir(): From 81bfddfdcd2f6aef9ebaf09307afded99f85f4df Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 18:35:25 -0700 Subject: [PATCH 054/187] save stim response dfs --- visual_behavior/data_access/loading.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 1377da00a..913952b03 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -593,12 +593,17 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o print('saved response df to', filepath) except: print('could not save', filepath) - else: # if file does not exist, generate response df - print('generating response df') + else: # if file does not exist, generate response df and save it + print('could not load file, generating response df') sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration) + try: # some experiments with lots of neurons cant save + sdf.to_hdf(filepath, key='df') + print('saved response df to', filepath) + except: + print('could not save', filepath) else: # if load_from_file is False, generate response df print('generating response df') sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, From 6dd2f564ae1e7ba3701c17d678972835bba15e3c Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 18:47:33 -0700 Subject: [PATCH 055/187] use mouse id instead of session id --- scripts/create_multi_session_df.py | 10 ++++---- scripts/run_create_multi_session_df.py | 6 ++--- visual_behavior/data_access/loading.py | 25 ++++++++++--------- .../ophys/io/create_multi_session_df.py | 13 +++++----- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 71c61bda4..8f5a2e61f 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -12,12 +12,12 @@ # define args parser = argparse.ArgumentParser() parser.add_argument('--project_code', type=str, help='project code to use') - parser.add_argument('--session_type', type=str, help='session type to use') + parser.add_argument('--mouse_id', type=str, help='mouse_id to use') args = parser.parse_args() project_code = args.project_code - session_type = args.session_type + mouse_id = args.mouse_id - print(project_code, session_type) + print(project_code, mouse_id) # params for stim response df creation time_window = [-3, 3.1] @@ -76,7 +76,7 @@ response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(project_code, session_type, conditions, data_type, event_type, + df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, @@ -96,7 +96,7 @@ # response_window_duration_seconds = 0.5 # print('creating multi_session_df for', data_type, event_type, conditions) # try: # use try except so that it skips over any conditions that fail to generate for some reason - # df = io.get_multi_session_df(project_code, session_type, conditions, data_type, event_type, + # df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, # time_window=time_window, interpolate=interpolate, # output_sampling_rate=output_sampling_rate, # response_window_duration_seconds=response_window_duration_seconds, diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 43bcb44e0..8910f409a 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -36,7 +36,7 @@ # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): print(project_code) - for session_type in experiments_table.session_type.unique(): + for mouse_id in experiments_table.mouse_id.unique(): # instantiate a Slurm object slurm = Slurm( @@ -44,10 +44,10 @@ cpus_per_task=1, time='20:00:00', partition='braintv', - job_name='multi_session_df_'+project_code+'_'+session_type, + job_name='multi_session_df_'+project_code+'_'+mouse_id, output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_path+' '+python_file+' --project_code '+str(project_code)+' --session_type'+' '+str(session_type)) + slurm.sbatch(python_path+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 913952b03..56b0cd4c5 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,8 +74,8 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - # cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir @@ -105,7 +105,7 @@ def get_single_cell_plots_dir(): def get_analysis_cache_dir(): - return r'\\allen\programs\braintv\workgroups\nc-ophys\learning_mFISH\learning_project_cache' + return r'//allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' def get_events_dir(): @@ -2731,20 +2731,21 @@ def add_superficial_deep_to_experiments_table(experiments_table): return experiments_table -def get_file_name_for_multi_session_df(data_type, event_type, project_code, session_type, conditions): +def get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions): + mouse_id = str(mouse_id) if len(conditions) == 6: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' elif len(conditions) == 4: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' elif len(conditions) == 3: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[1] + '_' + conditions[2] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' elif len(conditions) == 2: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[1] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '.h5' elif len(conditions) == 1: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + session_type + '_' + conditions[0] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[0] + '.h5' return filename @@ -2772,9 +2773,9 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o experiments = experiments_table[(experiments_table.project_code == project_code)] if project_code == 'VisualBehaviorMultiscope': experiments = experiments[experiments.session_type != 'OPHYS_2_images_B_passive'] - for session_type in np.sort(experiments.session_type.unique()): + for mouse_id in np.sort(experiments.mouse_id.unique()): try: - filename = get_file_name_for_multi_session_df(data_type, event_type, project_code, session_type, conditions) + filename = get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) df = pd.read_hdf(os.path.join(multi_session_df_dir, filename), key='df') diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 4e446e1ca..ff23be278 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -6,13 +6,13 @@ from visual_behavior.data_access import loading -def get_multi_session_df(project_code, session_type, conditions, data_type, event_type, +def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5, use_extended_stimulus_presentations=False, overwrite=False): """ :param project_code: - :param session_number: + :param mouse_id: :param conditions: :param data_type: :param event_type: @@ -42,12 +42,11 @@ def get_multi_session_df(project_code, session_type, conditions, data_type, even # session_type = float(session_type) experiments = experiments_table[(experiments_table.project_code == project_code) & - (experiments_table.session_type == session_type)].copy() - print('session_types:', experiments.session_type.unique(), - ' - there should only be one session_type per session_number') - session_type = experiments.session_type.unique()[0] + (experiments_table.mouse_id == mouse_id)].copy() - filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, session_type, conditions) + mouse_id = experiments.mouse_id.unique()[0] + + filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) From 734e2573a802955d273bb6a4a341ed623e166b01 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 18:56:42 -0700 Subject: [PATCH 056/187] path mixup --- scripts/create_multi_session_df.py | 34 +++++++++++++------------- scripts/run_create_multi_session_df.py | 25 ++++++++++--------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 8f5a2e61f..615985bec 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -67,23 +67,23 @@ # create dfs for all data types and conditions for physio data # for data_type in physio_data_types: data_type = 'dff' - for i, conditions in enumerate(physio_conditions): - print(conditions) - event_type = event_types_for_conditions[i] - if event_type == 'omissions': - response_window_duration = 0.75 - else: - response_window_duration = 0.5 - print('creating multi_session_df for', data_type, event_type, conditions) - try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, - time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration, - use_extended_stimulus_presentations=use_extended_stimulus_presentations, - overwrite=True) - except Exception as e: - print('failed to create multi_session_df for', data_type, event_type, conditions) - print(e) + for i, conditions in enumerate(physio_conditions): + print(conditions) + event_type = event_types_for_conditions[i] + if event_type == 'omissions': + response_window_duration = 0.75 + else: + response_window_duration = 0.5 + print('creating multi_session_df for', data_type, event_type, conditions) + try: # use try except so that it skips over any conditions that fail to generate for some reason + df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, + time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, + response_window_duration=response_window_duration, + use_extended_stimulus_presentations=use_extended_stimulus_presentations, + overwrite=True) + except Exception as e: + print('failed to create multi_session_df for', data_type, event_type, conditions) + print(e) # create dfs for all data types and conditions for behavior data diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 8910f409a..600fc95cf 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -1,4 +1,5 @@ import os +import argparse from simple_slurm import Slurm import visual_behavior.data_access.loading as loading @@ -12,17 +13,19 @@ # build the python path # this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' -python_path = os.path.join( - os.path.expanduser("~"), - 'anaconda2', - 'envs', - conda_environment, - 'bin', - 'python' -) +# python_path = os.path.join( +# os.path.expanduser("~"), +# 'anaconda2', +# 'envs', +# conda_environment, +# 'bin', +# 'python' +# ) + +python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), conda_environment) # define the job record output folder -stdout_location = r"/allen/programs/mindscope/workgroups/learning/cluster_jobs/multi_session_dfs" +stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/multi_session_dfs" # cache = VisualBehaviorOphysProjectCache.from_lims() @@ -37,7 +40,7 @@ for project_code in experiments_table.project_code.unique(): print(project_code) for mouse_id in experiments_table.mouse_id.unique(): - + print(mouse_id) # instantiate a Slurm object slurm = Slurm( mem='120g', # '24g' @@ -48,6 +51,6 @@ output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_path+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) + slurm.sbatch(python_executable+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) From 6d32d536687bfba0066dcf477dd734ab88bf4330 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 29 Mar 2022 20:09:50 -0700 Subject: [PATCH 057/187] minor change --- visual_behavior/ophys/response_analysis/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/response_analysis/utilities.py b/visual_behavior/ophys/response_analysis/utilities.py index 3fe34a788..7a8c24483 100644 --- a/visual_behavior/ophys/response_analysis/utilities.py +++ b/visual_behavior/ophys/response_analysis/utilities.py @@ -378,7 +378,7 @@ def get_window(analysis=None, flashes=False, omitted=False): def get_mean_df(response_df, conditions=['cell', 'change_image_name'], frame_rate=30., - window_around_timepoint_seconds=[-3, 3], response_window_duration_seconds=0.5, + window_around_timepoint_seconds=[-3, 3.1], response_window_duration_seconds=0.5, get_pref_stim=True, exclude_omitted_from_pref_stim=True): import visual_behavior.ophys.response_analysis.response_processing as rp From 89877680dd86e0e0bcea7d352630a323111cb05c Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 6 Apr 2022 13:12:11 -0700 Subject: [PATCH 058/187] slashes --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 56b0cd4c5..916f63e63 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -74,7 +74,7 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' - cache_dir = r'//allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir From 40919a08b53b53e98a88bc4b057fc04c370512cf Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 26 Apr 2022 20:15:15 -0700 Subject: [PATCH 059/187] updates to make multi session df --- visual_behavior/data_access/loading.py | 12 ++++++++---- visual_behavior/ophys/io/create_multi_session_df.py | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 916f63e63..de583745b 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -136,7 +136,7 @@ def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, even def get_multi_session_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): - base_dir = get_platform_analysis_cache_dir() + base_dir = get_production_cache_dir() if interpolate: save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', event_type, 'interpolate_' + str(output_sampling_rate) + 'Hz') else: @@ -350,7 +350,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o ### hack because of problem container - experiments = experiments[experiments.ophys_container_id!=1132424700] + # experiments = experiments[experiments.ophys_container_id!=1132424700] if overwrite_cached_file == True: print('overwriting pre-saved experiments table file') @@ -2766,6 +2766,7 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o # cache = bpc.from_s3_cache(cache_dir=cache_dir) # experiments_table = cache.get_ophys_experiment_table() experiments_table = get_filtered_ophys_experiment_table() + experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] project_codes = experiments_table.project_code.unique() multi_session_df = pd.DataFrame() @@ -2778,10 +2779,13 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o filename = get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) - df = pd.read_hdf(os.path.join(multi_session_df_dir, filename), key='df') + filepath = os.path.join(multi_session_df_dir, filename) + print(filepath) + df = pd.read_hdf(filepath, key='df') multi_session_df = pd.concat([multi_session_df, df]) + print(multi_session_df.mouse_id.unique()) except BaseException: - print('no multi_session_df for', project_code, session_type) + print('no multi_session_df for', project_code, mouse_id) return multi_session_df diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index ff23be278..7d67186d1 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -39,10 +39,11 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty experiments_table = loading.get_filtered_ophys_experiment_table() experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + print(len(experiments_table), 'expts in experiments table') # session_type = float(session_type) experiments = experiments_table[(experiments_table.project_code == project_code) & - (experiments_table.mouse_id == mouse_id)].copy() + (experiments_table.mouse_id == str(mouse_id))].copy() mouse_id = experiments.mouse_id.unique()[0] From ff9d0260e9ca64600f6a0a33e6588d9d14511269 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 28 Apr 2022 12:59:27 -0700 Subject: [PATCH 060/187] dont filter omissions in mdf --- visual_behavior/ophys/io/create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 7d67186d1..7b44e8729 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -81,8 +81,8 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty response_window_duration = df.response_window_duration.values[0] df['ophys_experiment_id'] = experiment_id # if using omissions, only include omissions where time from last change is more than 3 seconds - if event_type == 'omissions': - df = df[df.time_from_last_change>3] + # if event_type == 'omissions': + # df = df[df.time_from_last_change>3] # modify columns for specific conditions if 'passive' in dataset.metadata['session_type']: df['lick_on_next_flash'] = False From 46ba32148fd4344cb66cc6008daee7f418e05d13 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 28 Apr 2022 16:43:57 -0700 Subject: [PATCH 061/187] add ability to include invalid ROIs in dataset and stim response df --- visual_behavior/data_access/loading.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index de583745b..0f68b1695 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -549,7 +549,7 @@ def get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_t def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, - data_type='filtered_events', event_type='all', load_from_file=True): + data_type='filtered_events', event_type='all', load_from_file=True, exclude_invalid_rois=False): """ load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added inputs: @@ -561,6 +561,9 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate' event_type: how to filter stimulus presentations before creating table options: 'all', 'omissions', 'changes' + exclude_invalid_rois: Bool, if True, only 'valid' ROIs will be returned. If False, all ROIs including 'invalid' ROIs will be returned. + Only applies if the dataset object that is provided was loaded from lims (not via AWS NWB files, which only have 'valid' ROIs in them) + Note that including invalid ROIs will result in some cell traces being NaNs because traces are not computed for some types of invalid ROIs """ import mindscope_utilities.visual_behavior_ophys.data_formatting as vb_ophys # load stimulus response df from file if it exists otherwise generate it @@ -587,7 +590,8 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration) + response_window_duration=response_window_duration, + exclude_invalid_rois=exclude_invalid_rois) try: # some experiments with lots of neurons cant save sdf.to_hdf(filepath, key='df') print('saved response df to', filepath) @@ -598,7 +602,8 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration) + response_window_duration=response_window_duration, + exclude_invalid_rois=exclude_invalid_rois) try: # some experiments with lots of neurons cant save sdf.to_hdf(filepath, key='df') print('saved response df to', filepath) @@ -609,7 +614,8 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o sdf = vb_ophys.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration) + response_window_duration=response_window_duration, + exclude_invalid_rois=exclude_invalid_rois) # if extended_stimulus_presentations is an attribute of the dataset object, use it, otherwise get regular stimulus_presentations if 'extended_stimulus_presentations' in dir(dataset): @@ -772,8 +778,8 @@ def get_cell_specimen_id_for_cell_roi_id(self, cell_roi_id): return cell_specimen_id -def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from_lims=True, load_from_nwb=False, - get_extended_stimulus_presentations=True, get_behavior_movie_timestamps=False): +def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=False, load_from_lims=True, load_from_nwb=False, + get_extended_stimulus_presentations=False, get_behavior_movie_timestamps=False): """ Gets behavior + ophys data for one experiment (single imaging plane), either using the SDK LIMS API, SDK NWB API, or using BehaviorOphysDataset wrapper which inherits the LIMS API BehaviorOphysSession object, @@ -781,7 +787,8 @@ def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from Arguments: ophys_experiment_id {int} -- 9 digit ophys experiment ID - include_invalid_rois {Boolean} -- if True, return all ROIs including invalid. If False, filter out invalid ROIs + exclude_invalid_rois {Boolean} -- if False, return all ROIs including invalid. If True, only return valid ROIs + only works when loading experiment from lims load_from_lims -- if True, loads dataset directly from BehaviorOphysSession.from_lims(). Invalid ROIs will be included. load_from_nwb -- if True, loads dataset directly from BehaviorOphysSession.from_nwb_path(). Invalid ROIs will not be included. get_extended_stimulus_presentations -- if True, adds an attribute "extended_stimulus_presentations" to the dataset object @@ -799,9 +806,10 @@ def get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False, load_from assert id_type == 'ophys_experiment_id', "The passed ID type is {}. It must be an ophys_experiment_id".format(id_type) if load_from_lims: + print('loading from lims, exclude_invalid_rois = ', exclude_invalid_rois) cache = bpc.from_lims() - dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id)) - # dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id)) + # dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id)) + dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id), exclude_invalid_rois=exclude_invalid_rois) elif load_from_nwb: cache_dir = get_platform_analysis_cache_dir() cache = bpc.from_s3_cache(cache_dir=cache_dir) From 6a2c1cf9768d5403bfdcc0a1540940f1508b0436 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 28 Apr 2022 17:30:14 -0700 Subject: [PATCH 062/187] multi session df needs frame rate column --- visual_behavior/data_access/loading.py | 9 ++++++++- visual_behavior/data_access/utilities.py | 19 +++++++++++++++++++ .../ophys/io/create_multi_session_df.py | 7 ------- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 0f68b1695..1d28c5976 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -624,6 +624,13 @@ def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, o stimulus_presentations = vb_ophys.get_annotated_stimulus_presentations(dataset) sdf = sdf.merge(stimulus_presentations, on='stimulus_presentations_id') + # add run params + sdf['interpolate'] = interpolate + sdf['frame_rate'] = output_sampling_rate + sdf['data_type'] = data_type + sdf['event_type'] = event_type + sdf['exclude_invalid_rois'] = exclude_invalid_rois + return sdf @@ -806,7 +813,7 @@ def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=False, load_from assert id_type == 'ophys_experiment_id', "The passed ID type is {}. It must be an ophys_experiment_id".format(id_type) if load_from_lims: - print('loading from lims, exclude_invalid_rois = ', exclude_invalid_rois) + print('loading from lims, exclude_invalid_rois =', exclude_invalid_rois) cache = bpc.from_lims() # dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id)) dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id), exclude_invalid_rois=exclude_invalid_rois) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 644dd3b5f..1e46b990a 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1816,3 +1816,22 @@ def get_behavior_session_ids_to_analyze(): behavior_session_ids_to_analyze = ophys_behavior_session_ids + behavior_session_session_ids return behavior_session_ids_to_analyze + + +def get_nan_trace_csids(traces): + """ + function to loop through all cell_specimen_ids in traces and identify which traces are NaNs. + can be used to keep or exclude cells from analysis that have NaN traces. + NaN traces can occur when invalid ROIs are included in analysis because traces arent computed for some invalid ROIs. + + returns: nan_csids: a list of cell_specimen_ids where traces are NaN + csids_to_keep: a list of cell_specimen_ids where traces are not NaN + """ + nan_csids = [] + csids_to_keep = [] + for csid in traces.index.values: + if np.isnan(traces.loc[csid].dff[0]): + nan_csids.append(csid) + else: + csids_to_keep.append(csid) + return nan_csids, csids_to_keep \ No newline at end of file diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 7b44e8729..3d3bf2119 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -30,13 +30,6 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty get_pref_stim = False print('get_pref_stim', get_pref_stim) - # cache_dir = loading.get_platform_analysis_cache_dir() - # cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir=cache_dir) - # print(cache_dir) - # experiments_table = cache.get_ophys_experiment_table() - # # dont include Ai94 experiments because they makes things too slow - # experiments_table = experiments_table[(experiments_table.reporter_line != 'Ai94(TITL-GCaMP6s)')] - experiments_table = loading.get_filtered_ophys_experiment_table() experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] print(len(experiments_table), 'expts in experiments table') From cebac7c8b56006265b091d227db3ec26dde385ec Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 29 Apr 2022 15:34:16 -0700 Subject: [PATCH 063/187] adding option to provide hue_order to pop avgs plot --- .../visualization/ophys/platform_paper_figures.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/visual_behavior/visualization/ophys/platform_paper_figures.py b/visual_behavior/visualization/ophys/platform_paper_figures.py index 50a84a765..c89378b4c 100644 --- a/visual_behavior/visualization/ophys/platform_paper_figures.py +++ b/visual_behavior/visualization/ophys/platform_paper_figures.py @@ -23,7 +23,7 @@ sns.set_palette('deep') -def plot_population_averages_for_conditions(multi_session_df, data_type, event_type, axes_column, hue_column, +def plot_population_averages_for_conditions(multi_session_df, data_type, event_type, axes_column, hue_column, hue_order=None, project_code=None, timestamps=None, palette=None, title=None, suptitle=None, horizontal=True, xlim_seconds=None, save_dir=None, folder=None, suffix='', ax=None): if palette is None: @@ -68,10 +68,13 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t change = False xlabel = 'time (s)' - if hue_column == 'experience_level': - hue_conditions = ['Familiar', 'Novel 1', 'Novel >1'] + if not hue_order: + if hue_column == 'experience_level': + hue_conditions = ['Familiar', 'Novel 1', 'Novel >1'] + else: + hue_conditions = np.sort(sdf[hue_column].unique()) else: - hue_conditions = np.sort(sdf[hue_column].unique()) + hue_conditions = hue_order if axes_column == 'experience_level': axes_conditions = ['Familiar', 'Novel 1', 'Novel >1'] else: From a581ed853d94c90b8efc7be63b1fab17567ced04 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 6 Jun 2022 17:10:54 -0700 Subject: [PATCH 064/187] document get_multi_session_df --- .../ophys/io/create_multi_session_df.py | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 3d3bf2119..ce3de7ff2 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -10,18 +10,49 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5, use_extended_stimulus_presentations=False, overwrite=False): """ + For a given mouse_id within a given project_code, loop through all ophys_experiment_ids, load the SDK dataset object, + create stimulus_response_df with event aligned traces for provided data_type (ex: 'dff', 'events', 'pupil_width', etc), + then average across a given set of conditions to get a trial averaged trace for those conditions. + + Ex: if data_type = 'dff', event_type = 'changes', and conditions = ['cell_specimen_id', 'image_name'], this function + will compute the average change aligned dF/F trace for each 'image_name' for each 'cell_specimen_id'. + + For non-neural timeseries, including data_type = 'pupil_width, 'running_speed', or 'lick_rate', conditions should include + 'ophys_experiment_id' to use as index instead of 'cell_specimen_id' + + trial averaged multi_session_dfs are saved to the directory defined by loading.get_multi_session_df_dir() + Will overwrite existing dfs if overwrite=True, otherwise will only save the df if the file corresponding to the provided + project_code and mouse_id does not exist. + + Function can be run for multiple mouse_ids and/or project_codes using /scripts/run_create_multi_session_df.py + + + :param project_code: lims project code to use when identifying what experiment_ids to include in the multi_session_df + :param mouse_id: mouse_id to use when identifying what experiment_ids to include in the multi_session_df + :param conditions: columns in stimulus_response_df to group by when averaging across trials / stimulus presentations + if use_extended_stimulus_presentations is True, columns available include the set of columns provided in that table (ex: engagement_state) + :param data_type: which timeseries in dataset object to get event triggered responses for + options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate' + :param event_type: how to filter stimulus presentations when creating table with loading.get_stimulus_response_df() + options: 'all', 'omissions', 'changes' + filtering for just changes or just omissions makes loading of stim_response_df much faster than using 'all' + :param time_window: window over which to extract the event triggered response around each stimulus presentation time + :param interpolate: Boolean, whether or not to interpolate traces + :param output_sampling_rate: sampling rate for interpolation, only used if interpolate is True + :param response_window_duration: window of time, in seconds, relative to the stimulus start_time, over which to compute the mean response + (ex: if response_window_duration = 0.5, the mean cell (or pupil or running) trace in a 500ms window will be computed). + Creates a column called 'mean_response' in the multi_session_df containing this value. + The same window will be applied to the pre-stimulus response period to create a column called 'baseline_response' in the multi_session-df + :param use_extended_stimulus_presentations: Boolean, whether or not to call loading.extended_stimulus_presentations_table() when loading the dataset object, + setting to True will result in many additional columns being added to the stimulus_presentations_table that can be used as + conditions to group by when computing averaged responses, such as engagement state, time from last lick / change / omission + or an index breaking the session up into 10 minute epochs + :param overwrite: Boolean, if False, will search for existing files for the provided project_code and mouse_id and + will not save output if file exists. If True, will overwrite any existing files. + + :return: multi_session_df: dataframe containing trial averaged event triggered responses for a given set of conditions, + concatenated over all ophys_experiment_ids for the given mouse_id and project_code - :param project_code: - :param mouse_id: - :param conditions: - :param data_type: - :param event_type: - :param time_window: - :param interpolate: - :param output_sampling_rate: - :param response_window_duration: - :param use_extended_stimulus_presentations: - :return: """ # cant get prefered stimulus if images are not in the set of conditions if ('image_name' in conditions) or ('change_image_name' in conditions): From 45aa5d4e1643633233e06fd545b526224fd9ce28 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 8 Jun 2022 17:55:08 -0700 Subject: [PATCH 065/187] run QC for MSN an TTN --- visual_behavior/data_access/loading.py | 3 ++- .../visualization/qc/run_save_all_container_plots.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 1d28c5976..f3af4a24a 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -80,7 +80,7 @@ def get_production_cache_dir(): def get_qc_plots_dir(): - return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/MSN_TTN' # return r'\\allen\programs\mindscope\workgroups\learning\ophys\qc_plots' @@ -3170,6 +3170,7 @@ def get_cell_table_from_lims(ophys_experiment_ids=None, columns_to_return='*', v ophys_experiment_ids = experiment_table.index.unique() + if columns_to_return != '*': columns_to_return = ', '.join(columns_to_return).replace('cell_roi_id', 'id') diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index bf12bbfb9..c8af92b33 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -23,7 +23,8 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) -experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +# experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +experiments = experiments_table[experiments_table.project_code.isin(['MultiscopeSignalNoise', 'TaskTrainedNetworksMultiscope'])] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From 18f7366136953e4191eb1667a71266fed58d9686 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 10 Jun 2022 16:06:39 -0700 Subject: [PATCH 066/187] run create multi session df --- scripts/create_multi_session_df.py | 38 +++++++++---------- .../qc/run_save_all_container_plots.py | 3 +- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 615985bec..604375000 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -86,22 +86,22 @@ print(e) - # create dfs for all data types and conditions for behavior data - # for data_type in behavior_data_types: - # for i, conditions in enumerate(behavior_conditions): - # event_type = event_types_for_conditions[i] - # if event_type == 'omissions': - # response_window_duration_seconds = 0.75 - # else: - # response_window_duration_seconds = 0.5 - # print('creating multi_session_df for', data_type, event_type, conditions) - # try: # use try except so that it skips over any conditions that fail to generate for some reason - # df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, - # time_window=time_window, interpolate=interpolate, - # output_sampling_rate=output_sampling_rate, - # response_window_duration_seconds=response_window_duration_seconds, - # use_extended_stimulus_presentations=use_extended_stimulus_presentations, - # overwrite=True) - # except Exception as e: - # print('failed to create multi_session_df for', data_type, event_type, conditions) - # print(e) + create dfs for all data types and conditions for behavior data + for data_type in behavior_data_types: + for i, conditions in enumerate(behavior_conditions): + event_type = event_types_for_conditions[i] + if event_type == 'omissions': + response_window_duration_seconds = 0.75 + else: + response_window_duration_seconds = 0.5 + print('creating multi_session_df for', data_type, event_type, conditions) + try: # use try except so that it skips over any conditions that fail to generate for some reason + df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, + time_window=time_window, interpolate=interpolate, + output_sampling_rate=output_sampling_rate, + response_window_duration_seconds=response_window_duration_seconds, + use_extended_stimulus_presentations=use_extended_stimulus_presentations, + overwrite=True) + except Exception as e: + print('failed to create multi_session_df for', data_type, event_type, conditions) + print(e) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index c8af92b33..bf12bbfb9 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -23,8 +23,7 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) -# experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] -experiments = experiments_table[experiments_table.project_code.isin(['MultiscopeSignalNoise', 'TaskTrainedNetworksMultiscope'])] +experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From b003b0bc46da056a5556f3c42410db568b011156 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 10 Jun 2022 19:19:43 -0700 Subject: [PATCH 067/187] typo in script --- scripts/create_multi_session_df.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 604375000..77d9d5549 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -23,7 +23,6 @@ time_window = [-3, 3.1] interpolate = True output_sampling_rate = 30 - # response_window_duration_seconds = 0.5 use_extended_stimulus_presentations = False # set up conditions to make multi session dfs for @@ -53,7 +52,7 @@ ['cell_specimen_id', 'omitted', 'pre_omitted'],] - # event types corresponding to the above physio and behavior conditions - must be in same sequential order + # event types corresponding to the above physio and behavior conditions - must be in same sequential order!! event_types_for_conditions = ['changes', 'omissions', 'changes', 'omissions', 'changes', 'changes', 'changes', @@ -86,7 +85,7 @@ print(e) - create dfs for all data types and conditions for behavior data + # create dfs for all data types and conditions for behavior data for data_type in behavior_data_types: for i, conditions in enumerate(behavior_conditions): event_type = event_types_for_conditions[i] From 963bf8a3133450c457586a70a87365e7cee271e7 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 10 Jun 2022 19:20:18 -0700 Subject: [PATCH 068/187] exclude invalid ROIs by default --- visual_behavior/data_access/loading.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index f3af4a24a..ee9d55639 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -549,7 +549,7 @@ def get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_t def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, - data_type='filtered_events', event_type='all', load_from_file=True, exclude_invalid_rois=False): + data_type='filtered_events', event_type='all', load_from_file=False, exclude_invalid_rois=True): """ load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added inputs: @@ -785,7 +785,7 @@ def get_cell_specimen_id_for_cell_roi_id(self, cell_roi_id): return cell_specimen_id -def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=False, load_from_lims=True, load_from_nwb=False, +def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=True, load_from_lims=True, load_from_nwb=False, get_extended_stimulus_presentations=False, get_behavior_movie_timestamps=False): """ Gets behavior + ophys data for one experiment (single imaging plane), either using the SDK LIMS API, From c0ec976689cab8f68c78c328d2290abfd5d95642 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 10 Jun 2022 19:21:33 -0700 Subject: [PATCH 069/187] document and update key plotting functions --- .../ophys/platform_paper_figures.py | 53 ++++++++++++++++--- .../ophys/population_summary_figures.py | 3 +- visual_behavior/visualization/utils.py | 34 ++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/visual_behavior/visualization/ophys/platform_paper_figures.py b/visual_behavior/visualization/ophys/platform_paper_figures.py index c89378b4c..0b440589f 100644 --- a/visual_behavior/visualization/ophys/platform_paper_figures.py +++ b/visual_behavior/visualization/ophys/platform_paper_figures.py @@ -26,6 +26,41 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_type, axes_column, hue_column, hue_order=None, project_code=None, timestamps=None, palette=None, title=None, suptitle=None, horizontal=True, xlim_seconds=None, save_dir=None, folder=None, suffix='', ax=None): + """ + Plots population average response across cells in a multi session dataframe for various conditions defined by + axes_column and hue_column, which are columns in the multi_session_df. + There will be one axis for each value of axes_column in the multi_session_df (ex: axes_column='cre_line'). + Within each axis, data will be further split and colorized based on the values of hue_column (ex: hue_column='session_type') + + multi_session_df is created by vba.ophys.io.create_multi_session_df.get_multi_session_df(), which aggregates the output of + vba.ophys.response_analysis.utilities.get_mean_df(), which takes the average across trials of a stimulus_response_df for a set of conditions. + stimulus_response_df comes from mindscope_utilities.visual_behavior_ophys.data_formatting.get_stimulus_response_df() + or vba.data_access.loading.get_stimulus_response_df() + + + :param multi_session_df: dataframe containing trial averaged responses for a set of conditions, aggregated over multiple cells and sessions + :param data_type: can be ['dff', 'events', 'filtered_events', 'running_speed', 'pupil_width', 'lick_rate] + must be the same value as was used to create the stimulus_response_df that was used to create multi_session_df + :param event_type: can be either ['changes', 'omissions', 'all'], whichever was used to create the stimulus_response_df + that was used to create multi_session_df + :param axes_column: column in multi_session_df to split data by to plot on each axis + :param hue_column: column in multi_session_df to colorize data by within each axis + :param hue_order: order of hue values. If None, will sort hue values for each axis. + :param project_code: project_code string used for filename when saving plot + :param timestamps: timestamps to use. If None, will use timestamps available in multi_session_df (if any) + :param palette: color palette for hue labels. If None, uses experience_level_colors + :param metadata_as_title: if True, creates a title composed of mouse_id, container_id, cre_line, imaging_depth, and targeted_structure + If False, use axes_column value. + :param suptitle: suptitle for entire figure; if None is provided, title will be auto generated + :param horizontal: Boolean, Whether to plot axes in horizontal dimension, if False, plot vertical + :param xlim_seconds: time window around the event of interest to limit plot xaxis to. value must be less than the time_window used to create stimulus_response_df. + If None, infers xlims from timestamps + :param save_dir: top level directory to save figure to + :param folder: folder within save_dir to save to + :param suffix: string to append to filename + :param ax: if axis is provided, plot on that axis, otherwise generate a new figure and axes + :return: ax + """ if palette is None: palette = utils.get_experience_level_colors() @@ -68,13 +103,6 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t change = False xlabel = 'time (s)' - if not hue_order: - if hue_column == 'experience_level': - hue_conditions = ['Familiar', 'Novel 1', 'Novel >1'] - else: - hue_conditions = np.sort(sdf[hue_column].unique()) - else: - hue_conditions = hue_order if axes_column == 'experience_level': axes_conditions = ['Familiar', 'Novel 1', 'Novel >1'] else: @@ -95,6 +123,15 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t else: format_fig = False for i, axis in enumerate(axes_conditions): + # set hue order here in case each axis has different values for hue_column + if not hue_order: + if hue_column == 'experience_level': + hue_conditions = ['Familiar', 'Novel 1', 'Novel >1'] + else: + hue_conditions = np.sort(sdf[(sdf[axes_column] == axis)][hue_column].unique()) + else: + hue_conditions = hue_order + # now plot for each unique hue value for c, hue in enumerate(hue_conditions): # try: cdf = sdf[(sdf[axes_column] == axis) & (sdf[hue_column] == hue)] @@ -107,7 +144,7 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t if omitted: omission_color = sns.color_palette()[9] ax[i].axvline(x=0, ymin=0, ymax=1, linestyle='--', color=omission_color) - if title == 'metadata': + if metadata_as_title: metadata_string = utils.get_container_metadata_string(utils.get_metadata_for_row_of_multi_session_df(cdf)) ax[i].set_title(metadata_string) else: diff --git a/visual_behavior/visualization/ophys/population_summary_figures.py b/visual_behavior/visualization/ophys/population_summary_figures.py index dc71bf787..c4b4d17ee 100644 --- a/visual_behavior/visualization/ophys/population_summary_figures.py +++ b/visual_behavior/visualization/ophys/population_summary_figures.py @@ -653,8 +653,7 @@ def plot_stim_on_trace(ax, window=[-0.5, 0.75], alpha=0.3, facecolor='gray'): def plot_mean_trace_from_mean_df(cell_data, frame_rate=31., ylabel='dF/F', legend_label=None, color='k', interval_sec=1, - xlims=[-4, 4], - ax=None, plot_sem=True, width=3): + xlims=[-4, 4], ax=None, plot_sem=True, width=3): xlim = [0, xlims[1] + np.abs(xlims[0])] if ax is None: fig, ax = plt.subplots() diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index e3df40ba1..253702391 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -302,6 +302,40 @@ def plot_stimulus_response_df_trace(stimulus_response_df, time_window=[-1, 1], c return ax +def plot_mean_trace_from_mean_df(cell_data, frame_rate=31., ylabel='dF/F', legend_label=None, color='k', interval_sec=1, + xlims=[-4, 4], ax=None, plot_sem=True, width=3): + """ + plot mean trace for one row in a trial averaged dataframe generated by vba.ophys.response_analysis.utilities.get_mean_df() + dataframe must have 'mean_trace' and 'sem_trace' as columns + :param cell_data: + :param frame_rate: + :param ylabel: + :param legend_label: + :param color: + :param interval_sec: + :param xlims: + :param ax: + :param plot_sem: + :param width: + :return: + """ + if ax is None: + fig, ax = plt.subplots() + trace = cell_data.mean_trace.values[0] + timestamps = cell_data.trace_timestamps.values[0] + sem = cell_data.sem_trace.values[0] + ax.plot(timestamps, trace, label=legend_label, linewidth=width, color=color) + if plot_sem: + ax.fill_between(timestamps, trace + sem, trace - sem, alpha=0.5, color=color) + ax.set_xticks(np.arange(int(timestamps[0]), int(timestamps[-1]) + 1, interval_sec)) + ax.set_xlim([timestamps[0], timestamps[-1]]) + ax.set_xlabel('time (s)') + ax.set_ylabel(ylabel) + sns.despine(ax=ax) + return ax + + + def get_metadata_string(metadata): """ Create a string of metadata information to be used in filenames and figure titles. From 80e9de286b837b5fc911420f80cffc648507eafd Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 10 Jun 2022 19:21:51 -0700 Subject: [PATCH 070/187] document mean response df and align argument names with stim_response_df --- .../ophys/io/create_multi_session_df.py | 3 +- .../ophys/response_analysis/utilities.py | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index ce3de7ff2..990a9e0ca 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -130,8 +130,7 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty window_around_timepoint_seconds = [timestamps[0], timestamps[-1]] mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, - window_around_timepoint_seconds=time_window, - response_window_duration_seconds=response_window_duration, + time_window=time_window, response_window_duration=response_window_duration, get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) if 'correlation_values' in mdf.keys(): mdf = mdf.drop(columns=['correlation_values']) diff --git a/visual_behavior/ophys/response_analysis/utilities.py b/visual_behavior/ophys/response_analysis/utilities.py index 7a8c24483..30537acca 100644 --- a/visual_behavior/ophys/response_analysis/utilities.py +++ b/visual_behavior/ophys/response_analysis/utilities.py @@ -378,13 +378,35 @@ def get_window(analysis=None, flashes=False, omitted=False): def get_mean_df(response_df, conditions=['cell', 'change_image_name'], frame_rate=30., - window_around_timepoint_seconds=[-3, 3.1], response_window_duration_seconds=0.5, + time_window=[-3, 3.1], response_window_duration=0.5, get_pref_stim=True, exclude_omitted_from_pref_stim=True): + """ + + Takes stimulus_response_df as input (dataframe with trace for each cell for each stimulus presentation in a session, + and averages over the conditions provided to produce an average timeseries, mean response value, + and computes other metrics across the trials for each condition (such as trial to trial reliability). + + :param response_df: stimulus_response_df from mindscope_utilities.visual_behavior_ophys.data_formatting.get_stimulus_response_df() + :param conditions: columns in response_df to groupby before averaging. Columns must be Bool or categorical. + ex: ['cell_specimen_id', 'image_name', 'change'] gives the average response for each cell, for each image name for changes and non-changes + :param frame_rate: Frame rate of timeseries. Either the native frame rate of the physio, or if interpolation was used in + get_stimulus_response_df(), provide the output_sampling_rate used for interpolation + :param time_window: window of time around each stimulus_presentation_id that was used to extract stimulus aligned traces to create stimulus_response_df + :param response_window_duration: duration of time, in seconds, following the start_time for each stimulus_presentations_id, + over which to average to get a mean response for each stimulus + :param get_pref_stim: Boolean, whether or not to annotate dataframe with a 'pref_stim' column, + which is also a Boolean indicating which image gave the maximal response for each condition for each cell. + Only works if one of the conditions is 'image_name', 'change_image_name' or 'prior_image_name' + :param exclude_omitted_from_pref_stim: Boolean, whether or not to exclude image_name='omitted' from calculation of preferred stimulus + If False, stimulus omissions can be the 'pref_stim' for a given cell + + :return: response df averaged over the provided conditions, including the average timeseries, the mean response given by response_window_duration, + and several other metrics characterizing trial to trial variability (i.e. fano factor, reliability, etc) + """ import visual_behavior.ophys.response_analysis.response_processing as rp - window = window_around_timepoint_seconds - response_window_duration = response_window_duration_seconds + window = time_window rdf = response_df.copy() @@ -400,9 +422,9 @@ def get_mean_df(response_df, conditions=['cell', 'change_image_name'], frame_rat try: mdf = annotate_mean_df_with_fano_factor(mdf) - mdf = annotate_mean_df_with_time_to_peak(mdf, window, frame_rate) + mdf = annotate_mean_df_with_time_to_peak(mdf, time_window, frame_rate) mdf = annotate_mean_df_with_p_value(mdf, window, response_window_duration, frame_rate) - mdf = annotate_mean_df_with_sd_over_baseline(mdf, window, response_window_duration, frame_rate) + mdf = annotate_mean_df_with_sd_over_baseline(mdf, time_window, response_window_duration, frame_rate) except Exception as e: # NOQA E722 print(e) pass @@ -414,7 +436,7 @@ def get_mean_df(response_df, conditions=['cell', 'change_image_name'], frame_rat mdf['fraction_significant_p_value_gray_screen'] = fraction_significant_p_value_gray_screen.fraction_significant_p_value_gray_screen try: - reliability = rdf.groupby(conditions).apply(compute_reliability, window, response_window_duration, frame_rate) + reliability = rdf.groupby(conditions).apply(compute_reliability, time_window, response_window_duration, frame_rate) reliability = reliability.reset_index() mdf['reliability'] = reliability.reliability mdf['correlation_values'] = reliability.correlation_values From 4edd917fb53b08ada0e5492bf5a2a5c5fcbc448c Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 13 Jun 2022 18:32:14 -0700 Subject: [PATCH 071/187] update plot title --- visual_behavior/visualization/ophys/platform_paper_figures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/ophys/platform_paper_figures.py b/visual_behavior/visualization/ophys/platform_paper_figures.py index 0b440589f..2020607a3 100644 --- a/visual_behavior/visualization/ophys/platform_paper_figures.py +++ b/visual_behavior/visualization/ophys/platform_paper_figures.py @@ -24,7 +24,7 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_type, axes_column, hue_column, hue_order=None, - project_code=None, timestamps=None, palette=None, title=None, suptitle=None, + project_code=None, timestamps=None, palette=None, metadata_as_title=False, suptitle=None, horizontal=True, xlim_seconds=None, save_dir=None, folder=None, suffix='', ax=None): """ Plots population average response across cells in a multi session dataframe for various conditions defined by From ad8ecbe1c184587f928946ede93dfc91bca6e2f5 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 13 Jun 2022 18:32:24 -0700 Subject: [PATCH 072/187] add stim response df notebook --- ..._trial_averaged_stimulus_response_dfs.html | 17721 ++++++++++++++++ ...trial_averaged_stimulus_response_dfs.ipynb | 3887 ++++ 2 files changed, 21608 insertions(+) create mode 100644 220606_trial_averaged_stimulus_response_dfs.html create mode 100644 220606_trial_averaged_stimulus_response_dfs.ipynb diff --git a/220606_trial_averaged_stimulus_response_dfs.html b/220606_trial_averaged_stimulus_response_dfs.html new file mode 100644 index 000000000..8a6a49d24 --- /dev/null +++ b/220606_trial_averaged_stimulus_response_dfs.html @@ -0,0 +1,17721 @@ + + + + +220606_trial_averaged_stimulus_response_dfs + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+

Using learning_mFISH branch of visual_behavior_analysis

+ +
+
+
+
+
+
In [1]:
+
+
+
import os
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+
+import seaborn as sns
+sns.set_context('notebook', font_scale=1.5, rc={'lines.markeredgewidth': 2})
+
+ +
+
+
+ +
+
+
+
In [2]:
+
+
+
%load_ext autoreload
+%autoreload 2
+
+%matplotlib inline
+
+ +
+
+
+ +
+
+
+
In [3]:
+
+
+
import visual_behavior.data_access.loading as loading
+
+ +
+
+
+ +
+
+
+
+

get learning mFISH pilot data from lims

+
+
+
+
+
+
In [4]:
+
+
+
from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache
+
+cache = VisualBehaviorOphysProjectCache.from_lims()
+
+ +
+
+
+ +
+
+
+
In [5]:
+
+
+
experiments_table = cache.get_ophys_experiment_table(passed_only=False)
+experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A']
+
+ +
+
+
+ +
+
+
+
In [6]:
+
+
+
# what mice do we have? 
+print(experiments_table.mouse_id.unique())
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
['612764' '616502' '617911' '616505' '608368' '603892']
+
+
+
+ +
+
+ +
+
+
+
+

get behavior sessions for special mouse

+
+
+
+
+
+
In [7]:
+
+
+
behavior_sessions = cache.get_behavior_session_table()
+
+ +
+
+
+ +
+
+
+
In [8]:
+
+
+
mouse_id = '603892' # bestest GAD2 mouse
+            
+# look at training history for this mouse
+mouse_beh_data = behavior_sessions[behavior_sessions.mouse_id==mouse_id].sort_values(by='date_of_acquisition')
+mouse_beh_data[['mouse_id', 'session_type', 'ophys_experiment_id', 'prior_exposures_to_image_set']]
+
+ +
+
+
+ +
+
+ + +
+ +
Out[8]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
mouse_idsession_typeophys_experiment_idprior_exposures_to_image_set
behavior_session_id
1153004187603892TRAINING_0_gratings_autorewards_15min[1153099555, 1153099558, 1153099559, 115309956...NaN
1153543065603892TRAINING_1_gratings[1153662768, 1153662770, 1153662771, 115366277...NaN
1153793704603892TRAINING_1_gratings[1153920566, 1153920568, 1153920569, 115392057...NaN
1154034257603892TRAINING_2_gratings_flashed[1154288458, 1154288461, 1154288463, 115428846...NaN
1154262140603892TRAINING_2_gratings_flashed[1154369474, 1154369476, 1154369477, 115436947...0.0
1154472358603892TRAINING_3_images_A_10uL_reward[1154572286, 1154572288, 1154572289, 115457229...1.0
1155069835603892TRAINING_3_images_A_10uL_reward[1155282289, 1155282293, 1155282294, 115528229...2.0
1155426295603892TRAINING_4_images_A_training[1155524637, 1155524639, 1155524640, 115552464...3.0
1155634565603892TRAINING_5_images_A_epilogue[1155760201, 1155760204, 1155760205, 115576020...4.0
1155862755603892TRAINING_5_images_A_handoff_ready[1155949165, 1155949168, 1155949169, 115594917...5.0
1156064616603892OPHYS_1_images_A[1156751800, 1156751803, 1156751804, 115675180...6.0
1156620226603892OPHYS_1_images_A[1156776068, 1156776070, 1156776071, 115677607...7.0
1156884393603892OPHYS_4_images_B[1156990779, 1156990784, 1156990789, 115699079...0.0
1157136612603892OPHYS_4_images_B[1157244780, 1157244782, 1157244783, 115724478...1.0
1157372172603892OPHYS_6_images_B[1157477414, 1157477416, 1157477417, 115747741...2.0
1157575929603892OPHYS_6_images_B[1157708779, 1157708781, 1157708782, 115770878...3.0
+
+
+ +
+ +
+
+ +
+
+
+
+

Get an ophys_experiment_id for one of the OPHYS_1_images_A sessions

+
+
+
+
+
+
In [9]:
+
+
+
experiment_ids = experiments_table[experiments_table.behavior_session_id==1156064616].index.values
+print(experiment_ids)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
[1156751800 1156751803 1156751804 1156751806 1156751807 1156751809
+ 1156751810 1156751812]
+
+
+
+ +
+
+ +
+
+
+
In [10]:
+
+
+
experiment_id = experiment_ids[0]
+
+ +
+
+
+ +
+
+
+
+

load ophys dataset for this experiment

+
+
+
+
+
+
In [11]:
+
+
+
dataset = cache.get_behavior_ophys_experiment(experiment_id)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156039109\1156039109_20220204T11952.h5
+Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156039109\1156039109_20220204T11952.h5
+Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+
+ +
+
+
+
In [12]:
+
+
+
plt.imshow(dataset.max_projection, cmap='gray')
+
+ +
+
+
+ +
+
+ + +
+ +
Out[12]:
+ + + + +
+
<matplotlib.image.AxesImage at 0x22c6ac16780>
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [13]:
+
+
+
plt.imshow(dataset.segmentation_mask_image, cmap='gray')
+
+ +
+
+
+ +
+
+ + +
+ +
Out[13]:
+ + + + +
+
<matplotlib.image.AxesImage at 0x22c6b07c550>
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [14]:
+
+
+
dataset.dff_traces
+
+ +
+
+
+ +
+
+ + +
+ +
Out[14]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cell_roi_iddff
cell_specimen_id
11571529461158443134[1.7956229820633356, 1.7855465485410342, 1.387...
11673923001158443136[4.6408898183455385, 5.3055226028490585, 3.562...
11571530401158443137[1.86498742629028, 1.3428503785057742, 1.00591...
11571529991158443143[1.9320210116782677, 1.6513325990239662, 0.982...
11673923071158443149[3.7695539807775593, 2.99196758556438, 2.92723...
11571529891158443156[2.3044750898421107, 2.213317466747521, 1.5230...
11571529521158443157[3.151107572899905, 2.0914650518660785, 2.2618...
11571529671158443160[1.0118235004632778, 1.2237537530888847, 0.671...
11571528381158443162[2.7104277552009055, 2.3025511271370673, 2.030...
11571530851158443165[2.7264602911565303, 2.7098134227023056, 2.713...
11571528541158443167[5.106896608499884, 4.086827308728901, 3.41907...
11571529931158443174[1.4537320506408704, 1.6752925209526544, 1.326...
11571529341158443178[0.7327016564004085, 0.9744498065718734, 0.735...
11571528481158443182[2.552677441679444, 2.208307959680402, 1.72310...
11571530021158443183[2.634607265350429, 2.030799524784848, 1.96075...
11571528631158443188[2.5561709926073126, 2.20100917839999, 1.87895...
11571529401158443190[0.5955948321878124, 0.6319295811135481, 0.362...
11571528841158443194[1.7294731637146477, 1.1192965884821509, 1.230...
11571529221158443195[1.5357136367662858, 1.1643502403866912, 0.908...
11571530711158443197[1.6317978858771232, 1.4646353737712814, 0.990...
11673923241158443199[2.0329831800484763, 1.6030086329648214, 1.548...
11571528341158443205[2.3152891053205438, 1.65738012759169, 1.63434...
11571528721158443208[2.2877347439296525, 2.7624630263400753, 2.398...
11571529071158443223[2.227587737370925, 1.6921453294390625, 1.7207...
11571529101158443226[0.8240220897816866, 1.0151355421925268, 1.034...
+
+
+ +
+ +
+
+ +
+
+
+
+

generate a dataframe of stimulus aligned traces for all cells in this dataset

+
+
+
+
+
+
+

This VBA helper function calls core functions in mindscope_utilities, including mindscope_utilities.visual_behavior_ophys.data_formatting.get_stimulus_response_df and mindscope_utilities, including mindscope_utilities.visual_behavior_ophys.data_formatting.get_annotated_stimulus_presentations

+ +
+
+
+
+
+
In [15]:
+
+
+
stimulus_response_df = loading.get_stimulus_response_df(dataset, time_window=[-3, 3.1], 
+                                                        interpolate=True, output_sampling_rate=30,
+                                                        data_type='dff', event_type='all')
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|██████████████████████████████████████████████████████████████████████████████████| 25/25 [01:03<00:00,  2.55s/it]
+
+
+
+ +
+
+ +
+
+
+
+

stimulus_response_df contains the stimulus aligned trace for every cell, for every stimulus presentation in the session, in a defined time window around stimulus onset

+ +
+
+
+
+
+
In [16]:
+
+
+
stimulus_response_df.head()
+
+ +
+
+
+ +
+
+ + +
+ +
Out[16]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
stimulus_presentations_idcell_specimen_idtracetrace_timestampsmean_responsebaseline_responsep_value_gray_screenophys_frame_ratedata_typeevent_type...engagedengagement_statetime_from_last_changepre_changepre_omittedpost_omittedlickedlick_on_next_flashframe_rateexclude_invalid_rois
001157152834[-0.07069653720906108, -0.07207795680055795, -...[-3.0, -2.966666666666667, -2.933333333333333,...0.052041-0.0447290.270530dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
101157152838[-0.05911301125485008, -0.02572556703543228, 0...[-3.0, -2.966666666666667, -2.933333333333333,...0.093860-0.0643630.000030dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
201157152848[-0.04550768470120481, -0.04748771288143631, -...[-3.0, -2.966666666666667, -2.933333333333333,...0.005496-0.0364290.526030dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
301157152854[0.0023735727893200442, -0.030777591201634084,...[-3.0, -2.966666666666667, -2.933333333333333,...0.017920-0.1458220.044930dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
401157152863[-0.052457907697404965, -0.05176138603558173, ...[-3.0, -2.966666666666667, -2.933333333333333,...0.049606-0.1176520.221430dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
+

5 rows × 54 columns

+
+
+ +
+ +
+
+ +
+
+
+
+

plot average response for a few conditions

+
+
+
+
+
+
+

You can easily plot the population average response for all rows of the table (all stimulus conditions, all cells), or separating out just omissions or just changes

+ +
+
+
+
+
+
+

average everything in stim_response_df

+
+
+
+
+
+
In [17]:
+
+
+
plt.plot(stimulus_response_df.trace_timestamps.mean(), stimulus_response_df.trace.mean())
+plt.xlabel('time relative to stimulus onset (s)')
+plt.ylabel('population response')
+
+ +
+
+
+ +
+
+ + +
+ +
Out[17]:
+ + + + +
+
Text(0, 0.5, 'population response')
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

just image changes

+
+
+
+
+
+
In [18]:
+
+
+
# changes
+traces = stimulus_response_df[stimulus_response_df.is_change==True].trace.values
+timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows
+plt.plot(timestamps, traces.mean())
+plt.xlabel('time after change (s)')
+plt.ylabel('population response')
+
+ +
+
+
+ +
+
+ + +
+ +
Out[18]:
+ + + + +
+
Text(0, 0.5, 'population response')
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

across images

+
+
+
+
+
+
In [19]:
+
+
+
# for each image
+for image_name in stimulus_response_df.image_name.unique():
+    traces = stimulus_response_df[stimulus_response_df.image_name==image_name].trace.values
+    timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows
+    plt.plot(timestamps, traces.mean(), label=image_name)
+plt.xlabel('time (s)')
+plt.ylabel('population response')
+plt.legend(bbox_to_anchor=(1,1))
+
+ +
+
+
+ +
+
+ + +
+ +
Out[19]:
+ + + + +
+
<matplotlib.legend.Legend at 0x22c6a855908>
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

There is a lot of metadata for each stimulus presentation

+ +
+
+
+
+
+
In [20]:
+
+
+
stimulus_response_df.keys()
+
+ +
+
+
+ +
+
+ + +
+ +
Out[20]:
+ + + + +
+
Index(['stimulus_presentations_id', 'cell_specimen_id', 'trace',
+       'trace_timestamps', 'mean_response', 'baseline_response',
+       'p_value_gray_screen', 'ophys_frame_rate', 'data_type', 'event_type',
+       'interpolate', 'output_sampling_rate', 'response_window_duration',
+       'start_time', 'stop_time', 'duration', 'image_name', 'image_index',
+       'is_change', 'omitted', 'start_frame', 'end_frame', 'image_set',
+       'licks', 'mean_running_speed', 'reward_rate_trials', 'epoch',
+       'trials_id', 'change_time', 'go', 'catch', 'aborted', 'auto_rewarded',
+       'hit', 'miss', 'false_alarm', 'correct_reject', 'response_time',
+       'response_latency', 'reward_time', 'reward_volume', 'rewarded',
+       'reward_rate_per_second', 'reward_rate', 'engaged', 'engagement_state',
+       'time_from_last_change', 'pre_change', 'pre_omitted', 'post_omitted',
+       'licked', 'lick_on_next_flash', 'frame_rate', 'exclude_invalid_rois'],
+      dtype='object')
+
+ +
+ +
+
+ +
+
+
+
+

Plot population average for hit vs miss with sem

+
+
+
+
+
+
In [21]:
+
+
+
import visual_behavior.visualization.utils as utils
+
+ +
+
+
+ +
+
+
+
In [22]:
+
+
+
# hits vs. misses
+hit_traces = stimulus_response_df[(stimulus_response_df.is_change==True)&
+                                 (stimulus_response_df.licked==True)].trace.values
+miss_traces = stimulus_response_df[(stimulus_response_df.is_change==True)&
+                                 (stimulus_response_df.licked==False)].trace.values
+timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows
+
+fig, ax = plt.subplots()
+ax = utils.plot_mean_trace(hit_traces, timestamps, legend_label='hit', color='g', xlim_seconds=[-2, 2], ax=ax)
+ax = utils.plot_mean_trace(miss_traces, timestamps, legend_label='miss', color='r', xlim_seconds=[-2, 2], ax=ax)
+ax = utils.plot_flashes_on_trace(ax, timestamps, change=True)
+ax.set_xlabel('time after change (s)')
+ax.set_ylabel('population response')
+
+ +
+
+
+ +
+
+ + +
+ +
Out[22]:
+ + + + +
+
Text(0, 0.5, 'population response')
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

stimulus_response_df also works for behavior timeseries

+
+
+
+
+
+
+

check the documentation for what data_type values are accepted

+ +
+
+
+
+
+
In [23]:
+
+
+
help(loading.get_stimulus_response_df)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Help on function get_stimulus_response_df in module visual_behavior.data_access.loading:
+
+get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, data_type='filtered_events', event_type='all', load_from_file=False, exclude_invalid_rois=True)
+    load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added
+    inputs:
+        dataset: BehaviorOphysExperiment instance
+        time_window: window over which to extract the event triggered response around each stimulus presentation time
+        interpolate: Boolean, whether or not to interpolate traces
+        output_sampling_rate: sampling rate for interpolation, only used if interpolate is True
+        data_type: which timeseries to get event triggered responses for
+                    options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate'
+        event_type: how to filter stimulus presentations before creating table
+                    options: 'all', 'omissions', 'changes'
+        exclude_invalid_rois: Bool, if True, only 'valid' ROIs will be returned. If False, all ROIs including 'invalid' ROIs will be returned.
+                                Only applies if the dataset object that is provided was loaded from lims (not via AWS NWB files, which only have 'valid' ROIs in them)
+                                Note that including invalid ROIs will result in some cell traces being NaNs because traces are not computed for some types of invalid ROIs
+
+
+
+
+ +
+
+ +
+
+
+
+

population average running speed for changes

+
+
+
+
+
+
In [24]:
+
+
+
# use a different data_type, and set event_type to just get changes
+running_traces = loading.get_stimulus_response_df(dataset, time_window=[-2, 2.1], 
+                                                        interpolate=True, output_sampling_rate=30,
+                                                        data_type='running_speed', event_type='changes')
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00,  6.58it/s]
+
+
+
+ +
+
+ +
+
+
+
In [25]:
+
+
+
fig, ax = plt.subplots()
+timestamps = running_traces.trace_timestamps.values[0]
+traces =  running_traces.trace.values
+ax = utils.plot_mean_trace(traces, timestamps, xlim_seconds=[-2, 2.1], ax=ax)
+ax = utils.plot_flashes_on_trace(ax, timestamps, change=True)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

mean response dataframe

+
+
+
+
+
+
+

Compute trial averaged responses for some set of conditions for one experiment

+
+
+
+
+
+
In [26]:
+
+
+
# lets look at the first day of novel images
+expts = experiments_table[(experiments_table.mouse_id==mouse_id)&
+                                 (experiments_table.prior_exposures_to_image_set==0)&
+                                 (experiments_table.session_type=='OPHYS_4_images_B')]
+
+expts
+
+ +
+
+
+ +
+
+ + +
+ +
Out[26]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
equipment_namedonor_idfull_genotypemouse_idreporter_linedriver_linesexage_in_daysforaging_idcre_line...session_nameisi_experiment_idimaging_depthtargeted_structurepublished_atdate_of_acquisitionsession_typeexperience_levelpassiveimage_set
ophys_experiment_id
1156990779MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISpNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990784MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISpNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990789MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISlNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990795MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISlNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990801MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISalNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990807MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISalNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990811MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593160VISamNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990817MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISamNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
+

8 rows × 31 columns

+
+
+ +
+ +
+
+ +
+
+
+
In [27]:
+
+
+
experiment_id = expts.index.values[0]
+
+expts.loc[experiment_id][['targeted_structure', 'imaging_depth', 'cre_line']]
+
+ +
+
+
+ +
+
+ + +
+ +
Out[27]:
+ + + + +
+
targeted_structure             VISp
+imaging_depth                   165
+cre_line              Gad2-IRES-Cre
+Name: 1156990779, dtype: object
+
+ +
+ +
+
+ +
+
+
+
In [28]:
+
+
+
# include invalid ROIs so it actually loads something
+dataset = loading.get_ophys_dataset(experiment_id)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156842547\1156842547_20220208T11611.h5
+Error! The number of sync file frame times (272556) does not match the number of eye tracking frames (272557)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+
+ +
+
+
+
In [29]:
+
+
+
dataset.dff_traces
+
+ +
+
+
+ +
+
+ + +
+ +
Out[29]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cell_roi_iddff
cell_specimen_id
11571529931158441063[1.7447209311456708, 1.5865674673630625, 1.189...
11571529521158441079[0.9821773875653413, 0.6815864160489754, 0.875...
11571528381158441086[0.9440638128741594, 0.6928306142067617, 0.923...
11571530611158441126[0.4295891950014597, 0.12311089148520898, 0.17...
11571529171158441136[0.5040607683989946, 0.1996595831637253, 0.217...
11571529101158441151[0.34777108353096425, 0.22056436698997897, 0.1...
11673924611158441182[1.700689762686462, 1.5516912827058043, 1.1277...
11571530431158441211[0.6829429581189924, 0.5359614482912533, 0.860...
11571529341158441276[0.34941670363725896, 0.5404771402225802, 0.33...
11571528721158441280[1.5672415079239674, 1.2482531712780118, 1.081...
+
+
+ +
+ +
+
+ +
+
+
+
In [30]:
+
+
+
# set all params here so they can be used later
+time_window = [-3, 3.1]
+interpolate = True
+output_sampling_rate = 30
+data_type = 'dff'
+event_type = 'changes'
+# get stimulus_response_df for changes, with annotated stimulus presentations columns
+stimulus_response_df = loading.get_stimulus_response_df(dataset, time_window=time_window, 
+                                                        interpolate=interpolate, output_sampling_rate=output_sampling_rate,
+                                                        data_type=data_type, event_type=event_type)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|██████████████████████████████████████████████████████████████████████████████████| 10/10 [00:01<00:00,  9.28it/s]
+
+
+
+ +
+
+ +
+
+
+
+

get average response dataframe, averaging over all changes presentations in 10 minute epochs in the session

+
+
+
+
+
+
In [31]:
+
+
+
df = stimulus_response_df.copy()
+
+ +
+
+
+ +
+
+
+
In [32]:
+
+
+
# check that 'epoch' column is in the dataframe so it can be used as a condition to group by
+df.keys()
+
+ +
+
+
+ +
+
+ + +
+ +
Out[32]:
+ + + + +
+
Index(['stimulus_presentations_id', 'cell_specimen_id', 'trace',
+       'trace_timestamps', 'mean_response', 'baseline_response',
+       'p_value_gray_screen', 'ophys_frame_rate', 'data_type', 'event_type',
+       'interpolate', 'output_sampling_rate', 'response_window_duration',
+       'start_time', 'stop_time', 'duration', 'image_name', 'image_index',
+       'is_change', 'omitted', 'start_frame', 'end_frame', 'image_set',
+       'licks', 'mean_running_speed', 'reward_rate_trials', 'epoch',
+       'trials_id', 'change_time', 'go', 'catch', 'aborted', 'auto_rewarded',
+       'hit', 'miss', 'false_alarm', 'correct_reject', 'response_time',
+       'response_latency', 'reward_time', 'reward_volume', 'rewarded',
+       'reward_rate_per_second', 'reward_rate', 'engaged', 'engagement_state',
+       'time_from_last_change', 'pre_change', 'pre_omitted', 'post_omitted',
+       'licked', 'lick_on_next_flash', 'frame_rate', 'exclude_invalid_rois'],
+      dtype='object')
+
+ +
+ +
+
+ +
+
+
+
In [33]:
+
+
+
import visual_behavior.ophys.response_analysis.utilities as ut
+
+ +
+
+
+ +
+
+
+
In [34]:
+
+
+
# get params for mean df creation from stimulus_response_df
+if 'response_window_duration' in df.keys():
+    response_window_duration = df.response_window_duration.values[0]
+output_sampling_rate = df.frame_rate.unique()[0]
+timestamps = df.trace_timestamps.values[0]
+window_around_timepoint_seconds = [timestamps[0], timestamps[-1]]
+
+# set conditions to average over
+conditions = ['cell_specimen_id', 'epoch']
+# use VBA get_mean_df function to average over trials defined by `conditions`
+# must use same time_window and frame rate as was used to create the stim_response_df
+mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate,
+                     time_window=time_window, response_window_duration=response_window_duration)
+# get rid of last epoch which doesnt have 10 full minutes in it
+epochs = np.sort(mdf.epoch.unique())
+mdf = mdf[mdf.epoch<epochs[-1]]
+
+ +
+
+
+ +
+
+
+
In [35]:
+
+
+
mdf.head(3)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[35]:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cell_specimen_idepochmean_responsesem_responsemean_tracesem_tracetrace_timestampsmean_responsesmean_baselinesem_baselineresponse_window_durationfano_factorpeak_responsetime_to_peakp_valuesd_over_baselinefraction_significant_p_value_gray_screenreliabilitycorrelation_values
0115715283800.0462800.008041[-0.012199797613191779, -0.012247351090620482,...[0.015650344384645958, 0.014990897708778022, 0...[-3.0, -2.966666666666668, -2.9333333333333327...[0.060822094999087514, 0.0717100039089949, 0.1...-0.0099010.0105900.51.7374020.0769790.2666676.191129e-093.8834070.4400000.030802[0.18707681181671063, 0.7166727421288949, 0.28...
1115715283810.0503080.006432[0.011839090833916007, 0.012669363836058095, 0...[0.012479521220842157, 0.01250013591873294, 0....[-3.0, -2.966666666666668, -2.9333333333333336...[0.1159659258776442, -0.01590334638979228, 0.0...0.0164270.0076540.51.4688220.0894370.2333331.551863e-069.9419900.4242420.098395[-0.5203967795194145, 0.8731867866305196, -0.5...
2115715283820.0630590.006859[0.01616523979730032, 0.015763250202315136, 0....[0.013673149659509562, 0.013116437982355919, 0...[-3.0, -2.966666666666668, -2.933333333333334,...[0.04478745017078148, 0.02911787742961676, 0.0...0.0057280.0068280.51.2870000.1027160.2333337.256622e-088.6131480.5142860.131187[-0.8626962860970627, -0.6452970913391443, 0.5...
+
+
+ +
+ +
+
+ +
+
+
+
+

function to plot the average +/- sem trace for one cell

+
+
+
+
+
+
In [36]:
+
+
+
import visual_behavior.visualization.utils as utils
+
+ +
+
+
+ +
+
+
+
In [37]:
+
+
+
cell_specimen_id = mdf.cell_specimen_id.unique()[0]
+epochs = mdf.epoch.unique()
+colors = sns.color_palette('magma', len(epochs))
+fig, ax = plt.subplots()
+for i,epoch in enumerate(epochs):
+    cell_data = mdf[(mdf.cell_specimen_id==cell_specimen_id)&(mdf.epoch==epoch)]
+    ax = utils.plot_mean_trace_from_mean_df(cell_data, frame_rate=output_sampling_rate, color=colors[i],
+                                            legend_label=epoch, xlims=time_window, ax=ax)
+    ax = utils.plot_flashes_on_trace(ax, timestamps=cell_data.trace_timestamps.values[0], change=True)
+ax.legend(fontsize='x-small', title='epoch', title_fontsize='x-small', loc='upper left')
+ax.set_title('cell_specimen_id: '+str(cell_specimen_id))
+
+ +
+
+
+ +
+
+ + +
+ +
Out[37]:
+ + + + +
+
Text(0.5, 1.0, 'cell_specimen_id: 1157152838')
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
+

Aggregate trial averaged responses for all session for one container

+
+
+
+
+
+
In [39]:
+
+
+
# get some container for special mouse 
+mouse_data = experiments_table[experiments_table.mouse_id==mouse_id]
+container_id = mouse_data.ophys_container_id.unique()[0]
+experiment_ids = mouse_data[mouse_data.ophys_container_id==container_id].index.values
+print(len(experiment_ids))
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
16
+
+
+
+ +
+
+ +
+
+
+
+

Note: this takes a while

+ +
+
+
+
+
+
In [40]:
+
+
+
# params for stim_response_df 
+data_type = 'dff'
+event_type = 'changes'
+time_window = [-2, 2.1]
+interpolate = True
+output_sampling_rate = 30
+
+# conditions to average trials over
+conditions = ['cell_specimen_id', 'image_name']
+
+# loop through experients and aggregated trial averaged dfs
+big_mdf = pd.DataFrame()
+for experiment_id in experiment_ids:
+    try: # not all experiments load so need to to try except
+        print('expt_id:', experiment_id, ',', np.where(experiment_ids==experiment_id)[0][0], 'out of', len(experiment_ids))
+        # get dataset
+        dataset = loading.get_ophys_dataset(experiment_id)
+        # get stimulus_response_df
+        df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window,
+                                              interpolate=interpolate, output_sampling_rate=output_sampling_rate)
+        # get params for mean df creation from stimulus_response_df
+        if 'response_window_duration' in df.keys():
+            response_window_duration = df.response_window_duration.values[0]
+        output_sampling_rate = df.frame_rate.unique()[0]
+        timestamps = df.trace_timestamps.values[0]
+        window_around_timepoint_seconds = [timestamps[0], timestamps[-1]]
+        # get trial averaged response for conditions
+        mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate,
+                             time_window=time_window,
+                             response_window_duration=response_window_duration)
+        # add experiment ID to be able to distinguish different sessions
+        mdf['ophys_experiment_id'] = experiment_id
+        # aggregate
+        big_mdf = pd.concat([big_mdf, mdf])
+    except Exception as e:  
+        print(e)
+        print('problem for', experiment_id)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
expt_id: 1153662776 , 0 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662776
+expt_id: 1154288470 , 1 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288470
+expt_id: 1156776076 , 2 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00,  8.85it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1155524645 , 3 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1155380558\1155380558_20220201T112343.h5
+Error! The number of sync file frame times (221014) does not match the number of eye tracking frames (221015)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00,  5.95it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1155760211 , 4 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=48431).
+problem for 1155760211
+expt_id: 1156990807 , 5 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156842547\1156842547_20220208T11611.h5
+Error! The number of sync file frame times (272556) does not match the number of eye tracking frames (272557)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00,  9.59it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1157244788 , 6 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1157100446\1157100446_20220209T11113.h5
+Error! The number of sync file frame times (272866) does not match the number of eye tracking frames (272867)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+100%|████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00,  9.73it/s]
+
+
+
+ +
+ +
+ + +
+
generating response df
+expt_id: 1153099564 , 7 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=10154).
+problem for 1153099564
+expt_id: 1153920574 , 8 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (2823,) (3646,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1153776022\1153776022_20220125T11404.h5
+Error! The number of sync file frame times (221002) does not match the number of eye tracking frames (221003)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00,  5.80it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1154572294 , 9 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1154460714\1154460714_20220128T11471.h5
+Error! The number of sync file frame times (220762) does not match the number of eye tracking frames (220763)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00,  5.16it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1154369482 , 10 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39203).
+problem for 1154369482
+expt_id: 1155282299 , 11 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1155282299
+expt_id: 1155949174 , 12 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1155848487\1155848487_20220203T114150.h5
+Error! The number of sync file frame times (273014) does not match the number of eye tracking frames (273015)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00,  6.18it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1156751809 , 13 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156039109\1156039109_20220204T11952.h5
+Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00,  6.48it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1157477422 , 14 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1157331298\1157331298_20220210T11311.h5
+Error! The number of sync file frame times (273169) does not match the number of eye tracking frames (273170)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|██████████████████████████████████████████████████████████████████████████████████| 11/11 [00:02<00:00,  5.20it/s]
+
+
+
+ +
+ +
+ + +
+
expt_id: 1157708787 , 15 out of 16
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1157559454\1157559454_20220211T11365.h5
+Error! The number of sync file frame times (273285) does not match the number of eye tracking frames (273286)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
generating response df
+
+
+
+ +
+ +
+ + +
+
100%|██████████████████████████████████████████████████████████████████████████████████| 17/17 [00:03<00:00,  5.03it/s]
+
+
+
+ +
+
+ +
+
+
+
In [45]:
+
+
+
big_mdf.keys()
+
+ +
+
+
+ +
+
+ + +
+ +
Out[45]:
+ + + + +
+
Index(['cell_specimen_id', 'image_name', 'mean_response', 'sem_response',
+       'mean_trace', 'sem_trace', 'trace_timestamps', 'mean_responses',
+       'mean_baseline', 'sem_baseline', 'response_window_duration',
+       'pref_stim', 'fano_factor', 'peak_response', 'time_to_peak', 'p_value',
+       'sd_over_baseline', 'fraction_significant_p_value_gray_screen',
+       'reliability', 'correlation_values', 'ophys_experiment_id'],
+      dtype='object')
+
+ +
+ +
+
+ +
+
+
+
In [46]:
+
+
+
len(big_mdf)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[46]:
+ + + + +
+
608
+
+ +
+ +
+
+ +
+
+
+
In [47]:
+
+
+
big_mdf = big_mdf.merge(experiments_table, on='ophys_experiment_id')
+len(big_mdf)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[47]:
+ + + + +
+
608
+
+ +
+ +
+
+ +
+
+
+
+

plot population average response for each image_name within each session_type

+
+
+
+
+
+
In [48]:
+
+
+
def condense_session_types(df):
+    df = df.copy()
+    df = df.reset_index()
+    for row in range(len(df)):
+        session_type = df.iloc[row].session_type
+        # all of Training 4 and 5 are basically the same
+        if session_type in ['TRAINING_4_images_A_training', 'TRAINING_5_images_A_epilogue', 'TRAINING_5_images_A_handoff_ready']:
+            df.at[row, 'session_type'] = 'TRAINING_4_images_A' 
+        # for the first training session with images, remove the '10uL_reward bit'
+        elif session_type == 'TRAINING_3_images_A_10uL_reward':
+            df.at[row, 'session_type'] = 'TRAINING_3_images_A' 
+        # otherwise, keep the same name
+        else: 
+            pass
+    return df
+
+ +
+
+
+ +
+
+
+
In [52]:
+
+
+
big_mdf = condense_session_types(big_mdf)
+
+ +
+
+
+ +
+
+
+
In [53]:
+
+
+
import visual_behavior.visualization.ophys.platform_paper_figures as ppf
+
+ +
+
+
+ +
+
+
+
In [55]:
+
+
+
axes_column = 'session_type'
+hue_column = 'image_name'
+palette = sns.color_palette()
+
+ppf.plot_population_averages_for_conditions(big_mdf, data_type, event_type, axes_column, hue_column, palette=palette, 
+                                        suptitle=None, horizontal=True, save_dir=None, folder=None)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[55]:
+ + + + +
+
array([<matplotlib.axes._subplots.AxesSubplot object at 0x0000022C0AEF7E48>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x0000022C0CE37518>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x0000022C0CA32D30>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x0000022C0D64F710>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x0000022C101B50F0>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x0000022C0CD21A90>],
+      dtype=object)
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
+

Aggregate trial averaged responses accross all sessions and all imaging planes for a given mouse

+
+
+
+
+
+
In [215]:
+
+
+
from visual_behavior.ophys.io.create_multi_session_df import get_multi_session_df
+
+ +
+
+
+ +
+
+
+
+

This function will run the steps above iteratively for all sessions for the provided mouse_id and project code:

+ +
* load dataset for each experiment
+* generate stimulus_response_df to get stimulus aligned traces
+* compute mean response dataframe averaged over the provided set of conditions
+* concatenate with all other sessions
+ +
+
+
+
+
+
+

NOTE: this takes tens of minutes

+ +
+
+
+
+
+
+

There is code to do this on the cluster in visual_behavior_analysis.scripts.run_create_multi_session_df.py

+ +
+
+
+
+
+
In [216]:
+
+
+
project_code = 'LearningmFISHTask1A'
+conditions = ['cell_specimen_id', 'image_name']
+data_type = 'dff'
+event_type = 'changes'
+
+multi_session_df = get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type,
+                         time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
get_pref_stim True
+getting up-to-date experiment_table from lims
+including failed data
+excluding Ai94 data
+616 expts in experiments table
+creating multi session mean df for mean_response_df_dff_changes_LearningmFISHTask1A_603892_image_name.h5
+1153662776
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662776
+1153662777
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662777
+1153662768
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1153529245\1153529245_20220124T105312.h5
+Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1153662768 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1153662768
+1153662770
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662770
+1153662771
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1153529245\1153529245_20220124T105312.h5
+Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1153662771 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1153662771
+1153662774
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1153529245\1153529245_20220124T105312.h5
+Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1153662774 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1153662774
+1153662779
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662779
+1153662773
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39108).
+problem for 1153662773
+1154288458
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288458
+1154288461
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288461
+1154288463
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288463
+1154288465
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
Unable to open file (unable to open file: name = '\\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1154000392\ophys_experiment_1154288465\demix\1154288465_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)
+problem for 1154288465
+1154288467
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288467
+1154288470
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
dff_frames (len=10000) is not equal to number of split timestamps (len=39171).
+problem for 1154288470
+1154288472
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
Unable to open file (unable to open file: name = '\\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1154000392\ophys_experiment_1154288472\demix\1154288472_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)
+problem for 1154288472
+1154288475
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\util.py:63: UserWarning: Monitory delay calculation failed with ValueError
+    "operands could not be broadcast together with shapes (3131,) (3644,) "
+looking monitor delay up from table for rig: MESO.2 
+delay: 0.03613 seconds
+  warnings.warn(warning_msg)
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+
+
+
+ +
+ +
+ + +
+
Unable to open file (unable to open file: name = '\\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1154000392\ophys_experiment_1154288475\demix\1154288475_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)
+problem for 1154288475
+1156776068
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\running_speed\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed
+
+  "value removed\n", UserWarning, stacklevel=1)
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1156776068 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1156776068
+1156776070
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1156776070 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1156776070
+1156776071
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1156776071 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1156776071
+1156776073
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1156776073 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1156776073
+1156776074
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\metadata\subject_metadata\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter
+  'Could not parse indicator from reporter because none'
+C:\Users\marinag\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\eye_tracking\eye_tracking_table.py:233: UserWarning: 
+in sync_file: \\allen\programs\mindscope\production\learning\prod0\specimen_1142290487\ophys_session_1156589145\1156589145_20220207T11326.h5
+Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!
+returning empty eye_tracking DataFrame
+  warnings.warn(msg)
+
+
+
+ +
+ +
+ + +
+
file exists:
+loading response df from file for 1156776074 dff changes
+get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'
+problem for 1156776074
+1156776076
+loading from lims, exclude_invalid_rois = True
+
+
+
+ +
+ +
+ + +
+
WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+WARNING:root:Could not find valid lines for the following data sources
+WARNING:root:2p (valid line label(s) = ['2p_vsync']
+WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']
+
+
+
+ +
+ +
+ + +
+
+---------------------------------------------------------------------------
+KeyboardInterrupt                         Traceback (most recent call last)
+<ipython-input-216-fa2b432be17d> in <module>
+      5 
+      6 multi_session_df = get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type,
+----> 7                          time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5)
+
+c:\users\marinag\documents\code\visual_behavior_analysis\visual_behavior\ophys\io\create_multi_session_df.py in get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, time_window, interpolate, output_sampling_rate, response_window_duration, use_extended_stimulus_presentations, overwrite)
+     96                 # get dataset
+     97                 dataset = loading.get_ophys_dataset(experiment_id,
+---> 98                                                     get_extended_stimulus_presentations=use_extended_stimulus_presentations)
+     99                 # get stimulus_response_df
+    100                 df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window,
+
+c:\users\marinag\documents\code\visual_behavior_analysis\visual_behavior\data_access\loading.py in get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois, load_from_lims, load_from_nwb, get_extended_stimulus_presentations, get_behavior_movie_timestamps)
+    818         cache = bpc.from_lims()
+    819         # dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id))
+--> 820         dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id), exclude_invalid_rois=exclude_invalid_rois)
+    821     elif load_from_nwb:
+    822         cache_dir = get_platform_analysis_cache_dir()
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\behavior_ophys_experiment.py in from_lims(cls, ophys_experiment_id, eye_tracking_z_threshold, eye_tracking_dilation_frames, events_filter_scale_seconds, events_filter_n_time_steps, exclude_invalid_rois, skip_eye_tracking)
+    188             stimulus_timestamps=stimulus_timestamps,
+    189             monitor_delay=monitor_delay,
+--> 190             date_of_acquisition=date_of_acquisition
+    191         )
+    192         if is_multiplane_session:
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\behavior_session.py in from_lims(cls, behavior_session_id, lims_db, stimulus_timestamps, monitor_delay, date_of_acquisition)
+    203             cls._read_data_from_stimulus_file(
+    204                 stimulus_file=stimulus_file,
+--> 205                 stimulus_timestamps=stimulus_timestamps,
+    206             )
+    207         if date_of_acquisition is None:
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\behavior_session.py in _read_data_from_stimulus_file(cls, stimulus_file, stimulus_timestamps)
+    892         stimuli = Stimuli.from_stimulus_file(
+    893             stimulus_file=stimulus_file,
+--> 894             stimulus_timestamps=stimulus_timestamps)
+    895         task_parameters = TaskParameters.from_stimulus_file(
+    896             stimulus_file=stimulus_file)
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\stimuli.py in from_stimulus_file(cls, stimulus_file, stimulus_timestamps, limit_to_images)
+     52             limit_to_images=limit_to_images)
+     53         t = Templates.from_stimulus_file(stimulus_file=stimulus_file,
+---> 54                                          limit_to_images=limit_to_images)
+     55         return Stimuli(presentations=p, templates=t)
+     56 
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\templates.py in from_stimulus_file(cls, stimulus_file, limit_to_images)
+     85         t = get_stimulus_templates(pkl=pkl,
+     86                                    grating_images_dict=grating_images_dict,
+---> 87                                    limit_to_images=limit_to_images)
+     88         return Templates(templates=t)
+     89 
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\stimulus_processing.py in get_stimulus_templates(pkl, grating_images_dict, limit_to_images)
+    217             image_set_name=image_set_name,
+    218             image_attributes=attrs,
+--> 219             images=image_values
+    220         )
+    221     elif 'grating' in pkl_stimuli:
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\stimulus_templates.py in from_unprocessed(image_set_name, image_attributes, images)
+    238             name = image_attributes[i]['image_name']
+    239             stimulus_image = StimulusImageFactory().from_unprocessed(
+--> 240                 name=name, input_array=image)
+    241             stimulus_images.append(stimulus_image)
+    242         return StimulusTemplate(image_set_name=image_set_name,
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\stimulus_templates.py in from_unprocessed(self, input_array, name)
+     40         """Creates a StimulusImage from unprocessed input (usually pkl).
+     41         Image needs to be warped and preprocessed"""
+---> 42         resized, unwarped = self._get_unwarped(arr=input_array)
+     43         warped = self._get_warped(arr=resized)
+     44         image = StimulusImage(name=name, warped=warped, unwarped=unwarped)
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\stimulus_templates.py in _get_unwarped(self, arr)
+     66             arr, origin='upper')
+     67         # 2. Remove unseen pixels
+---> 68         arr = self._exclude_unseen_pixels(arr=resized_array)
+     69 
+     70         return resized_array, arr
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\behavior\data_objects\stimuli\stimulus_templates.py in _exclude_unseen_pixels(self, arr)
+     73         """After warping, some pixels are not visible on the screen.
+     74         This sets those pixels to nan to make downstream analysis easier."""
+---> 75         mask = self._monitor.get_mask()
+     76         arr = arr.astype(np.float)
+     77         arr *= mask
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\stimulus_info.py in get_mask(self)
+    588     def get_mask(self):
+    589 
+--> 590         mask = make_display_mask(display_shape=(self.n_pixels_c, self.n_pixels_r)).T
+    591         assert mask.shape[0] == self.n_pixels_r
+    592         assert mask.shape[1] == self.n_pixels_c
+
+~\Documents\Code\AllenSDK\allensdk\brain_observatory\stimulus_info.py in make_display_mask(display_shape)
+    822     used_coords = set()
+    823     for i in range(off_warped_coords.shape[1]):
+--> 824         used_coords.add((off_warped_coords[0, i], off_warped_coords[1, i]))
+    825 
+    826     used_coords = (np.array([x for (x, y) in used_coords]).astype(int),
+
+KeyboardInterrupt: 
+
+
+ +
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+
In [ ]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+ + + + + + diff --git a/220606_trial_averaged_stimulus_response_dfs.ipynb b/220606_trial_averaged_stimulus_response_dfs.ipynb new file mode 100644 index 000000000..b25000ef3 --- /dev/null +++ b/220606_trial_averaged_stimulus_response_dfs.ipynb @@ -0,0 +1,3887 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using `learning_mFISH` branch of `visual_behavior_analysis`" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import seaborn as sns\n", + "sns.set_context('notebook', font_scale=1.5, rc={'lines.markeredgewidth': 2})" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import visual_behavior.data_access.loading as loading" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### get learning mFISH pilot data from lims" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache\n", + "\n", + "cache = VisualBehaviorOphysProjectCache.from_lims()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "experiments_table = cache.get_ophys_experiment_table(passed_only=False)\n", + "experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A']" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['612764' '616502' '617911' '616505' '608368' '603892']\n" + ] + } + ], + "source": [ + "# what mice do we have? \n", + "print(experiments_table.mouse_id.unique())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### get behavior sessions for special mouse" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "behavior_sessions = cache.get_behavior_session_table()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mouse_idsession_typeophys_experiment_idprior_exposures_to_image_set
behavior_session_id
1153004187603892TRAINING_0_gratings_autorewards_15min[1153099555, 1153099558, 1153099559, 115309956...NaN
1153543065603892TRAINING_1_gratings[1153662768, 1153662770, 1153662771, 115366277...NaN
1153793704603892TRAINING_1_gratings[1153920566, 1153920568, 1153920569, 115392057...NaN
1154034257603892TRAINING_2_gratings_flashed[1154288458, 1154288461, 1154288463, 115428846...NaN
1154262140603892TRAINING_2_gratings_flashed[1154369474, 1154369476, 1154369477, 115436947...0.0
1154472358603892TRAINING_3_images_A_10uL_reward[1154572286, 1154572288, 1154572289, 115457229...1.0
1155069835603892TRAINING_3_images_A_10uL_reward[1155282289, 1155282293, 1155282294, 115528229...2.0
1155426295603892TRAINING_4_images_A_training[1155524637, 1155524639, 1155524640, 115552464...3.0
1155634565603892TRAINING_5_images_A_epilogue[1155760201, 1155760204, 1155760205, 115576020...4.0
1155862755603892TRAINING_5_images_A_handoff_ready[1155949165, 1155949168, 1155949169, 115594917...5.0
1156064616603892OPHYS_1_images_A[1156751800, 1156751803, 1156751804, 115675180...6.0
1156620226603892OPHYS_1_images_A[1156776068, 1156776070, 1156776071, 115677607...7.0
1156884393603892OPHYS_4_images_B[1156990779, 1156990784, 1156990789, 115699079...0.0
1157136612603892OPHYS_4_images_B[1157244780, 1157244782, 1157244783, 115724478...1.0
1157372172603892OPHYS_6_images_B[1157477414, 1157477416, 1157477417, 115747741...2.0
1157575929603892OPHYS_6_images_B[1157708779, 1157708781, 1157708782, 115770878...3.0
\n", + "
" + ], + "text/plain": [ + " mouse_id session_type \\\n", + "behavior_session_id \n", + "1153004187 603892 TRAINING_0_gratings_autorewards_15min \n", + "1153543065 603892 TRAINING_1_gratings \n", + "1153793704 603892 TRAINING_1_gratings \n", + "1154034257 603892 TRAINING_2_gratings_flashed \n", + "1154262140 603892 TRAINING_2_gratings_flashed \n", + "1154472358 603892 TRAINING_3_images_A_10uL_reward \n", + "1155069835 603892 TRAINING_3_images_A_10uL_reward \n", + "1155426295 603892 TRAINING_4_images_A_training \n", + "1155634565 603892 TRAINING_5_images_A_epilogue \n", + "1155862755 603892 TRAINING_5_images_A_handoff_ready \n", + "1156064616 603892 OPHYS_1_images_A \n", + "1156620226 603892 OPHYS_1_images_A \n", + "1156884393 603892 OPHYS_4_images_B \n", + "1157136612 603892 OPHYS_4_images_B \n", + "1157372172 603892 OPHYS_6_images_B \n", + "1157575929 603892 OPHYS_6_images_B \n", + "\n", + " ophys_experiment_id \\\n", + "behavior_session_id \n", + "1153004187 [1153099555, 1153099558, 1153099559, 115309956... \n", + "1153543065 [1153662768, 1153662770, 1153662771, 115366277... \n", + "1153793704 [1153920566, 1153920568, 1153920569, 115392057... \n", + "1154034257 [1154288458, 1154288461, 1154288463, 115428846... \n", + "1154262140 [1154369474, 1154369476, 1154369477, 115436947... \n", + "1154472358 [1154572286, 1154572288, 1154572289, 115457229... \n", + "1155069835 [1155282289, 1155282293, 1155282294, 115528229... \n", + "1155426295 [1155524637, 1155524639, 1155524640, 115552464... \n", + "1155634565 [1155760201, 1155760204, 1155760205, 115576020... \n", + "1155862755 [1155949165, 1155949168, 1155949169, 115594917... \n", + "1156064616 [1156751800, 1156751803, 1156751804, 115675180... \n", + "1156620226 [1156776068, 1156776070, 1156776071, 115677607... \n", + "1156884393 [1156990779, 1156990784, 1156990789, 115699079... \n", + "1157136612 [1157244780, 1157244782, 1157244783, 115724478... \n", + "1157372172 [1157477414, 1157477416, 1157477417, 115747741... \n", + "1157575929 [1157708779, 1157708781, 1157708782, 115770878... \n", + "\n", + " prior_exposures_to_image_set \n", + "behavior_session_id \n", + "1153004187 NaN \n", + "1153543065 NaN \n", + "1153793704 NaN \n", + "1154034257 NaN \n", + "1154262140 0.0 \n", + "1154472358 1.0 \n", + "1155069835 2.0 \n", + "1155426295 3.0 \n", + "1155634565 4.0 \n", + "1155862755 5.0 \n", + "1156064616 6.0 \n", + "1156620226 7.0 \n", + "1156884393 0.0 \n", + "1157136612 1.0 \n", + "1157372172 2.0 \n", + "1157575929 3.0 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mouse_id = '603892' # bestest GAD2 mouse\n", + " \n", + "# look at training history for this mouse\n", + "mouse_beh_data = behavior_sessions[behavior_sessions.mouse_id==mouse_id].sort_values(by='date_of_acquisition')\n", + "mouse_beh_data[['mouse_id', 'session_type', 'ophys_experiment_id', 'prior_exposures_to_image_set']]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Get an ophys_experiment_id for one of the OPHYS_1_images_A sessions" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1156751800 1156751803 1156751804 1156751806 1156751807 1156751809\n", + " 1156751810 1156751812]\n" + ] + } + ], + "source": [ + "experiment_ids = experiments_table[experiments_table.behavior_session_id==1156064616].index.values\n", + "print(experiment_ids)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "experiment_id = experiment_ids[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### load ophys dataset for this experiment" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156039109\\1156039109_20220204T11952.h5\n", + "Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156039109\\1156039109_20220204T11952.h5\n", + "Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + } + ], + "source": [ + "dataset = cache.get_behavior_ophys_experiment(experiment_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(dataset.max_projection, cmap='gray')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(dataset.segmentation_mask_image, cmap='gray')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
cell_roi_iddff
cell_specimen_id
11571529461158443134[1.7956229820633356, 1.7855465485410342, 1.387...
11673923001158443136[4.6408898183455385, 5.3055226028490585, 3.562...
11571530401158443137[1.86498742629028, 1.3428503785057742, 1.00591...
11571529991158443143[1.9320210116782677, 1.6513325990239662, 0.982...
11673923071158443149[3.7695539807775593, 2.99196758556438, 2.92723...
11571529891158443156[2.3044750898421107, 2.213317466747521, 1.5230...
11571529521158443157[3.151107572899905, 2.0914650518660785, 2.2618...
11571529671158443160[1.0118235004632778, 1.2237537530888847, 0.671...
11571528381158443162[2.7104277552009055, 2.3025511271370673, 2.030...
11571530851158443165[2.7264602911565303, 2.7098134227023056, 2.713...
11571528541158443167[5.106896608499884, 4.086827308728901, 3.41907...
11571529931158443174[1.4537320506408704, 1.6752925209526544, 1.326...
11571529341158443178[0.7327016564004085, 0.9744498065718734, 0.735...
11571528481158443182[2.552677441679444, 2.208307959680402, 1.72310...
11571530021158443183[2.634607265350429, 2.030799524784848, 1.96075...
11571528631158443188[2.5561709926073126, 2.20100917839999, 1.87895...
11571529401158443190[0.5955948321878124, 0.6319295811135481, 0.362...
11571528841158443194[1.7294731637146477, 1.1192965884821509, 1.230...
11571529221158443195[1.5357136367662858, 1.1643502403866912, 0.908...
11571530711158443197[1.6317978858771232, 1.4646353737712814, 0.990...
11673923241158443199[2.0329831800484763, 1.6030086329648214, 1.548...
11571528341158443205[2.3152891053205438, 1.65738012759169, 1.63434...
11571528721158443208[2.2877347439296525, 2.7624630263400753, 2.398...
11571529071158443223[2.227587737370925, 1.6921453294390625, 1.7207...
11571529101158443226[0.8240220897816866, 1.0151355421925268, 1.034...
\n", + "
" + ], + "text/plain": [ + " cell_roi_id \\\n", + "cell_specimen_id \n", + "1157152946 1158443134 \n", + "1167392300 1158443136 \n", + "1157153040 1158443137 \n", + "1157152999 1158443143 \n", + "1167392307 1158443149 \n", + "1157152989 1158443156 \n", + "1157152952 1158443157 \n", + "1157152967 1158443160 \n", + "1157152838 1158443162 \n", + "1157153085 1158443165 \n", + "1157152854 1158443167 \n", + "1157152993 1158443174 \n", + "1157152934 1158443178 \n", + "1157152848 1158443182 \n", + "1157153002 1158443183 \n", + "1157152863 1158443188 \n", + "1157152940 1158443190 \n", + "1157152884 1158443194 \n", + "1157152922 1158443195 \n", + "1157153071 1158443197 \n", + "1167392324 1158443199 \n", + "1157152834 1158443205 \n", + "1157152872 1158443208 \n", + "1157152907 1158443223 \n", + "1157152910 1158443226 \n", + "\n", + " dff \n", + "cell_specimen_id \n", + "1157152946 [1.7956229820633356, 1.7855465485410342, 1.387... \n", + "1167392300 [4.6408898183455385, 5.3055226028490585, 3.562... \n", + "1157153040 [1.86498742629028, 1.3428503785057742, 1.00591... \n", + "1157152999 [1.9320210116782677, 1.6513325990239662, 0.982... \n", + "1167392307 [3.7695539807775593, 2.99196758556438, 2.92723... \n", + "1157152989 [2.3044750898421107, 2.213317466747521, 1.5230... \n", + "1157152952 [3.151107572899905, 2.0914650518660785, 2.2618... \n", + "1157152967 [1.0118235004632778, 1.2237537530888847, 0.671... \n", + "1157152838 [2.7104277552009055, 2.3025511271370673, 2.030... \n", + "1157153085 [2.7264602911565303, 2.7098134227023056, 2.713... \n", + "1157152854 [5.106896608499884, 4.086827308728901, 3.41907... \n", + "1157152993 [1.4537320506408704, 1.6752925209526544, 1.326... \n", + "1157152934 [0.7327016564004085, 0.9744498065718734, 0.735... \n", + "1157152848 [2.552677441679444, 2.208307959680402, 1.72310... \n", + "1157153002 [2.634607265350429, 2.030799524784848, 1.96075... \n", + "1157152863 [2.5561709926073126, 2.20100917839999, 1.87895... \n", + "1157152940 [0.5955948321878124, 0.6319295811135481, 0.362... \n", + "1157152884 [1.7294731637146477, 1.1192965884821509, 1.230... \n", + "1157152922 [1.5357136367662858, 1.1643502403866912, 0.908... \n", + "1157153071 [1.6317978858771232, 1.4646353737712814, 0.990... \n", + "1167392324 [2.0329831800484763, 1.6030086329648214, 1.548... \n", + "1157152834 [2.3152891053205438, 1.65738012759169, 1.63434... \n", + "1157152872 [2.2877347439296525, 2.7624630263400753, 2.398... \n", + "1157152907 [2.227587737370925, 1.6921453294390625, 1.7207... \n", + "1157152910 [0.8240220897816866, 1.0151355421925268, 1.034... " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.dff_traces" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "scrolled": true + }, + "source": [ + "### generate a dataframe of stimulus aligned traces for all cells in this dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This VBA helper function calls core functions in `mindscope_utilities`, including `mindscope_utilities.visual_behavior_ophys.data_formatting.get_stimulus_response_df` and `mindscope_utilities`, including `mindscope_utilities.visual_behavior_ophys.data_formatting.get_annotated_stimulus_presentations`" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████████████████████████████████████████████████████████████████████████████| 25/25 [01:03<00:00, 2.55s/it]\n" + ] + } + ], + "source": [ + "stimulus_response_df = loading.get_stimulus_response_df(dataset, time_window=[-3, 3.1], \n", + " interpolate=True, output_sampling_rate=30,\n", + " data_type='dff', event_type='all')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`stimulus_response_df` contains the stimulus aligned trace for every cell, for every stimulus presentation in the session, in a defined time window around stimulus onset" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
stimulus_presentations_idcell_specimen_idtracetrace_timestampsmean_responsebaseline_responsep_value_gray_screenophys_frame_ratedata_typeevent_type...engagedengagement_statetime_from_last_changepre_changepre_omittedpost_omittedlickedlick_on_next_flashframe_rateexclude_invalid_rois
001157152834[-0.07069653720906108, -0.07207795680055795, -...[-3.0, -2.966666666666667, -2.933333333333333,...0.052041-0.0447290.270530dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
101157152838[-0.05911301125485008, -0.02572556703543228, 0...[-3.0, -2.966666666666667, -2.933333333333333,...0.093860-0.0643630.000030dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
201157152848[-0.04550768470120481, -0.04748771288143631, -...[-3.0, -2.966666666666667, -2.933333333333333,...0.005496-0.0364290.526030dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
301157152854[0.0023735727893200442, -0.030777591201634084,...[-3.0, -2.966666666666667, -2.933333333333333,...0.017920-0.1458220.044930dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
401157152863[-0.052457907697404965, -0.05176138603558173, ...[-3.0, -2.966666666666667, -2.933333333333333,...0.049606-0.1176520.221430dffall...FalsedisengagedNaNFalseFalseNaNTrueTrue30True
\n", + "

5 rows × 54 columns

\n", + "
" + ], + "text/plain": [ + " stimulus_presentations_id cell_specimen_id \\\n", + "0 0 1157152834 \n", + "1 0 1157152838 \n", + "2 0 1157152848 \n", + "3 0 1157152854 \n", + "4 0 1157152863 \n", + "\n", + " trace \\\n", + "0 [-0.07069653720906108, -0.07207795680055795, -... \n", + "1 [-0.05911301125485008, -0.02572556703543228, 0... \n", + "2 [-0.04550768470120481, -0.04748771288143631, -... \n", + "3 [0.0023735727893200442, -0.030777591201634084,... \n", + "4 [-0.052457907697404965, -0.05176138603558173, ... \n", + "\n", + " trace_timestamps mean_response \\\n", + "0 [-3.0, -2.966666666666667, -2.933333333333333,... 0.052041 \n", + "1 [-3.0, -2.966666666666667, -2.933333333333333,... 0.093860 \n", + "2 [-3.0, -2.966666666666667, -2.933333333333333,... 0.005496 \n", + "3 [-3.0, -2.966666666666667, -2.933333333333333,... 0.017920 \n", + "4 [-3.0, -2.966666666666667, -2.933333333333333,... 0.049606 \n", + "\n", + " baseline_response p_value_gray_screen ophys_frame_rate data_type \\\n", + "0 -0.044729 0.2705 30 dff \n", + "1 -0.064363 0.0000 30 dff \n", + "2 -0.036429 0.5260 30 dff \n", + "3 -0.145822 0.0449 30 dff \n", + "4 -0.117652 0.2214 30 dff \n", + "\n", + " event_type ... engaged engagement_state time_from_last_change \\\n", + "0 all ... False disengaged NaN \n", + "1 all ... False disengaged NaN \n", + "2 all ... False disengaged NaN \n", + "3 all ... False disengaged NaN \n", + "4 all ... False disengaged NaN \n", + "\n", + " pre_change pre_omitted post_omitted licked lick_on_next_flash \\\n", + "0 False False NaN True True \n", + "1 False False NaN True True \n", + "2 False False NaN True True \n", + "3 False False NaN True True \n", + "4 False False NaN True True \n", + "\n", + " frame_rate exclude_invalid_rois \n", + "0 30 True \n", + "1 30 True \n", + "2 30 True \n", + "3 30 True \n", + "4 30 True \n", + "\n", + "[5 rows x 54 columns]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "stimulus_response_df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### plot average response for a few conditions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can easily plot the population average response for all rows of the table (all stimulus conditions, all cells), or separating out just omissions or just changes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### average everything in stim_response_df" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'population response')" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(stimulus_response_df.trace_timestamps.mean(), stimulus_response_df.trace.mean())\n", + "plt.xlabel('time relative to stimulus onset (s)')\n", + "plt.ylabel('population response')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### just image changes" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'population response')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# changes\n", + "traces = stimulus_response_df[stimulus_response_df.is_change==True].trace.values\n", + "timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows\n", + "plt.plot(timestamps, traces.mean())\n", + "plt.xlabel('time after change (s)')\n", + "plt.ylabel('population response')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### across images" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# for each image\n", + "for image_name in stimulus_response_df.image_name.unique():\n", + " traces = stimulus_response_df[stimulus_response_df.image_name==image_name].trace.values\n", + " timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows\n", + " plt.plot(timestamps, traces.mean(), label=image_name)\n", + "plt.xlabel('time (s)')\n", + "plt.ylabel('population response')\n", + "plt.legend(bbox_to_anchor=(1,1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is a lot of metadata for each stimulus presentation" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['stimulus_presentations_id', 'cell_specimen_id', 'trace',\n", + " 'trace_timestamps', 'mean_response', 'baseline_response',\n", + " 'p_value_gray_screen', 'ophys_frame_rate', 'data_type', 'event_type',\n", + " 'interpolate', 'output_sampling_rate', 'response_window_duration',\n", + " 'start_time', 'stop_time', 'duration', 'image_name', 'image_index',\n", + " 'is_change', 'omitted', 'start_frame', 'end_frame', 'image_set',\n", + " 'licks', 'mean_running_speed', 'reward_rate_trials', 'epoch',\n", + " 'trials_id', 'change_time', 'go', 'catch', 'aborted', 'auto_rewarded',\n", + " 'hit', 'miss', 'false_alarm', 'correct_reject', 'response_time',\n", + " 'response_latency', 'reward_time', 'reward_volume', 'rewarded',\n", + " 'reward_rate_per_second', 'reward_rate', 'engaged', 'engagement_state',\n", + " 'time_from_last_change', 'pre_change', 'pre_omitted', 'post_omitted',\n", + " 'licked', 'lick_on_next_flash', 'frame_rate', 'exclude_invalid_rois'],\n", + " dtype='object')" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "stimulus_response_df.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Plot population average for hit vs miss with sem" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "import visual_behavior.visualization.utils as utils" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'population response')" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# hits vs. misses\n", + "hit_traces = stimulus_response_df[(stimulus_response_df.is_change==True)&\n", + " (stimulus_response_df.licked==True)].trace.values\n", + "miss_traces = stimulus_response_df[(stimulus_response_df.is_change==True)&\n", + " (stimulus_response_df.licked==False)].trace.values\n", + "timestamps = stimulus_response_df.trace_timestamps.values[0] # timestamps are shared across all rows\n", + "\n", + "fig, ax = plt.subplots()\n", + "ax = utils.plot_mean_trace(hit_traces, timestamps, legend_label='hit', color='g', xlim_seconds=[-2, 2], ax=ax)\n", + "ax = utils.plot_mean_trace(miss_traces, timestamps, legend_label='miss', color='r', xlim_seconds=[-2, 2], ax=ax)\n", + "ax = utils.plot_flashes_on_trace(ax, timestamps, change=True)\n", + "ax.set_xlabel('time after change (s)')\n", + "ax.set_ylabel('population response')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### stimulus_response_df also works for behavior timeseries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "check the documentation for what `data_type` values are accepted" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function get_stimulus_response_df in module visual_behavior.data_access.loading:\n", + "\n", + "get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, data_type='filtered_events', event_type='all', load_from_file=False, exclude_invalid_rois=True)\n", + " load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added\n", + " inputs:\n", + " dataset: BehaviorOphysExperiment instance\n", + " time_window: window over which to extract the event triggered response around each stimulus presentation time\n", + " interpolate: Boolean, whether or not to interpolate traces\n", + " output_sampling_rate: sampling rate for interpolation, only used if interpolate is True\n", + " data_type: which timeseries to get event triggered responses for\n", + " options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate'\n", + " event_type: how to filter stimulus presentations before creating table\n", + " options: 'all', 'omissions', 'changes'\n", + " exclude_invalid_rois: Bool, if True, only 'valid' ROIs will be returned. If False, all ROIs including 'invalid' ROIs will be returned.\n", + " Only applies if the dataset object that is provided was loaded from lims (not via AWS NWB files, which only have 'valid' ROIs in them)\n", + " Note that including invalid ROIs will result in some cell traces being NaNs because traces are not computed for some types of invalid ROIs\n", + "\n" + ] + } + ], + "source": [ + "help(loading.get_stimulus_response_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### population average running speed for changes" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 6.58it/s]\n" + ] + } + ], + "source": [ + "# use a different data_type, and set event_type to just get changes\n", + "running_traces = loading.get_stimulus_response_df(dataset, time_window=[-2, 2.1], \n", + " interpolate=True, output_sampling_rate=30,\n", + " data_type='running_speed', event_type='changes')" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "timestamps = running_traces.trace_timestamps.values[0]\n", + "traces = running_traces.trace.values\n", + "ax = utils.plot_mean_trace(traces, timestamps, xlim_seconds=[-2, 2.1], ax=ax)\n", + "ax = utils.plot_flashes_on_trace(ax, timestamps, change=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## mean response dataframe" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compute trial averaged responses for some set of conditions for one experiment" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
equipment_namedonor_idfull_genotypemouse_idreporter_linedriver_linesexage_in_daysforaging_idcre_line...session_nameisi_experiment_idimaging_depthtargeted_structurepublished_atdate_of_acquisitionsession_typeexperience_levelpassiveimage_set
ophys_experiment_id
1156990779MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISpNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990784MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISpNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990789MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISlNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990795MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISlNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990801MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593165VISalNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990807MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISalNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990811MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593160VISamNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
1156990817MESO.21142290477Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT...603892Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg[Slc32a1-T2A-FlpO, Gad2-IRES-Cre]M129.04ff292b1-07ad-4b42-9769-d154736e1398Gad2-IRES-Cre...20220208_603892_ophys41145918593265VISamNaT2022-02-08 10:36:49OPHYS_4_images_BNovel 1FalseB
\n", + "

8 rows × 31 columns

\n", + "
" + ], + "text/plain": [ + " equipment_name donor_id \\\n", + "ophys_experiment_id \n", + "1156990779 MESO.2 1142290477 \n", + "1156990784 MESO.2 1142290477 \n", + "1156990789 MESO.2 1142290477 \n", + "1156990795 MESO.2 1142290477 \n", + "1156990801 MESO.2 1142290477 \n", + "1156990807 MESO.2 1142290477 \n", + "1156990811 MESO.2 1142290477 \n", + "1156990817 MESO.2 1142290477 \n", + "\n", + " full_genotype \\\n", + "ophys_experiment_id \n", + "1156990779 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990784 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990789 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990795 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990801 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990807 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990811 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "1156990817 Gad2-IRES-Cre/wt;Slc32a1-T2A-FlpO/wt;Ai195(TIT... \n", + "\n", + " mouse_id reporter_line \\\n", + "ophys_experiment_id \n", + "1156990779 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990784 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990789 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990795 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990801 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990807 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990811 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "1156990817 603892 Ai195(TIT2L-GC7s-ICF-IRES-tTA2)-hyg \n", + "\n", + " driver_line sex age_in_days \\\n", + "ophys_experiment_id \n", + "1156990779 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990784 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990789 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990795 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990801 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990807 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990811 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "1156990817 [Slc32a1-T2A-FlpO, Gad2-IRES-Cre] M 129.0 \n", + "\n", + " foraging_id cre_line ... \\\n", + "ophys_experiment_id ... \n", + "1156990779 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990784 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990789 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990795 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990801 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990807 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990811 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "1156990817 4ff292b1-07ad-4b42-9769-d154736e1398 Gad2-IRES-Cre ... \n", + "\n", + " session_name isi_experiment_id imaging_depth \\\n", + "ophys_experiment_id \n", + "1156990779 20220208_603892_ophys4 1145918593 165 \n", + "1156990784 20220208_603892_ophys4 1145918593 265 \n", + "1156990789 20220208_603892_ophys4 1145918593 165 \n", + "1156990795 20220208_603892_ophys4 1145918593 265 \n", + "1156990801 20220208_603892_ophys4 1145918593 165 \n", + "1156990807 20220208_603892_ophys4 1145918593 265 \n", + "1156990811 20220208_603892_ophys4 1145918593 160 \n", + "1156990817 20220208_603892_ophys4 1145918593 265 \n", + "\n", + " targeted_structure published_at date_of_acquisition \\\n", + "ophys_experiment_id \n", + "1156990779 VISp NaT 2022-02-08 10:36:49 \n", + "1156990784 VISp NaT 2022-02-08 10:36:49 \n", + "1156990789 VISl NaT 2022-02-08 10:36:49 \n", + "1156990795 VISl NaT 2022-02-08 10:36:49 \n", + "1156990801 VISal NaT 2022-02-08 10:36:49 \n", + "1156990807 VISal NaT 2022-02-08 10:36:49 \n", + "1156990811 VISam NaT 2022-02-08 10:36:49 \n", + "1156990817 VISam NaT 2022-02-08 10:36:49 \n", + "\n", + " session_type experience_level passive image_set \n", + "ophys_experiment_id \n", + "1156990779 OPHYS_4_images_B Novel 1 False B \n", + "1156990784 OPHYS_4_images_B Novel 1 False B \n", + "1156990789 OPHYS_4_images_B Novel 1 False B \n", + "1156990795 OPHYS_4_images_B Novel 1 False B \n", + "1156990801 OPHYS_4_images_B Novel 1 False B \n", + "1156990807 OPHYS_4_images_B Novel 1 False B \n", + "1156990811 OPHYS_4_images_B Novel 1 False B \n", + "1156990817 OPHYS_4_images_B Novel 1 False B \n", + "\n", + "[8 rows x 31 columns]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# lets look at the first day of novel images\n", + "expts = experiments_table[(experiments_table.mouse_id==mouse_id)&\n", + " (experiments_table.prior_exposures_to_image_set==0)&\n", + " (experiments_table.session_type=='OPHYS_4_images_B')]\n", + "\n", + "expts" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "targeted_structure VISp\n", + "imaging_depth 165\n", + "cre_line Gad2-IRES-Cre\n", + "Name: 1156990779, dtype: object" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "experiment_id = expts.index.values[0]\n", + "\n", + "expts.loc[experiment_id][['targeted_structure', 'imaging_depth', 'cre_line']]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156842547\\1156842547_20220208T11611.h5\n", + "Error! The number of sync file frame times (272556) does not match the number of eye tracking frames (272557)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + } + ], + "source": [ + "# include invalid ROIs so it actually loads something\n", + "dataset = loading.get_ophys_dataset(experiment_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
cell_roi_iddff
cell_specimen_id
11571529931158441063[1.7447209311456708, 1.5865674673630625, 1.189...
11571529521158441079[0.9821773875653413, 0.6815864160489754, 0.875...
11571528381158441086[0.9440638128741594, 0.6928306142067617, 0.923...
11571530611158441126[0.4295891950014597, 0.12311089148520898, 0.17...
11571529171158441136[0.5040607683989946, 0.1996595831637253, 0.217...
11571529101158441151[0.34777108353096425, 0.22056436698997897, 0.1...
11673924611158441182[1.700689762686462, 1.5516912827058043, 1.1277...
11571530431158441211[0.6829429581189924, 0.5359614482912533, 0.860...
11571529341158441276[0.34941670363725896, 0.5404771402225802, 0.33...
11571528721158441280[1.5672415079239674, 1.2482531712780118, 1.081...
\n", + "
" + ], + "text/plain": [ + " cell_roi_id \\\n", + "cell_specimen_id \n", + "1157152993 1158441063 \n", + "1157152952 1158441079 \n", + "1157152838 1158441086 \n", + "1157153061 1158441126 \n", + "1157152917 1158441136 \n", + "1157152910 1158441151 \n", + "1167392461 1158441182 \n", + "1157153043 1158441211 \n", + "1157152934 1158441276 \n", + "1157152872 1158441280 \n", + "\n", + " dff \n", + "cell_specimen_id \n", + "1157152993 [1.7447209311456708, 1.5865674673630625, 1.189... \n", + "1157152952 [0.9821773875653413, 0.6815864160489754, 0.875... \n", + "1157152838 [0.9440638128741594, 0.6928306142067617, 0.923... \n", + "1157153061 [0.4295891950014597, 0.12311089148520898, 0.17... \n", + "1157152917 [0.5040607683989946, 0.1996595831637253, 0.217... \n", + "1157152910 [0.34777108353096425, 0.22056436698997897, 0.1... \n", + "1167392461 [1.700689762686462, 1.5516912827058043, 1.1277... \n", + "1157153043 [0.6829429581189924, 0.5359614482912533, 0.860... \n", + "1157152934 [0.34941670363725896, 0.5404771402225802, 0.33... \n", + "1157152872 [1.5672415079239674, 1.2482531712780118, 1.081... " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.dff_traces" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████████████████████████████████████████████████████████████████████████████| 10/10 [00:01<00:00, 9.28it/s]\n" + ] + } + ], + "source": [ + "# set all params here so they can be used later\n", + "time_window = [-3, 3.1]\n", + "interpolate = True\n", + "output_sampling_rate = 30\n", + "data_type = 'dff'\n", + "event_type = 'changes'\n", + "# get stimulus_response_df for changes, with annotated stimulus presentations columns\n", + "stimulus_response_df = loading.get_stimulus_response_df(dataset, time_window=time_window, \n", + " interpolate=interpolate, output_sampling_rate=output_sampling_rate,\n", + " data_type=data_type, event_type=event_type)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### get average response dataframe, averaging over all changes presentations in 10 minute epochs in the session" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "df = stimulus_response_df.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['stimulus_presentations_id', 'cell_specimen_id', 'trace',\n", + " 'trace_timestamps', 'mean_response', 'baseline_response',\n", + " 'p_value_gray_screen', 'ophys_frame_rate', 'data_type', 'event_type',\n", + " 'interpolate', 'output_sampling_rate', 'response_window_duration',\n", + " 'start_time', 'stop_time', 'duration', 'image_name', 'image_index',\n", + " 'is_change', 'omitted', 'start_frame', 'end_frame', 'image_set',\n", + " 'licks', 'mean_running_speed', 'reward_rate_trials', 'epoch',\n", + " 'trials_id', 'change_time', 'go', 'catch', 'aborted', 'auto_rewarded',\n", + " 'hit', 'miss', 'false_alarm', 'correct_reject', 'response_time',\n", + " 'response_latency', 'reward_time', 'reward_volume', 'rewarded',\n", + " 'reward_rate_per_second', 'reward_rate', 'engaged', 'engagement_state',\n", + " 'time_from_last_change', 'pre_change', 'pre_omitted', 'post_omitted',\n", + " 'licked', 'lick_on_next_flash', 'frame_rate', 'exclude_invalid_rois'],\n", + " dtype='object')" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# check that 'epoch' column is in the dataframe so it can be used as a condition to group by\n", + "df.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "import visual_behavior.ophys.response_analysis.utilities as ut" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "# get params for mean df creation from stimulus_response_df\n", + "if 'response_window_duration' in df.keys():\n", + " response_window_duration = df.response_window_duration.values[0]\n", + "output_sampling_rate = df.frame_rate.unique()[0]\n", + "timestamps = df.trace_timestamps.values[0]\n", + "window_around_timepoint_seconds = [timestamps[0], timestamps[-1]]\n", + "\n", + "# set conditions to average over\n", + "conditions = ['cell_specimen_id', 'epoch']\n", + "# use VBA get_mean_df function to average over trials defined by `conditions`\n", + "# must use same time_window and frame rate as was used to create the stim_response_df\n", + "mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate,\n", + " time_window=time_window, response_window_duration=response_window_duration)\n", + "# get rid of last epoch which doesnt have 10 full minutes in it\n", + "epochs = np.sort(mdf.epoch.unique())\n", + "mdf = mdf[mdf.epoch\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
cell_specimen_idepochmean_responsesem_responsemean_tracesem_tracetrace_timestampsmean_responsesmean_baselinesem_baselineresponse_window_durationfano_factorpeak_responsetime_to_peakp_valuesd_over_baselinefraction_significant_p_value_gray_screenreliabilitycorrelation_values
0115715283800.0462800.008041[-0.012199797613191779, -0.012247351090620482,...[0.015650344384645958, 0.014990897708778022, 0...[-3.0, -2.966666666666668, -2.9333333333333327...[0.060822094999087514, 0.0717100039089949, 0.1...-0.0099010.0105900.51.7374020.0769790.2666676.191129e-093.8834070.4400000.030802[0.18707681181671063, 0.7166727421288949, 0.28...
1115715283810.0503080.006432[0.011839090833916007, 0.012669363836058095, 0...[0.012479521220842157, 0.01250013591873294, 0....[-3.0, -2.966666666666668, -2.9333333333333336...[0.1159659258776442, -0.01590334638979228, 0.0...0.0164270.0076540.51.4688220.0894370.2333331.551863e-069.9419900.4242420.098395[-0.5203967795194145, 0.8731867866305196, -0.5...
2115715283820.0630590.006859[0.01616523979730032, 0.015763250202315136, 0....[0.013673149659509562, 0.013116437982355919, 0...[-3.0, -2.966666666666668, -2.933333333333334,...[0.04478745017078148, 0.02911787742961676, 0.0...0.0057280.0068280.51.2870000.1027160.2333337.256622e-088.6131480.5142860.131187[-0.8626962860970627, -0.6452970913391443, 0.5...
\n", + "" + ], + "text/plain": [ + " cell_specimen_id epoch mean_response sem_response \\\n", + "0 1157152838 0 0.046280 0.008041 \n", + "1 1157152838 1 0.050308 0.006432 \n", + "2 1157152838 2 0.063059 0.006859 \n", + "\n", + " mean_trace \\\n", + "0 [-0.012199797613191779, -0.012247351090620482,... \n", + "1 [0.011839090833916007, 0.012669363836058095, 0... \n", + "2 [0.01616523979730032, 0.015763250202315136, 0.... \n", + "\n", + " sem_trace \\\n", + "0 [0.015650344384645958, 0.014990897708778022, 0... \n", + "1 [0.012479521220842157, 0.01250013591873294, 0.... \n", + "2 [0.013673149659509562, 0.013116437982355919, 0... \n", + "\n", + " trace_timestamps \\\n", + "0 [-3.0, -2.966666666666668, -2.9333333333333327... \n", + "1 [-3.0, -2.966666666666668, -2.9333333333333336... \n", + "2 [-3.0, -2.966666666666668, -2.933333333333334,... \n", + "\n", + " mean_responses mean_baseline \\\n", + "0 [0.060822094999087514, 0.0717100039089949, 0.1... -0.009901 \n", + "1 [0.1159659258776442, -0.01590334638979228, 0.0... 0.016427 \n", + "2 [0.04478745017078148, 0.02911787742961676, 0.0... 0.005728 \n", + "\n", + " sem_baseline response_window_duration fano_factor peak_response \\\n", + "0 0.010590 0.5 1.737402 0.076979 \n", + "1 0.007654 0.5 1.468822 0.089437 \n", + "2 0.006828 0.5 1.287000 0.102716 \n", + "\n", + " time_to_peak p_value sd_over_baseline \\\n", + "0 0.266667 6.191129e-09 3.883407 \n", + "1 0.233333 1.551863e-06 9.941990 \n", + "2 0.233333 7.256622e-08 8.613148 \n", + "\n", + " fraction_significant_p_value_gray_screen reliability \\\n", + "0 0.440000 0.030802 \n", + "1 0.424242 0.098395 \n", + "2 0.514286 0.131187 \n", + "\n", + " correlation_values \n", + "0 [0.18707681181671063, 0.7166727421288949, 0.28... \n", + "1 [-0.5203967795194145, 0.8731867866305196, -0.5... \n", + "2 [-0.8626962860970627, -0.6452970913391443, 0.5... " + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mdf.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### function to plot the average +/- sem trace for one cell" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "import visual_behavior.visualization.utils as utils" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'cell_specimen_id: 1157152838')" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "cell_specimen_id = mdf.cell_specimen_id.unique()[0]\n", + "epochs = mdf.epoch.unique()\n", + "colors = sns.color_palette('magma', len(epochs))\n", + "fig, ax = plt.subplots()\n", + "for i,epoch in enumerate(epochs):\n", + " cell_data = mdf[(mdf.cell_specimen_id==cell_specimen_id)&(mdf.epoch==epoch)]\n", + " ax = utils.plot_mean_trace_from_mean_df(cell_data, frame_rate=output_sampling_rate, color=colors[i],\n", + " legend_label=epoch, xlims=time_window, ax=ax)\n", + " ax = utils.plot_flashes_on_trace(ax, timestamps=cell_data.trace_timestamps.values[0], change=True)\n", + "ax.legend(fontsize='x-small', title='epoch', title_fontsize='x-small', loc='upper left')\n", + "ax.set_title('cell_specimen_id: '+str(cell_specimen_id))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Aggregate trial averaged responses for all session for one container" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "16\n" + ] + } + ], + "source": [ + "# get some container for special mouse \n", + "mouse_data = experiments_table[experiments_table.mouse_id==mouse_id]\n", + "container_id = mouse_data.ophys_container_id.unique()[0]\n", + "experiment_ids = mouse_data[mouse_data.ophys_container_id==container_id].index.values\n", + "print(len(experiment_ids))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: this takes a while" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1153662776 , 0 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662776\n", + "expt_id: 1154288470 , 1 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288470\n", + "expt_id: 1156776076 , 2 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 8.85it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1155524645 , 3 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1155380558\\1155380558_20220201T112343.h5\n", + "Error! The number of sync file frame times (221014) does not match the number of eye tracking frames (221015)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00, 5.95it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1155760211 , 4 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=48431).\n", + "problem for 1155760211\n", + "expt_id: 1156990807 , 5 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156842547\\1156842547_20220208T11611.h5\n", + "Error! The number of sync file frame times (272556) does not match the number of eye tracking frames (272557)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 9.59it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1157244788 , 6 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1157100446\\1157100446_20220209T11113.h5\n", + "Error! The number of sync file frame times (272866) does not match the number of eye tracking frames (272867)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n", + "100%|████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 9.73it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n", + "expt_id: 1153099564 , 7 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=10154).\n", + "problem for 1153099564\n", + "expt_id: 1153920574 , 8 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (2823,) (3646,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1153776022\\1153776022_20220125T11404.h5\n", + "Error! The number of sync file frame times (221002) does not match the number of eye tracking frames (221003)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00, 5.80it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1154572294 , 9 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1154460714\\1154460714_20220128T11471.h5\n", + "Error! The number of sync file frame times (220762) does not match the number of eye tracking frames (220763)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00, 5.16it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1154369482 , 10 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39203).\n", + "problem for 1154369482\n", + "expt_id: 1155282299 , 11 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1155282299\n", + "expt_id: 1155949174 , 12 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1155848487\\1155848487_20220203T114150.h5\n", + "Error! The number of sync file frame times (273014) does not match the number of eye tracking frames (273015)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00, 6.18it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1156751809 , 13 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156039109\\1156039109_20220204T11952.h5\n", + "Error! The number of sync file frame times (272777) does not match the number of eye tracking frames (272778)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:01<00:00, 6.48it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1157477422 , 14 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1157331298\\1157331298_20220210T11311.h5\n", + "Error! The number of sync file frame times (273169) does not match the number of eye tracking frames (273170)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████████████████████████████████████████████████████████████████████████████| 11/11 [00:02<00:00, 5.20it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "expt_id: 1157708787 , 15 out of 16\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1157559454\\1157559454_20220211T11365.h5\n", + "Error! The number of sync file frame times (273285) does not match the number of eye tracking frames (273286)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "generating response df\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████████████████████████████████████████████████████████████████████████████| 17/17 [00:03<00:00, 5.03it/s]\n" + ] + } + ], + "source": [ + "# params for stim_response_df \n", + "data_type = 'dff'\n", + "event_type = 'changes'\n", + "time_window = [-2, 2.1]\n", + "interpolate = True\n", + "output_sampling_rate = 30\n", + "\n", + "# conditions to average trials over\n", + "conditions = ['cell_specimen_id', 'image_name']\n", + "\n", + "# loop through experients and aggregated trial averaged dfs\n", + "big_mdf = pd.DataFrame()\n", + "for experiment_id in experiment_ids:\n", + " try: # not all experiments load so need to to try except\n", + " print('expt_id:', experiment_id, ',', np.where(experiment_ids==experiment_id)[0][0], 'out of', len(experiment_ids))\n", + " # get dataset\n", + " dataset = loading.get_ophys_dataset(experiment_id)\n", + " # get stimulus_response_df\n", + " df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window,\n", + " interpolate=interpolate, output_sampling_rate=output_sampling_rate)\n", + " # get params for mean df creation from stimulus_response_df\n", + " if 'response_window_duration' in df.keys():\n", + " response_window_duration = df.response_window_duration.values[0]\n", + " output_sampling_rate = df.frame_rate.unique()[0]\n", + " timestamps = df.trace_timestamps.values[0]\n", + " window_around_timepoint_seconds = [timestamps[0], timestamps[-1]]\n", + " # get trial averaged response for conditions\n", + " mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate,\n", + " time_window=time_window,\n", + " response_window_duration=response_window_duration)\n", + " # add experiment ID to be able to distinguish different sessions\n", + " mdf['ophys_experiment_id'] = experiment_id\n", + " # aggregate\n", + " big_mdf = pd.concat([big_mdf, mdf])\n", + " except Exception as e: \n", + " print(e)\n", + " print('problem for', experiment_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['cell_specimen_id', 'image_name', 'mean_response', 'sem_response',\n", + " 'mean_trace', 'sem_trace', 'trace_timestamps', 'mean_responses',\n", + " 'mean_baseline', 'sem_baseline', 'response_window_duration',\n", + " 'pref_stim', 'fano_factor', 'peak_response', 'time_to_peak', 'p_value',\n", + " 'sd_over_baseline', 'fraction_significant_p_value_gray_screen',\n", + " 'reliability', 'correlation_values', 'ophys_experiment_id'],\n", + " dtype='object')" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "big_mdf.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "608" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(big_mdf)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "608" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "big_mdf = big_mdf.merge(experiments_table, on='ophys_experiment_id')\n", + "len(big_mdf)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### plot population average response for each image_name within each session_type" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "def condense_session_types(df):\n", + " df = df.copy()\n", + " df = df.reset_index()\n", + " for row in range(len(df)):\n", + " session_type = df.iloc[row].session_type\n", + " # all of Training 4 and 5 are basically the same\n", + " if session_type in ['TRAINING_4_images_A_training', 'TRAINING_5_images_A_epilogue', 'TRAINING_5_images_A_handoff_ready']:\n", + " df.at[row, 'session_type'] = 'TRAINING_4_images_A' \n", + " # for the first training session with images, remove the '10uL_reward bit'\n", + " elif session_type == 'TRAINING_3_images_A_10uL_reward':\n", + " df.at[row, 'session_type'] = 'TRAINING_3_images_A' \n", + " # otherwise, keep the same name\n", + " else: \n", + " pass\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [], + "source": [ + "big_mdf = condense_session_types(big_mdf)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "import visual_behavior.visualization.ophys.platform_paper_figures as ppf" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ],\n", + " dtype=object)" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "axes_column = 'session_type'\n", + "hue_column = 'image_name'\n", + "palette = sns.color_palette()\n", + "\n", + "ppf.plot_population_averages_for_conditions(big_mdf, data_type, event_type, axes_column, hue_column, palette=palette, \n", + " suptitle=None, horizontal=True, save_dir=None, folder=None)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Aggregate trial averaged responses accross all sessions and all imaging planes for a given mouse" + ] + }, + { + "cell_type": "code", + "execution_count": 215, + "metadata": {}, + "outputs": [], + "source": [ + "from visual_behavior.ophys.io.create_multi_session_df import get_multi_session_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This function will run the steps above iteratively for all sessions for the provided mouse_id and project code:\n", + " * load dataset for each experiment\n", + " * generate stimulus_response_df to get stimulus aligned traces\n", + " * compute mean response dataframe averaged over the provided set of conditions\n", + " * concatenate with all other sessions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NOTE: this takes tens of minutes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is code to do this on the cluster in `visual_behavior_analysis.scripts.run_create_multi_session_df.py`" + ] + }, + { + "cell_type": "code", + "execution_count": 216, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "get_pref_stim True\n", + "getting up-to-date experiment_table from lims\n", + "including failed data\n", + "excluding Ai94 data\n", + "616 expts in experiments table\n", + "creating multi session mean df for mean_response_df_dff_changes_LearningmFISHTask1A_603892_image_name.h5\n", + "1153662776\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662776\n", + "1153662777\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662777\n", + "1153662768\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1153529245\\1153529245_20220124T105312.h5\n", + "Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1153662768 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1153662768\n", + "1153662770\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662770\n", + "1153662771\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1153529245\\1153529245_20220124T105312.h5\n", + "Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1153662771 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1153662771\n", + "1153662774\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1153529245\\1153529245_20220124T105312.h5\n", + "Error! The number of sync file frame times (220581) does not match the number of eye tracking frames (220582)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1153662774 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1153662774\n", + "1153662779\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662779\n", + "1153662773\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39108).\n", + "problem for 1153662773\n", + "1154288458\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288458\n", + "1154288461\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288461\n", + "1154288463\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288463\n", + "1154288465\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unable to open file (unable to open file: name = '\\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1154000392\\ophys_experiment_1154288465\\demix\\1154288465_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)\n", + "problem for 1154288465\n", + "1154288467\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288467\n", + "1154288470\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dff_frames (len=10000) is not equal to number of split timestamps (len=39171).\n", + "problem for 1154288470\n", + "1154288472\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unable to open file (unable to open file: name = '\\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1154000392\\ophys_experiment_1154288472\\demix\\1154288472_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)\n", + "problem for 1154288472\n", + "1154288475\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\util.py:63: UserWarning: Monitory delay calculation failed with ValueError\n", + " \"operands could not be broadcast together with shapes (3131,) (3644,) \"\n", + "looking monitor delay up from table for rig: MESO.2 \n", + "delay: 0.03613 seconds\n", + " warnings.warn(warning_msg)\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unable to open file (unable to open file: name = '\\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1154000392\\ophys_experiment_1154288475\\demix\\1154288475_demixed_traces.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)\n", + "problem for 1154288475\n", + "1156776068\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\running_speed\\running_processing.py:368: UserWarning: Time array is 1 value shorter than encoder array. Last encoder value removed\n", + "\n", + " \"value removed\\n\", UserWarning, stacklevel=1)\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1156776068 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1156776068\n", + "1156776070\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1156776070 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1156776070\n", + "1156776071\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1156776071 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1156776071\n", + "1156776073\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1156776073 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1156776073\n", + "1156776074\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\metadata\\subject_metadata\\reporter_line.py:111: UserWarning: Could not parse indicator from reporter because noneof the expected substrings were found in the reporter\n", + " 'Could not parse indicator from reporter because none'\n", + "C:\\Users\\marinag\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\eye_tracking\\eye_tracking_table.py:233: UserWarning: \n", + "in sync_file: \\\\allen\\programs\\mindscope\\production\\learning\\prod0\\specimen_1142290487\\ophys_session_1156589145\\1156589145_20220207T11326.h5\n", + "Error! The number of sync file frame times (272758) does not match the number of eye tracking frames (272759)!\n", + "returning empty eye_tracking DataFrame\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "file exists:\n", + "loading response df from file for 1156776074 dff changes\n", + "get_mean_df() got an unexpected keyword argument 'window_around_timepoint_seconds'\n", + "problem for 1156776074\n", + "1156776076\n", + "loading from lims, exclude_invalid_rois = True\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n", + "WARNING:root:Could not find valid lines for the following data sources\n", + "WARNING:root:2p (valid line label(s) = ['2p_vsync']\n", + "WARNING:root:acquiring (valid line label(s) = ['2p_acquiring', 'acq_trigger']\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m multi_session_df = get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type,\n\u001b[1;32m----> 7\u001b[1;33m time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5)\n\u001b[0m", + "\u001b[1;32mc:\\users\\marinag\\documents\\code\\visual_behavior_analysis\\visual_behavior\\ophys\\io\\create_multi_session_df.py\u001b[0m in \u001b[0;36mget_multi_session_df\u001b[1;34m(project_code, mouse_id, conditions, data_type, event_type, time_window, interpolate, output_sampling_rate, response_window_duration, use_extended_stimulus_presentations, overwrite)\u001b[0m\n\u001b[0;32m 96\u001b[0m \u001b[1;31m# get dataset\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 97\u001b[0m dataset = loading.get_ophys_dataset(experiment_id,\n\u001b[1;32m---> 98\u001b[1;33m get_extended_stimulus_presentations=use_extended_stimulus_presentations)\n\u001b[0m\u001b[0;32m 99\u001b[0m \u001b[1;31m# get stimulus_response_df\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 100\u001b[0m df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window,\n", + "\u001b[1;32mc:\\users\\marinag\\documents\\code\\visual_behavior_analysis\\visual_behavior\\data_access\\loading.py\u001b[0m in \u001b[0;36mget_ophys_dataset\u001b[1;34m(ophys_experiment_id, exclude_invalid_rois, load_from_lims, load_from_nwb, get_extended_stimulus_presentations, get_behavior_movie_timestamps)\u001b[0m\n\u001b[0;32m 818\u001b[0m \u001b[0mcache\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mbpc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfrom_lims\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 819\u001b[0m \u001b[1;31m# dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id))\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 820\u001b[1;33m \u001b[0mdataset\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mBehaviorOphysExperiment\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfrom_lims\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mophys_experiment_id\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mexclude_invalid_rois\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mexclude_invalid_rois\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 821\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0mload_from_nwb\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 822\u001b[0m \u001b[0mcache_dir\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mget_platform_analysis_cache_dir\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\behavior_ophys_experiment.py\u001b[0m in \u001b[0;36mfrom_lims\u001b[1;34m(cls, ophys_experiment_id, eye_tracking_z_threshold, eye_tracking_dilation_frames, events_filter_scale_seconds, events_filter_n_time_steps, exclude_invalid_rois, skip_eye_tracking)\u001b[0m\n\u001b[0;32m 188\u001b[0m \u001b[0mstimulus_timestamps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstimulus_timestamps\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 189\u001b[0m \u001b[0mmonitor_delay\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mmonitor_delay\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 190\u001b[1;33m \u001b[0mdate_of_acquisition\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mdate_of_acquisition\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 191\u001b[0m )\n\u001b[0;32m 192\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mis_multiplane_session\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\behavior_session.py\u001b[0m in \u001b[0;36mfrom_lims\u001b[1;34m(cls, behavior_session_id, lims_db, stimulus_timestamps, monitor_delay, date_of_acquisition)\u001b[0m\n\u001b[0;32m 203\u001b[0m cls._read_data_from_stimulus_file(\n\u001b[0;32m 204\u001b[0m \u001b[0mstimulus_file\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstimulus_file\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 205\u001b[1;33m \u001b[0mstimulus_timestamps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstimulus_timestamps\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 206\u001b[0m )\n\u001b[0;32m 207\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mdate_of_acquisition\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\behavior_session.py\u001b[0m in \u001b[0;36m_read_data_from_stimulus_file\u001b[1;34m(cls, stimulus_file, stimulus_timestamps)\u001b[0m\n\u001b[0;32m 892\u001b[0m stimuli = Stimuli.from_stimulus_file(\n\u001b[0;32m 893\u001b[0m \u001b[0mstimulus_file\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstimulus_file\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 894\u001b[1;33m stimulus_timestamps=stimulus_timestamps)\n\u001b[0m\u001b[0;32m 895\u001b[0m task_parameters = TaskParameters.from_stimulus_file(\n\u001b[0;32m 896\u001b[0m stimulus_file=stimulus_file)\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\stimuli.py\u001b[0m in \u001b[0;36mfrom_stimulus_file\u001b[1;34m(cls, stimulus_file, stimulus_timestamps, limit_to_images)\u001b[0m\n\u001b[0;32m 52\u001b[0m limit_to_images=limit_to_images)\n\u001b[0;32m 53\u001b[0m t = Templates.from_stimulus_file(stimulus_file=stimulus_file,\n\u001b[1;32m---> 54\u001b[1;33m limit_to_images=limit_to_images)\n\u001b[0m\u001b[0;32m 55\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mStimuli\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpresentations\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mp\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtemplates\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 56\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\templates.py\u001b[0m in \u001b[0;36mfrom_stimulus_file\u001b[1;34m(cls, stimulus_file, limit_to_images)\u001b[0m\n\u001b[0;32m 85\u001b[0m t = get_stimulus_templates(pkl=pkl,\n\u001b[0;32m 86\u001b[0m \u001b[0mgrating_images_dict\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mgrating_images_dict\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 87\u001b[1;33m limit_to_images=limit_to_images)\n\u001b[0m\u001b[0;32m 88\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mTemplates\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtemplates\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 89\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\stimulus_processing.py\u001b[0m in \u001b[0;36mget_stimulus_templates\u001b[1;34m(pkl, grating_images_dict, limit_to_images)\u001b[0m\n\u001b[0;32m 217\u001b[0m \u001b[0mimage_set_name\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mimage_set_name\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 218\u001b[0m \u001b[0mimage_attributes\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mattrs\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 219\u001b[1;33m \u001b[0mimages\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mimage_values\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 220\u001b[0m )\n\u001b[0;32m 221\u001b[0m \u001b[1;32melif\u001b[0m \u001b[1;34m'grating'\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mpkl_stimuli\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\stimulus_templates.py\u001b[0m in \u001b[0;36mfrom_unprocessed\u001b[1;34m(image_set_name, image_attributes, images)\u001b[0m\n\u001b[0;32m 238\u001b[0m \u001b[0mname\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mimage_attributes\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'image_name'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 239\u001b[0m stimulus_image = StimulusImageFactory().from_unprocessed(\n\u001b[1;32m--> 240\u001b[1;33m name=name, input_array=image)\n\u001b[0m\u001b[0;32m 241\u001b[0m \u001b[0mstimulus_images\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstimulus_image\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 242\u001b[0m return StimulusTemplate(image_set_name=image_set_name,\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\stimulus_templates.py\u001b[0m in \u001b[0;36mfrom_unprocessed\u001b[1;34m(self, input_array, name)\u001b[0m\n\u001b[0;32m 40\u001b[0m \"\"\"Creates a StimulusImage from unprocessed input (usually pkl).\n\u001b[0;32m 41\u001b[0m Image needs to be warped and preprocessed\"\"\"\n\u001b[1;32m---> 42\u001b[1;33m \u001b[0mresized\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0munwarped\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_unwarped\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0minput_array\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 43\u001b[0m \u001b[0mwarped\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_warped\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mresized\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 44\u001b[0m \u001b[0mimage\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mStimulusImage\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwarped\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mwarped\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0munwarped\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0munwarped\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\stimulus_templates.py\u001b[0m in \u001b[0;36m_get_unwarped\u001b[1;34m(self, arr)\u001b[0m\n\u001b[0;32m 66\u001b[0m arr, origin='upper')\n\u001b[0;32m 67\u001b[0m \u001b[1;31m# 2. Remove unseen pixels\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 68\u001b[1;33m \u001b[0marr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_exclude_unseen_pixels\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mresized_array\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 69\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 70\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mresized_array\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0marr\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\behavior\\data_objects\\stimuli\\stimulus_templates.py\u001b[0m in \u001b[0;36m_exclude_unseen_pixels\u001b[1;34m(self, arr)\u001b[0m\n\u001b[0;32m 73\u001b[0m \"\"\"After warping, some pixels are not visible on the screen.\n\u001b[0;32m 74\u001b[0m This sets those pixels to nan to make downstream analysis easier.\"\"\"\n\u001b[1;32m---> 75\u001b[1;33m \u001b[0mmask\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_monitor\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_mask\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 76\u001b[0m \u001b[0marr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0marr\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mastype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfloat\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 77\u001b[0m \u001b[0marr\u001b[0m \u001b[1;33m*=\u001b[0m \u001b[0mmask\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\stimulus_info.py\u001b[0m in \u001b[0;36mget_mask\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 588\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mget_mask\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 589\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 590\u001b[1;33m \u001b[0mmask\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmake_display_mask\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdisplay_shape\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mn_pixels_c\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mn_pixels_r\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mT\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 591\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mmask\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mn_pixels_r\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 592\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mmask\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mn_pixels_c\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32m~\\Documents\\Code\\AllenSDK\\allensdk\\brain_observatory\\stimulus_info.py\u001b[0m in \u001b[0;36mmake_display_mask\u001b[1;34m(display_shape)\u001b[0m\n\u001b[0;32m 822\u001b[0m \u001b[0mused_coords\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mset\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 823\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0moff_warped_coords\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 824\u001b[1;33m \u001b[0mused_coords\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0moff_warped_coords\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mi\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moff_warped_coords\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mi\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 825\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 826\u001b[0m used_coords = (np.array([x for (x, y) in used_coords]).astype(int),\n", + "\u001b[1;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "project_code = 'LearningmFISHTask1A'\n", + "conditions = ['cell_specimen_id', 'image_name']\n", + "data_type = 'dff'\n", + "event_type = 'changes'\n", + "\n", + "multi_session_df = get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type,\n", + " time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "visual_behavior_sdk", + "language": "python", + "name": "visual_behavior_sdk" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 5aa3524daa2ef30c836476dc7f0adcf329caa695 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 13 Jun 2022 18:35:08 -0700 Subject: [PATCH 073/187] put notebooks in correct location --- .../220606_trial_averaged_stimulus_response_dfs.html | 0 .../220606_trial_averaged_stimulus_response_dfs.ipynb | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename 220606_trial_averaged_stimulus_response_dfs.html => notebooks/220606_trial_averaged_stimulus_response_dfs.html (100%) rename 220606_trial_averaged_stimulus_response_dfs.ipynb => notebooks/220606_trial_averaged_stimulus_response_dfs.ipynb (100%) diff --git a/220606_trial_averaged_stimulus_response_dfs.html b/notebooks/220606_trial_averaged_stimulus_response_dfs.html similarity index 100% rename from 220606_trial_averaged_stimulus_response_dfs.html rename to notebooks/220606_trial_averaged_stimulus_response_dfs.html diff --git a/220606_trial_averaged_stimulus_response_dfs.ipynb b/notebooks/220606_trial_averaged_stimulus_response_dfs.ipynb similarity index 100% rename from 220606_trial_averaged_stimulus_response_dfs.ipynb rename to notebooks/220606_trial_averaged_stimulus_response_dfs.ipynb From d61c141837586fe41ab4ca76069a31e3f9d2f891 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 15:45:04 -0700 Subject: [PATCH 074/187] update stim response df loading in single cell plots --- visual_behavior/data_access/utilities.py | 14 ++++++++++++++ .../visualization/qc/single_cell_plots.py | 18 +++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 1e46b990a..1203446b9 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1793,6 +1793,20 @@ def count_mice_expts_containers(df): return counts +def replace_cell_specimen_id_with_cell_roi_id(df): + """ + in cases where cell matching has not been run or has failed, the cell_specimen_id of all ROIs will be NaN + This function takes in any dataframe with cell_specimen_id as the index, and cell_roi_id as a column, + and replaces the cell_specimen_id with the cell_roi_id so that downstream functions that use the cell_specimen_id dont fail due to NaNs + works with SDK attribute dataframes such as dff_traces and cell_specimen_table + """ + if df.index.values[0] is None: + df['cell_specimen_id'] = df.cell_roi_id.values + df = df.set_index('cell_specimen_id') + else: + print('the index values of this df are not NaN, this function is not needed') + return df + def get_behavior_session_ids_to_analyze(): """ Gets a list of behavior_session_ids by combining the behavior_session_ids present in the platform paper experiments table diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 8ef848835..33a832897 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -17,6 +17,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even Compares across all sessions in a container for each cell, including the ROI mask across days. Useful to validate cell matching as well as examine changes in activity profiles over days. """ + import visual_behavior.data_access.utilities as utilities experiments_table = data_loading.get_filtered_ophys_experiment_table(release_data_only=True) container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id] expts = np.sort(container_expts.index.values) @@ -35,19 +36,14 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even for i, ophys_experiment_id in enumerate(expts): print('ophys_experiment_id:', ophys_experiment_id) try: - dataset = data_loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) + # dff_traces = dataset.dff_traces.copy() + # dff_traces = utilities.replace_cell_specimen_id_with_cell_roi_id(dff_traces) if cell_specimen_id in dataset.dff_traces.index: - analysis = ResponseAnalysis(dataset, use_events=use_events, use_extended_stimulus_presentations=False) - sdf = ut.get_mean_df(analysis.get_response_df(df_name='stimulus_response_df'), analysis=analysis, - conditions=['cell_specimen_id', 'is_change', 'image_name'], flashes=True, omitted=False, - get_reliability=False, get_pref_stim=True, exclude_omitted_from_pref_stim=True) - odf = ut.get_mean_df(analysis.get_response_df(df_name='omission_response_df'), analysis=analysis, - conditions=['cell_specimen_id'], flashes=False, omitted=True, - get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=False) - tdf = ut.get_mean_df(analysis.get_response_df(df_name='trials_response_df'), analysis=analysis, - conditions=['cell_specimen_id', 'go', 'hit', 'change_image_name'], flashes=False, omitted=False, - get_reliability=False, get_pref_stim=True, exclude_omitted_from_pref_stim=True) + sdf = loading.get_stimulus_response_df(dataset, data_type='dff', event_type='all') + sdf = ut.get_mean_df(sdf, conditions=['cell_roi_id', 'is_change', 'image_name']) + odf = ut.get_mean_df(sdf[sdf.omitted==True], conditions=['cell_specimen_id']) + tdf = ut.get_mean_df(sdf[sdf.is_change==True], conditions=['cell_specimen_id', 'go', 'hit', 'change_image_name']) ct = dataset.cell_specimen_table.copy() cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id From 2e2123f6677ae377533ebf737970de89056b1c18 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 15:46:02 -0700 Subject: [PATCH 075/187] dont invert masks in plot cell zoom --- visual_behavior/visualization/ophys/summary_figures.py | 2 +- visual_behavior/visualization/qc/single_cell_plots.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index f5a11e88e..228442118 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -53,7 +53,7 @@ def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, if show_mask: ax.imshow(mask, cmap='jet', alpha=0.3, vmin=0, vmax=1) ax.set_xlim(xmin - spacex, xmax + spacex) - ax.set_ylim(ymin - spacey, ymax + spacey) + ax.set_ylim(ymax + spacey, ymin - spacey) ax.set_title('cell_roi_id ' + str(cell_roi_id)) ax.grid(False) ax.axis('off') diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 33a832897..dfce96912 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -37,8 +37,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even print('ophys_experiment_id:', ophys_experiment_id) try: dataset = data_loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) - # dff_traces = dataset.dff_traces.copy() - # dff_traces = utilities.replace_cell_specimen_id_with_cell_roi_id(dff_traces) + if cell_specimen_id in dataset.dff_traces.index: sdf = loading.get_stimulus_response_df(dataset, data_type='dff', event_type='all') sdf = ut.get_mean_df(sdf, conditions=['cell_roi_id', 'is_change', 'image_name']) From 3dc563683755a0fd6d16c15892cad0ecdcf105e5 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 15:56:39 -0700 Subject: [PATCH 076/187] more updates to stim response df plotting --- .../visualization/qc/single_cell_plots.py | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index dfce96912..cee96a5af 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -9,6 +9,8 @@ from visual_behavior.ophys.response_analysis.response_analysis import ResponseAnalysis import visual_behavior.ophys.response_analysis.response_processing as rp import visual_behavior.ophys.response_analysis.utilities as ut +import visual_behavior.visualization.utils as utils + def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_events=False, save_figure=True): @@ -27,6 +29,9 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even else: ylabel = 'dF/F' suffix = '' + window = [-0.5, 1.5] + interpolate = True + output_sampling_rate = 30 n = len(expts) figsize = (25, 20) @@ -39,10 +44,12 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even dataset = data_loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) if cell_specimen_id in dataset.dff_traces.index: - sdf = loading.get_stimulus_response_df(dataset, data_type='dff', event_type='all') - sdf = ut.get_mean_df(sdf, conditions=['cell_roi_id', 'is_change', 'image_name']) + sdf = loading.get_stimulus_response_df(dataset, data_type='dff', event_type='all', + time_window=window, interpolate=interpolate, output_sampling_rate=output_sampling_rate) + cdf = ut.get_mean_df(sdf, conditions=['cell_specimen_id', 'is_change', 'image_name']) odf = ut.get_mean_df(sdf[sdf.omitted==True], conditions=['cell_specimen_id']) tdf = ut.get_mean_df(sdf[sdf.is_change==True], conditions=['cell_specimen_id', 'go', 'hit', 'change_image_name']) + timestamps = sdf.trace_timestamps.values[0] ct = dataset.cell_specimen_table.copy() cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id @@ -52,55 +59,59 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even colors = sns.color_palette('hls', 8) + [(0.5, 0.5, 0.5)] - window = rp.get_default_stimulus_response_params()["window_around_timepoint_seconds"] - cell_data = sdf[(sdf.cell_specimen_id == cell_specimen_id) & (sdf.is_change == False)] + # average image response each image + cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == False)] for c, image_name in enumerate(np.sort(cell_data.image_name.unique())): - ax[i + n] = sf.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], - frame_rate=analysis.ophys_frame_rate, ylabel=ylabel, + ax[i + n] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], + frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=image_name, color=colors[c], interval_sec=0.5, xlims=window, ax=ax[i + n]) - ax[i + n] = sf.plot_flashes_on_trace(ax[i + n], analysis, window=window, trial_type=None, omitted=False, alpha=0.15, facecolor='gray') + ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') ax[i + n].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n image response') - analysis = ResponseAnalysis(dataset, use_events=False, use_extended_stimulus_presentations=True) - tmp = analysis.get_response_df(df_name='stimulus_response_df') - tmp['running'] = [True if run_speed > 2 else False for run_speed in tmp.mean_running_speed.values] - sdf = ut.get_mean_df(tmp, analysis=analysis, - conditions=['cell_specimen_id', 'is_change', 'image_name', 'running'], flashes=True, omitted=False, - get_reliability=False, get_pref_stim=True, exclude_omitted_from_pref_stim=False) - - cell_data = sdf[(sdf.cell_specimen_id == cell_specimen_id) & (sdf.is_change == False) & (sdf.pref_stim == True)] - run_colors = [sns.color_palette()[3], sns.color_palette()[2]] - for c, running in enumerate(np.sort(cell_data.running.unique())): - if len(cell_data[cell_data.running == running]) > 0: - ax[i + (n * 2)] = sf.plot_mean_trace_from_mean_df(cell_data[cell_data.running == running], - frame_rate=analysis.ophys_frame_rate, ylabel=ylabel, - legend_label=running, color=run_colors[c], interval_sec=0.5, - xlims=window, ax=ax[i + (n * 2)]) - ax[i + (n * 2)].legend(fontsize='xx-small', title='running', title_fontsize='xx-small') - ax[i + (n * 2)] = sf.plot_flashes_on_trace(ax[i + (n * 2)], analysis, window=window, trial_type=None, omitted=False, alpha=0.15, facecolor='gray') - ax[i + (n * 2)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n image response') - - window = rp.get_default_omission_response_params()["window_around_timepoint_seconds"] + # running vs not-running + try: + tmp = sdf.cpoy() + tmp['running'] = [True if run_speed > 2 else False for run_speed in tmp.mean_running_speed.values] + sdf = ut.get_mean_df(tmp, analysis=analysis, + conditions=['cell_specimen_id', 'is_change', 'image_name', 'running'], flashes=True, omitted=False, + get_pref_stim=True, exclude_omitted_from_pref_stim=False) + + cell_data = sdf[(sdf.cell_specimen_id == cell_specimen_id) & (sdf.is_change == False) & (sdf.pref_stim == True)] + run_colors = [sns.color_palette()[3], sns.color_palette()[2]] + for c, running in enumerate(np.sort(cell_data.running.unique())): + if len(cell_data[cell_data.running == running]) > 0: + ax[i + (n * 2)] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.running == running], + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=running, color=run_colors[c], interval_sec=0.5, + xlims=window, ax=ax[i + (n * 2)]) + ax[i + (n * 2)].legend(fontsize='xx-small', title='running', title_fontsize='xx-small') + ax[i + (n * 2)] = utils.plot_flashes_on_trace(ax[i + (n * 2)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') + ax[i + (n * 2)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n image response') + except: + print('couldnt plot running / not-running panel') + + # omissions cell_data = odf[(odf.cell_specimen_id == cell_specimen_id)] - ax[i + (n * 3)] = sf.plot_mean_trace_from_mean_df(cell_data, - frame_rate=analysis.ophys_frame_rate, ylabel=ylabel, + ax[i + (n * 3)] = utils.plot_mean_trace_from_mean_df(cell_data, + frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=image_name, color='gray', interval_sec=1, xlims=window, ax=ax[i + (n * 3)]) - ax[i + (n * 3)] = sf.plot_flashes_on_trace(ax[i + (n * 3)], analysis, window=window, trial_type=None, omitted=True, alpha=0.15, facecolor='gray') + ax[i + (n * 3)] = utils.plot_flashes_on_trace(ax[i + (n * 3)], timstamps, change=False, omitted=True, alpha=0.15, facecolor='gray') ax[i + (n * 3)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n omission response') - window = rp.get_default_trial_response_params()["window_around_timepoint_seconds"] + + # hit miss cell_data = tdf[(tdf.cell_specimen_id == cell_specimen_id) & (tdf.go == True) & (tdf.pref_stim == True)] hit_colors = [sns.color_palette()[2], sns.color_palette()[3]] for c, hit in enumerate([True, False]): if len(cell_data[cell_data.hit == hit]) > 0: - ax[i + (n * 4)] = sf.plot_mean_trace_from_mean_df(cell_data[cell_data.hit == hit], - frame_rate=analysis.ophys_frame_rate, ylabel=ylabel, + ax[i + (n * 4)] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.hit == hit], + frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=hit, color=hit_colors[c], interval_sec=1, xlims=window, ax=ax[i + (n * 4)]) ax[i + (n * 4)].legend(fontsize='xx-small', title='hit', title_fontsize='xx-small') - ax[i + (n * 4)] = sf.plot_flashes_on_trace(ax[i + (n * 4)], analysis, window=window, trial_type='go', omitted=False, alpha=0.15, facecolor='gray') + ax[i + (n * 4)] = utils.plot_flashes_on_trace(ax[i + (n * 4)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') ax[i + (n * 4)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n change response') fig.tight_layout() From f6f1f28ec496fd736188c3858ce9ba7f49d83771 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 16:03:02 -0700 Subject: [PATCH 077/187] update cell zoom fig --- .../visualization/ophys/summary_figures.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index 228442118..788402db3 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -33,11 +33,20 @@ def plot_max_projection_image(dataset, save_dir=None, folder='max_projection'): save_figure(fig, figsize, save_dir, folder, str(dataset.experiment_id)) -def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, show_mask=False, ax=None): - # if isinstance(list(roi_mask_dict.keys())[0], str): - # m = roi_mask_dict[str(cell_specimen_id)] - # else: - # m = roi_mask_dict[int(cell_specimen_id)] +def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, show_mask=False, full_image=False, ax=None): + """ + Plot roi mask image, with or without max projection, either in full image or in a zoomed in portion of image + + :param roi_masks: dataframe with columns 'cell_roi_id', and 'roi_mask'; typically cell_specimen_table attribute of SDK dataset + :param max_projection: max intensity projection image, typically from 'max_projection' attribute of SDK dataset + :param cell_roi_id: cell ROI ID of cell to plot + :param spacex: how much space in x you want to show around the provided OI + :param spacey: how much space in y you want to show around the provided OI + :param show_mask: Boolean, whether or not to plot the ROI mask over the max projection + :param full_image: if True, dont use spacex and y to zoom in, just show the full max image + :param ax: + :return: + """ m = roi_masks[roi_masks.cell_roi_id == cell_roi_id].roi_mask.values[0] (y, x) = np.where(m == 1) xmin = np.min(x) @@ -52,8 +61,9 @@ def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection) / 2.) if show_mask: ax.imshow(mask, cmap='jet', alpha=0.3, vmin=0, vmax=1) - ax.set_xlim(xmin - spacex, xmax + spacex) - ax.set_ylim(ymax + spacey, ymin - spacey) + if not full_image: + ax.set_xlim(xmin - spacex, xmax + spacex) + ax.set_ylim(ymax + spacey, ymin - spacey) ax.set_title('cell_roi_id ' + str(cell_roi_id)) ax.grid(False) ax.axis('off') From d9bcc441c4676c211a563652359314877066bed2 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 16:46:20 -0700 Subject: [PATCH 078/187] add alpha to cell zoom plot --- visual_behavior/visualization/ophys/summary_figures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index 788402db3..0eeedc6be 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -33,7 +33,7 @@ def plot_max_projection_image(dataset, save_dir=None, folder='max_projection'): save_figure(fig, figsize, save_dir, folder, str(dataset.experiment_id)) -def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, show_mask=False, full_image=False, ax=None): +def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, show_mask=False, alpha=0.3, full_image=False, ax=None): """ Plot roi mask image, with or without max projection, either in full image or in a zoomed in portion of image @@ -60,7 +60,7 @@ def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, fig, ax = plt.subplots() ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection) / 2.) if show_mask: - ax.imshow(mask, cmap='jet', alpha=0.3, vmin=0, vmax=1) + ax.imshow(mask, cmap='jet', alpha=alpha, vmin=0, vmax=1) if not full_image: ax.set_xlim(xmin - spacex, xmax + spacex) ax.set_ylim(ymax + spacey, ymin - spacey) From 5e2c919cfbdd295f06a33611c7ba6a33d358b615 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 16:46:54 -0700 Subject: [PATCH 079/187] revise QC plotting to save per expt in addn to per container --- .../visualization/qc/container_plots.py | 82 +++--- .../visualization/qc/experiment_plots.py | 238 ++++++++++++++---- .../qc/save_all_container_plots.py | 1 + .../visualization/qc/single_cell_plots.py | 35 ++- 4 files changed, 272 insertions(+), 84 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 2b7921af1..7d54a217b 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -29,7 +29,33 @@ def ax_to_array(ax): ax = np.array([ax]) return ax -# Container sequence + +def get_metadata_string(ophys_container_id): + """ + Create a string of metadata information to be used in filenames and figure titles. + Includes information such as experiment_id, cre_line, acquisition_date, rig_id, etc + :param ophys_container_id: + :return: + """ + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) + dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) + m = dataset.metadata.copy() + metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + return metadata_string + + +def get_file_name_for_container(ophys_container_id): + """ + gets standardized filename for saving figures + format "container_id_"+str(ophys_container_id) is necessary for files to be able to be viewed in Dougs QC viewer + using get_metadata_string(ophys_container_id) gives a more interpretable filename with cre line, area, etc + :param ophys_container_id: + :return: + """ + # filename = "container_id_"+str(ophys_container_id) + filename = get_metadata_string(ophys_container_id) + return filename def plot_container_session_sequence(ophys_container_id, save_figure=True): @@ -106,6 +132,8 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) + # plot and save to experiment_plots dir when ax=None + ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -141,6 +169,7 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=ax[i]) + ep.plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -176,6 +205,7 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + ep.plot_average_image_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -211,6 +241,7 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=ax[i]) + ep.plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -261,6 +292,7 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=ax[i]) + ep.plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -317,7 +349,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur if save_figure: ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'segmentation_mask_overlays', get_file_name_for_container(ophys_container_id)) - ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', + ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', get_file_name_for_container(ophys_container_id)+'_segmentation_mask_overlays') @@ -428,6 +460,7 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: ax[i] = ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=ax[i]) + ep.plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=None) except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -460,6 +493,7 @@ def plot_average_intensity_timeseries_for_container(ophys_container_id, save_fig for i, ophys_experiment_id in enumerate(container_df["ophys_experiment_id"].unique()): try: ax = ep.plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=ax) + ep.plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=None) except: pass ax.legend(exp_order_and_stage["stage_name_lims"], fontsize='xx-small', title='stage name', title_fontsize='xx-small', @@ -961,34 +995,6 @@ def plot_flashes_on_trace(ax, timestamps, trial_type=None, omitted=False, alpha= return ax -def get_metadata_string(ophys_container_id): - """ - Create a string of metadata information to be used in filenames and figure titles. - Includes information such as experiment_id, cre_line, acquisition_date, rig_id, etc - :param ophys_container_id: - :return: - """ - experiments_table = loading.get_filtered_ophys_experiment_table() - ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) - m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] - return metadata_string - - -def get_file_name_for_container(ophys_container_id): - """ - gets standardized filename for saving figures - format "container_id_"+str(ophys_container_id) is necessary for files to be able to be viewed in Dougs QC viewer - using get_metadata_string(ophys_container_id) gives a more interpretable filename with cre line, area, etc - :param ophys_container_id: - :return: - """ - # filename = "container_id_"+str(ophys_container_id) - filename = get_metadata_string(ophys_container_id) - return filename - - def plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='all', save_figure=True): """ Plots population average response across all sessions within a container @@ -1381,6 +1387,22 @@ def plot_dff_trace_and_behavior_for_container(ophys_container_id, save_figure=Tr pass +def plot_cell_rois_and_dff_traces_for_container(ophys_container_id, save_figure=True): + """ + Plots the full and zoomed in cell ROI masks and dFF traces for each cell in each experiment in the container + Useful to visualize how the masks relate to the traces and whether output looks reasonable + """ + experiments_table = loading.get_filtered_ophys_experiment_table() + ophys_experiments = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by='date_of_acquisition') + ophys_experiment_ids = ophys_experiments.index.values + + for ophys_experiment_id in ophys_experiment_ids: + try: + ep.plot_cell_roi_masks_and_dff_traces_for_experiment(ophys_experiment_id, save_figure=save_figure) + except: + pass + + def plot_classifier_validation_for_container(ophys_container_id, save_figure=True): """ Creates a plot showing ROI masks matched between production and development versions of segmentation classifier. diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index c79c2c1f8..2d243396e 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -21,59 +21,124 @@ bitdepth_16 = 65536 +def get_metadata_string(ophys_experiment_id): + """ + Create a string of metadata information to be used in filenames and figure titles. + Includes information such as experiment_id, cre_line, acquisition_date, rig_id, etc + :param ophys_experiment_id: + :return: + """ + dataset = loading.get_ophys_dataset(ophys_experiment_id) + m = dataset.metadata.copy() + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + return metadata_string + + +def get_file_name_for_experiment(ophys_experiment_id): + """ + gets standardized filename for saving figures + format "experiment_id_"+str(ophys_experiment_id) is necessary for files to be able to be viewed in Dougs QC viewer + using get_metadata_string(ophys_experiment_id) gives a more interpretable filename with cre line, area, etc + :param ophys_experiment_id: + :return: + """ + # filename = 'experiment_id'+str(ophys_experiment_id) + filename = get_metadata_string(ophys_experiment_id) + return filename + def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False dataset = loading.get_ophys_dataset(ophys_experiment_id) max_projection = dataset.max_projection.data ax.imshow(max_projection, cmap='gray', vmax=np.percentile(max_projection, 99)) ax.set_title(str(ophys_experiment_id)) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_average_image_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False dataset = loading.get_ophys_dataset(ophys_experiment_id) average_projection = dataset.average_projection.data ax.imshow(average_projection, cmap='gray', vmax=np.amax(average_projection)) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_image', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False average_image = processing.experiment_average_FOV_from_motion_corrected_movie(ophys_experiment_id) ax.imshow(average_image, cmap='gray', vmin=0, vmax=8000) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_image_movie', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False max_image = processing.experiment_max_FOV_from_motion_corrected_movie(ophys_experiment_id) ax.imshow(max_image, cmap='gray', vmin=0, vmax=8000) ax.set_title(str(ophys_experiment_id)) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_image_movie', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_segmentation_mask_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False + dataset = loading.get_ophys_dataset(ophys_experiment_id) segmentation_mask = dataset.segmentation_mask_image # i am not sure if this is correct, check relevant SDK issue to see what they did ax.imshow(segmentation_mask, cmap='gray', vmin=0, vmax=1) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_image', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) try: dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) @@ -85,12 +150,19 @@ def plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax= except BaseException: pass ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_all_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) dataset = loading.get_ophys_dataset(ophys_experiment_id) segmentation_mask = dataset.segmentation_mask_image # i am not sure if this is correct, check relevant SDK issue to see what they did @@ -99,12 +171,19 @@ def plot_all_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=No mask[segmentation_mask[0] == 1] = 1 ax.imshow(mask, cmap='hsv', vmax=1, alpha=0.5) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay_all', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) segmentation_mask = dataset.segmentation_mask_image # i am not sure if this is correct, check relevant SDK issue to see what they did @@ -113,12 +192,19 @@ def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax ax.contour(mask, levels=0, colors=['red'], linewidths=[0.6]) ax.set_title(str(ophys_experiment_id)) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) dataset = loading.get_ophys_dataset(ophys_experiment_id) cell_specimen_table = dataset.cell_specimen_table.copy() @@ -128,12 +214,19 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime ax.contour(mask, levels=0, colors=['red'], linewidths=[0.6]) ax.set_title(str(ophys_experiment_id)+'\nn valid ROIs = ' + str(len(cell_specimen_table.cell_roi_id.values))) ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_outlines', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(ophys_experiment_id, ax=None): if ax is None: - fig, ax = plt.subplots() + figsize = (5,5) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) dataset = loading.get_ophys_dataset(ophys_experiment_id) cell_specimen_table = dataset.cell_specimen_table.copy() @@ -158,35 +251,64 @@ def plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(oph except BaseException: pass ax.axis('off') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_invalid_overlay', + get_file_name_for_experiment(ophys_experiment_id)) return ax def plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=None): - dataset = loading.get_ophys_dataset(ophys_experiment_id, load_from_lims=True) # this means it will have invalid traces + dataset = loading.get_ophys_dataset(ophys_experiment_id) dff_traces = dataset.dff_traces.dff.values dff_traces = np.vstack(dff_traces) if ax is None: figsize = (14, 5) fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False # ax.pcolormesh(dff_traces, cmap='magma', vmin=0, vmax=0.5) ax = sns.heatmap(dff_traces, cmap='magma', vmin=0, vmax=0.5, cbar_kws={'label': 'dF/F'}, ax=ax) ax.set_ylim(-0.5, dff_traces.shape[0] + 0.5) ax.set_ylabel('cells') ax.set_xlabel('2P frames') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'dff_traces_heatmap', + get_file_name_for_experiment(ophys_experiment_id)) return ax +def plot_cell_roi_masks_and_dff_traces_for_experiment(ophys_experiment_id, save_figure=True): + """ + For each cell in the experiment, plot the ROI mask and dff traces, and save to single cell plots directory + :param ophys_experiment_id: + :param save_figure: + :return: + """ + dataset = loading.get_ophys_dataset(ophys_experiment_id) + cell_roi_ids = dataset.cell_specimen_table.cell_roi_id.values + for cell_roi_id in cell_roi_ids: + scp.plot_cell_roi_mask_and_dff_trace(dataset, cell_roi_id, save_figure=save_figure) + + def plot_csid_snr_for_experiment(ophys_experiment_id, ax=None): experiment_df = processing.ophys_experiment_info_df(ophys_experiment_id) exp_snr = processing.experiment_cell_specimen_id_snr_table(ophys_experiment_id) exp_snr["stage_name_lims"] = experiment_df["stage_name_lims"][0] exp_stage_color_dict = pu.experiment_id_stage_color_dict_for_experiment(ophys_experiment_id) if ax is None: - fig, ax = plt.subplots(figsize=(6, 4)) + figsize = (6,4) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax = sns.violinplot(x="stage_name_lims", y="robust_snr", data=exp_snr.loc[exp_snr["snr_zscore"] < 3], color=exp_stage_color_dict[ophys_experiment_id]) ax.set_ylabel("robust snr") ax.set_xlabel("stage name") + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_snr', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -209,12 +331,20 @@ def plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=Non exp_stage_color_dict = pu.map_stage_name_colors_to_ophys_experiment_ids(experiment_df) average_intensity, frame_numbers = processing.get_experiment_average_intensity_timeseries(ophys_experiment_id) if ax is None: - fig, ax = plt.subplots() + figsize = (6,4) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False + ax.plot(frame_numbers, average_intensity, color=exp_stage_color_dict[ophys_experiment_id], label=experiment_df["stage_name_lims"][0]) ax.set_ylabel('fluorescence value') ax.set_xlabel('frame #') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_timeseries', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -224,7 +354,11 @@ def plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=None) df = dataset.motion_correction.copy() timestamps = dataset.ophys_timestamps if ax is None: - fig, ax = plt.subplots(figsize=(20, 4)) + figsize = (20,4) + fig, ax = plt.subplots(figsize=figsize) + save_figure = True + else: + save_figure = False ax.plot(timestamps, df.x.values, color=sns.color_palette()[3], label='x_shift') ax.plot(timestamps, df.y.values, color=sns.color_palette()[2], label='y_shift') ax.set_xlim(timestamps[0], timestamps[-1]) @@ -247,6 +381,9 @@ def plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=None) for col in row_data.values_over_threshold: title = title + col + ', ' ax.set_title(title) + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', + get_file_name_for_experiment(ophys_experiment_id)) return ax # BEHAVIOR @@ -287,8 +424,12 @@ def make_pupil_area_plot(ophys_experiment_id, ax=None, label_x=True): time = ed['time'].values # might need to be updated to timestamps in the future' area = ed['pupil_area'].values # this should be blink corrected - no giant spikes if ax is None: - fig, ax = plt.subplots(figsize=(20, 4)) + figsize = (20,4) + fig, ax = plt.subplots(figsize=figsize) ax.plot(time, area) + save_figure = True + else: + save_figure = False if label_x: ax.set_xlabel('time (minutes)') ax.set_ylabel('pupil diameter\n(pixels$^2$)') @@ -301,6 +442,9 @@ def make_pupil_area_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil area plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_area_plot', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -313,8 +457,12 @@ def make_pupil_area_plot_sdk(ophys_experiment_id, ax=None, label_x=True): time = et['time'].values / 60. # might need to be updated to timestamps in the future' area = et['pupil_area_raw'].values # this will have blink artifacts in it if ax is None: - fig, ax = plt.subplots(figsize=(20, 4)) + figsize = (20,4) + fig, ax = plt.subplots(figsize=figsize) ax.plot(time, area) + save_figure = True + else: + save_figure = False if label_x: ax.set_xlabel('time (seconds)') ax.set_ylabel('pupil diameter\n(pixels$^2$)') @@ -341,9 +489,13 @@ def make_pupil_position_plot(ophys_experiment_id, ax=None, label_x=True): y = ed['pupil_center_y'].values # need to check eye_tracking table in SDK and replace with proper names if ax is None: - fig, ax = plt.subplots(figsize=(20, 4)) + figsize = (20, 4) + fig, ax = plt.subplots(figsize=figsize) ax.plot(time, x, color='darkorange') ax.plot(time, y, color='olive') + save_figure = True + else: + save_figure = False if label_x: ax.set_xlabel('time (minutes)') @@ -358,6 +510,9 @@ def make_pupil_position_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil position plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_position', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -365,11 +520,17 @@ def plot_cell_snr_distribution_for_experiment(ophys_experiment_id, ax=None): import visual_behavior.data_access.processing as processing if ax is None: fig, ax = plt.subplots() + save_figure = True + else: + save_figure = False dataset = loading.get_ophys_dataset(ophys_experiment_id) dff_traces = processing.compute_robust_snr_on_dataframe(dataset.dff_traces.copy()) ax.hist(dff_traces.robust_snr.values) ax.set_xlabel('robust_snr') ax.set_ylabel('n_cells') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_robust_snr', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -399,12 +560,15 @@ def plot_behavior_timeseries_for_experiment(ophys_experiment_id, xlim_seconds=No pupil_timestamps = dataset.eye_tracking["timestamps"].values if ax is None: + save_figure = True if plot_face_motion_energy: figsize = (20, 8) fig, ax = plt.subplots(4, 1, figsize=figsize, sharex=True) else: figsize = (20, 6) fig, ax = plt.subplots(3, 1, figsize=figsize, sharex=True) + else: + save_figure = False colors = sns.color_palette() ax[2].plot(lick_timestamps, licks, '|', label='licks', color='gray') ax[2].plot(reward_timestamps, rewards, 'o', label='rewards', color=colors[9]) @@ -499,6 +663,9 @@ def plot_motion_correction_and_population_average(experiment_id, ax=None): if ax is None: figsize = (20, 10) fig, ax = plt.subplots(3, 1, figsize=figsize, sharex=True) + save_figure = True + else: + save_figure = False dataset = loading.get_ophys_dataset(experiment_id) timestamps = dataset.ophys_timestamps @@ -526,6 +693,9 @@ def plot_motion_correction_and_population_average(experiment_id, ax=None): # ax[1].set_ylabel('run speed\n(cm/s)') # ax[1].set_xlim(running_timestamps[0], running_timestamps[-1]) # ax[1].set_xlabel('time (sec)') + if save_figure: + ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pop_avg_and_motion_corr', + get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -901,40 +1071,6 @@ def plot_classifier_validation_for_experiment(ophys_experiment_id, save_figure=T classification_threshold) utils.save_figure(fig, figsize, save_dir, folder, metadata_string + '_' + str(cell_roi_id) + '_' + str(roi_id)) -# -# def plot_metrics_mask(roi_mask_dict, metrics_dict, metric_name, max_projection=None, vmin=-1, vmax=1, cmap='RdBu', -# ax=None, save_dir=None, folder=None, colorbar=False): -# """ -# roi_mask_dict: dictionary with keys as cell_specimen_id or cell_roi_id and values as the ROI masks, -# placed within the full 512x512 image -# metrics_dict: dictionary with keys as cell_specimen_id or cell_roi_id and corresponding metric value for each ROI -# metric_name: name of metric provided to be used for colorbar label and filename of saved figure -# max_projection: maximum intensity projection. If None, only ROI masks will be shown, without max projection overlay. -# vmin: min value of metric to scale image by -# vmax: max value of metric to scale image by -# cmap: colormap to use -# ax: if axis is provided, image will be plotted on that axis. If None, a figure and axis will be created. -# save_dir: top level directory to save figure in. save_dir must be provided for figure to save. -# folder: folder within save_dir to save figure in -# colorbar: Boolean to indicate whether colorbar is displayed -# """ -# if ax is None: -# figsize = (10, 10) -# fig, ax = plt.subplots(figsize=figsize) -# if max_projection is not None: -# ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection)) -# for roi_id in list(roi_mask_dict.keys()): -# roi_mask_dict[roi_id][roi_mask_dict[roi_id] == 1] = metrics_dict[roi_id] -# mask = np.sum(np.asarray(list(roi_mask_dict.values())), axis=0) -# cax = ax.imshow(mask, cmap=cmap, alpha=0.5, vmin=vmin, vmax=vmax) -# if colorbar: -# cbar = plt.colorbar(cax, ax=ax, use_gridspec=True) -# cbar.set_label(metric_name) -# if save_dir: -# plt.tight_layout() -# utils.save_figure(fig, figsize, save_dir, folder, fig_title=metric_name) -# return ax - def plot_metrics_mask(roi_mask_dict, metrics_dict, metric_name, max_projection=None, title=None, outlines=False, cmap='RdBu', cmap_range=[0, 1], ax=None, colorbar=False): diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 052c47548..1c8181d39 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -13,6 +13,7 @@ def main(): "segmentation_masks": cp.plot_segmentation_masks_for_container, "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, "nway_match_fraction": cp.plot_nway_match_fraction, "nway_warp_overlay": cp.plot_nway_warp_overlay, diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index cee96a5af..04a01494e 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -2,7 +2,7 @@ import seaborn as sns import matplotlib.pyplot as plt -from visual_behavior.data_access import loading as data_loading +from visual_behavior.data_access import loading as loading from visual_behavior.visualization import utils as utils import visual_behavior.visualization.ophys.summary_figures as sf @@ -20,7 +20,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even Useful to validate cell matching as well as examine changes in activity profiles over days. """ import visual_behavior.data_access.utilities as utilities - experiments_table = data_loading.get_filtered_ophys_experiment_table(release_data_only=True) + experiments_table = loading.get_filtered_ophys_experiment_table(release_data_only=True) container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id] expts = np.sort(container_expts.index.values) if use_events: @@ -41,7 +41,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even for i, ophys_experiment_id in enumerate(expts): print('ophys_experiment_id:', ophys_experiment_id) try: - dataset = data_loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) + dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) if cell_specimen_id in dataset.dff_traces.index: sdf = loading.get_stimulus_response_df(dataset, data_type='dff', event_type='all', @@ -181,3 +181,32 @@ def plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figur utils.save_figure(fig, figsize, utils.get_single_cell_plots_dir(), 'dff_trace_and_behavior', str(cell_specimen_id) + '_' + dataset.metadata_string + '_dff_trace_and_behavior') plt.close() + + +def plot_cell_roi_mask_and_dff_trace(dataset, cell_roi_id, save_figure=True): + dff_traces = dataset.dff_traces.copy() + roi_masks = dataset.roi_masks.copy() + max_projection = dataset.max_projection.data.copy() + average_image = dataset.average_projection.data.copy() + metadata = dataset.metadata.copy() + + figsize=(20,10) + fig, ax = plt.subplots(2, 2, figsize=figsize, gridspec_kw={'width_ratios':[1,3]}) + ax = ax.ravel() + ax[0] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, spacex=40, spacey=40, show_mask=True, ax=ax[0]) + ax[1].plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id==cell_roi_id].dff.values[0]) + ax[1].set_xlim(500, 560) + ax[1].set_xlabel('time (sec)') + ax[1].set_ylabel('dF/F') + + ax[2] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, show_mask=True, alpha=1, full_image=True, ax=ax[2]) + ax[3].plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id==cell_roi_id].dff.values[0]) + ax[3].set_xlabel('time (sec)') + ax[3].set_ylabel('dF/F') + + filename = utils.get_metadata_string(metadata) + filename = filename+'_'+str(cell_roi_id) + fig.suptitle(filename, x=0.5, y=1.) + if save_figure: + save_dir = loading.get_single_cell_plots_dir() + utils.save_figure(fig, figsize, save_dir, 'cell_roi_traces_and_masks', filename) \ No newline at end of file From 8fea65616b6a7d0f28e4dbd0a497801ab583bd27 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 16:54:16 -0700 Subject: [PATCH 080/187] use correct plot directory --- visual_behavior/data_access/loading.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index ee9d55639..6ac71152b 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -80,8 +80,7 @@ def get_production_cache_dir(): def get_qc_plots_dir(): - return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/MSN_TTN' - # return r'\\allen\programs\mindscope\workgroups\learning\ophys\qc_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' def get_super_container_plots_dir(): From badd167ac5d87b9aeee7150ce6fb3c1deb99e95a Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 17:29:28 -0700 Subject: [PATCH 081/187] typo --- .../visualization/qc/experiment_plots.py | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index 2d243396e..cd9ef46d0 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -46,6 +46,7 @@ def get_file_name_for_experiment(ophys_experiment_id): filename = get_metadata_string(ophys_experiment_id) return filename + def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): if ax is None: figsize = (5,5) @@ -59,7 +60,7 @@ def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -76,7 +77,7 @@ def plot_average_image_for_experiment(ophys_experiment_id, ax=None): ax.imshow(average_projection, cmap='gray', vmax=np.amax(average_projection)) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_image', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_image', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -92,7 +93,7 @@ def plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax= ax.imshow(average_image, cmap='gray', vmin=0, vmax=8000) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_image_movie', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_image_movie', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -109,7 +110,7 @@ def plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=None ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_image_movie', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_image_movie', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -127,7 +128,7 @@ def plot_segmentation_mask_for_experiment(ophys_experiment_id, ax=None): ax.imshow(segmentation_mask, cmap='gray', vmin=0, vmax=1) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_image', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_image', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -151,7 +152,7 @@ def plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax= pass ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -172,7 +173,7 @@ def plot_all_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=No ax.imshow(mask, cmap='hsv', vmax=1, alpha=0.5) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay_all', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay_all', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -193,7 +194,7 @@ def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -215,7 +216,7 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime ax.set_title(str(ophys_experiment_id)+'\nn valid ROIs = ' + str(len(cell_specimen_table.cell_roi_id.values))) ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_outlines', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_outlines', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -252,7 +253,7 @@ def plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(oph pass ax.axis('off') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_invalid_overlay', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_invalid_overlay', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -273,7 +274,7 @@ def plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=None): ax.set_ylabel('cells') ax.set_xlabel('2P frames') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'dff_traces_heatmap', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'dff_traces_heatmap', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -307,7 +308,7 @@ def plot_csid_snr_for_experiment(ophys_experiment_id, ax=None): ax.set_ylabel("robust snr") ax.set_xlabel("stage name") if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_snr', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_snr', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -343,7 +344,7 @@ def plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=Non ax.set_ylabel('fluorescence value') ax.set_xlabel('frame #') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_timeseries', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_timeseries', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -382,7 +383,7 @@ def plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=None) title = title + col + ', ' ax.set_title(title) if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -443,7 +444,7 @@ def make_pupil_area_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil area plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_area_plot', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_area_plot', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -511,7 +512,7 @@ def make_pupil_position_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil position plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_position', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_position', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -529,7 +530,7 @@ def plot_cell_snr_distribution_for_experiment(ophys_experiment_id, ax=None): ax.set_xlabel('robust_snr') ax.set_ylabel('n_cells') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_robust_snr', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_robust_snr', get_file_name_for_experiment(ophys_experiment_id)) return ax @@ -694,7 +695,7 @@ def plot_motion_correction_and_population_average(experiment_id, ax=None): # ax[1].set_xlim(running_timestamps[0], running_timestamps[-1]) # ax[1].set_xlabel('time (sec)') if save_figure: - ut.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pop_avg_and_motion_corr', + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pop_avg_and_motion_corr', get_file_name_for_experiment(ophys_experiment_id)) return ax From 846f1fdaf7b36d8fce7655826f51d5b8dbb5fd5f Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 20 Jun 2022 22:08:09 -0700 Subject: [PATCH 082/187] run just for suite2p mouse --- visual_behavior/visualization/qc/run_save_all_container_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index bf12bbfb9..8a0c83894 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -24,6 +24,7 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +experiments = experiments[experiments.mouse_id=='612764'] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From c72298b72153309fc85459096b6868d65af1e15a Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 22 Jun 2022 13:31:44 -0700 Subject: [PATCH 083/187] exlude acq date from container QC --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 6ac71152b..3512335a4 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2627,7 +2627,7 @@ def build_container_df(experiment_table): 'cre_line': subset['cre_line'][0], 'targeted_structure': subset['targeted_structure'].unique()[0], 'imaging_depth': subset['imaging_depth'].unique()[0], - 'first_acquisition_date': subset['date_of_acquisition'].min().split(' ')[0], + # 'first_acquisition_date': subset['date_of_acquisition'].min().split(' ')[0], 'equipment_name': subset['equipment_name'].unique(), } for idx, row in subset.iterrows(): From 952e6558bb26eef2932b1f1754e1fa79a8b35022 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 29 Jun 2022 17:40:01 -0700 Subject: [PATCH 084/187] print statements to debug saving --- visual_behavior/visualization/qc/container_plots.py | 4 ++++ visual_behavior/visualization/qc/experiment_plots.py | 1 + 2 files changed, 5 insertions(+) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 7d54a217b..ae71a3c76 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -131,9 +131,11 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): try: + print('plotting max intensity projection for ',ophys_experiment_id) ax[i] = ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax[i]) # plot and save to experiment_plots dir when ax=None ep.plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None) + print('done') except: pass session_type = loading.get_session_type_for_ophys_experiment_id(ophys_experiment_id, experiments_table) @@ -141,10 +143,12 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure ax[i].set_title(str(ophys_experiment_id) + '\n' + session_type) if save_figure: + print('saving plot for container', ophys_container_id) ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'max_intensity_projection', get_file_name_for_container(ophys_container_id)) ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', get_file_name_for_container(ophys_container_id) + '_max_intensity_projection') + print('done') def plot_movie_max_projection_images_for_container(ophys_container_id, save_figure=True): diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index cd9ef46d0..38ec2108b 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -60,6 +60,7 @@ def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: + print('plotting max intensity projection for ', ophys_experiment_id) utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', get_file_name_for_experiment(ophys_experiment_id)) return ax From f241b671e77f2317902f26875ef9fe607ef8a454 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 29 Jun 2022 18:22:31 -0700 Subject: [PATCH 085/187] dont need to load cache when getting dataset --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 3512335a4..0bff570af 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -813,7 +813,7 @@ def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=True, load_from_ if load_from_lims: print('loading from lims, exclude_invalid_rois =', exclude_invalid_rois) - cache = bpc.from_lims() + # cache = bpc.from_lims() # dataset = cache.get_behavior_ophys_experiment(int(ophys_experiment_id)) dataset = BehaviorOphysExperiment.from_lims(int(ophys_experiment_id), exclude_invalid_rois=exclude_invalid_rois) elif load_from_nwb: From 99f7918b00465afde8fc91bb9c0d8919dc490ae1 Mon Sep 17 00:00:00 2001 From: matchings Date: Wed, 29 Jun 2022 18:22:51 -0700 Subject: [PATCH 086/187] dont re-load dataset when getting metadata_string --- .../visualization/qc/container_plots.py | 1 + .../visualization/qc/experiment_plots.py | 73 +++++++------------ 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index ae71a3c76..6cb38acd1 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -102,6 +102,7 @@ def plot_container_session_sequence(ophys_container_id, save_figure=True): fig.subplots_adjust(right=0.1) fig.subplots_adjust(top=0.9) if save_figure: + print('saving ophys_session_sequence for', ophys_container_id) ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'ophys_session_sequence', get_file_name_for_container(ophys_container_id)) ut.save_figure(fig, figsize, loading.get_container_plots_dir(), 'all', diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index 38ec2108b..8ae75d9c5 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -21,14 +21,14 @@ bitdepth_16 = 65536 -def get_metadata_string(ophys_experiment_id): +def get_metadata_string(dataset): """ Create a string of metadata information to be used in filenames and figure titles. Includes information such as experiment_id, cre_line, acquisition_date, rig_id, etc - :param ophys_experiment_id: + :param dataset: BehaviorOphysExperiment object :return: """ - dataset = loading.get_ophys_dataset(ophys_experiment_id) + # dataset = loading.get_ophys_dataset(ophys_experiment_id) m = dataset.metadata.copy() metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string @@ -42,8 +42,7 @@ def get_file_name_for_experiment(ophys_experiment_id): :param ophys_experiment_id: :return: """ - # filename = 'experiment_id'+str(ophys_experiment_id) - filename = get_metadata_string(ophys_experiment_id) + filename = 'experiment_id'+str(ophys_experiment_id) return filename @@ -60,9 +59,8 @@ def plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=None): ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - print('plotting max intensity projection for ', ophys_experiment_id) - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', - get_file_name_for_experiment(ophys_experiment_id)) + print('saving max intensity projection for ', ophys_experiment_id) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_intensity_projection', get_metadata_string(dataset)) return ax @@ -78,8 +76,7 @@ def plot_average_image_for_experiment(ophys_experiment_id, ax=None): ax.imshow(average_projection, cmap='gray', vmax=np.amax(average_projection)) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_image', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_image', get_metadata_string(dataset)) return ax @@ -94,8 +91,7 @@ def plot_motion_correction_average_image_for_experiment(ophys_experiment_id, ax= ax.imshow(average_image, cmap='gray', vmin=0, vmax=8000) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_image_movie', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_image_movie', get_metadata_string(dataset)) return ax @@ -111,8 +107,7 @@ def plot_motion_correction_max_image_for_experiment(ophys_experiment_id, ax=None ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_image_movie', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'max_image_movie', get_metadata_string(dataset)) return ax @@ -129,8 +124,7 @@ def plot_segmentation_mask_for_experiment(ophys_experiment_id, ax=None): ax.imshow(segmentation_mask, cmap='gray', vmin=0, vmax=1) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_image', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_image', get_metadata_string(dataset)) return ax @@ -153,8 +147,7 @@ def plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax= pass ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay', get_metadata_string(dataset)) return ax @@ -174,8 +167,7 @@ def plot_all_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax=No ax.imshow(mask, cmap='hsv', vmax=1, alpha=0.5) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay_all', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_overlay_all', get_metadata_string(dataset)) return ax @@ -195,8 +187,7 @@ def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax ax.set_title(str(ophys_experiment_id)) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid', get_metadata_string(dataset)) return ax @@ -217,8 +208,7 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime ax.set_title(str(ophys_experiment_id)+'\nn valid ROIs = ' + str(len(cell_specimen_table.cell_roi_id.values))) ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_outlines', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_outlines', get_metadata_string(dataset)) return ax @@ -254,8 +244,7 @@ def plot_valid_and_invalid_segmentation_mask_overlay_per_cell_for_experiment(oph pass ax.axis('off') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_invalid_overlay', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'segmentation_mask_valid_invalid_overlay', get_metadata_string(dataset)) return ax @@ -275,8 +264,7 @@ def plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=None): ax.set_ylabel('cells') ax.set_xlabel('2P frames') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'dff_traces_heatmap', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'dff_traces_heatmap', get_metadata_string(dataset)) return ax @@ -309,8 +297,7 @@ def plot_csid_snr_for_experiment(ophys_experiment_id, ax=None): ax.set_ylabel("robust snr") ax.set_xlabel("stage name") if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_snr', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_snr', get_metadata_string(dataset)) return ax @@ -345,8 +332,7 @@ def plot_average_intensity_timeseries_for_experiment(ophys_experiment_id, ax=Non ax.set_ylabel('fluorescence value') ax.set_xlabel('frame #') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_timeseries', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'average_intensity_timeseries', get_metadata_string(dataset)) return ax @@ -384,8 +370,7 @@ def plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=None) title = title + col + ', ' ax.set_title(title) if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', get_metadata_string(dataset)) return ax # BEHAVIOR @@ -445,8 +430,7 @@ def make_pupil_area_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil area plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_area_plot', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_area_plot', get_metadata_string(dataset)) return ax @@ -513,8 +497,7 @@ def make_pupil_position_plot(ophys_experiment_id, ax=None, label_x=True): error_text = 'could not generate pupil position plot for ophys_experiment_id {}\n{}'.format(ophys_experiment_id, e) ax.set_title(error_text, ha='left') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_position', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pupil_position', get_metadata_string(dataset)) return ax @@ -531,8 +514,7 @@ def plot_cell_snr_distribution_for_experiment(ophys_experiment_id, ax=None): ax.set_xlabel('robust_snr') ax.set_ylabel('n_cells') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_robust_snr', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'cell_trace_robust_snr', get_metadata_string(dataset)) return ax @@ -610,7 +592,7 @@ def plot_behavior_timeseries_for_experiment(ophys_experiment_id, xlim_seconds=No # ax[0].set_title(dataset.metadata_string) if save_figure: utils.save_figure(fig, figsize, utils.get_experiment_plots_dir(), 'population_activity_and_behavior', - dataset.metadata_string + '_population_activity_and_behavior') + get_metadata_string(dataset) + '_population_activity_and_behavior') plt.close() return ax @@ -696,8 +678,7 @@ def plot_motion_correction_and_population_average(experiment_id, ax=None): # ax[1].set_xlim(running_timestamps[0], running_timestamps[-1]) # ax[1].set_xlabel('time (sec)') if save_figure: - utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pop_avg_and_motion_corr', - get_file_name_for_experiment(ophys_experiment_id)) + utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'pop_avg_and_motion_corr', get_metadata_string(dataset)) return ax @@ -731,7 +712,7 @@ def plot_event_detection_for_experiment(ophys_experiment_id, save_figure=True): :return: """ dataset = loading.get_ophys_dataset(ophys_experiment_id) - metadata_string = dataset.metadata_string + metadata_string = get_metadata_string(dataset) colors = sns.color_palette() ophys_timestamps = dataset.ophys_timestamps.copy() dff_traces = dataset.dff_traces.copy() @@ -824,7 +805,7 @@ def plot_population_activity_and_behavior_for_experiment(ophys_experiment_id, sa ax[0].set_title(dataset.metadata_string) if save_figure: utils.save_figure(fig, figsize, utils.get_experiment_plots_dir(), 'population_activity_and_behavior', - dataset.metadata_string + '_population_activity_and_behavior') + get_metadata_string(dataset) + '_population_activity_and_behavior') plt.close() @@ -944,7 +925,7 @@ def plot_classifier_validation_for_experiment(ophys_experiment_id, save_figure=T max_projection = dataset.max_projection.data dff_traces = dataset.dff_traces.copy() ophys_timestamps = dataset.ophys_timestamps - metadata_string = dataset.metadata_string + metadata_string = get_metadata_string(dataset) # get average response df analysis = ResponseAnalysis(dataset) sdf = analysis.get_response_df(df_name='stimulus_response_df') From 57114d8f64e67a9b9d178e3eb72dd01b28ed55b7 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 15:19:57 -0700 Subject: [PATCH 087/187] run the rest of the QC plots --- .../qc/save_all_container_plots.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 1c8181d39..ab2c1b90d 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,15 +4,15 @@ def main(): possible_plots = { - "ophys_session_sequence": cp.plot_container_session_sequence, - "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - "average_images": cp.plot_sdk_average_images_for_container, - "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - "average_images_movies": cp.plot_movie_average_images_for_container, - "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - "segmentation_masks": cp.plot_segmentation_masks_for_container, - "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + # "ophys_session_sequence": cp.plot_container_session_sequence, + # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, + # "average_images": cp.plot_sdk_average_images_for_container, + # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + # "average_images_movies": cp.plot_movie_average_images_for_container, + # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + # "segmentation_masks": cp.plot_segmentation_masks_for_container, + # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, + # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, "nway_match_fraction": cp.plot_nway_match_fraction, From 3f2e6fa6cbb103efd094034e53f0f6aae3f35dfc Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 15:20:37 -0700 Subject: [PATCH 088/187] more QC plots --- .../qc/save_all_container_plots.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index ab2c1b90d..8b7c24e09 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -13,17 +13,17 @@ def main(): # "segmentation_masks": cp.plot_segmentation_masks_for_container, # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, - "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - "nway_match_fraction": cp.plot_nway_match_fraction, - "nway_warp_overlay": cp.plot_nway_warp_overlay, - "nway_warp_summary": cp.plot_nway_warp_summary, - "number_matched_cells": cp.plot_number_matched_cells_for_container, - "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - "experiment_summary": cp.plot_experiment_summary_figure_for_container, + # "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + # "nway_match_fraction": cp.plot_nway_match_fraction, + # "nway_warp_overlay": cp.plot_nway_warp_overlay, + # "nway_warp_summary": cp.plot_nway_warp_summary, + # "number_matched_cells": cp.plot_number_matched_cells_for_container, + # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + # "experiment_summary": cp.plot_experiment_summary_figure_for_container, "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, From d281484fab599ea6ebaaa2f86a5ff1b5836f0fee Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 15:50:40 -0700 Subject: [PATCH 089/187] add corrected fluor to dff trace plots --- .../visualization/qc/single_cell_plots.py | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 04a01494e..7aed8bc15 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -185,6 +185,7 @@ def plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figur def plot_cell_roi_mask_and_dff_trace(dataset, cell_roi_id, save_figure=True): dff_traces = dataset.dff_traces.copy() + corrected_fluorescence = dataset.corrected_fluorescence_traces.copy() roi_masks = dataset.roi_masks.copy() max_projection = dataset.max_projection.data.copy() average_image = dataset.average_projection.data.copy() @@ -194,15 +195,31 @@ def plot_cell_roi_mask_and_dff_trace(dataset, cell_roi_id, save_figure=True): fig, ax = plt.subplots(2, 2, figsize=figsize, gridspec_kw={'width_ratios':[1,3]}) ax = ax.ravel() ax[0] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, spacex=40, spacey=40, show_mask=True, ax=ax[0]) - ax[1].plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id==cell_roi_id].dff.values[0]) - ax[1].set_xlim(500, 560) - ax[1].set_xlabel('time (sec)') - ax[1].set_ylabel('dF/F') + # create twinax + ax1 = ax[1].twinx() + # dff traces + ax1.plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id == cell_roi_id].dff.values[0], label='dF/F', color='purple') + ax1.set_xlim(500, 680) + ax1.set_xlabel('time (sec)') + ax1.set_ylabel('dF/F') + ax1.legend() + # show corrected fluorescence as well + ax[1].plot(dataset.ophys_timestamps, corrected_fluorescence[corrected_fluorescence.cell_roi_id==cell_roi_id].corrected_fluorescence.values[0], color='g', label='fluor') + ax[1].set_ylabel('corrected fluorescence') + ax[1].legend() + ax[2] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, show_mask=True, alpha=1, full_image=True, ax=ax[2]) - ax[3].plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id==cell_roi_id].dff.values[0]) - ax[3].set_xlabel('time (sec)') - ax[3].set_ylabel('dF/F') + # create twinx + ax3 = ax[3].twinx() + ax3.plot(dataset.ophys_timestamps, dff_traces[dff_traces.cell_roi_id==cell_roi_id].dff.values[0], label='dF/F', color='purple') + ax3.set_xlabel('time (sec)') + ax3.set_ylabel('dF/F') + ax3.legend() + # show corrected fluorescence as well + ax[3].plot(dataset.ophys_timestamps, corrected_fluorescence[corrected_fluorescence.cell_roi_id==cell_roi_id].corrected_fluorescence.values[0], color='g', label='fluor') + ax[3].set_ylabel('corrected fluorescence') + ax[3].legend() filename = utils.get_metadata_string(metadata) filename = filename+'_'+str(cell_roi_id) From 9128b58ab068b41ff63ff0e4eaf6ace3bfc5cad6 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 15:50:51 -0700 Subject: [PATCH 090/187] use avg instead of mask in overlays --- visual_behavior/visualization/qc/experiment_plots.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index 8ae75d9c5..e97ade59a 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -198,13 +198,16 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime save_figure = True else: save_figure = False - ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) + # plot max or average image + # ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) + ax = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax) + # plot mask outlines dataset = loading.get_ophys_dataset(ophys_experiment_id) cell_specimen_table = dataset.cell_specimen_table.copy() if len(cell_specimen_table) > 0: for cell_roi_id in cell_specimen_table.cell_roi_id.values: mask = cell_specimen_table[cell_specimen_table.cell_roi_id == cell_roi_id].roi_mask.values[0] - ax.contour(mask, levels=0, colors=['red'], linewidths=[0.6]) + ax.contour(mask, levels=0, colors=['red'], linewidths=[0.4]) ax.set_title(str(ophys_experiment_id)+'\nn valid ROIs = ' + str(len(cell_specimen_table.cell_roi_id.values))) ax.axis('off') if save_figure: From 96eac4b62e23705bb7d38629d0ab670a62e940b3 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 15:50:57 -0700 Subject: [PATCH 091/187] plot traces and masks --- .../qc/save_all_container_plots.py | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 8b7c24e09..afca0cf49 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -10,10 +10,11 @@ def main(): # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, # "average_images_movies": cp.plot_movie_average_images_for_container, # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - # "segmentation_masks": cp.plot_segmentation_masks_for_container, + "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + "segmentation_masks": cp.plot_segmentation_masks_for_container, # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - # "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, # "nway_match_fraction": cp.plot_nway_match_fraction, # "nway_warp_overlay": cp.plot_nway_warp_overlay, @@ -24,27 +25,27 @@ def main(): # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, # "experiment_summary": cp.plot_experiment_summary_figure_for_container, - "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - "running_speed": cp.plot_running_speed_for_container, - "lick_rasters": cp.plot_lick_rasters_for_container, - "behavior_summary": cp.plot_behavior_summary, - "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - "pupil_area_sdk": cp.plot_pupil_area_sdk, - "pupil_area": cp.plot_pupil_area, - "pupil_position": cp.plot_pupil_position, - "FOV_average_intensity": cp.plot_average_intensity_for_container, - "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - "pmt_settings": cp.plot_pmt_for_container, - "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - "event_detection": cp.plot_event_detection_for_container, - "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + # "running_speed": cp.plot_running_speed_for_container, + # "lick_rasters": cp.plot_lick_rasters_for_container, + # "behavior_summary": cp.plot_behavior_summary, + # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + # "pupil_area_sdk": cp.plot_pupil_area_sdk, + # "pupil_area": cp.plot_pupil_area, + # "pupil_position": cp.plot_pupil_position, + # "FOV_average_intensity": cp.plot_average_intensity_for_container, + # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + # "pmt_settings": cp.plot_pmt_for_container, + # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + # "event_detection": cp.plot_event_detection_for_container, + # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From fded1d8705b410ab8378b818dc9108c6818c7156 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:13:27 -0700 Subject: [PATCH 092/187] plots for golden mouse --- .../visualization/qc/run_save_all_container_plots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 8a0c83894..63934d080 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -24,7 +24,8 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] -experiments = experiments[experiments.mouse_id=='612764'] +# experiments = experiments[experiments.mouse_id=='612764'] +experiments = experiments[experiments.mouse_id=='603892'] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From a9f5792b8acd80cdece39a2684e18316059e973d Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:20:41 -0700 Subject: [PATCH 093/187] use doug's figrid tool for experiment summary --- .../visualization/qc/experiment_summary.py | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_summary.py b/visual_behavior/visualization/qc/experiment_summary.py index 1c3959603..b6455b687 100644 --- a/visual_behavior/visualization/qc/experiment_summary.py +++ b/visual_behavior/visualization/qc/experiment_summary.py @@ -3,7 +3,7 @@ import visual_behavior.ophys.response_analysis.response_analysis as ra import visual_behavior.ophys.response_analysis.utilities as ut import visual_behavior.data_access.loading as loading -import mpl_figure_formatter as ff +import figrid as ff import matplotlib.pyplot as plt import seaborn as sns @@ -50,7 +50,6 @@ def make_fig_ax(): def plot_experiment_summary_figure(experiment_id, save_figure=True): dataset = loading.get_ophys_dataset(experiment_id) - analysis = ra.ResponseAnalysis(dataset, use_events=True) fig, ax, figsize = make_fig_ax() ax['0_0'] = ep.plot_max_intensity_projection_for_experiment(experiment_id, ax=ax['0_0']) @@ -75,39 +74,42 @@ def plot_experiment_summary_figure(experiment_id, save_figure=True): ax['2_2'] = ep.plot_cell_snr_distribution_for_experiment(experiment_id, ax=ax['2_2']) ax['2_3:'] = ep.plot_traces_heatmap_for_experiment(experiment_id, ax=ax['2_3:']) - df_name = 'trials_response_df' - df = analysis.get_response_df(df_name) - mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['cell_specimen_id', 'go'], flashes=False, omitted=False, - get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=True) - ax['3_0'] = ep.plot_population_average_for_experiment(experiment_id, df, mean_df, df_name, color=None, label=None, ax=ax['3_0']) - ax['3_0'].set_xlim(-2.5, 2.8) - - df_name = 'omission_response_df' - df = analysis.get_response_df(df_name) - mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['cell_specimen_id'], flashes=False, omitted=True, - get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=False) - ax['3_1'] = ep.plot_population_average_for_experiment(experiment_id, df, mean_df, df_name, color=None, label=None, ax=ax['3_1']) - ax['3_1'].set_xlim(-2.5, 2.8) - - df_name = 'trials_run_speed_df' - df = analysis.get_response_df(df_name) - df['condition'] = True - mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['condition', 'go'], flashes=False, omitted=True, - get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=False) - ax['4_0'] = ep.plot_population_average_for_experiment(experiment_id, df, df, df_name, trace_type='trace', - color=sns.color_palette()[4], label=None, ax=ax['4_0']) - ax['4_0'].set_ylabel('run speed (cm/s)') - ax['4_0'].set_xlim(-2.5, 2.8) - - df_name = 'omission_run_speed_df' - df = analysis.get_response_df(df_name) - df['condition'] = True - mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['condition'], flashes=False, omitted=False, - get_reliability=False, get_pref_stim=True, exclude_omitted_from_pref_stim=True) - ax['4_1'] = ep.plot_population_average_for_experiment(experiment_id, df, df, df_name, trace_type='trace', - color=sns.color_palette()[4], label=None, ax=ax['4_1']) - ax['4_1'].set_ylabel('run speed (cm/s)') - ax['4_1'].set_xlim(-2.5, 2.8) + try: + df_name = 'trials_response_df' + df = analysis.get_response_df(df_name) + mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['cell_specimen_id', 'go'], flashes=False, omitted=False, + get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=True) + ax['3_0'] = ep.plot_population_average_for_experiment(experiment_id, df, mean_df, df_name, color=None, label=None, ax=ax['3_0']) + ax['3_0'].set_xlim(-2.5, 2.8) + + df_name = 'omission_response_df' + df = analysis.get_response_df(df_name) + mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['cell_specimen_id'], flashes=False, omitted=True, + get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=False) + ax['3_1'] = ep.plot_population_average_for_experiment(experiment_id, df, mean_df, df_name, color=None, label=None, ax=ax['3_1']) + ax['3_1'].set_xlim(-2.5, 2.8) + + df_name = 'trials_run_speed_df' + df = analysis.get_response_df(df_name) + df['condition'] = True + mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['condition', 'go'], flashes=False, omitted=True, + get_reliability=False, get_pref_stim=False, exclude_omitted_from_pref_stim=False) + ax['4_0'] = ep.plot_population_average_for_experiment(experiment_id, df, df, df_name, trace_type='trace', + color=sns.color_palette()[4], label=None, ax=ax['4_0']) + ax['4_0'].set_ylabel('run speed (cm/s)') + ax['4_0'].set_xlim(-2.5, 2.8) + + df_name = 'omission_run_speed_df' + df = analysis.get_response_df(df_name) + df['condition'] = True + mean_df = ut.get_mean_df(df, analysis=analysis, conditions=['condition'], flashes=False, omitted=False, + get_reliability=False, get_pref_stim=True, exclude_omitted_from_pref_stim=True) + ax['4_1'] = ep.plot_population_average_for_experiment(experiment_id, df, df, df_name, trace_type='trace', + color=sns.color_palette()[4], label=None, ax=ax['4_1']) + ax['4_1'].set_ylabel('run speed (cm/s)') + ax['4_1'].set_xlim(-2.5, 2.8) + except: + print('cant plot mean responses - need to update to work with mindscope_utilities') xlim_seconds = [int(10 * 60), int(15 * 60)] ax['3_3:'] = ep.plot_high_low_snr_trace_examples(experiment_id, xlim_seconds=xlim_seconds, ax=ax['3_3:']) From 666ba536ef25f6a51e9b6a4ffe641d0283da4e19 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:21:14 -0700 Subject: [PATCH 094/187] reorder QC plots --- .../qc/save_all_container_plots.py | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index afca0cf49..077d0db08 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,48 +4,47 @@ def main(): possible_plots = { - # "ophys_session_sequence": cp.plot_container_session_sequence, - # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - # "average_images": cp.plot_sdk_average_images_for_container, - # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - # "average_images_movies": cp.plot_movie_average_images_for_container, - # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + "ophys_session_sequence": cp.plot_container_session_sequence, + "max_projection_images": cp.plot_sdk_max_projection_images_for_container, + "average_images": cp.plot_sdk_average_images_for_container, "segmentation_masks": cp.plot_segmentation_masks_for_container, - # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - - # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - # "nway_match_fraction": cp.plot_nway_match_fraction, - # "nway_warp_overlay": cp.plot_nway_warp_overlay, - # "nway_warp_summary": cp.plot_nway_warp_summary, - # "number_matched_cells": cp.plot_number_matched_cells_for_container, - # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - # "experiment_summary": cp.plot_experiment_summary_figure_for_container, - # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - # "running_speed": cp.plot_running_speed_for_container, - # "lick_rasters": cp.plot_lick_rasters_for_container, - # "behavior_summary": cp.plot_behavior_summary, - # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - # "pupil_area_sdk": cp.plot_pupil_area_sdk, - # "pupil_area": cp.plot_pupil_area, - # "pupil_position": cp.plot_pupil_position, - # "FOV_average_intensity": cp.plot_average_intensity_for_container, - # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - # "pmt_settings": cp.plot_pmt_for_container, - # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - # "event_detection": cp.plot_event_detection_for_container, - # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + "average_images_movies": cp.plot_movie_average_images_for_container, + "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, + "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + "nway_match_fraction": cp.plot_nway_match_fraction, + "nway_warp_overlay": cp.plot_nway_warp_overlay, + "nway_warp_summary": cp.plot_nway_warp_summary, + "number_matched_cells": cp.plot_number_matched_cells_for_container, + "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + "experiment_summary": cp.plot_experiment_summary_figure_for_container, + "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + "running_speed": cp.plot_running_speed_for_container, + "lick_rasters": cp.plot_lick_rasters_for_container, + "behavior_summary": cp.plot_behavior_summary, + "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + "pupil_area_sdk": cp.plot_pupil_area_sdk, + "pupil_area": cp.plot_pupil_area, + "pupil_position": cp.plot_pupil_position, + "FOV_average_intensity": cp.plot_average_intensity_for_container, + "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + "pmt_settings": cp.plot_pmt_for_container, + "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + "event_detection": cp.plot_event_detection_for_container, + "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From 58475aab973146138d7e9074d9de715637d1ff99 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:22:52 -0700 Subject: [PATCH 095/187] plots for golden mouse --- .../visualization/qc/experiment_plots.py | 2 +- .../qc/save_all_container_plots.py | 78 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index e97ade59a..dc2ce1c4b 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -200,7 +200,7 @@ def plot_valid_segmentation_mask_outlines_per_cell_for_experiment(ophys_experime save_figure = False # plot max or average image # ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) - ax = ep.plot_average_image_for_experiment(ophys_experiment_id, ax=ax) + ax = plot_average_image_for_experiment(ophys_experiment_id, ax=ax) # plot mask outlines dataset = loading.get_ophys_dataset(ophys_experiment_id) cell_specimen_table = dataset.cell_specimen_table.copy() diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 077d0db08..e53372632 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,47 +4,47 @@ def main(): possible_plots = { - "ophys_session_sequence": cp.plot_container_session_sequence, - "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - "average_images": cp.plot_sdk_average_images_for_container, + # "ophys_session_sequence": cp.plot_container_session_sequence, + # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, + # "average_images": cp.plot_sdk_average_images_for_container, "segmentation_masks": cp.plot_segmentation_masks_for_container, "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, - "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - "average_images_movies": cp.plot_movie_average_images_for_container, - "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - "nway_match_fraction": cp.plot_nway_match_fraction, - "nway_warp_overlay": cp.plot_nway_warp_overlay, - "nway_warp_summary": cp.plot_nway_warp_summary, - "number_matched_cells": cp.plot_number_matched_cells_for_container, - "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - "experiment_summary": cp.plot_experiment_summary_figure_for_container, - "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - "running_speed": cp.plot_running_speed_for_container, - "lick_rasters": cp.plot_lick_rasters_for_container, - "behavior_summary": cp.plot_behavior_summary, - "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - "pupil_area_sdk": cp.plot_pupil_area_sdk, - "pupil_area": cp.plot_pupil_area, - "pupil_position": cp.plot_pupil_position, - "FOV_average_intensity": cp.plot_average_intensity_for_container, - "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - "pmt_settings": cp.plot_pmt_for_container, - "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - "event_detection": cp.plot_event_detection_for_container, - "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + # "average_images_movies": cp.plot_movie_average_images_for_container, + # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, + # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + # "nway_match_fraction": cp.plot_nway_match_fraction, + # "nway_warp_overlay": cp.plot_nway_warp_overlay, + # "nway_warp_summary": cp.plot_nway_warp_summary, + # "number_matched_cells": cp.plot_number_matched_cells_for_container, + # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + # "experiment_summary": cp.plot_experiment_summary_figure_for_container, + # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + # "running_speed": cp.plot_running_speed_for_container, + # "lick_rasters": cp.plot_lick_rasters_for_container, + # "behavior_summary": cp.plot_behavior_summary, + # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + # "pupil_area_sdk": cp.plot_pupil_area_sdk, + # "pupil_area": cp.plot_pupil_area, + # "pupil_position": cp.plot_pupil_position, + # "FOV_average_intensity": cp.plot_average_intensity_for_container, + # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + # "pmt_settings": cp.plot_pmt_for_container, + # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + # "event_detection": cp.plot_event_detection_for_container, + # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From bf6f687e8da6ddfb21bf0443c8c5d7aa99fedadd Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:46:14 -0700 Subject: [PATCH 096/187] include experiment summary figure --- .../visualization/qc/experiment_plots.py | 55 ++++++++++--------- .../qc/save_all_container_plots.py | 1 + 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_plots.py b/visual_behavior/visualization/qc/experiment_plots.py index dc2ce1c4b..bc64bf8d0 100644 --- a/visual_behavior/visualization/qc/experiment_plots.py +++ b/visual_behavior/visualization/qc/experiment_plots.py @@ -137,7 +137,7 @@ def plot_valid_segmentation_mask_overlay_for_experiment(ophys_experiment_id, ax= save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) try: - dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) + dataset = loading.get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=True) segmentation_mask = dataset.segmentation_mask_image # i am not sure if this is correct, check relevant SDK issue to see what they did mask = np.zeros(segmentation_mask[0].shape) mask[:] = np.nan @@ -179,7 +179,7 @@ def plot_valid_segmentation_mask_outlines_for_experiment(ophys_experiment_id, ax else: save_figure = False ax = plot_max_intensity_projection_for_experiment(ophys_experiment_id, ax=ax) - dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=False) + dataset = loading.get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=True) segmentation_mask = dataset.segmentation_mask_image # i am not sure if this is correct, check relevant SDK issue to see what they did mask = np.zeros(segmentation_mask[0].shape) mask[segmentation_mask[0] == 1] = 1 @@ -261,7 +261,6 @@ def plot_traces_heatmap_for_experiment(ophys_experiment_id, ax=None): save_figure = True else: save_figure = False - # ax.pcolormesh(dff_traces, cmap='magma', vmin=0, vmax=0.5) ax = sns.heatmap(dff_traces, cmap='magma', vmin=0, vmax=0.5, cbar_kws={'label': 'dF/F'}, ax=ax) ax.set_ylim(-0.5, dff_traces.shape[0] + 0.5) ax.set_ylabel('cells') @@ -357,21 +356,24 @@ def plot_motion_correction_xy_shift_for_experiment(ophys_experiment_id, ax=None) ax.set_xlabel('time (sec)') ax.set_ylabel('pixels') # get metrics from saved file and add to plot title - save_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/motion_correction' - # motion_df = pd.read_csv(os.path.join(save_dir, 'motion_correction_values_passing_experiments.csv')) - motion_df = pd.read_hdf(os.path.join(save_dir, 'motion_correction_values_all_experiments.h5'), key='df') - motion_df = motion_df.set_index('ophys_experiment_id') - cols_to_plot = ['x_mean', 'x_min', 'x_max', 'x_range', 'x_std', - 'y_mean', 'y_min', 'y_max', 'y_range', 'y_std'] - row_data = motion_df.loc[ophys_experiment_id] - title = str(ophys_experiment_id) + ' - ' - for col in cols_to_plot: # plot all metric values - title = title + col + ': ' + str(np.round(row_data[col], 2)) + ', ' - if len(row_data.values_over_threshold) > 0: - title = title + '\n outlier for: ' - for col in row_data.values_over_threshold: - title = title + col + ', ' - ax.set_title(title) + try: + save_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/motion_correction' + # motion_df = pd.read_csv(os.path.join(save_dir, 'motion_correction_values_passing_experiments.csv')) + motion_df = pd.read_hdf(os.path.join(save_dir, 'motion_correction_values_all_experiments.h5'), key='df') + motion_df = motion_df.set_index('ophys_experiment_id') + cols_to_plot = ['x_mean', 'x_min', 'x_max', 'x_range', 'x_std', + 'y_mean', 'y_min', 'y_max', 'y_range', 'y_std'] + row_data = motion_df.loc[ophys_experiment_id] + title = str(ophys_experiment_id) + ' - ' + for col in cols_to_plot: # plot all metric values + title = title + col + ': ' + str(np.round(row_data[col], 2)) + ', ' + if len(row_data.values_over_threshold) > 0: + title = title + '\n outlier for: ' + for col in row_data.values_over_threshold: + title = title + col + ', ' + ax.set_title(title) + except: + print('cant get motion metrics from file in', save_dir) if save_figure: utils.save_figure(fig, figsize, loading.get_experiment_plots_dir(), 'motion_correction_xy_shift', get_metadata_string(dataset)) return ax @@ -596,12 +598,11 @@ def plot_behavior_timeseries_for_experiment(ophys_experiment_id, xlim_seconds=No if save_figure: utils.save_figure(fig, figsize, utils.get_experiment_plots_dir(), 'population_activity_and_behavior', get_metadata_string(dataset) + '_population_activity_and_behavior') - plt.close() return ax def plot_high_low_snr_trace_examples(experiment_id, xlim_seconds=None, plot_stimuli=False, ax=None): - dataset = loading.get_ophys_dataset(experiment_id, include_invalid_rois=False) + dataset = loading.get_ophys_dataset(experiment_id) if xlim_seconds is None: xlim_seconds = [dataset.ophys_timestamps[0], dataset.ophys_timestamps[-1]] # inds = [0, len(dataset.ophys_timestamps) - 1] @@ -687,7 +688,7 @@ def plot_motion_correction_and_population_average(experiment_id, ax=None): def plot_remaining_decrosstalk_masks_for_experiment(experiment_id, ax=None): - dataset = loading.get_ophys_dataset(experiment_id, include_invalid_rois=True) + dataset = loading.get_ophys_dataset(experiment_id, exclude_invalid_rois=False) remaining_crosstalk_dict = loading.get_remaining_crosstalk_amount_dict(experiment_id) decrosstalk_rois = [int(cell_roi_id) for cell_roi_id in list(remaining_crosstalk_dict.keys())] roi_masks = {k: dataset.roi_masks[k] for k in decrosstalk_rois} @@ -910,7 +911,7 @@ def plot_classifier_validation_for_experiment(ophys_experiment_id, save_figure=T cell_table = get_suite2p_rois(segmentation_output_file) cell_table['experiment_id'] = expt # move suite2P masks to the proper place - dataset = loading.get_ophys_dataset(expt, include_invalid_rois=True) + dataset = loading.get_ophys_dataset(expt, exclude_invalid_rois=False) # cell_table = place_masks_in_full_image(cell_table, dataset.max_projection.data) # merge with classifier results cell_table = cell_table.merge(data, on=['experiment_id', 'id']) @@ -922,7 +923,7 @@ def plot_classifier_validation_for_experiment(ophys_experiment_id, save_figure=T # limit to classifier results for this experiment expt_data = data[data.experiment_id == expt].copy() # get production segmentation & classification from SDK - # dataset = loading.get_ophys_dataset(expt, include_invalid_rois=True) + # dataset = loading.get_ophys_dataset(expt, exclude_invalid_rois=False) ct = dataset.cell_specimen_table.copy() roi_masks = dataset.roi_masks.copy() max_projection = dataset.max_projection.data @@ -1093,8 +1094,8 @@ def plot_metrics_mask(roi_mask_dict, metrics_dict, metric_name, max_projection=N return ax -def plot_metrics_mask_for_experiment(ophys_experiment_id, metric, include_invalid_rois=True, ax=None): - dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=include_invalid_rois) +def plot_metrics_mask_for_experiment(ophys_experiment_id, metric, exclude_invalid_rois=True, ax=None): + dataset = loading.get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=exnclude_invalid_rois) cell_table = dataset.cell_specimen_table.copy() metrics_df = loading.get_metrics_df(ophys_experiment_id) @@ -1121,8 +1122,8 @@ def plot_metrics_mask_for_experiment(ophys_experiment_id, metric, include_invali return ax -def plot_filtered_masks_for_experiment(ophys_experiment_id, include_invalid_rois=True, ax=None): - dataset = loading.get_ophys_dataset(ophys_experiment_id, include_invalid_rois=include_invalid_rois) +def plot_filtered_masks_for_experiment(ophys_experiment_id, exclude_invalid_rois=False, ax=None): + dataset = loading.get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=exclude_invalid_rois) max_projection = dataset.max_projection.data cell_table = dataset.cell_specimen_table.copy() metrics_df = loading.get_metrics_df(ophys_experiment_id) diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index e53372632..e7d496414 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -7,6 +7,7 @@ def main(): # "ophys_session_sequence": cp.plot_container_session_sequence, # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, # "average_images": cp.plot_sdk_average_images_for_container, + "experiment_summary": cp.plot_experiment_summary_figure_for_container, "segmentation_masks": cp.plot_segmentation_masks_for_container, "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, From 7bf36f778632c937dfdd049a6d3dbaec40193b71 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:48:47 -0700 Subject: [PATCH 097/187] run for gold, silver and bronze --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 63934d080..11992054c 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -25,7 +25,7 @@ experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # experiments = experiments[experiments.mouse_id=='612764'] -experiments = experiments[experiments.mouse_id=='603892'] +experiments = experiments[experiments.mouse_id.isin(['603892', '612764'])] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From b0336bb9288e80f692f4f22684825f1ad6a1db3a Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 16:48:55 -0700 Subject: [PATCH 098/187] ditto --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 11992054c..53ee62105 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -25,7 +25,7 @@ experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # experiments = experiments[experiments.mouse_id=='612764'] -experiments = experiments[experiments.mouse_id.isin(['603892', '612764'])] +experiments = experiments[experiments.mouse_id.isin(['603892', '612764', '624942'])] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": From 6457aadacc4857410cb93cf766ad5790b10dd87b Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 17:03:05 -0700 Subject: [PATCH 099/187] try exp summary again --- visual_behavior/visualization/qc/experiment_summary.py | 2 +- visual_behavior/visualization/qc/save_all_container_plots.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/visual_behavior/visualization/qc/experiment_summary.py b/visual_behavior/visualization/qc/experiment_summary.py index b6455b687..339e36a1d 100644 --- a/visual_behavior/visualization/qc/experiment_summary.py +++ b/visual_behavior/visualization/qc/experiment_summary.py @@ -117,7 +117,7 @@ def plot_experiment_summary_figure(experiment_id, save_figure=True): ax['4_3:'] = ep.plot_behavior_timeseries_for_experiment(experiment_id, xlim_seconds=xlim_seconds, ax=ax['4_3:']) fig.tight_layout() - title = dataset.metadata_string + title = ep.get_metadata_string(dataset) plt.suptitle(title, x=0.5, y=.91, fontsize=20) if save_figure: # save_dir = r'\\allen\programs\braintv\workgroups\nc-ophys\visual_behavior\qc_plots\experiment_plots' diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index e7d496414..4f2128183 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -8,8 +8,8 @@ def main(): # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, # "average_images": cp.plot_sdk_average_images_for_container, "experiment_summary": cp.plot_experiment_summary_figure_for_container, - "segmentation_masks": cp.plot_segmentation_masks_for_container, - "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + # "segmentation_masks": cp.plot_segmentation_masks_for_container, + # "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, # "average_images_movies": cp.plot_movie_average_images_for_container, # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, From 08a7d63cb740da0225747e1bf502c40644d0f9f9 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 17:18:12 -0700 Subject: [PATCH 100/187] clean up paths --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 0bff570af..9d1d728f5 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -73,7 +73,6 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/learning_mFISH/learning_project_cache' cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir @@ -81,6 +80,7 @@ def get_production_cache_dir(): def get_qc_plots_dir(): return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots' + # return r'\\allen\programs\mindscope\workgroups\learning\ophys\qc_plots' def get_super_container_plots_dir(): From c02e04a68acddf8c50815139c4322e1b2a80a692 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 30 Jun 2022 20:10:32 -0700 Subject: [PATCH 101/187] QC plots for LAMF dev mouse --- .../visualization/qc/run_save_all_container_plots.py | 7 ++++--- .../visualization/qc/save_all_container_plots.py | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 53ee62105..67c446908 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -23,9 +23,10 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) -experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] -# experiments = experiments[experiments.mouse_id=='612764'] -experiments = experiments[experiments.mouse_id.isin(['603892', '612764', '624942'])] +# experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +# experiments = experiments[experiments.mouse_id.isin(['603892', '612764', '624942'])] +experiments = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] +experiments = experiments[experiments.mouse_id.isin(['582466'])] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index 4f2128183..a9e01e89f 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -4,17 +4,17 @@ def main(): possible_plots = { - # "ophys_session_sequence": cp.plot_container_session_sequence, - # "max_projection_images": cp.plot_sdk_max_projection_images_for_container, - # "average_images": cp.plot_sdk_average_images_for_container, + "ophys_session_sequence": cp.plot_container_session_sequence, + "max_projection_images": cp.plot_sdk_max_projection_images_for_container, + "average_images": cp.plot_sdk_average_images_for_container, "experiment_summary": cp.plot_experiment_summary_figure_for_container, - # "segmentation_masks": cp.plot_segmentation_masks_for_container, - # "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, + "segmentation_masks": cp.plot_segmentation_masks_for_container, + "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, # "average_images_movies": cp.plot_movie_average_images_for_container, # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, - # "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, + "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, # "nway_match_fraction": cp.plot_nway_match_fraction, # "nway_warp_overlay": cp.plot_nway_warp_overlay, From 19938d7d17af238a82a158a451d5aab943973a1c Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 14 Jul 2022 14:52:03 -0700 Subject: [PATCH 102/187] run save all container plots --- .../qc/run_save_all_container_plots.py | 6 +- .../qc/save_all_container_plots.py | 70 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 67c446908..01bafd847 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -23,10 +23,10 @@ cache = VisualBehaviorOphysProjectCache.from_lims() experiments_table = cache.get_ophys_experiment_table(passed_only=False) -# experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # experiments = experiments[experiments.mouse_id.isin(['603892', '612764', '624942'])] -experiments = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] -experiments = experiments[experiments.mouse_id.isin(['582466'])] +# experiments = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] +# experiments = experiments[experiments.mouse_id.isin(['582466'])] container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": diff --git a/visual_behavior/visualization/qc/save_all_container_plots.py b/visual_behavior/visualization/qc/save_all_container_plots.py index a9e01e89f..71c31045b 100644 --- a/visual_behavior/visualization/qc/save_all_container_plots.py +++ b/visual_behavior/visualization/qc/save_all_container_plots.py @@ -10,42 +10,42 @@ def main(): "experiment_summary": cp.plot_experiment_summary_figure_for_container, "segmentation_masks": cp.plot_segmentation_masks_for_container, "cell_roi_and_dff_traces": cp.plot_cell_rois_and_dff_traces_for_container, - # "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, - # "average_images_movies": cp.plot_movie_average_images_for_container, - # "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, - # "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, + "max_projection_images_movies": cp.plot_movie_max_projection_images_for_container, + "average_images_movies": cp.plot_movie_average_images_for_container, + "segmented_rois_by_experiment": cp.plot_number_segmented_rois_for_container, + "segmentation_mask_overlays": cp.plot_segmentation_mask_overlays_for_container, "dff_traces_heatmaps": cp.plot_dff_traces_heatmaps_for_container, - # "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, - # "nway_match_fraction": cp.plot_nway_match_fraction, - # "nway_warp_overlay": cp.plot_nway_warp_overlay, - # "nway_warp_summary": cp.plot_nway_warp_summary, - # "number_matched_cells": cp.plot_number_matched_cells_for_container, - # "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, - # "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, - # "cell_matching_registration_output": cp.plot_cell_matching_registration_output, - # "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, - # "experiment_summary": cp.plot_experiment_summary_figure_for_container, - # "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, - # "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, - # "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, - # "pupil_timeseries": cp.plot_pupil_timeseries_for_container, - # "running_speed": cp.plot_running_speed_for_container, - # "lick_rasters": cp.plot_lick_rasters_for_container, - # "behavior_summary": cp.plot_behavior_summary, - # "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, - # "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, - # "pupil_area_sdk": cp.plot_pupil_area_sdk, - # "pupil_area": cp.plot_pupil_area, - # "pupil_position": cp.plot_pupil_position, - # "FOV_average_intensity": cp.plot_average_intensity_for_container, - # "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, - # "pmt_settings": cp.plot_pmt_for_container, - # "snr_by_pmt": cp.plot_snr_by_pmt_for_container, - # "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, - # "cell_snr_by_experiment": cp.plot_cell_snr_for_container, - # "event_detection": cp.plot_event_detection_for_container, - # "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, - # "event_triggered_averages": cp.plot_event_triggered_averages_for_container, + "motion_correction_xy_shift": cp.plot_motion_correction_xy_shift_for_container, + "nway_match_fraction": cp.plot_nway_match_fraction, + "nway_warp_overlay": cp.plot_nway_warp_overlay, + "nway_warp_summary": cp.plot_nway_warp_summary, + "number_matched_cells": cp.plot_number_matched_cells_for_container, + "fraction_matched_cells": cp.plot_fraction_matched_cells_for_container, + "cell_matching_registration_overlay_grid": cp.plot_cell_matching_registration_overlay_grid, + "cell_matching_registration_output": cp.plot_cell_matching_registration_output, + "OphysRegistrationSummaryImage": cp.plot_OphysRegistrationSummaryImage, + "experiment_summary": cp.plot_experiment_summary_figure_for_container, + "population_average_across_sessions_omission": cp.plot_omission_population_average_across_sessions, + "population_average_across_sessions_trials": cp.plot_trials_population_average_across_sessions, + "population_average_across_sessions_stimulus": cp.plot_stimulus_population_average_across_sessions, + "pupil_timeseries": cp.plot_pupil_timeseries_for_container, + "running_speed": cp.plot_running_speed_for_container, + "lick_rasters": cp.plot_lick_rasters_for_container, + "behavior_summary": cp.plot_behavior_summary, + "traces_and_behavior": cp.plot_dff_trace_and_behavior_for_container, + "eye_tracking_sample_frames": cp.plot_eye_tracking_sample_frames, + "pupil_area_sdk": cp.plot_pupil_area_sdk, + "pupil_area": cp.plot_pupil_area, + "pupil_position": cp.plot_pupil_position, + "FOV_average_intensity": cp.plot_average_intensity_for_container, + "average_intensity_timeseries": cp.plot_average_intensity_timeseries_for_container, + "pmt_settings": cp.plot_pmt_for_container, + "snr_by_pmt": cp.plot_snr_by_pmt_for_container, + "snr_by_pmt_and_intensity": cp.plot_snr_by_pmt_gain_and_intensity_for_container, + "cell_snr_by_experiment": cp.plot_cell_snr_for_container, + "event_detection": cp.plot_event_detection_for_container, + "single_cell_response_plots": cp.plot_single_cell_response_plots_for_container, + "event_triggered_averages": cp.plot_event_triggered_averages_for_container, # "roi_filtering_metrics_all_cells": cp.plot_roi_filtering_metrics_for_all_rois_for_container, # "roi_filtering_metrics_valid_cells": cp.plot_roi_filtering_metrics_for_valid_rois_for_container, # "filtered_roi_masks": cp.plot_filtered_roi_masks_for_container, From d800a02a5d96103a47711c8fc3cfeee3c9fab765 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 19 Jul 2022 18:19:39 -0700 Subject: [PATCH 103/187] add sesion type color map --- visual_behavior/visualization/utils.py | 93 ++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index 253702391..d37a8f09a 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -74,19 +74,97 @@ def get_cre_line_colors(): return colors +def get_stimulus_color_map(as_rgb=False): + session_number_colors = get_colors_for_session_numbers() + session_number_colors_GH = get_colors_for_session_numbers_GH() + black = np.array([0, 0, 0]).astype(np.uint8) + + stimulus_color_map = { + 'gratings_static': (0.25, 0.25, 0.25), + 'gratings_flashed': (0.5, 0.5, 0.5), + 'images_A': session_number_colors[0], + 'images_A_passive': session_number_colors[2], + 'images_A_habituation': session_number_colors[0], + 'images_B': session_number_colors[3], + 'images_B_passive': session_number_colors[5], + 'images_B_habituation': session_number_colors[3], + 'images_G': session_number_colors_GH[0], + 'images_G_passive': session_number_colors_GH[2], + 'images_G_habituation': session_number_colors_GH[0], + 'images_H': session_number_colors_GH[3], + 'images_H_passive': session_number_colors_GH[5], + } + + if as_rgb: + for key in list(stimulus_color_map.keys()): + stimulus_color_map[key] = np.floor(np.array([x for x in list(stimulus_color_map[key])]) * 255).astype( + np.uint8) + + return stimulus_color_map + + +def get_stimulus_phase_color_map(as_rgb=False): + session_number_colors = get_colors_for_session_numbers() + session_number_colors_GH = get_colors_for_session_numbers_GH() + white = np.array([1, 1, 1]).astype(np.uint8) + + training_scale = 0.7 + passive_scale = 0.4 + + stimulus_phase_color_map = { + 'gratings_static_training': (0.4, 0.4, 0.4), + 'gratings_flashed_training': (0.7, 0.7, 0.7), + 'images_A_training': (session_number_colors[0] + (white-session_number_colors[0]) * training_scale), + 'images_A_habituation_ophys': (session_number_colors[0] + (white-session_number_colors[0]) * training_scale), + 'images_A_ophys': session_number_colors[0], + 'images_A_passive_ophys': (session_number_colors[0] + (white-session_number_colors[0]) * passive_scale), + 'images_B_training': (session_number_colors[3] + (white-session_number_colors[3]) * training_scale), + 'images_B_habituation_ophys': (session_number_colors[3] + (white - session_number_colors[3]) * training_scale), + 'images_B_ophys': session_number_colors[3], + 'images_B_passive_ophys': (session_number_colors[3] + (white-session_number_colors[3]) * passive_scale), + 'images_G_training': (session_number_colors_GH[0] + (white-session_number_colors_GH[0]) * training_scale), + 'images_G_habituation_ophys': (session_number_colors_GH[0] + (white - session_number_colors_GH[0]) * training_scale), + 'images_G_ophys': session_number_colors_GH[0], + 'images_G_passive_ophys': (session_number_colors_GH[0] + (white-session_number_colors_GH[0]) * passive_scale), + 'images_H_ophys': session_number_colors_GH[3], + 'images_H_passive_ophys': (session_number_colors_GH[3] + (white-session_number_colors_GH[3]) * passive_scale), + } + + if as_rgb: + for key in list(stimulus_phase_color_map.keys()): + stimulus_phase_color_map[key] = np.floor( + np.array([x for x in list(stimulus_phase_color_map[key])]) * 255).astype(np.uint8) + + return stimulus_phase_color_map + + def get_session_type_color_map(): colors = np.floor(np.array([list(x) for x in get_colors_for_session_numbers()]) * 255).astype(np.uint8) black = np.array([0, 0, 0]).astype(np.uint8) - session_type_color_map = { - 'TRAINING_0_gratings_autorewards_15min': lighter(black, 0.1), + session_type_color_map = { + 'TRAINING_0_gratings_autorewards_15min': black, 'TRAINING_1_gratings': lighter(black, 0.2), - 'TRAINING_2_gratings_flashed': lighter(black, 0.3), - 'TRAINING_3_images_A_10uL_reward': lighter(black, 0.4), - 'TRAINING_4_images_A_training': lighter(black, 0.5), - 'TRAINING_5_images_A_epilogue': lighter(black, 0.6), - 'TRAINING_5_images_A_handoff_ready': lighter(black, 0.7), + 'TRAINING_2_gratings_flashed': lighter(black, 0.4), + 'TRAINING_3_images_A_10uL_reward': lighter(black, 0.6), + 'TRAINING_3_images_B_10uL_reward': lighter(black, 0.6), + 'TRAINING_3_images_G_10uL_reward': lighter(black, 0.6), + 'TRAINING_4_images_A_handoff_lapsed': lighter(black, 0.8), + 'TRAINING_4_images_A_handoff_ready': lighter(black, 0.8), + 'TRAINING_4_images_A_training': lighter(black, 0.8), + 'TRAINING_4_images_B_training': lighter(black, 0.8), + 'TRAINING_4_images_G_training': lighter(black, 0.8), + 'TRAINING_5_images_A_epilogue': lighter(black, 0.8), + 'TRAINING_5_images_A_handoff_lapsed': lighter(black, 0.8), + 'TRAINING_5_images_A_handoff_ready': lighter(black, 0.8), + 'TRAINING_5_images_B_epilogue': lighter(black, 0.8), + 'TRAINING_5_images_B_handoff_lapsed': lighter(black, 0.8), + 'TRAINING_5_images_B_handoff_ready': lighter(black, 0.8), + 'TRAINING_5_images_G_epilogue': lighter(black, 0.8), + 'TRAINING_5_images_G_handoff_lapsed': lighter(black, 0.8), + 'TRAINING_5_images_G_handoff_ready': lighter(black, 0.8), + 'OPHYS_0_images_A_habituation': lighter(colors[0, :], 0.8), 'OPHYS_1_images_A': colors[0, :], @@ -121,6 +199,7 @@ def get_session_type_color_map(): return session_type_color_map + def get_location_color(location, project_code): colors = sns.color_palette() if (project_code == 'VisualBehavior') or (project_code == 'VisualBehaviorTask1B'): From eea7df6b37627d309b5e569a0edf83701e7b4fb0 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 12 Sep 2022 14:49:29 -0700 Subject: [PATCH 104/187] include omFISH 210 mice for processing --- scripts/run_create_multi_session_df.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 600fc95cf..0ce9c0c5a 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -28,13 +28,15 @@ stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/multi_session_dfs" -# cache = VisualBehaviorOphysProjectCache.from_lims() -# experiments_table = cache.get_ophys_experiment_table(passed_only=False) -# experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] -# experiments_table = experiments_table[experiments_table.session_type!='OPHYS_7_receptive_field_mapping'] - -experiments_table = loading.get_filtered_ophys_experiment_table() -experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +cache = VisualBehaviorOphysProjectCache.from_lims() +experiments_table = cache.get_ophys_experiment_table(passed_only=False) +experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment', + 'omFISHGad2Meso'])] +experiments_table = experiments_table[experiments_table.session_type.isin(['STAGE_0', 'STAGE_1', + 'OPHYS_7_receptive_field_mapping'])==False] + +# experiments_table = loading.get_filtered_ophys_experiment_table() +# experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): From 216f6725c03ec0277efdebca02b05bed21fe1669 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 12 Sep 2022 14:52:09 -0700 Subject: [PATCH 105/187] lint --- scripts/run_create_multi_session_df.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 0ce9c0c5a..aa2dd7622 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -49,9 +49,8 @@ cpus_per_task=1, time='20:00:00', partition='braintv', - job_name='multi_session_df_'+project_code+'_'+mouse_id, - output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', - ) + job_name='multi_session_df_'+project_code+'_'+str(mouse_id), + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out') slurm.sbatch(python_executable+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) From 313a6b1f246f9d35c970b068f39149fec82696b1 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 12 Sep 2022 14:54:37 -0700 Subject: [PATCH 106/187] lint --- scripts/run_create_multi_session_df.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index aa2dd7622..050529f77 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -50,7 +50,8 @@ time='20:00:00', partition='braintv', job_name='multi_session_df_'+project_code+'_'+str(mouse_id), - output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out') + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out',) + slurm.sbatch(python_executable+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) From 5ac96a5bc52d637db05e7ac4eca28b2f51f0c156 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 12 Sep 2022 14:57:11 -0700 Subject: [PATCH 107/187] weird --- scripts/run_create_multi_session_df.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 050529f77..1112bea56 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -44,13 +44,13 @@ for mouse_id in experiments_table.mouse_id.unique(): print(mouse_id) # instantiate a Slurm object - slurm = Slurm( - mem='120g', # '24g' - cpus_per_task=1, - time='20:00:00', - partition='braintv', - job_name='multi_session_df_'+project_code+'_'+str(mouse_id), - output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out',) + slurm = Slurm(mem='120g', # '24g' + cpus_per_task=1, + time='20:00:00', + partition='braintv', + job_name='multi_session_df_'+project_code+'_'+str(mouse_id), + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', + ) slurm.sbatch(python_executable+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) From 20c4a3dc313be268ef3d12192bdbbc10ebe5bef8 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 25 Sep 2022 13:50:29 -0700 Subject: [PATCH 108/187] load expts table from file --- scripts/create_multi_session_df.py | 46 ++++++++++--------- scripts/run_create_multi_session_df.py | 17 ++++--- .../ophys/io/create_multi_session_df.py | 9 +++- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 77d9d5549..559c8b98f 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -20,43 +20,45 @@ print(project_code, mouse_id) # params for stim response df creation - time_window = [-3, 3.1] + time_window = [-2, 2.1] interpolate = True output_sampling_rate = 30 use_extended_stimulus_presentations = False # set up conditions to make multi session dfs for - physio_data_types = ['dff', 'filtered_events', 'events'] + physio_data_types = ['dff',]# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] - physio_conditions = [['cell_specimen_id', 'is_change'], + physio_conditions = [['cell_specimen_id', 'is_change', 'omitted'], + ['cell_specimen_id', 'is_change'], ['cell_specimen_id', 'omitted'], - ['cell_specimen_id', 'is_change', 'epoch'], - ['cell_specimen_id', 'omitted', 'epoch'], + # ['cell_specimen_id', 'is_change', 'epoch'], + # ['cell_specimen_id', 'omitted', 'epoch'], ['cell_specimen_id', 'is_change', 'image_name'], - ['cell_specimen_id', 'is_change', 'image_name', 'epoch'], - ['cell_specimen_id', 'is_change', 'hit'], - ['cell_specimen_id', 'pre_change', 'epoch'], - ['cell_specimen_id', 'is_change', 'hit', 'epoch'], - ['cell_specimen_id', 'omitted', 'pre_omitted'],] + # ['cell_specimen_id', 'is_change', 'image_name', 'epoch'], + ['cell_specimen_id', 'is_change', 'hit'],] + # ['cell_specimen_id', 'pre_change', 'epoch'], + # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], + # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['ophys_experiment_id', 'is_change'], + behavior_conditions = [['cell_specimen_id', 'is_change', 'omitted'], + ['ophys_experiment_id', 'is_change'], ['ophys_experiment_id', 'omitted'], - ['ophys_experiment_id', 'is_change', 'epoch'], - ['ophys_experiment_id', 'omitted', 'epoch'], + # ['ophys_experiment_id', 'is_change', 'epoch'], + # ['ophys_experiment_id', 'omitted', 'epoch'], ['ophys_experiment_id', 'is_change', 'image_name'], - ['ophys_experiment_id', 'is_change', 'image_name', 'epoch'], - ['ophys_experiment_id', 'is_change', 'hit'], - ['ophys_experiment_id', 'is_change', 'pre_change', 'epoch'], - ['ophys_experiment_id', 'is_change', 'hit', 'epoch'], - ['cell_specimen_id', 'omitted', 'pre_omitted'],] + # ['ophys_experiment_id', 'is_change', 'image_name', 'epoch'], + ['ophys_experiment_id', 'is_change', 'hit'],] + # ['ophys_experiment_id', 'is_change', 'pre_change', 'epoch'], + # ['ophys_experiment_id', 'is_change', 'hit', 'epoch'], + # ['cell_specimen_id', 'omitted', 'pre_omitted'],] # event types corresponding to the above physio and behavior conditions - must be in same sequential order!! - event_types_for_conditions = ['changes', 'omissions', - 'changes', 'omissions', - 'changes', 'changes', 'changes', - 'all', 'all', 'all'] + event_types_for_conditions = ['all', 'changes', 'omissions', + # 'changes', 'omissions', + 'changes', 'changes',] #'changes', + # 'all', 'all', 'all'] # add engagement state to all conditions # for i in range(len(physio_conditions)): diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 1112bea56..c1ae902bb 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -28,12 +28,17 @@ stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/multi_session_dfs" -cache = VisualBehaviorOphysProjectCache.from_lims() -experiments_table = cache.get_ophys_experiment_table(passed_only=False) -experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment', - 'omFISHGad2Meso'])] -experiments_table = experiments_table[experiments_table.session_type.isin(['STAGE_0', 'STAGE_1', - 'OPHYS_7_receptive_field_mapping'])==False] +# cache = VisualBehaviorOphysProjectCache.from_lims() +# experiments_table = cache.get_ophys_experiment_table(passed_only=False) +# experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment', +# 'omFISHGad2Meso'])] +# experiments_table = experiments_table[experiments_table.session_type.isin(['STAGE_0', 'STAGE_1', +# 'OPHYS_7_receptive_field_mapping'])==False] + +save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' +import pandas as pd +experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) +print(len(experiments_table)) # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 990a9e0ca..b254e8cae 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -61,8 +61,13 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty get_pref_stim = False print('get_pref_stim', get_pref_stim) - experiments_table = loading.get_filtered_ophys_experiment_table() - experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + # experiments_table = loading.get_filtered_ophys_experiment_table() + # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + print(len(experiments_table)) + print(len(experiments_table), 'expts in experiments table') # session_type = float(session_type) From d42acf0bba9b46a66bab68dfab35c889aa1b8f47 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 25 Sep 2022 14:02:07 -0700 Subject: [PATCH 109/187] debug --- scripts/create_multi_session_df.py | 6 +++--- visual_behavior/ophys/io/create_multi_session_df.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 559c8b98f..5fddfd3e7 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -92,15 +92,15 @@ for i, conditions in enumerate(behavior_conditions): event_type = event_types_for_conditions[i] if event_type == 'omissions': - response_window_duration_seconds = 0.75 + response_window_duration = 0.75 else: - response_window_duration_seconds = 0.5 + response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration_seconds=response_window_duration_seconds, + response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, overwrite=True) except Exception as e: diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index b254e8cae..2a02a3de8 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -75,11 +75,13 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty (experiments_table.mouse_id == str(mouse_id))].copy() mouse_id = experiments.mouse_id.unique()[0] + print(mouse_id) filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) + print(filepath) if not overwrite: # if we dont want to overwrite if os.path.exists(filepath): # and file exists, dont regenerate From 2766f9b9914fdfbb2eb00b96c4a5dee789c2f82c Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 25 Sep 2022 14:06:19 -0700 Subject: [PATCH 110/187] debug --- scripts/create_multi_session_df.py | 2 +- visual_behavior/ophys/io/create_multi_session_df.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 5fddfd3e7..9508e9f63 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -41,7 +41,7 @@ # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['cell_specimen_id', 'is_change', 'omitted'], + behavior_conditions = [['ophys_experiment_id', 'is_change', 'omitted'], ['ophys_experiment_id', 'is_change'], ['ophys_experiment_id', 'omitted'], # ['ophys_experiment_id', 'is_change', 'epoch'], diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 2a02a3de8..9cd32d62b 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -71,6 +71,7 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty print(len(experiments_table), 'expts in experiments table') # session_type = float(session_type) + print(project_code, mouse_id) experiments = experiments_table[(experiments_table.project_code == project_code) & (experiments_table.mouse_id == str(mouse_id))].copy() From d54441bae210fea8202193daa55c18b795ab5589 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 25 Sep 2022 14:21:46 -0700 Subject: [PATCH 111/187] mouse_id as int --- scripts/run_create_multi_session_df.py | 3 ++- visual_behavior/ophys/io/create_multi_session_df.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index c1ae902bb..23cfe6390 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -46,7 +46,8 @@ # call the `sbatch` command to run the jobs. for project_code in experiments_table.project_code.unique(): print(project_code) - for mouse_id in experiments_table.mouse_id.unique(): + mouse_ids = experiments_table[experiments_table.project_code==project_code].mouse_id.unique() + for mouse_id in mouse_ids: print(mouse_id) # instantiate a Slurm object slurm = Slurm(mem='120g', # '24g' diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 9cd32d62b..de7bffdb1 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -73,9 +73,10 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty # session_type = float(session_type) print(project_code, mouse_id) experiments = experiments_table[(experiments_table.project_code == project_code) & - (experiments_table.mouse_id == str(mouse_id))].copy() + (experiments_table.mouse_id == int(mouse_id))] + print(len(experiments)) - mouse_id = experiments.mouse_id.unique()[0] + # mouse_id = experiments.mouse_id.unique()[0] print(mouse_id) filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) From de3673c35f08f8ff8f8d0e46adada0a396a3ffc3 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 25 Sep 2022 14:24:01 -0700 Subject: [PATCH 112/187] set index properly --- visual_behavior/ophys/io/create_multi_session_df.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index de7bffdb1..5e231be05 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -66,6 +66,7 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table)) print(len(experiments_table), 'expts in experiments table') From f6ea71bbfac600f8788262e2ffa6bbde17fa282f Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 13:43:07 -0700 Subject: [PATCH 113/187] updates to loading.get_data_dict() --- visual_behavior/data_access/loading.py | 41 +++++++++++++++----------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 9d1d728f5..1731c0f92 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -291,6 +291,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o Returns: experiment_table -- returns a dataframe with ophys_experiment_id as the index and metadata as columns. """ + if include_failed_data is True: release_data_only = False if release_data_only: @@ -315,6 +316,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o # get everything in lims cache = bpc.from_lims() experiments = cache.get_ophys_experiment_table(passed_only=False) + print(len(experiments), 'experiments in ophys_experiment_table') # limit to the 4 VisualBehavior project codes # experiments = filtering.limit_to_production_project_codes(experiments) if add_extra_columns: @@ -2786,8 +2788,6 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o multi_session_df = pd.DataFrame() for project_code in project_codes: experiments = experiments_table[(experiments_table.project_code == project_code)] - if project_code == 'VisualBehaviorMultiscope': - experiments = experiments[experiments.session_type != 'OPHYS_2_images_B_passive'] for mouse_id in np.sort(experiments.mouse_id.unique()): try: filename = get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) @@ -3229,10 +3229,11 @@ def get_data_dict(ophys_experiment_ids, data_types=None): data_types = ['filtered_events', 'running_speed', 'pupil_width', 'lick_rate'] # get cache from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache - cache_dir = get_platform_analysis_cache_dir() - cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir) + # cache_dir = get_platform_analysis_cache_dir() + # cache = VisualBehaviorOphysProjectCache.from_s3_cache(cache_dir) + cache = VisualBehaviorOphysProjectCache.from_lims() # define params - time_window = [-3, 3.1] + time_window = [-2, 2.1] interpolate = True output_sampling_rate = 30 # set up dict to collect data in @@ -3245,20 +3246,26 @@ def get_data_dict(ophys_experiment_ids, data_types=None): # aggregate data for ophys_experiment_id in ophys_experiment_ids: - + print('loading dataset for', ophys_experiment_id) # dataset = get_ophys_dataset(ophys_experiment_id) - dataset = cache.get_behavior_ophys_experiment(ophys_experiment_id) - data_dict[ophys_experiment_id]['dataset']['dataset'] = dataset + try: + dataset = cache.get_behavior_ophys_experiment(ophys_experiment_id) + data_dict[ophys_experiment_id]['dataset']['dataset'] = dataset - for data_type in data_types: - try: - sdf = get_stimulus_response_df(dataset, data_type=data_type, event_type='all', - time_window=time_window, interpolate=interpolate, - output_sampling_rate=output_sampling_rate, - load_from_file=True) - data_dict[ophys_experiment_id][data_type]['stimulus_response_df'] = sdf - except BaseException: - print('could not get response df for', ophys_experiment_id, data_type) + for data_type in data_types: + try: + print('creating stimulus_response_df for', ophys_experiment_id) + sdf = get_stimulus_response_df(dataset, data_type=data_type, event_type='all', + time_window=time_window, interpolate=interpolate, + output_sampling_rate=output_sampling_rate, + load_from_file=True) + data_dict[ophys_experiment_id][data_type]['stimulus_response_df'] = sdf + except Exception as e: + print(e) + print('could not get response df for', ophys_experiment_id, data_type) + except Exception as e: + print(e) + print('could not get response df for', ophys_experiment_id, data_type) return data_dict From ed8862f4923fcd0e15b445c6111a949a34f12b7a Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 13:43:14 -0700 Subject: [PATCH 114/187] typo --- visual_behavior/visualization/qc/single_cell_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 7aed8bc15..fb87f9cf2 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -71,7 +71,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even # running vs not-running try: - tmp = sdf.cpoy() + tmp = sdf.copy() tmp['running'] = [True if run_speed > 2 else False for run_speed in tmp.mean_running_speed.values] sdf = ut.get_mean_df(tmp, analysis=analysis, conditions=['cell_specimen_id', 'is_change', 'image_name', 'running'], flashes=True, omitted=False, From 70d30b9ee06bc7892b4863974332af6419f594d5 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 13:45:18 -0700 Subject: [PATCH 115/187] change default sdf window --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 1731c0f92..ccaa1d4b7 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -549,7 +549,7 @@ def get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_t return filepath -def get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, +def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, output_sampling_rate=30, data_type='filtered_events', event_type='all', load_from_file=False, exclude_invalid_rois=True): """ load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added From 807b1290673e820a6d763fc8eddd06d16597ff7b Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 15:01:25 -0700 Subject: [PATCH 116/187] update conda --- scripts/run_create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 23cfe6390..055526d54 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -22,7 +22,7 @@ # 'python' # ) -python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), conda_environment) +python_executable = "{}/anaconda3/envs/{}/bin/python".format(os.path.expanduser('~'), conda_environment) # define the job record output folder stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/multi_session_dfs" From c6fffd0f6d9fd9a653158e62d226b6dd57f7ab40 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 15:18:33 -0700 Subject: [PATCH 117/187] use correct conda env --- scripts/run_create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 055526d54..31d19a81e 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -9,7 +9,7 @@ python_file = r"/home/marinag/visual_behavior_analysis/scripts/create_multi_session_df.py" # conda environment to use -conda_environment = 'visual_behavior_sdk' +conda_environment = 'learning_mFISH' # build the python path # this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' From 435adddf60d9ad8b0d53206db418ca3099f6d852 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 15:40:19 -0700 Subject: [PATCH 118/187] cleanup --- visual_behavior/data_access/loading.py | 6 ++++++ visual_behavior/ophys/io/create_multi_session_df.py | 13 ++++--------- .../ophys/response_analysis/utilities.py | 2 +- visual_behavior/visualization/utils.py | 13 ++++++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index ccaa1d4b7..c83ebdef7 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -617,6 +617,12 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, exclude_invalid_rois=exclude_invalid_rois) + # save + try: # some experiments with lots of neurons cant save + sdf.to_hdf(filepath, key='df') + print('saved response df to', filepath) + except: + print('could not save', filepath) # if extended_stimulus_presentations is an attribute of the dataset object, use it, otherwise get regular stimulus_presentations if 'extended_stimulus_presentations' in dir(dataset): diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 5e231be05..4383670e1 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -67,24 +67,19 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') - print(len(experiments_table)) - - print(len(experiments_table), 'expts in experiments table') + print(len(experiments_table), 'experiments in experiments table') # session_type = float(session_type) - print(project_code, mouse_id) + print('project_code:', project_code, ', mouse_id:', mouse_id) experiments = experiments_table[(experiments_table.project_code == project_code) & (experiments_table.mouse_id == int(mouse_id))] - print(len(experiments)) - - # mouse_id = experiments.mouse_id.unique()[0] - print(mouse_id) + print(len(experiments), 'experiments for this mouse_id and project_code') filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) - print(filepath) + print('saving to', filepath) if not overwrite: # if we dont want to overwrite if os.path.exists(filepath): # and file exists, dont regenerate diff --git a/visual_behavior/ophys/response_analysis/utilities.py b/visual_behavior/ophys/response_analysis/utilities.py index 30537acca..656e9f21f 100644 --- a/visual_behavior/ophys/response_analysis/utilities.py +++ b/visual_behavior/ophys/response_analysis/utilities.py @@ -406,7 +406,7 @@ def get_mean_df(response_df, conditions=['cell', 'change_image_name'], frame_rat import visual_behavior.ophys.response_analysis.response_processing as rp - window = time_window + window = time_window.copy() rdf = response_df.copy() diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index d37a8f09a..2bde614d9 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -7,19 +7,19 @@ sns.set_context('notebook', font_scale=1.5, rc={'lines.markeredgewidth': 2}) def get_container_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/container_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/container_plots' def get_session_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/session_plots' + return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/session_plots' def get_experiment_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/experiment_plots' + return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/experiment_plots' def get_single_cell_plots_dir(): - return r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/qc_plots/single_cell_plots' + return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/single_cell_plots' def save_figure(fig, figsize, save_dir, folder, fig_title, formats=['.png']): @@ -407,7 +407,10 @@ def plot_mean_trace_from_mean_df(cell_data, frame_rate=31., ylabel='dF/F', legen if plot_sem: ax.fill_between(timestamps, trace + sem, trace - sem, alpha=0.5, color=color) ax.set_xticks(np.arange(int(timestamps[0]), int(timestamps[-1]) + 1, interval_sec)) - ax.set_xlim([timestamps[0], timestamps[-1]]) + if xlims: + ax.set_xlim(xlims) + else: + ax.set_xlim([timestamps[0], timestamps[-1]]) ax.set_xlabel('time (s)') ax.set_ylabel(ylabel) sns.despine(ax=ax) From 5e9c36902ba68b06621ce60f1957cbbff0d6735a Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 15:46:57 -0700 Subject: [PATCH 119/187] remove try except --- .../ophys/io/create_multi_session_df.py | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 4383670e1..a61889213 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -96,55 +96,56 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty if process_data: mega_mdf = pd.DataFrame() for experiment_id in experiments.index.unique(): - try: - print(experiment_id) - # get dataset - dataset = loading.get_ophys_dataset(experiment_id, - get_extended_stimulus_presentations=use_extended_stimulus_presentations) - # get stimulus_response_df - df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, - interpolate=interpolate, output_sampling_rate=output_sampling_rate, - load_from_file=True) - # use response_window duration from stim response df if it exists - if response_window_duration in df.keys(): - response_window_duration = df.response_window_duration.values[0] - df['ophys_experiment_id'] = experiment_id - # if using omissions, only include omissions where time from last change is more than 3 seconds - # if event_type == 'omissions': - # df = df[df.time_from_last_change>3] - # modify columns for specific conditions - if 'passive' in dataset.metadata['session_type']: - df['lick_on_next_flash'] = False - df['engaged'] = False - df['engagement_state'] = 'disengaged' - if 'running_state' in conditions: # create 'running_state' Boolean column based on threshold on mean_running_speed - df['running'] = [True if mean_running_speed > 2 else False for mean_running_speed in - df.mean_running_speed.values] - if 'pupil_state' in conditions: # create 'pupil_state' Boolean column based on threshold on mean_pupil_ - if 'mean_pupil_area' in df.keys(): - df = df[df.mean_pupil_area.isnull() == False] - if len(df) > 100: - median_pupil_area = df.mean_pupil_area.median() - df['large_pupil'] = [True if mean_pupil_area > median_pupil_area else False for mean_pupil_area in - df.mean_pupil_area.values] - if 'pre_change' in conditions: - df = df[df.pre_change.isnull() == False] - # get params for mean df creation from stimulus_response_df - output_sampling_rate = df.frame_rate.unique()[0] - timestamps = df.trace_timestamps.values[0] - window_around_timepoint_seconds = [timestamps[0], timestamps[-1]] - - mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, - time_window=time_window, response_window_duration=response_window_duration, - get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) - if 'correlation_values' in mdf.keys(): - mdf = mdf.drop(columns=['correlation_values']) - mdf['ophys_experiment_id'] = experiment_id - print('mean df created for', experiment_id) - mega_mdf = pd.concat([mega_mdf, mdf]) - except Exception as e: # flake8: noqa: E722 - print(e) - print('problem for', experiment_id) + # try: + print(experiment_id) + # get dataset + dataset = loading.get_ophys_dataset(experiment_id, + get_extended_stimulus_presentations=use_extended_stimulus_presentations) + # get stimulus_response_df + df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, + interpolate=interpolate, output_sampling_rate=output_sampling_rate, + load_from_file=False) + print('stim response df loaded') + # use response_window duration from stim response df if it exists + if response_window_duration in df.keys(): + response_window_duration = df.response_window_duration.values[0] + df['ophys_experiment_id'] = experiment_id + # if using omissions, only include omissions where time from last change is more than 3 seconds + # if event_type == 'omissions': + # df = df[df.time_from_last_change>3] + # modify columns for specific conditions + if 'passive' in dataset.metadata['session_type']: + df['lick_on_next_flash'] = False + df['engaged'] = False + df['engagement_state'] = 'disengaged' + if 'running_state' in conditions: # create 'running_state' Boolean column based on threshold on mean_running_speed + df['running'] = [True if mean_running_speed > 2 else False for mean_running_speed in + df.mean_running_speed.values] + if 'pupil_state' in conditions: # create 'pupil_state' Boolean column based on threshold on mean_pupil_ + if 'mean_pupil_area' in df.keys(): + df = df[df.mean_pupil_area.isnull() == False] + if len(df) > 100: + median_pupil_area = df.mean_pupil_area.median() + df['large_pupil'] = [True if mean_pupil_area > median_pupil_area else False for mean_pupil_area in + df.mean_pupil_area.values] + if 'pre_change' in conditions: + df = df[df.pre_change.isnull() == False] + # get params for mean df creation from stimulus_response_df + output_sampling_rate = df.frame_rate.unique()[0] + timestamps = df.trace_timestamps.values[0] + window_around_timepoint_seconds = [timestamps[0], timestamps[-1]] + + mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, + time_window=time_window, response_window_duration=response_window_duration, + get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) + if 'correlation_values' in mdf.keys(): + mdf = mdf.drop(columns=['correlation_values']) + mdf['ophys_experiment_id'] = experiment_id + print('mean df created for', experiment_id) + mega_mdf = pd.concat([mega_mdf, mdf]) + # except Exception as e: # flake8: noqa: E722 + # print(e) + # print('problem for', experiment_id) if 'level_0' in mega_mdf.keys(): mega_mdf = mega_mdf.drop(columns='level_0') From 91ae9eaa65e7d448fb3cbb64886b19939591e5d0 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 16:50:55 -0700 Subject: [PATCH 120/187] try using container instead of project code --- scripts/create_multi_session_df.py | 78 +++++----- scripts/run_create_multi_session_df.py | 16 +-- visual_behavior/data_access/loading.py | 15 +- .../ophys/io/create_multi_session_df.py | 136 +++++++++--------- 4 files changed, 122 insertions(+), 123 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 9508e9f63..986d70050 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -11,13 +11,13 @@ if __name__ == '__main__': # define args parser = argparse.ArgumentParser() - parser.add_argument('--project_code', type=str, help='project code to use') + parser.add_argument('--ophys_container_id', type=str, help='ophys_container_id to use') parser.add_argument('--mouse_id', type=str, help='mouse_id to use') args = parser.parse_args() - project_code = args.project_code + ophys_container_id = args.ophys_container_id mouse_id = args.mouse_id - print(project_code, mouse_id) + print('mouse_id:', mouse_id, 'ophys_container_id:', ophys_container_id) # params for stim response df creation time_window = [-2, 2.1] @@ -26,38 +26,38 @@ use_extended_stimulus_presentations = False # set up conditions to make multi session dfs for - physio_data_types = ['dff',]# 'filtered_events', 'events'] + physio_data_types = ['dff', 'events']# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] - physio_conditions = [['cell_specimen_id', 'is_change', 'omitted'], - ['cell_specimen_id', 'is_change'], + physio_conditions = [['cell_specimen_id', 'is_change'], ['cell_specimen_id', 'omitted'], # ['cell_specimen_id', 'is_change', 'epoch'], # ['cell_specimen_id', 'omitted', 'epoch'], ['cell_specimen_id', 'is_change', 'image_name'], # ['cell_specimen_id', 'is_change', 'image_name', 'epoch'], - ['cell_specimen_id', 'is_change', 'hit'],] + ['cell_specimen_id', 'is_change', 'hit'], + ['cell_specimen_id', 'is_change', 'omitted'],] # ['cell_specimen_id', 'pre_change', 'epoch'], # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['ophys_experiment_id', 'is_change', 'omitted'], - ['ophys_experiment_id', 'is_change'], + behavior_conditions = [['ophys_experiment_id', 'is_change'], ['ophys_experiment_id', 'omitted'], # ['ophys_experiment_id', 'is_change', 'epoch'], # ['ophys_experiment_id', 'omitted', 'epoch'], ['ophys_experiment_id', 'is_change', 'image_name'], # ['ophys_experiment_id', 'is_change', 'image_name', 'epoch'], - ['ophys_experiment_id', 'is_change', 'hit'],] + ['ophys_experiment_id', 'is_change', 'hit'], + ['ophys_experiment_id', 'is_change', 'omitted'],] # ['ophys_experiment_id', 'is_change', 'pre_change', 'epoch'], # ['ophys_experiment_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] # event types corresponding to the above physio and behavior conditions - must be in same sequential order!! - event_types_for_conditions = ['all', 'changes', 'omissions', + event_types_for_conditions = ['changes', 'omissions', # 'changes', 'omissions', - 'changes', 'changes',] #'changes', + 'changes', 'changes', 'all', ] #'changes', # 'all', 'all', 'all'] # add engagement state to all conditions @@ -66,30 +66,10 @@ # behavior_conditions[i].insert(1, 'engagement_state') # create dfs for all data types and conditions for physio data - # for data_type in physio_data_types: - data_type = 'dff' - for i, conditions in enumerate(physio_conditions): - print(conditions) - event_type = event_types_for_conditions[i] - if event_type == 'omissions': - response_window_duration = 0.75 - else: - response_window_duration = 0.5 - print('creating multi_session_df for', data_type, event_type, conditions) - try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, - time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - response_window_duration=response_window_duration, - use_extended_stimulus_presentations=use_extended_stimulus_presentations, - overwrite=True) - except Exception as e: - print('failed to create multi_session_df for', data_type, event_type, conditions) - print(e) - - - # create dfs for all data types and conditions for behavior data - for data_type in behavior_data_types: - for i, conditions in enumerate(behavior_conditions): + for data_type in physio_data_types: + # data_type = 'dff' + for i, conditions in enumerate(physio_conditions): + print(conditions) event_type = event_types_for_conditions[i] if event_type == 'omissions': response_window_duration = 0.75 @@ -97,12 +77,32 @@ response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, - time_window=time_window, interpolate=interpolate, - output_sampling_rate=output_sampling_rate, + df = io.get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, + time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, overwrite=True) except Exception as e: print('failed to create multi_session_df for', data_type, event_type, conditions) print(e) + + + # # create dfs for all data types and conditions for behavior data + # for data_type in behavior_data_types: + # for i, conditions in enumerate(behavior_conditions): + # event_type = event_types_for_conditions[i] + # if event_type == 'omissions': + # response_window_duration = 0.75 + # else: + # response_window_duration = 0.5 + # print('creating multi_session_df for', data_type, event_type, conditions) + # try: # use try except so that it skips over any conditions that fail to generate for some reason + # df = io.get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, + # time_window=time_window, interpolate=interpolate, + # output_sampling_rate=output_sampling_rate, + # response_window_duration=response_window_duration, + # use_extended_stimulus_presentations=use_extended_stimulus_presentations, + # overwrite=True) + # except Exception as e: + # print('failed to create multi_session_df for', data_type, event_type, conditions) + # print(e) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 31d19a81e..45e1eea6a 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -38,27 +38,27 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -print(len(experiments_table)) +print(len(experiments_table), 'experiments') # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # call the `sbatch` command to run the jobs. -for project_code in experiments_table.project_code.unique(): - print(project_code) - mouse_ids = experiments_table[experiments_table.project_code==project_code].mouse_id.unique() - for mouse_id in mouse_ids: - print(mouse_id) +for mouse_id in experiments_table.mouse_id.unique(): + print('mouse_id:', mouse_id) + ophys_container_id = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() + for ophys_container_id in ophys_container_id: + print('ophys_container_id:', ophys_container_id) # instantiate a Slurm object slurm = Slurm(mem='120g', # '24g' cpus_per_task=1, time='20:00:00', partition='braintv', - job_name='multi_session_df_'+project_code+'_'+str(mouse_id), + job_name='multi_session_df_'+str(mouse_id)+'_'+str(ophys_container_id), output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', ) - slurm.sbatch(python_executable+' '+python_file+' --project_code '+str(project_code)+' --mouse_id'+' '+str(mouse_id)) + slurm.sbatch(python_executable+' '+python_file+' --mouse_id '+str(mouse_id)+' --ophys_container_id'+' '+str(ophys_container_id)) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index c83ebdef7..f6a04d2f6 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2753,21 +2753,22 @@ def add_superficial_deep_to_experiments_table(experiments_table): return experiments_table -def get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions): +def get_file_name_for_multi_session_df(data_type, event_type, ophys_container_id, mouse_id, conditions): mouse_id = str(mouse_id) + ophys_container_id = str(ophys_container_id) if len(conditions) == 6: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' elif len(conditions) == 4: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' elif len(conditions) == 3: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' elif len(conditions) == 2: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[1] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '.h5' elif len(conditions) == 1: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + project_code + '_' + mouse_id + '_' + conditions[0] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[0] + '.h5' return filename diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index a61889213..09eba0c08 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -6,11 +6,11 @@ from visual_behavior.data_access import loading -def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_type, +def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, ophys_experiment_ids=None, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5, use_extended_stimulus_presentations=False, overwrite=False): """ - For a given mouse_id within a given project_code, loop through all ophys_experiment_ids, load the SDK dataset object, + For a given mouse_id and container, loop through all ophys_experiment_ids, load the SDK dataset object, create stimulus_response_df with event aligned traces for provided data_type (ex: 'dff', 'events', 'pupil_width', etc), then average across a given set of conditions to get a trial averaged trace for those conditions. @@ -27,7 +27,7 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty Function can be run for multiple mouse_ids and/or project_codes using /scripts/run_create_multi_session_df.py - :param project_code: lims project code to use when identifying what experiment_ids to include in the multi_session_df + :param ophys_container_id: ophys_container_id to use when identifying what experiment_ids to include in the multi_session_df :param mouse_id: mouse_id to use when identifying what experiment_ids to include in the multi_session_df :param conditions: columns in stimulus_response_df to group by when averaging across trials / stimulus presentations if use_extended_stimulus_presentations is True, columns available include the set of columns provided in that table (ex: engagement_state) @@ -64,20 +64,28 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') - # session_type = float(session_type) - print('project_code:', project_code, ', mouse_id:', mouse_id) - experiments = experiments_table[(experiments_table.project_code == project_code) & - (experiments_table.mouse_id == int(mouse_id))] - print(len(experiments), 'experiments for this mouse_id and project_code') - - filename = loading.get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) - mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, - event_type=event_type) + if ophys_experiment_ids is None: + print('mouse_id:', mouse_id, ', ophys_container_id:', ophys_container_id) + experiments = experiments_table[(experiments_table.ophys_container_id == ophys_container_id) & + (experiments_table.mouse_id == int(mouse_id))] + ophys_experiment_ids = experiments.index.unique() + print(len(ophys_experiment_ids), 'experiments for this mouse_id and project_code') + save_mega_mdf = True + else: + print('ophys_experiment_ids provided, ignoring mouse_id and ophys_container_id') + print('generating multi_session_df for provided list of ophys_experiment_ids') + print(len(ophys_experiment_ids), 'experiments are in the provided list') + print('multi_session_df will not be automatically saved') + save_mega_mdf = False + + filename = loading.get_file_name_for_multi_session_df(data_type, event_type, ophys_container_id, mouse_id, conditions) + mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) print('saving to', filepath) @@ -95,57 +103,46 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty if process_data: mega_mdf = pd.DataFrame() - for experiment_id in experiments.index.unique(): - # try: - print(experiment_id) - # get dataset - dataset = loading.get_ophys_dataset(experiment_id, - get_extended_stimulus_presentations=use_extended_stimulus_presentations) - # get stimulus_response_df - df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, - interpolate=interpolate, output_sampling_rate=output_sampling_rate, - load_from_file=False) - print('stim response df loaded') - # use response_window duration from stim response df if it exists - if response_window_duration in df.keys(): - response_window_duration = df.response_window_duration.values[0] - df['ophys_experiment_id'] = experiment_id - # if using omissions, only include omissions where time from last change is more than 3 seconds - # if event_type == 'omissions': - # df = df[df.time_from_last_change>3] - # modify columns for specific conditions - if 'passive' in dataset.metadata['session_type']: - df['lick_on_next_flash'] = False - df['engaged'] = False - df['engagement_state'] = 'disengaged' - if 'running_state' in conditions: # create 'running_state' Boolean column based on threshold on mean_running_speed - df['running'] = [True if mean_running_speed > 2 else False for mean_running_speed in - df.mean_running_speed.values] - if 'pupil_state' in conditions: # create 'pupil_state' Boolean column based on threshold on mean_pupil_ - if 'mean_pupil_area' in df.keys(): - df = df[df.mean_pupil_area.isnull() == False] - if len(df) > 100: - median_pupil_area = df.mean_pupil_area.median() - df['large_pupil'] = [True if mean_pupil_area > median_pupil_area else False for mean_pupil_area in - df.mean_pupil_area.values] - if 'pre_change' in conditions: - df = df[df.pre_change.isnull() == False] - # get params for mean df creation from stimulus_response_df - output_sampling_rate = df.frame_rate.unique()[0] - timestamps = df.trace_timestamps.values[0] - window_around_timepoint_seconds = [timestamps[0], timestamps[-1]] - - mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, - time_window=time_window, response_window_duration=response_window_duration, - get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) - if 'correlation_values' in mdf.keys(): - mdf = mdf.drop(columns=['correlation_values']) - mdf['ophys_experiment_id'] = experiment_id - print('mean df created for', experiment_id) - mega_mdf = pd.concat([mega_mdf, mdf]) - # except Exception as e: # flake8: noqa: E722 - # print(e) - # print('problem for', experiment_id) + for experiment_id in ophys_experiment_ids: + try: + print(experiment_id) + # get dataset + dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) + # get stimulus_response_df + df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, + interpolate=interpolate, output_sampling_rate=output_sampling_rate, + load_from_file=True) + print('stim response df loaded') + # use response_window duration from stim response df if it exists + if response_window_duration in df.keys(): + response_window_duration = df.response_window_duration.values[0] + df['ophys_experiment_id'] = experiment_id + # modify columns for specific conditions + if 'passive' in dataset.metadata['session_type']: + df['lick_on_next_flash'] = False + df['engaged'] = False + df['engagement_state'] = 'disengaged' + if 'running_state' in conditions: # create 'running_state' Boolean column based on threshold on mean_running_speed + df['running'] = [True if mean_running_speed > 2 else False for mean_running_speed in + df.mean_running_speed.values] + if 'pre_change' in conditions: + df = df[df.pre_change.isnull() == False] + # get params for mean df creation from stimulus_response_df + output_sampling_rate = df.frame_rate.unique()[0] + timestamps = df.trace_timestamps.values[0] + window_around_timepoint_seconds = [timestamps[0], timestamps[-1]] + # compute trial average and other metrics + mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, + time_window=time_window, response_window_duration=response_window_duration, + get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) + if 'correlation_values' in mdf.keys(): + mdf = mdf.drop(columns=['correlation_values']) + mdf['ophys_experiment_id'] = experiment_id + print('mean df created for', experiment_id) + mega_mdf = pd.concat([mega_mdf, mdf]) + except Exception as e: # flake8: noqa: E722 + print(e) + print('problem for', experiment_id) if 'level_0' in mega_mdf.keys(): mega_mdf = mega_mdf.drop(columns='level_0') @@ -154,11 +151,12 @@ def get_multi_session_df(project_code, mouse_id, conditions, data_type, event_ty # if file of the same name exists, delete & overwrite to prevent files from getting huge - if os.path.exists(filepath): - os.remove(filepath) - print('saving multi session mean df as ', filename) - mega_mdf.to_hdf(filepath, key='df') - print('saved to', mega_mdf_write_dir) + if save_mega_mdf: + if os.path.exists(filepath): + os.remove(filepath) + print('saving multi session mean df as ', filename) + mega_mdf.to_hdf(filepath, key='df') + print('saved to', mega_mdf_write_dir) return mega_mdf From 635af73bca704bd963a5f903be8771fa479c617e Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 16:53:03 -0700 Subject: [PATCH 121/187] backslashes --- visual_behavior/ophys/io/create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 09eba0c08..59dbb100e 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -64,8 +64,8 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') From 788ea75af63589bc8532e143348ce9b91f28d886 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:08:27 -0700 Subject: [PATCH 122/187] typo --- scripts/run_create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 45e1eea6a..e2696c613 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -46,8 +46,8 @@ # call the `sbatch` command to run the jobs. for mouse_id in experiments_table.mouse_id.unique(): print('mouse_id:', mouse_id) - ophys_container_id = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() - for ophys_container_id in ophys_container_id: + ophys_container_ids = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() + for ophys_container_id in ophys_container_ids: print('ophys_container_id:', ophys_container_id) # instantiate a Slurm object slurm = Slurm(mem='120g', # '24g' From dde3cd422697f984f3b79a2034c6fabed8b0f5ce Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:16:32 -0700 Subject: [PATCH 123/187] print statements --- scripts/run_create_multi_session_df.py | 1 + visual_behavior/ophys/io/create_multi_session_df.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index e2696c613..5348f9866 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -47,6 +47,7 @@ for mouse_id in experiments_table.mouse_id.unique(): print('mouse_id:', mouse_id) ophys_container_ids = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() + print('there are', len(ophys_container_ids),'ophys_container_ids for this mouse') for ophys_container_id in ophys_container_ids: print('ophys_container_id:', ophys_container_id) # instantiate a Slurm object diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 59dbb100e..cd6899d12 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -75,7 +75,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev experiments = experiments_table[(experiments_table.ophys_container_id == ophys_container_id) & (experiments_table.mouse_id == int(mouse_id))] ophys_experiment_ids = experiments.index.unique() - print(len(ophys_experiment_ids), 'experiments for this mouse_id and project_code') + print(len(ophys_experiment_ids), 'experiments for this mouse_id and ophys_container_id') save_mega_mdf = True else: print('ophys_experiment_ids provided, ignoring mouse_id and ophys_container_id') From 4d6d6a95a0e82607f253cdab5c4a15f3741dfbfd Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:27:47 -0700 Subject: [PATCH 124/187] try enforcing types --- scripts/create_multi_session_df.py | 6 +++--- visual_behavior/ophys/io/create_multi_session_df.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 986d70050..ba1b5586f 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -14,8 +14,8 @@ parser.add_argument('--ophys_container_id', type=str, help='ophys_container_id to use') parser.add_argument('--mouse_id', type=str, help='mouse_id to use') args = parser.parse_args() - ophys_container_id = args.ophys_container_id - mouse_id = args.mouse_id + ophys_container_id = int(args.ophys_container_id) + mouse_id = int(args.mouse_id) print('mouse_id:', mouse_id, 'ophys_container_id:', ophys_container_id) @@ -77,7 +77,7 @@ response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, + df = io.get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, ophys_experiment_ids=None, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index cd6899d12..639529539 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -72,9 +72,9 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev if ophys_experiment_ids is None: print('mouse_id:', mouse_id, ', ophys_container_id:', ophys_container_id) - experiments = experiments_table[(experiments_table.ophys_container_id == ophys_container_id) & + experiments = experiments_table[(experiments_table.ophys_container_id == int(ophys_container_id)) & (experiments_table.mouse_id == int(mouse_id))] - ophys_experiment_ids = experiments.index.unique() + ophys_experiment_ids = experiments.index.values print(len(ophys_experiment_ids), 'experiments for this mouse_id and ophys_container_id') save_mega_mdf = True else: From 8918a49d862201e4e31ce3112f4683d82cfc8de3 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:53:00 -0700 Subject: [PATCH 125/187] update file saving --- visual_behavior/data_access/loading.py | 60 ++++++++++--------- .../ophys/io/create_multi_session_df.py | 4 +- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index f6a04d2f6..706713946 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -126,9 +126,9 @@ def get_ophys_glm_dir(): def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): base_dir = os.path.join(get_production_cache_dir(), 'stimulus_response_dfs') if interpolate: - save_dir = os.path.join(base_dir, event_type, 'interpolate_' + str(output_sampling_rate) + 'Hz') + save_dir = os.path.join(base_dir, 'interpolated_to_' + str(output_sampling_rate) + 'Hz', event_type) else: - save_dir = os.path.join(base_dir, event_type, 'original_frame_rate') + save_dir = os.path.join(base_dir, 'original_frame_rate', event_type) if not os.path.exists(save_dir): os.mkdir(save_dir) return save_dir @@ -137,9 +137,9 @@ def get_stimulus_response_df_dir(interpolate=True, output_sampling_rate=30, even def get_multi_session_df_dir(interpolate=True, output_sampling_rate=30, event_type='all'): base_dir = get_production_cache_dir() if interpolate: - save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', event_type, 'interpolate_' + str(output_sampling_rate) + 'Hz') + save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', 'interpolated_to_' + str(output_sampling_rate) + 'Hz', event_type) else: - save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', event_type, 'original_frame_rate') + save_dir = os.path.join(base_dir, 'multi_session_mean_response_dfs', 'original_frame_rate', event_type) if not os.path.exists(save_dir): os.mkdir(save_dir) return save_dir @@ -553,6 +553,10 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o data_type='filtered_events', event_type='all', load_from_file=False, exclude_invalid_rois=True): """ load stimulus response df using mindscope_utilities and merge with stimulus_presentations that has trials metadata added + if load_from_file is False, the dataframe will be generated and saved + if load_from_file is True, the dataframe will be loaded from pre-existing file if it exists, otherwise it will generate it and save it + Note of caution: It is a good idea to select load_from_file = False if you are unsure whether all existing saved response dfs + were generated with the same input parameters or not (particularly the time_window value) inputs: dataset: BehaviorOphysExperiment instance time_window: window over which to extract the event triggered response around each stimulus presentation time @@ -572,7 +576,6 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o filepath = get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_type, event_type, interpolate=interpolate, output_sampling_rate=output_sampling_rate) - if event_type == 'omissions': response_window_duration = 0.75 else: @@ -605,6 +608,7 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, exclude_invalid_rois=exclude_invalid_rois) + # since it did not exist, save it try: # some experiments with lots of neurons cant save sdf.to_hdf(filepath, key='df') print('saved response df to', filepath) @@ -617,19 +621,21 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, exclude_invalid_rois=exclude_invalid_rois) - # save + try: # some experiments with lots of neurons cant save sdf.to_hdf(filepath, key='df') print('saved response df to', filepath) except: print('could not save', filepath) + print(len(sdf), 'length of stimulus response df') # if extended_stimulus_presentations is an attribute of the dataset object, use it, otherwise get regular stimulus_presentations if 'extended_stimulus_presentations' in dir(dataset): stimulus_presentations = dataset.extended_stimulus_presentations.copy() else: stimulus_presentations = vb_ophys.get_annotated_stimulus_presentations(dataset) sdf = sdf.merge(stimulus_presentations, on='stimulus_presentations_id') + print(len(sdf), 'length of stimulus response df after merging with stimulus presentations') # add run params sdf['interpolate'] = interpolate @@ -2753,30 +2759,31 @@ def add_superficial_deep_to_experiments_table(experiments_table): return experiments_table -def get_file_name_for_multi_session_df(data_type, event_type, ophys_container_id, mouse_id, conditions): +def get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions): mouse_id = str(mouse_id) ophys_container_id = str(ophys_container_id) if len(conditions) == 6: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' elif len(conditions) == 4: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' elif len(conditions) == 3: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' elif len(conditions) == 2: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[1] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '.h5' elif len(conditions) == 1: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + ophys_container_id + '_' + mouse_id + '_' + conditions[0] + '.h5' + filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[0] + '.h5' return filename def load_multi_session_df(data_type, event_type, conditions, interpolate=True, output_sampling_rate=30): """ - Loops through all experiments in the provided experiments_table and loads pre-generated dataframes containing - trial averaged responses for each cell in each session, for the provided set of conditions, data_type, and event_type. + Loads pre-generated dataframes containing trial averaged responses for each cell in each session, + for the provided set of conditions, data_type, and event_type. + Limited to LearningmFISHTask1A and LearningmFISHDevelopment project codes :param data_type: :param event_type: @@ -2785,28 +2792,25 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o :param output_sampling_rate: :return: """ - cache_dir = get_platform_analysis_cache_dir() - # cache = bpc.from_s3_cache(cache_dir=cache_dir) - # experiments_table = cache.get_ophys_experiment_table() - experiments_table = get_filtered_ophys_experiment_table() - experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + cache = bpc.from_lims() + experiments_table = cache.get_ophys_experiment_table() + experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment']) - project_codes = experiments_table.project_code.unique() + mouse_ids = experiments_table.mouse_id.unique() multi_session_df = pd.DataFrame() - for project_code in project_codes: - experiments = experiments_table[(experiments_table.project_code == project_code)] - for mouse_id in np.sort(experiments.mouse_id.unique()): + for mouse_id in mouse_ids: + experiments = experiments_table[(experiments_table.mouse_id == mouse_id)] + for ophys_container_id in np.sort(experiments.ophys_container_id.unique()): try: - filename = get_file_name_for_multi_session_df(data_type, event_type, project_code, mouse_id, conditions) - multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, - output_sampling_rate=output_sampling_rate, event_type=event_type) + filename = get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) + multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(multi_session_df_dir, filename) print(filepath) df = pd.read_hdf(filepath, key='df') multi_session_df = pd.concat([multi_session_df, df]) print(multi_session_df.mouse_id.unique()) except BaseException: - print('no multi_session_df for', project_code, mouse_id) + print('no multi_session_df for', mouse_id, ophys_container_id) return multi_session_df diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 639529539..13a57544f 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -111,7 +111,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # get stimulus_response_df df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - load_from_file=True) + load_from_file=False) print('stim response df loaded') # use response_window duration from stim response df if it exists if response_window_duration in df.keys(): @@ -161,7 +161,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev return mega_mdf else: - print('multi_session_df not created') + print('multi_session_df not created because it already exists') From 4e0bdb11d2e09a7d2b8ce94f0769157f407b1f63 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:53:15 -0700 Subject: [PATCH 126/187] function to get matched cells --- visual_behavior/data_access/utilities.py | 37 +++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 1203446b9..f2308e216 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1848,4 +1848,39 @@ def get_nan_trace_csids(traces): nan_csids.append(csid) else: csids_to_keep.append(csid) - return nan_csids, csids_to_keep \ No newline at end of file + return nan_csids, csids_to_keep + + +def get_max_matched_cells_for_learning_mFISH(): + """ + Function to identify cell_specimen_ids that are matched in the maximum possible number of sessions for a given container + (i.e. cells that are matched in all sessions for that mouse) + Limited to LearningmFISHTask1A and LearningmFISHDevelopment + + Returns a dataframe with cell_specimen_id and metadata for matched cells. + """ + cache = bpc.from_lims() + experiments_table = cache.get_ophys_experiment_table(passed_only=False) + experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + print(len(experiments), 'experiments') + + ophys_cells_table = cache.get_ophys_cells_table() + ophys_cells_table = ophys_cells_table.merge(experiments, on='ophys_experiment_id') + print(len(ophys_cells_table.cell_specimen_id.unique()), 'unique cells') + + counts = ophys_cells_table.value_counts(['mouse_id', 'cell_specimen_id']) + counts = pd.DataFrame(counts, columns=['session_count']) + counts = counts.reset_index() + + matched_cells_list = [] + for mouse_id in counts.mouse_id.unique(): + tmp = counts[counts.mouse_id == mouse_id] + max_matched_sessions = tmp.session_count.max() + cells_with_max_num_matched_sessions = tmp[tmp.session_count == max_matched_sessions].cell_specimen_id.values + matched_cells_list = np.hstack((matched_cells_list, cells_with_max_num_matched_sessions)) + matched_cells_list = [int(cell) for cell in matched_cells_list] + matched_cells_df = ophys_cells_table[ophys_cells_table.cell_specimen_id.isin(matched_cells_list)] + + matched_cells_df = matched_cells_df.drop_duplicates(subset=['cell_specimen_id']) + print(len(matched_cells_df), 'cells matched across all sessions in their container') + return matched_cells_df \ No newline at end of file From 7eef9da50559d690735db829a89e3b31747b747c Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:57:15 -0700 Subject: [PATCH 127/187] add single cell plots for matched cells --- scripts/create_single_cell_plots.py | 34 ++-- scripts/run_single_cell_plots.py | 13 +- .../visualization/qc/single_cell_plots.py | 148 ++++++++++++++++++ 3 files changed, 178 insertions(+), 17 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 25552784e..4fc7d8fc9 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -3,6 +3,7 @@ import visual_behavior.data_access.loading as loading import visual_behavior.data_access.utilities as utilities import visual_behavior.visualization.ophys.platform_paper_figures as ppf +import visual_behavior.visualization.ophys.qc as scp if __name__ == '__main__': @@ -13,25 +14,32 @@ ophys_container_id = args.ophys_container_id print('ophys_container_id:', ophys_container_id) - use_events = False - filter_events = False + # use_events = False + # filter_events = False - folder = 'matched_cell_roi_and_trace_examples' - save_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_plots/cell_matching' + # folder = 'matched_cell_roi_and_trace_examples' + # save_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_plots/cell_matching' - cells_table = loading.get_cell_table() - cells_table = utilities.limit_to_last_familiar_second_novel_active(cells_table) - cells_table = utilities.limit_to_containers_with_all_experience_levels(cells_table) + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + import pandas as pd + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + print(len(experiments_table)) - for cell_specimen_id in cells_table[cells_table.ophys_container_id == ophys_container_id].cell_specimen_id.unique(): + matched_cells_df = utilities.get_matched_cells_for_learning_mFISH() + matched_cell_specimen_ids = matched_cells_df[matched_cells_df.ophys_container_id==ophys_container_id].cell_specimen_id.unique() + + for cell_specimen_id in matched_cell_specimen_ids: try: + scp.plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_specimen_id, experiments_table, + data_type='dff', save_figure=True) + # ppf.plot_matched_roi_and_trace(ophys_container_id, cell_specimen_id, limit_to_last_familiar_second_novel=True, # use_events=use_events, filter_events=filter_events, save_figure=True) - cell_metadata = cells_table[cells_table.cell_specimen_id==cell_specimen_id] - if len(cell_metadata) == 3: - ppf.plot_matched_roi_and_traces_example(cell_metadata, include_omissions=True, - use_events=use_events, filter_events=filter_events, save_dir=save_dir, folder=folder) - print('plot saved for', cell_specimen_id) + # cell_metadata = cells_table[cells_table.cell_specimen_id==cell_specimen_id] + # if len(cell_metadata) == 3: + # ppf.plot_matched_roi_and_traces_example(cell_metadata, include_omissions=True, + # use_events=use_events, filter_events=filter_events, save_dir=save_dir, folder=folder) + # print('plot saved for', cell_specimen_id) except Exception as e: print('problem for', cell_specimen_id) print(e) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 92cb0fd0a..5dfbe7c65 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -7,16 +7,21 @@ from simple_slurm import Slurm parser = argparse.ArgumentParser(description='run cell plots generation functions on the cluster') -parser.add_argument('--env', type=str, default='visual_behavior_sdk', metavar='name of conda environment to use') +parser.add_argument('--env', type=str, default='learning_mFISH', metavar='name of conda environment to use') parser.add_argument('--scriptname', type=str, default='create_single_cell_plots.py', metavar='name of script to run (must be in same folder)') +save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' +import pandas as pd +experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) +print(len(experiments_table)) +container_ids = experiments_table.ophys_container_id.unique() -container_ids = loading.get_ophys_container_ids(platform_paper_only=True, add_extra_columns=True) +# container_ids = loading.get_ophys_container_ids(platform_paper_only=True, add_extra_columns=True) if __name__ == "__main__": args = parser.parse_args() - python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) + python_executable = "{}/anaconda3/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) python_file = os.path.join(os.getcwd(), args.scriptname) # define the job record output folder @@ -26,7 +31,7 @@ slurm = Slurm( mem='40g', # '24g' cpus_per_task=1, - time='5:00:00', + time='10:00:00', partition='braintv', job_name='single_cell_plots', output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index fb87f9cf2..4a89a69fd 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -127,6 +127,154 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even plt.close() +def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_specimen_id, experiments_table, + data_type='dff', save_figure=True): + """ + Generates plots characterizing single cell activity across sessions, in response to stimulus, omissions, and changes. + Compares across all sessions in a container for each cell, including the ROI mask across days. + Additional panels include response for each image, change response for hit vs miss, and change response for running vs. not running + Useful to validate cell matching as well as examine changes in activity profiles over days. + """ + + container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by=['date_of_acquisition']) + ophys_experiment_ids = container_expts.index.values + suffix = '_'+data_type + if data_type == 'dff': + ylabel = 'dF/F' + else: + ylabel = 'response' + + window = [-0.5, 1.5] + + n = len(ophys_experiment_ids) + figsize = (4*n, 20) + fig, ax = plt.subplots(5, n, figsize=figsize, sharey='row') + ax = ax.ravel() + print('ophys_container_id:', ophys_container_id) + for i, ophys_experiment_id in enumerate(ophys_experiment_ids[:3]): + print('ophys_experiment_id:', ophys_experiment_id) + try: + dataset = data_dict[ophys_experiment_id]['dataset']['dataset'] + + # if the selected cell is not in this session (i.e. is not matched), plot it, otherwise skip + if cell_specimen_id in dataset.dff_traces.index: + print(cell_specimen_id, 'is in', ophys_experiment_id) + # get stimulus response df for this experiment (response df should have been created using event_type='all') + sdf = data_dict[ophys_experiment_id][data_type]['stimulus_response_df'].copy() + output_sampling_rate = sdf.output_sampling_rate.values[0] + response_window_duration = sdf.response_window_duration.values[0] + timestamps = sdf.trace_timestamps.values[0] + window = [timestamps[0], timestamps[-1]] + + # plot cell mask + print('plotting ROI mask') + ct = dataset.cell_specimen_table.copy() + cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id + ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.max_projection, cell_roi_id, + spacex=20, spacey=20, show_mask=True, ax=ax[i]) + + ax[i].set_title(container_expts.loc[ophys_experiment_id].session_type+'\n'+ + str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]) + + + # plot average response for each image (for all non-change image presentations) + # get trial averaged responses for various conditions + print('plotting image responses') + cdf = ut.get_mean_df(sdf, conditions=['cell_specimen_id', 'is_change', 'image_name'], + frame_rate=output_sampling_rate, time_window=window, + response_window_duration=response_window_duration, + get_pref_stim=True) + colors = sns.color_palette('hls', 8) + [(0.5, 0.5, 0.5)] + cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == False)] + for c, image_name in enumerate(np.sort(cell_data.image_name.unique())): + ax[i + n] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=image_name, color=colors[c], interval_sec=0.5, + xlims=[-0.5, 0.75], ax=ax[i + n]) + ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') + ax[i + n].set_title(container_expts.loc[ophys_experiment_id].session_type + '\n image response') + + # plot mean omission response + print('plotting omissions') + try: + odf = ut.get_mean_df(sdf[sdf.omitted==True], conditions=['cell_specimen_id'], + frame_rate=output_sampling_rate, time_window=window, + response_window_duration=response_window_duration, + get_pref_stim=False) + cell_data = odf[(odf.cell_specimen_id == cell_specimen_id)] + ax[i + (n * 2)] = utils.plot_mean_trace_from_mean_df(cell_data, + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=image_name, color='gray', interval_sec=1, + xlims=[-1, 2], ax=ax[i + (n * 2)]) + ax[i + (n * 2)] = utils.plot_flashes_on_trace(ax[i + (n * 2)], timstamps, change=False, omitted=True, alpha=0.15, facecolor='gray') + ax[i + (n * 2)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n omission response') + except: + print('couldnt plot omissions') + + + # plot mean stimulus response for running vs not-running + print('plotting running not running') + try: + tmp = sdf.copy() + # create boolean column indicating running vs. not running, using 2cm/sec as threshold + tmp['running'] = [True if run_speed > 2 else False for run_speed in tmp.mean_running_speed.values] + rdf = ut.get_mean_df(tmp, conditions=['cell_specimen_id', 'is_change', 'image_name', 'running'], + frame_rate=output_sampling_rate, time_window=window, + response_window_duration=response_window_duration, + get_pref_stim=True) + # get responses for non-changes with preferred stimulus + cell_data = rdf[(rdf.cell_specimen_id == cell_specimen_id) & (rdf.is_change == False) & (rdf.pref_stim == True)] + run_colors = [sns.color_palette()[3], sns.color_palette()[2]] + # loop through running conditions and plot + for c, running in enumerate(np.sort(cell_data.running.unique())): + if len(cell_data[cell_data.running == running]) > 5: # must be at least 5 trials per condition + ax[i + (n * 3)] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.running == running], + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=running, color=run_colors[c], interval_sec=0.5, + xlims=[-1, 2], ax=ax[i + (n * 3)]) + ax[i + (n * 3)].legend(fontsize='xx-small', title='running', title_fontsize='xx-small') + ax[i + (n * 3)] = utils.plot_flashes_on_trace(ax[i + (n * 3)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') + ax[i + (n * 3)].set_title(container_expts.loc[ophys_experiment_id].session_type + '\n image response') + except: + print('couldnt plot running / not-running panel') + + + # plot change repsonse for hit vs. miss for pref image + print('plotting hit vs. miss') + try: + tmp = sdf.copy() + tdf = ut.get_mean_df(tmp[tmp.is_change==True], conditions=['cell_specimen_id', 'go', 'hit'], + frame_rate=output_sampling_rate, time_window=window, + response_window_duration=response_window_duration, + get_pref_stim=False) + cell_data = tdf[(tdf.cell_specimen_id == cell_specimen_id) & (tdf.go == True)] + hit_colors = [sns.color_palette()[2], sns.color_palette()[3]] + for c, hit in enumerate([True, False]): + if len(cell_data[cell_data.hit == hit]) > 0: + ax[i + (n * 4)] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.hit == hit], + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=hit, color=hit_colors[c], interval_sec=1, + xlims=[-1, 2], ax=ax[i + (n * 4)]) + ax[i + (n * 4)].legend(fontsize='xx-small', title='hit', title_fontsize='xx-small') + ax[i + (n * 4)] = utils.plot_flashes_on_trace(ax[i + (n * 4)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') + ax[i + (n * 4)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n change response') + except: + print('couldnt plot hit vs. miss') + + # overall plot title + fig.tight_layout() + metadata_string = utils.get_container_metadata_string(dataset.metadata) + fig.suptitle(str(cell_specimen_id) + '_' + metadata_string, x=0.5, y=1.01, horizontalalignment='center') + except Exception as e: + print('problem for cell_specimen_id:', cell_specimen_id, ', ophys_experiment_id:', ophys_experiment_id) + print(e) + if save_figure: + save_dir = utils.get_single_cell_plots_dir() + utils.save_figure(fig, figsize, save_dir, 'matched_cell_across_session_responses', str( + cell_specimen_id) + '_' + metadata_string + suffix) + + + def plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figure=True): """ Plots the full dFF trace for a cell, along with licking behavior, rewards, running speed, pupil area, and face motion. From 2c47ea43e6a01f01ad7a9ac162c9bc4ba00383fc Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 17:58:56 -0700 Subject: [PATCH 128/187] update mdf file name --- visual_behavior/data_access/loading.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 706713946..fa8d0f4b0 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2761,8 +2761,8 @@ def add_superficial_deep_to_experiments_table(experiments_table): def get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions): - mouse_id = str(mouse_id) - ophys_container_id = str(ophys_container_id) + mouse_id = 'mouse_id_'+str(mouse_id) + ophys_container_id = 'container_id_'+str(ophys_container_id) if len(conditions) == 6: filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: From 94ac4e8e903b22cb8f7757780391b73d796e6327 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 18:00:32 -0700 Subject: [PATCH 129/187] typo --- visual_behavior/data_access/loading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index fa8d0f4b0..be3411825 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2794,7 +2794,7 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o """ cache = bpc.from_lims() experiments_table = cache.get_ophys_experiment_table() - experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment']) + experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] mouse_ids = experiments_table.mouse_id.unique() multi_session_df = pd.DataFrame() From cce4f951d563f64210612d88e549821fc054a8ee Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 18:17:21 -0700 Subject: [PATCH 130/187] edit file saving --- visual_behavior/data_access/loading.py | 12 ++++++------ visual_behavior/ophys/io/create_multi_session_df.py | 10 ++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index be3411825..6a3b57bf9 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2764,17 +2764,17 @@ def get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_co mouse_id = 'mouse_id_'+str(mouse_id) ophys_container_id = 'container_id_'+str(ophys_container_id) if len(conditions) == 6: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' elif len(conditions) == 4: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' elif len(conditions) == 3: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' elif len(conditions) == 2: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '.h5' elif len(conditions) == 1: - filename = 'mean_response_df_' + data_type + '_' + event_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[0] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[0] + '.h5' return filename diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 13a57544f..753a83316 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -74,17 +74,18 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev print('mouse_id:', mouse_id, ', ophys_container_id:', ophys_container_id) experiments = experiments_table[(experiments_table.ophys_container_id == int(ophys_container_id)) & (experiments_table.mouse_id == int(mouse_id))] - ophys_experiment_ids = experiments.index.values + experiment_ids = experiments.index.values print(len(ophys_experiment_ids), 'experiments for this mouse_id and ophys_container_id') save_mega_mdf = True else: + experiment_ids = ophys_experiment_ids.copy() print('ophys_experiment_ids provided, ignoring mouse_id and ophys_container_id') print('generating multi_session_df for provided list of ophys_experiment_ids') - print(len(ophys_experiment_ids), 'experiments are in the provided list') + print(len(experiment_ids), 'experiments are in the provided list') print('multi_session_df will not be automatically saved') save_mega_mdf = False - filename = loading.get_file_name_for_multi_session_df(data_type, event_type, ophys_container_id, mouse_id, conditions) + filename = loading.get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) print('saving to', filepath) @@ -103,7 +104,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev if process_data: mega_mdf = pd.DataFrame() - for experiment_id in ophys_experiment_ids: + for experiment_id in experiment_ids: try: print(experiment_id) # get dataset @@ -140,6 +141,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev mdf['ophys_experiment_id'] = experiment_id print('mean df created for', experiment_id) mega_mdf = pd.concat([mega_mdf, mdf]) + print('length of multi_session_df:', len(mega_mdf)) except Exception as e: # flake8: noqa: E722 print(e) print('problem for', experiment_id) From bc5f462d8f0ad9d55373fdd44ab184d03ca44b6c Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 18:34:07 -0700 Subject: [PATCH 131/187] stupid mistake --- visual_behavior/ophys/io/create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 753a83316..ae6f8637e 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -75,7 +75,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev experiments = experiments_table[(experiments_table.ophys_container_id == int(ophys_container_id)) & (experiments_table.mouse_id == int(mouse_id))] experiment_ids = experiments.index.values - print(len(ophys_experiment_ids), 'experiments for this mouse_id and ophys_container_id') + print(len(experiment_ids), 'experiments for this mouse_id and ophys_container_id') save_mega_mdf = True else: experiment_ids = ophys_experiment_ids.copy() From 037f3974c59c587d5485fe6a90e4cec060fab321 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 18:50:40 -0700 Subject: [PATCH 132/187] prints --- visual_behavior/data_access/loading.py | 1 + visual_behavior/ophys/io/create_multi_session_df.py | 1 + 2 files changed, 2 insertions(+) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 6a3b57bf9..3ce7c7a8f 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -575,6 +575,7 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o ophys_experiment_id = dataset.ophys_experiment_id filepath = get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_type, event_type, interpolate=interpolate, output_sampling_rate=output_sampling_rate) + print('stimulus_response_df filepath:', filepath) if event_type == 'omissions': response_window_duration = 0.75 diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index ae6f8637e..2bbe90cb0 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -110,6 +110,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # get dataset dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) # get stimulus_response_df + print('loading stimulus response df') df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, load_from_file=False) From 2b2e3fcae338e1ac55662a37772284ecf79820ce Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:09:20 -0700 Subject: [PATCH 133/187] dont use extended stim presentations --- visual_behavior/data_access/loading.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 3ce7c7a8f..15a7d4c33 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -629,12 +629,15 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o except: print('could not save', filepath) + # merge with stimulus presentations to get relevant metadata columns print(len(sdf), 'length of stimulus response df') # if extended_stimulus_presentations is an attribute of the dataset object, use it, otherwise get regular stimulus_presentations - if 'extended_stimulus_presentations' in dir(dataset): - stimulus_presentations = dataset.extended_stimulus_presentations.copy() - else: - stimulus_presentations = vb_ophys.get_annotated_stimulus_presentations(dataset) + # if 'extended_stimulus_presentations' in dir(dataset): + # print('getting extended stim presentations') + # stimulus_presentations = dataset.extended_stimulus_presentations.copy() + # else: + print('loading annotated stimulus presentations using mindscope_utilities') + stimulus_presentations = vb_ophys.get_annotated_stimulus_presentations(dataset) sdf = sdf.merge(stimulus_presentations, on='stimulus_presentations_id') print(len(sdf), 'length of stimulus response df after merging with stimulus presentations') From d67897c71436368c606b72cfdf6dd0181ef9174b Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:17:48 -0700 Subject: [PATCH 134/187] LAMF only --- scripts/run_create_multi_session_df.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 5348f9866..613bb623d 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -38,6 +38,7 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) +experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1S','LearningmFISHDevelopment'])] print(len(experiments_table), 'experiments') # experiments_table = loading.get_filtered_ophys_experiment_table() From 187ee653a520b6eed360d7a002e8df8f74e2bd6a Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:20:50 -0700 Subject: [PATCH 135/187] single cell plots --- scripts/create_single_cell_plots.py | 2 ++ scripts/run_single_cell_plots.py | 1 + 2 files changed, 3 insertions(+) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 4fc7d8fc9..04a400027 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -25,7 +25,9 @@ experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) print(len(experiments_table)) + # get cells that are matched in all sessions matched_cells_df = utilities.get_matched_cells_for_learning_mFISH() + # get just the matched cells for this container matched_cell_specimen_ids = matched_cells_df[matched_cells_df.ophys_container_id==ophys_container_id].cell_specimen_id.unique() for cell_specimen_id in matched_cell_specimen_ids: diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 5dfbe7c65..4a60884f8 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -14,6 +14,7 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) +experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] print(len(experiments_table)) container_ids = experiments_table.ophys_container_id.unique() From d21b2f2a59c0092c285a192d0f7d9a1e3f709634 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:23:10 -0700 Subject: [PATCH 136/187] fix paths --- scripts/run_single_cell_plots.py | 2 +- visual_behavior/visualization/utils.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 4a60884f8..ac398c69c 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -26,7 +26,7 @@ python_file = os.path.join(os.getcwd(), args.scriptname) # define the job record output folder - stdout_location = r"/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/cluster_jobs/paper_figures" + stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/single_cell_plots" # instantiate a Slurm object slurm = Slurm( diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index 2bde614d9..ab32c5a27 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -19,7 +19,9 @@ def get_experiment_plots_dir(): def get_single_cell_plots_dir(): - return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/single_cell_plots' + # return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/single_cell_plots' + return r'\allen\programs\mindscope\workgroups\learning\ophys\qc_plots\single_cell_plots' + def save_figure(fig, figsize, save_dir, folder, fig_title, formats=['.png']): From 75b952701620e658a462ef26f9b2b51cc20c1af2 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:26:47 -0700 Subject: [PATCH 137/187] imports --- scripts/create_single_cell_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 04a400027..684c9dc9d 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -3,7 +3,7 @@ import visual_behavior.data_access.loading as loading import visual_behavior.data_access.utilities as utilities import visual_behavior.visualization.ophys.platform_paper_figures as ppf -import visual_behavior.visualization.ophys.qc as scp +import visual_behavior.visualization.qc.single_cell_plots as scp if __name__ == '__main__': From 59ad51d779c158b204d2579e606f31dc454f5ddf Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:29:08 -0700 Subject: [PATCH 138/187] more imports --- scripts/create_single_cell_plots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 684c9dc9d..b3c87baa5 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -1,4 +1,6 @@ +import os import argparse +import pandas as pd import visual_behavior.data_access.loading as loading import visual_behavior.data_access.utilities as utilities @@ -21,7 +23,6 @@ # save_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_plots/cell_matching' save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) print(len(experiments_table)) From 3788a5338ab26729dae4f5fd7ce373df48ba8446 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:30:55 -0700 Subject: [PATCH 139/187] erros --- scripts/create_single_cell_plots.py | 2 +- visual_behavior/visualization/qc/single_cell_plots.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index b3c87baa5..ea390f7e6 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -27,7 +27,7 @@ print(len(experiments_table)) # get cells that are matched in all sessions - matched_cells_df = utilities.get_matched_cells_for_learning_mFISH() + matched_cells_df = utilities.get_max_matched_cells_for_learning_mFISH() # get just the matched cells for this container matched_cell_specimen_ids = matched_cells_df[matched_cells_df.ophys_container_id==ophys_container_id].cell_specimen_id.unique() diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 4a89a69fd..ad651427f 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -151,7 +151,7 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe fig, ax = plt.subplots(5, n, figsize=figsize, sharey='row') ax = ax.ravel() print('ophys_container_id:', ophys_container_id) - for i, ophys_experiment_id in enumerate(ophys_experiment_ids[:3]): + for i, ophys_experiment_id in enumerate(ophys_experiment_ids): print('ophys_experiment_id:', ophys_experiment_id) try: dataset = data_dict[ophys_experiment_id]['dataset']['dataset'] From 69d2ae5429604d840633650240b678b827bd534a Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:34:59 -0700 Subject: [PATCH 140/187] load expts from file --- scripts/run_single_cell_plots.py | 2 +- visual_behavior/data_access/utilities.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index ac398c69c..46b6ec6f1 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -1,6 +1,7 @@ import os import sys import argparse +import pandas as pd from visual_behavior.data_access import loading as loading from visual_behavior.data_access import utilities as utilities @@ -12,7 +13,6 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' -import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] print(len(experiments_table)) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index f2308e216..7ce0678b0 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1859,9 +1859,14 @@ def get_max_matched_cells_for_learning_mFISH(): Returns a dataframe with cell_specimen_id and metadata for matched cells. """ - cache = bpc.from_lims() - experiments_table = cache.get_ophys_experiment_table(passed_only=False) - experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + # cache = bpc.from_lims() + # experiments_table = cache.get_ophys_experiment_table(passed_only=False) + # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + print(len(experiments), 'experiments') ophys_cells_table = cache.get_ophys_cells_table() From ecfdbd1c34f04c76e4916b26b7c9dcf46ee35391 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:38:31 -0700 Subject: [PATCH 141/187] need cache --- visual_behavior/data_access/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 7ce0678b0..07c3c9856 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1859,7 +1859,7 @@ def get_max_matched_cells_for_learning_mFISH(): Returns a dataframe with cell_specimen_id and metadata for matched cells. """ - # cache = bpc.from_lims() + cache = bpc.from_lims() # experiments_table = cache.get_ophys_experiment_table(passed_only=False) # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] From 38845bea59f15ffa9c66decadbac3d29440df923 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:41:38 -0700 Subject: [PATCH 142/187] cache --- visual_behavior/data_access/utilities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 07c3c9856..d33157d62 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1859,7 +1859,7 @@ def get_max_matched_cells_for_learning_mFISH(): Returns a dataframe with cell_specimen_id and metadata for matched cells. """ - cache = bpc.from_lims() + # experiments_table = cache.get_ophys_experiment_table(passed_only=False) # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] @@ -1869,6 +1869,7 @@ def get_max_matched_cells_for_learning_mFISH(): print(len(experiments), 'experiments') + cache = bpc.from_lims() ophys_cells_table = cache.get_ophys_cells_table() ophys_cells_table = ophys_cells_table.merge(experiments, on='ophys_experiment_id') print(len(ophys_cells_table.cell_specimen_id.unique()), 'unique cells') From e37fb9258c5f4887b7085bf892e4bf85f2a31501 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:48:52 -0700 Subject: [PATCH 143/187] read cells table from file --- visual_behavior/data_access/utilities.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index d33157d62..c7fe633fd 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1869,8 +1869,13 @@ def get_max_matched_cells_for_learning_mFISH(): print(len(experiments), 'experiments') - cache = bpc.from_lims() - ophys_cells_table = cache.get_ophys_cells_table() + # cache = bpc.from_lims() + # ophys_cells_table = cache.get_ophys_cells_table() + + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) + + print(len(ophys_cells_table), 'length of ophys cells table') ophys_cells_table = ophys_cells_table.merge(experiments, on='ophys_experiment_id') print(len(ophys_cells_table.cell_specimen_id.unique()), 'unique cells') From 9874d9ec1b0181db20f58574b6600dfa9f07e9ca Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:53:29 -0700 Subject: [PATCH 144/187] create data dictionary --- visual_behavior/visualization/qc/single_cell_plots.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index ad651427f..86d41759f 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -136,6 +136,7 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe Useful to validate cell matching as well as examine changes in activity profiles over days. """ + # get info for this container container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by=['date_of_acquisition']) ophys_experiment_ids = container_expts.index.values suffix = '_'+data_type @@ -144,8 +145,10 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe else: ylabel = 'response' - window = [-0.5, 1.5] + # create dictionary of all datasets and stimulus_response_dfs + data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) + window = [-0.5, 1.5] n = len(ophys_experiment_ids) figsize = (4*n, 20) fig, ax = plt.subplots(5, n, figsize=figsize, sharey='row') From e43dd0f3d0010350faf6a459b6b0cfea44818c23 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 19:58:22 -0700 Subject: [PATCH 145/187] print statements --- visual_behavior/visualization/qc/single_cell_plots.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 86d41759f..c6141fa74 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -139,6 +139,7 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe # get info for this container container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by=['date_of_acquisition']) ophys_experiment_ids = container_expts.index.values + print('mouse_id:', container_expts.mouse_id.unique()) suffix = '_'+data_type if data_type == 'dff': ylabel = 'dF/F' @@ -146,7 +147,9 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe ylabel = 'response' # create dictionary of all datasets and stimulus_response_dfs + print('generating data dictionary') data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) + print('data dictionary created') window = [-0.5, 1.5] n = len(ophys_experiment_ids) From ac3a5a47c5df48c5bc1d77c0e6e3cc9e99690f7b Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 20:01:57 -0700 Subject: [PATCH 146/187] set index --- scripts/create_single_cell_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index ea390f7e6..39eada81d 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -24,6 +24,7 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table)) # get cells that are matched in all sessions From 14b5c9195678ad97cdb9b4ff08fb95861a33b020 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 20:25:37 -0700 Subject: [PATCH 147/187] create data_dict before looping --- scripts/create_single_cell_plots.py | 10 ++++++++-- visual_behavior/data_access/loading.py | 9 +++++---- visual_behavior/visualization/qc/single_cell_plots.py | 9 +++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 39eada81d..88e68af16 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -32,10 +32,16 @@ # get just the matched cells for this container matched_cell_specimen_ids = matched_cells_df[matched_cells_df.ophys_container_id==ophys_container_id].cell_specimen_id.unique() + ophys_experiment_ids = experiments_table[experiments_table.ophys_container_id==ophys_container_id].index.values + # create dictionary of all datasets and stimulus_response_dfs + print('generating data dictionary') + data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) + print('data dictionary created') + for cell_specimen_id in matched_cell_specimen_ids: try: - scp.plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_specimen_id, experiments_table, - data_type='dff', save_figure=True) + scp.plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, + experiments_table, data_type='dff', save_figure=True) # ppf.plot_matched_roi_and_trace(ophys_container_id, cell_specimen_id, limit_to_last_familiar_second_novel=True, # use_events=use_events, filter_events=filter_events, save_figure=True) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 15a7d4c33..ba5318422 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2797,23 +2797,24 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o :return: """ cache = bpc.from_lims() - experiments_table = cache.get_ophys_experiment_table() + experiments_table = cache.get_ophys_experiment_table(passed_only=False) experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + print(len(experiments_table), 'expts in expt table') mouse_ids = experiments_table.mouse_id.unique() multi_session_df = pd.DataFrame() for mouse_id in mouse_ids: + print('mouse_id:', mouse_id) experiments = experiments_table[(experiments_table.mouse_id == mouse_id)] for ophys_container_id in np.sort(experiments.ophys_container_id.unique()): try: filename = get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(multi_session_df_dir, filename) - print(filepath) df = pd.read_hdf(filepath, key='df') multi_session_df = pd.concat([multi_session_df, df]) - print(multi_session_df.mouse_id.unique()) - except BaseException: + except Exception as e: + # print(e) print('no multi_session_df for', mouse_id, ophys_container_id) return multi_session_df diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index c6141fa74..2350dd834 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -127,7 +127,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even plt.close() -def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_specimen_id, experiments_table, +def plot_across_session_responses_from_dataset_dict(data_dit, ophys_container_id, cell_specimen_id, experiments_table, data_type='dff', save_figure=True): """ Generates plots characterizing single cell activity across sessions, in response to stimulus, omissions, and changes. @@ -140,17 +140,14 @@ def plot_across_session_responses_from_dataset_dict(ophys_container_id, cell_spe container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by=['date_of_acquisition']) ophys_experiment_ids = container_expts.index.values print('mouse_id:', container_expts.mouse_id.unique()) + print('ophys_container_id:', ophys_container_id) + print('there are', len(ophys_experiment_ids), 'in this container') suffix = '_'+data_type if data_type == 'dff': ylabel = 'dF/F' else: ylabel = 'response' - # create dictionary of all datasets and stimulus_response_dfs - print('generating data dictionary') - data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) - print('data dictionary created') - window = [-0.5, 1.5] n = len(ophys_experiment_ids) figsize = (4*n, 20) From fa9882c29f5cd99e697b7bfdad64b3acfc1af3a2 Mon Sep 17 00:00:00 2001 From: matchings Date: Thu, 6 Oct 2022 20:44:53 -0700 Subject: [PATCH 148/187] ophys_container not expt_container --- visual_behavior/visualization/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index ab32c5a27..b4f84be1c 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -428,13 +428,13 @@ def get_metadata_string(metadata): :return: """ m = metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_experiment_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string def get_container_metadata_string(metadata): m = metadata - metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) return metadata_string From 076cf4c8a3bcf5497c1724d8e3bdb6b99582a164 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 09:43:24 -0700 Subject: [PATCH 149/187] typo --- visual_behavior/visualization/qc/single_cell_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 2350dd834..acbd4866f 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -127,7 +127,7 @@ def plot_across_session_responses(ophys_container_id, cell_specimen_id, use_even plt.close() -def plot_across_session_responses_from_dataset_dict(data_dit, ophys_container_id, cell_specimen_id, experiments_table, +def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, experiments_table, data_type='dff', save_figure=True): """ Generates plots characterizing single cell activity across sessions, in response to stimulus, omissions, and changes. From d7d0e9cae762aeaef7ae017818395d3e716b73d1 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 10:23:34 -0700 Subject: [PATCH 150/187] wrong directory --- scripts/create_single_cell_plots.py | 1 + visual_behavior/visualization/utils.py | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 88e68af16..6c3b8e3a6 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -40,6 +40,7 @@ for cell_specimen_id in matched_cell_specimen_ids: try: + print('plotting cell_specimen_id', cell_specimen_id) scp.plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, experiments_table, data_type='dff', save_figure=True) diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index b4f84be1c..9dd7c94c3 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -11,17 +11,16 @@ def get_container_plots_dir(): def get_session_plots_dir(): - return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/session_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/session_plots' def get_experiment_plots_dir(): - return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/experiment_plots' + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/experiment_plots' def get_single_cell_plots_dir(): - # return r'//allen/programs/mindscope/workgroups/learning/ophys/qc_plots/single_cell_plots' - return r'\allen\programs\mindscope\workgroups\learning\ophys\qc_plots\single_cell_plots' - + return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/single_cell_plots' + # return r'\\allen\programs\mindscope\workgroups\learning\ophys\qc_plots\single_cell_plots' def save_figure(fig, figsize, save_dir, folder, fig_title, formats=['.png']): From 6ca2a85db04694fbb216c12b3e49651d1581031f Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 12:12:03 -0700 Subject: [PATCH 151/187] update cell zoom panel --- scripts/create_single_cell_plots.py | 2 +- visual_behavior/visualization/ophys/summary_figures.py | 4 ++-- visual_behavior/visualization/qc/single_cell_plots.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 6c3b8e3a6..d9209ba5a 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -13,7 +13,7 @@ parser.add_argument("--ophys_container_id", type=int, help="Container ID to process") args = parser.parse_args() - ophys_container_id = args.ophys_container_id + ophys_container_id = int(args.ophys_container_id) print('ophys_container_id:', ophys_container_id) # use_events = False diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index 0eeedc6be..c8dd24614 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -58,12 +58,12 @@ def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, mask[y, x] = 1 if ax is None: fig, ax = plt.subplots() - ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection) / 2.) + ax.imshow(max_projection, cmap='gray', vmin=0, vmax=np.amax(max_projection)) if show_mask: ax.imshow(mask, cmap='jet', alpha=alpha, vmin=0, vmax=1) if not full_image: ax.set_xlim(xmin - spacex, xmax + spacex) - ax.set_ylim(ymax + spacey, ymin - spacey) + ax.set_ylim(ymin + spacey, ymax - spacey) ax.set_title('cell_roi_id ' + str(cell_roi_id)) ax.grid(False) ax.axis('off') diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index acbd4866f..0ce2e39be 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -173,8 +173,8 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i print('plotting ROI mask') ct = dataset.cell_specimen_table.copy() cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id - ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.max_projection, cell_roi_id, - spacex=20, spacey=20, show_mask=True, ax=ax[i]) + ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.average_projection, cell_roi_id, + spacex=40, spacey=40, show_mask=True, ax=ax[i]) ax[i].set_title(container_expts.loc[ophys_experiment_id].session_type+'\n'+ str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]) From 861693ca1027525339bf4073a1969f889b4dc15b Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 12:36:35 -0700 Subject: [PATCH 152/187] function to get simple genotype --- visual_behavior/data_access/utilities.py | 50 ++++++++++++++++++------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index c7fe633fd..9753a5a22 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1860,22 +1860,19 @@ def get_max_matched_cells_for_learning_mFISH(): Returns a dataframe with cell_specimen_id and metadata for matched cells. """ - # experiments_table = cache.get_ophys_experiment_table(passed_only=False) - # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + cache = bpc.from_lims() + experiments_table = cache.get_ophys_experiment_table(passed_only=False) + experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + ophys_cells_table = cache.get_ophys_cells_table() - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) - experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + # experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + # ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) print(len(experiments), 'experiments') - - # cache = bpc.from_lims() - # ophys_cells_table = cache.get_ophys_cells_table() - - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) - print(len(ophys_cells_table), 'length of ophys cells table') + ophys_cells_table = ophys_cells_table.merge(experiments, on='ophys_experiment_id') print(len(ophys_cells_table.cell_specimen_id.unique()), 'unique cells') @@ -1894,4 +1891,31 @@ def get_max_matched_cells_for_learning_mFISH(): matched_cells_df = matched_cells_df.drop_duplicates(subset=['cell_specimen_id']) print(len(matched_cells_df), 'cells matched across all sessions in their container') - return matched_cells_df \ No newline at end of file + return matched_cells_df + + +def get_simple_genotype(full_genotype): + """ + Create simple genotype as reporter gene + Ai reporter line + """ + # gene of driver line is always the first element of full genotype + driver = full_genotype.split('-')[0] + try: # need try except for cases where reporter is atypical or doesnt include 'Ai' + # pull out the reporter based on location of 'Ai' in the genotype string + reporter = full_genotype[full_genotype.index('Ai'):full_genotype.index('Ai')+5] + #get rid of parentheses for reporters that only have 2 numbers + reporter = reporter.split('(')[0] + except: + reporter = 'unknown' + # combine driver and reporter to get simple genotype + genotype = driver+';'+reporter + return genotype + + +def add_simple_genotype(df): + """ + Function to add a column 'genotype' to any dataframe containing 'full_genotype' + the 'genotype' column will be the gene of the driver line and the Ai# of the reporter line + """ + df['genotype'] = [get_simple_genotype(full_genotype) for full_genotype in df.full_genotype.values] + return df \ No newline at end of file From 6bfbf4b3c40b36ec18c558167f23816cd63d7151 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 14:04:13 -0700 Subject: [PATCH 153/187] update matched cell plots --- scripts/create_single_cell_plots.py | 1 + .../visualization/qc/single_cell_plots.py | 31 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index d9209ba5a..07417be12 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -40,6 +40,7 @@ for cell_specimen_id in matched_cell_specimen_ids: try: + cell_specimen_id = int(cell_specimen_id) print('plotting cell_specimen_id', cell_specimen_id) scp.plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, experiments_table, data_type='dff', save_figure=True) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 0ce2e39be..8114334c2 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -176,9 +176,8 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.average_projection, cell_roi_id, spacex=40, spacey=40, show_mask=True, ax=ax[i]) - ax[i].set_title(container_expts.loc[ophys_experiment_id].session_type+'\n'+ - str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]) - + ax[i].set_title(str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]+'\n'+ + container_expts.loc[ophys_experiment_id].session_type) # plot average response for each image (for all non-change image presentations) # get trial averaged responses for various conditions @@ -188,14 +187,15 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i response_window_duration=response_window_duration, get_pref_stim=True) colors = sns.color_palette('hls', 8) + [(0.5, 0.5, 0.5)] - cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == False)] + cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == True)] for c, image_name in enumerate(np.sort(cell_data.image_name.unique())): ax[i + n] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=image_name, color=colors[c], interval_sec=0.5, xlims=[-0.5, 0.75], ax=ax[i + n]) + ax[i + n].legend(loc='lower right', fontsize='xx-small') ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') - ax[i + n].set_title(container_expts.loc[ophys_experiment_id].session_type + '\n image response') + ax[i + n].set_title('oeid: '+str(ophys_experiment_id)) # plot mean omission response print('plotting omissions') @@ -210,7 +210,7 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i legend_label=image_name, color='gray', interval_sec=1, xlims=[-1, 2], ax=ax[i + (n * 2)]) ax[i + (n * 2)] = utils.plot_flashes_on_trace(ax[i + (n * 2)], timstamps, change=False, omitted=True, alpha=0.15, facecolor='gray') - ax[i + (n * 2)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n omission response') + ax[i + (n * 2)].set_title('osid: '+str(container_expts.loc[ophys_experiment_id].ophys_session_id)) except: print('couldnt plot omissions') @@ -230,14 +230,14 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i run_colors = [sns.color_palette()[3], sns.color_palette()[2]] # loop through running conditions and plot for c, running in enumerate(np.sort(cell_data.running.unique())): - if len(cell_data[cell_data.running == running]) > 5: # must be at least 5 trials per condition + if len(cell_data[cell_data.running == running]) > 0: # only plot if there is data for this condition ax[i + (n * 3)] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.running == running], frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=running, color=run_colors[c], interval_sec=0.5, xlims=[-1, 2], ax=ax[i + (n * 3)]) ax[i + (n * 3)].legend(fontsize='xx-small', title='running', title_fontsize='xx-small') ax[i + (n * 3)] = utils.plot_flashes_on_trace(ax[i + (n * 3)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') - ax[i + (n * 3)].set_title(container_expts.loc[ophys_experiment_id].session_type + '\n image response') + ax[i + (n * 3)].set_title('running vs stationary') except: print('couldnt plot running / not-running panel') @@ -260,21 +260,22 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i xlims=[-1, 2], ax=ax[i + (n * 4)]) ax[i + (n * 4)].legend(fontsize='xx-small', title='hit', title_fontsize='xx-small') ax[i + (n * 4)] = utils.plot_flashes_on_trace(ax[i + (n * 4)], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') - ax[i + (n * 4)].set_title(container_expts.loc[ophys_experiment_id].session_type[6:] + '\n change response') + ax[i + (n * 4)].set_title('hit vs miss') except: print('couldnt plot hit vs. miss') - # overall plot title - fig.tight_layout() - metadata_string = utils.get_container_metadata_string(dataset.metadata) - fig.suptitle(str(cell_specimen_id) + '_' + metadata_string, x=0.5, y=1.01, horizontalalignment='center') except Exception as e: print('problem for cell_specimen_id:', cell_specimen_id, ', ophys_experiment_id:', ophys_experiment_id) print(e) + + # overall plot title + fig.tight_layout() + metadata_string = utils.get_container_metadata_string(dataset.metadata) + fig.suptitle(metadata_string + '_' + str(cell_specimen_id), x=0.5, y=1.01, horizontalalignment='center') if save_figure: save_dir = utils.get_single_cell_plots_dir() - utils.save_figure(fig, figsize, save_dir, 'matched_cell_across_session_responses', str( - cell_specimen_id) + '_' + metadata_string + suffix) + utils.save_figure(fig, figsize, save_dir, 'matched_cell_across_session_responses', + metadata_string + '_' + str(cell_specimen_id) + suffix) From 96303d65dc003815cd76e4969595a6ecad7e2941 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 14:04:22 -0700 Subject: [PATCH 154/187] fix cell zoom --- visual_behavior/visualization/ophys/summary_figures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index c8dd24614..1c7de972d 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -63,7 +63,7 @@ def plot_cell_zoom(roi_masks, max_projection, cell_roi_id, spacex=10, spacey=10, ax.imshow(mask, cmap='jet', alpha=alpha, vmin=0, vmax=1) if not full_image: ax.set_xlim(xmin - spacex, xmax + spacex) - ax.set_ylim(ymin + spacey, ymax - spacey) + ax.set_ylim(ymax + spacey, ymin - spacey) ax.set_title('cell_roi_id ' + str(cell_roi_id)) ax.grid(False) ax.axis('off') From 155acf5b665c520f0f9d0cf43db48408550e3b2f Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 7 Oct 2022 14:05:52 -0700 Subject: [PATCH 155/187] last change --- visual_behavior/visualization/qc/single_cell_plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 8114334c2..f098426ae 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -186,13 +186,13 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i frame_rate=output_sampling_rate, time_window=window, response_window_duration=response_window_duration, get_pref_stim=True) - colors = sns.color_palette('hls', 8) + [(0.5, 0.5, 0.5)] + colors = sns.color_palette() cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == True)] for c, image_name in enumerate(np.sort(cell_data.image_name.unique())): ax[i + n] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=image_name, color=colors[c], interval_sec=0.5, - xlims=[-0.5, 0.75], ax=ax[i + n]) + xlims=[-1, 2], ax=ax[i + n]) ax[i + n].legend(loc='lower right', fontsize='xx-small') ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') ax[i + n].set_title('oeid: '+str(ophys_experiment_id)) From 98a6addcb12a05207855280840958b16b7bc7037 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 15:23:50 -0700 Subject: [PATCH 156/187] load expts from file --- visual_behavior/data_access/utilities.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 9753a5a22..230e6f4fb 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1861,14 +1861,14 @@ def get_max_matched_cells_for_learning_mFISH(): """ cache = bpc.from_lims() - experiments_table = cache.get_ophys_experiment_table(passed_only=False) - experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] - ophys_cells_table = cache.get_ophys_cells_table() - - # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - # experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) - # experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - # ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) + # experiments_table = cache.get_ophys_experiment_table(passed_only=False) + # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + # ophys_cells_table = cache.get_ophys_cells_table() + + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) print(len(experiments), 'experiments') print(len(ophys_cells_table), 'length of ophys cells table') From 195f6ed9416819079b7556f7ca45019d809ed398 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 16:14:19 -0700 Subject: [PATCH 157/187] multi session df for omFISH mice --- scripts/run_create_multi_session_df.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 613bb623d..50bc1b7a4 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -38,7 +38,11 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' import pandas as pd experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1S','LearningmFISHDevelopment'])] +# experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', +# 'LearningmFISHDevelopment', +# 'omFISHGad2Meso'])] +experiments_table = experiments_table[(experiments_table.project_code.isin(['omFISHGad2Meso']))& + (experiments_table.session_type=='OPHYS_2_images_A_passive')] print(len(experiments_table), 'experiments') # experiments_table = loading.get_filtered_ophys_experiment_table() From d8db7fabfa9f518d87f3c88db7d80eea420b643f Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 16:22:23 -0700 Subject: [PATCH 158/187] multi session df LAMF development mice --- scripts/run_create_multi_session_df.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index 50bc1b7a4..f0a617b10 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -41,8 +41,9 @@ # experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', # 'LearningmFISHDevelopment', # 'omFISHGad2Meso'])] -experiments_table = experiments_table[(experiments_table.project_code.isin(['omFISHGad2Meso']))& - (experiments_table.session_type=='OPHYS_2_images_A_passive')] +# experiments_table = experiments_table[(experiments_table.project_code.isin(['omFISHGad2Meso']))& +# (experiments_table.session_type=='OPHYS_2_images_A_passive')] +experiments_table = experiments_table[(experiments_table.project_code.isin(['LearningmFISHDevelopment']))] print(len(experiments_table), 'experiments') # experiments_table = loading.get_filtered_ophys_experiment_table() From 9109177e250b2336acca57ec5b0d99c420bf21bf Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 17:56:52 -0700 Subject: [PATCH 159/187] functions to simplify genotype and label dox mice --- visual_behavior/data_access/utilities.py | 58 +++++++++++++++++++++++- visual_behavior/visualization/utils.py | 9 +++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 230e6f4fb..7e93bf80d 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1908,7 +1908,7 @@ def get_simple_genotype(full_genotype): except: reporter = 'unknown' # combine driver and reporter to get simple genotype - genotype = driver+';'+reporter + genotype = driver+'_'+reporter return genotype @@ -1918,4 +1918,58 @@ def add_simple_genotype(df): the 'genotype' column will be the gene of the driver line and the Ai# of the reporter line """ df['genotype'] = [get_simple_genotype(full_genotype) for full_genotype in df.full_genotype.values] - return df \ No newline at end of file + return df + + +def add_dox_to_simple_genotype(df): + """ + function that loops through all rows in df, checks whether the mouse_id for that row is one of the dox mice, + then adds '_dox' to the end of the genotype column + """ + # if simple genotype column doesnt exist, create it + if 'genotype' not in df.columns: + df = utilities.add_simple_genotype(df) + # list of mice that have been treated with doxycycline to suppress GCaMP expression during development + dox_mice = get_list_of_dox_mice() + # loop across rows, check if mouse_id is in dox mice list, if so, add that to the genotype column + for i, index in enumerate(df.index.values): + if df.iloc[i].mouse_id in dox_mice: + df.at[index, 'genotype'] = df.iloc[i]['genotype'] + '_dox' + return df + + +def get_list_of_dox_mice(): + """ + Function to return a list of hard coded mouse_ids of mice who were treated with doxycycline + """ + dox_mice = ['623975', '623972', '631563', '637848', '637851'] + return dox_mice + + +def get_omFISH_passive_mice(experiments_table): + """ + Filters experiments table to limit to omFISHGad2Meso project code & session_type OPHYS_2_images_A_passive + returns list of mouse_ids + """ + omFISH_mice = experiments_table[(experiments_table.project_code == 'omFISHGad2Meso') & ( + experiments_table.session_type == 'OPHYS_2_images_A_passive')].mouse_id.unique() + return omFISH_mice + + +def get_mFISH_projects_experiments_table(): + """ + Load experiments table from cache, limit to learning mFISH and omFISH project codes, + and remove session_types that wont load via SDK + """ + + from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache + + cache = VisualBehaviorOphysProjectCache.from_lims() + experiments_table = cache.get_ophys_experiment_table(passed_only=False) + experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', + 'LearningmFISHDevelopment', + 'omFISHGad2Meso'])] + experiments = experiments[ + experiments.session_type.isin(['STAGE_0', 'STAGE_1', 'OPHYS_7_receptive_field_mapping']) == False] + return experiments + diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index 9dd7c94c3..a43c90daf 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -432,8 +432,15 @@ def get_metadata_string(metadata): def get_container_metadata_string(metadata): + import visual_behavior.data_access.utilities as utilities m = metadata - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + # genotype = m['cre_line'].split('-')[0] + genotype = utilities.get_simple_genotype(m['full_genotype']) + # add _dox if mouse is a dox mouse + dox_mice = utilities.get_list_of_dox_mice() + if str(m['mouse_id']) in dox_mice: + genotype = genotype+'_dox' + metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + genotype + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) return metadata_string From fa5c54477ca72449313720cb9c6338ba43cc76eb Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 18:23:30 -0700 Subject: [PATCH 160/187] multi session df with images --- scripts/create_multi_session_df.py | 26 ++++++++++--------- scripts/run_create_multi_session_df.py | 2 +- .../ophys/io/create_multi_session_df.py | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index ba1b5586f..bdbae71bd 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -29,35 +29,37 @@ physio_data_types = ['dff', 'events']# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] - physio_conditions = [['cell_specimen_id', 'is_change'], - ['cell_specimen_id', 'omitted'], + physio_conditions = [['cell_specimen_id', 'image_name'], + # ['cell_specimen_id', 'is_change'], + # ['cell_specimen_id', 'omitted'], # ['cell_specimen_id', 'is_change', 'epoch'], # ['cell_specimen_id', 'omitted', 'epoch'], - ['cell_specimen_id', 'is_change', 'image_name'], + # ['cell_specimen_id', 'is_change', 'image_name'], # ['cell_specimen_id', 'is_change', 'image_name', 'epoch'], - ['cell_specimen_id', 'is_change', 'hit'], - ['cell_specimen_id', 'is_change', 'omitted'],] + # ['cell_specimen_id', 'is_change', 'hit'], + # ['cell_specimen_id', 'is_change', 'omitted'],] # ['cell_specimen_id', 'pre_change', 'epoch'], # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['ophys_experiment_id', 'is_change'], - ['ophys_experiment_id', 'omitted'], + behavior_conditions = [['ophys_experiment_id', 'image_name'], + # ['ophys_experiment_id', 'is_change'], + # ['ophys_experiment_id', 'omitted'], # ['ophys_experiment_id', 'is_change', 'epoch'], # ['ophys_experiment_id', 'omitted', 'epoch'], - ['ophys_experiment_id', 'is_change', 'image_name'], + # ['ophys_experiment_id', 'is_change', 'image_name'], # ['ophys_experiment_id', 'is_change', 'image_name', 'epoch'], - ['ophys_experiment_id', 'is_change', 'hit'], - ['ophys_experiment_id', 'is_change', 'omitted'],] + # ['ophys_experiment_id', 'is_change', 'hit'], + # ['ophys_experiment_id', 'is_change', 'omitted'],] # ['ophys_experiment_id', 'is_change', 'pre_change', 'epoch'], # ['ophys_experiment_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] # event types corresponding to the above physio and behavior conditions - must be in same sequential order!! - event_types_for_conditions = ['changes', 'omissions', + event_types_for_conditions = ['all', ]#'changes', 'omissions', # 'changes', 'omissions', - 'changes', 'changes', 'all', ] #'changes', + # 'changes', 'changes', 'all', ] #'changes', # 'all', 'all', 'all'] # add engagement state to all conditions diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index f0a617b10..e05848ed9 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -43,7 +43,7 @@ # 'omFISHGad2Meso'])] # experiments_table = experiments_table[(experiments_table.project_code.isin(['omFISHGad2Meso']))& # (experiments_table.session_type=='OPHYS_2_images_A_passive')] -experiments_table = experiments_table[(experiments_table.project_code.isin(['LearningmFISHDevelopment']))] +# experiments_table = experiments_table[(experiments_table.project_code.isin(['LearningmFISHDevelopment']))] print(len(experiments_table), 'experiments') # experiments_table = loading.get_filtered_ophys_experiment_table() diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 2bbe90cb0..292ded7c4 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -136,7 +136,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # compute trial average and other metrics mdf = ut.get_mean_df(df, conditions=conditions, frame_rate=output_sampling_rate, time_window=time_window, response_window_duration=response_window_duration, - get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=True) + get_pref_stim=get_pref_stim, exclude_omitted_from_pref_stim=False) if 'correlation_values' in mdf.keys(): mdf = mdf.drop(columns=['correlation_values']) mdf['ophys_experiment_id'] = experiment_id From ef533d5c085e5bc9a9fd4ebeccbfc7b9762c7b9c Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 18:26:26 -0700 Subject: [PATCH 161/187] update single cell plots --- scripts/run_single_cell_plots.py | 4 ++-- visual_behavior/visualization/qc/single_cell_plots.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 46b6ec6f1..61bf784b7 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -30,9 +30,9 @@ # instantiate a Slurm object slurm = Slurm( - mem='40g', # '24g' + mem='80g', # '24g' cpus_per_task=1, - time='10:00:00', + time='20:00:00', partition='braintv', job_name='single_cell_plots', output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index f098426ae..9926b5b5c 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -174,10 +174,12 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i ct = dataset.cell_specimen_table.copy() cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.average_projection, cell_roi_id, - spacex=40, spacey=40, show_mask=True, ax=ax[i]) + spacex=50, spacey=50, show_mask=True, ax=ax[i]) - ax[i].set_title(str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]+'\n'+ - container_expts.loc[ophys_experiment_id].session_type) + session_type = container_expts.loc[ophys_experiment_id].session_type + s = session_type.split('_') + session_type = s[0] + '_' + s[1] + '_' + s[2] + ax[i].set_title(str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]+'\n'+session_type) # plot average response for each image (for all non-change image presentations) # get trial averaged responses for various conditions @@ -193,7 +195,7 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i frame_rate=output_sampling_rate, ylabel=ylabel, legend_label=image_name, color=colors[c], interval_sec=0.5, xlims=[-1, 2], ax=ax[i + n]) - ax[i + n].legend(loc='lower right', fontsize='xx-small') + ax[i + n].legend(loc='upper right', fontsize='xx-small') ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') ax[i + n].set_title('oeid: '+str(ophys_experiment_id)) From d9e8ff805c14600e38be3595e382711bec15ad75 Mon Sep 17 00:00:00 2001 From: matchings Date: Sun, 9 Oct 2022 18:57:43 -0700 Subject: [PATCH 162/187] typo --- scripts/create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index bdbae71bd..4f4bcb248 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -29,7 +29,7 @@ physio_data_types = ['dff', 'events']# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] - physio_conditions = [['cell_specimen_id', 'image_name'], + physio_conditions = [['cell_specimen_id', 'image_name'],] # ['cell_specimen_id', 'is_change'], # ['cell_specimen_id', 'omitted'], # ['cell_specimen_id', 'is_change', 'epoch'], @@ -42,7 +42,7 @@ # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['ophys_experiment_id', 'image_name'], + behavior_conditions = [['ophys_experiment_id', 'image_name'],] # ['ophys_experiment_id', 'is_change'], # ['ophys_experiment_id', 'omitted'], # ['ophys_experiment_id', 'is_change', 'epoch'], From 34675a3c4a2e3d0c0b5858da431616729f39c69d Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 12:44:11 -0700 Subject: [PATCH 163/187] ophys container id not expt --- visual_behavior/visualization/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index a43c90daf..f5b8fbf7d 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -440,7 +440,7 @@ def get_container_metadata_string(metadata): dox_mice = utilities.get_list_of_dox_mice() if str(m['mouse_id']) in dox_mice: genotype = genotype+'_dox' - metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + genotype + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + genotype + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) return metadata_string From 7b3985db2915698c2c6ed190ccc059fde43bd1f8 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:10:20 -0700 Subject: [PATCH 164/187] add new matched cell plot with mdfs --- .../visualization/qc/single_cell_plots.py | 156 +++++++++++++++--- visual_behavior/visualization/utils.py | 14 +- 2 files changed, 148 insertions(+), 22 deletions(-) diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 9926b5b5c..be5618bbf 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -3,7 +3,8 @@ import matplotlib.pyplot as plt from visual_behavior.data_access import loading as loading -from visual_behavior.visualization import utils as utils +from visual_behavior.data_access import utilities as utilities + import visual_behavior.visualization.ophys.summary_figures as sf from visual_behavior.ophys.response_analysis.response_analysis import ResponseAnalysis @@ -281,59 +282,176 @@ def plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_i + +def plot_across_session_responses_from_multi_session_dfs(ophys_container_id, cell_specimen_id, experiments_table, + change_image_mdf, omission_mdf, output_sampling_rate=30, + data_type='dff', save_figure=True): + """ + Generates plots characterizing single cell activity across sessions for matched cells. + Plots the ROI mask across days, as well as the average change response for each image in the session, + and the average omission response for sessions where omissions are present. + Useful to validate cell matching as well as examine changes in activity profiles over days. + """ + + # get info for this container + container_expts = experiments_table[experiments_table.ophys_container_id == ophys_container_id].sort_values(by=['date_of_acquisition']) + ophys_experiment_ids = container_expts.index.values + print('mouse_id:', container_expts.mouse_id.unique()) + print('ophys_container_id:', ophys_container_id) + print('there are', len(ophys_experiment_ids), 'experiments in this container') + suffix = '_'+data_type + if data_type == 'dff': + ylabel = 'dF/F' + else: + ylabel = 'response' + + window = [-1, 2] + + n = len(ophys_experiment_ids) + figsize = (4*n, 15) + fig, ax = plt.subplots(3, n, figsize=figsize, sharey='row') + ax = ax.ravel() + print('ophys_container_id:', ophys_container_id) + for i, ophys_experiment_id in enumerate(ophys_experiment_ids): + print('ophys_experiment_id:', ophys_experiment_id) + try: + dataset = loading.get_ophys_dataset(ophys_experiment_id) + + # create another plot while we have dataset loaded + try: + plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figure=True) + except: + print('couldnt plot single cell activity and behavior') + + # if the selected cell is not in this session (i.e. is not matched), plot it, otherwise skip + if cell_specimen_id in dataset.dff_traces.index: + print(cell_specimen_id, 'is in', ophys_experiment_id) + + # plot cell mask + print('plotting ROI mask') + ct = dataset.cell_specimen_table.copy() + cell_roi_id = ct.loc[cell_specimen_id].cell_roi_id + ax[i] = sf.plot_cell_zoom(dataset.roi_masks, dataset.average_projection, cell_roi_id, + spacex=50, spacey=50, show_mask=True, ax=ax[i]) + + session_type = container_expts.loc[ophys_experiment_id].session_type + s = session_type.split('_') + session_type = s[0] + '_' + s[1] + '_' + s[2] + ax[i].set_title(str(container_expts.loc[ophys_experiment_id].date_of_acquisition)[:10]+'\n'+session_type) + + # plot average response for each image (for all non-change image presentations) + # get trial averaged responses for various conditions + print('plotting image responses') + cdf = change_image_mdf.copy() + cdf = cdf[cdf.ophys_experiment_id==ophys_experiment_id] + colors = sns.color_palette() + cell_data = cdf[(cdf.cell_specimen_id == cell_specimen_id) & (cdf.is_change == True)] + timestamps = cell_data.trace_timestamps.values[0] + for c, image_name in enumerate(np.sort(cell_data.image_name.unique())): + ax[i + n] = utils.plot_mean_trace_from_mean_df(cell_data[cell_data.image_name == image_name], + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=image_name, color=colors[c], interval_sec=0.5, + xlims=[-1, 2], ax=ax[i + n]) + ax[i + n].legend(loc='upper right', fontsize='xx-small') + ax[i + n] = utils.plot_flashes_on_trace(ax[i + n], timestamps, change=True, omitted=False, alpha=0.15, facecolor='gray') + ax[i + n].set_title('oeid: '+str(ophys_experiment_id)) + + # plot mean omission response + print('plotting omissions') + try: + odf = omission_mdf.copy() + odf = odf[odf.ophys_experiment_id==ophys_experiment_id] + cell_data = odf[(odf.cell_specimen_id == cell_specimen_id)] + timestamps = cell_data.trace_timestamps.values[0] + ax[i + (n * 2)] = utils.plot_mean_trace_from_mean_df(cell_data, + frame_rate=output_sampling_rate, ylabel=ylabel, + legend_label=image_name, color='gray', interval_sec=1, + xlims=[-1, 2], ax=ax[i + (n * 2)]) + ax[i + (n * 2)] = utils.plot_flashes_on_trace(ax[i + (n * 2)], timstamps, change=False, omitted=True, alpha=0.15, facecolor='gray') + ax[i + (n * 2)].set_title('osid: '+str(container_expts.loc[ophys_experiment_id].ophys_session_id)) + except: + print('couldnt plot omissions') + except: + print('problem for', ophys_experiment_id) + + + # overall plot title + fig.tight_layout() + metadata_string = utils.get_container_metadata_string(dataset.metadata) + fig.suptitle(metadata_string + '_' + str(cell_specimen_id), x=0.5, y=1.01, horizontalalignment='center') + if save_figure: + save_dir = utils.get_single_cell_plots_dir() + utils.save_figure(fig, figsize, save_dir, 'matched_cell_across_session_responses_from_mdf', + metadata_string + '_' + str(cell_specimen_id) + suffix) + + + def plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figure=True): """ Plots the full dFF trace for a cell, along with licking behavior, rewards, running speed, pupil area, and face motion. Useful to visualize whether the dFF trace tracks the behavior variables """ - figsize = (20, 10) - fig, ax = plt.subplots(5, 1, figsize=figsize, sharex=True) + figsize = (20, 12) + fig, ax = plt.subplots(6, 1, figsize=figsize, sharex=True) colors = sns.color_palette() + trace_timestamps = dataset.ophys_timestamps + trace = dataset.corrected_fluorescence_traces.loc[cell_specimen_id].corrected_fluorescence + ax[0].plot(trace_timestamps, trace, label='corrected fluorescence', color=colors[2]) + ax[0].set_ylabel('fluorescence') + trace = dataset.dff_traces.loc[cell_specimen_id].dff - ax[0].plot(trace_timestamps, trace, label='mean_trace', color=colors[0]) - ax[0].set_ylabel('dF/F') + events = dataset.events.loc[cell_specimen_id].events + # events[events==0] = np.nan + ax[1].plot(trace_timestamps, trace, label='dF/F', color=colors[0]) + ax[1].set_ylabel('dF/F') + ax2 = ax[1].twinx() + ax2.plot(trace_timestamps, events, label='events', color=colors[8]) + ax2.set_ylabel('events') + ax[1].legend(fontsize='xx-small', loc='upper left') + ax2.legend(fontsize='xx-small', loc='upper right') lick_timestamps = dataset.licks.timestamps.values licks = np.ones(len(lick_timestamps)) - ax[1].plot(lick_timestamps, licks, '|', label='licks', color=colors[3]) - ax[1].set_ylabel('licks') - ax[1].set_yticklabels([]) + ax[2].plot(lick_timestamps, licks, '|', label='licks', color=colors[3]) + ax[2].set_ylabel('licks') + ax[2].set_yticklabels([]) running_speed = dataset.running_speed.speed.values running_timestamps = dataset.running_speed.timestamps.values - ax[2].plot(running_timestamps, running_speed, label='running_speed', color=colors[4]) - ax[2].set_ylabel('run speed\n(cm/s)') + ax[3].plot(running_timestamps, running_speed, label='running_speed', color=colors[4]) + ax[3].set_ylabel('run speed\n(cm/s)') try: pupil_area = dataset.eye_tracking.pupil_area.values pupil_timestamps = dataset.eye_tracking.timestamps.values - ax[3].plot(pupil_timestamps, pupil_area, label='pupil_area', color=colors[9]) + ax[4].plot(pupil_timestamps, pupil_area, label='pupil_area', color=colors[9]) except Exception: print('no pupil for', dataset.ophys_experiment_id) - ax[3].set_ylabel('pupil area\n pixels**2') - ax[3].set_ylim(-50, 30000) + ax[4].set_ylabel('pupil area\n pixels**2') + ax[4].set_ylim(-50, 30000) try: face_motion = dataset.behavior_movie_pc_activations[:, 0] face_timestamps = dataset.timestamps['eye_tracking'].timestamps - ax[4].plot(face_timestamps, face_motion, label='face_motion_PC0', color=colors[2]) + ax[5].plot(face_timestamps, face_motion, label='face_motion_PC0', color=colors[2]) except Exception: print('no face motion for', dataset.ophys_experiment_id) - ax[4].set_ylabel('face motion\n PC0 activation') + ax[5].set_ylabel('face motion\n PC0 activation') - for x in range(5): + for x in range(6): ax[x].tick_params(which='both', bottom=False, top=False, right=False, left=True, labelbottom=False, labeltop=False, labelright=False, labelleft=True) - ax[4].tick_params(which='both', bottom=False, top=False, right=False, left=True, + ax[5].tick_params(which='both', bottom=False, top=False, right=False, left=True, labelbottom=True, labeltop=False, labelright=False, labelleft=True) # ax[x].legend(loc='upper left', fontsize='x-small') plt.subplots_adjust(wspace=0, hspace=0.1) - ax[0].set_title(str(cell_specimen_id) + '_' + dataset.metadata_string) + metadata_string = utils.get_metadata_string(dataset.metadata) + ax[0].set_title(str(cell_specimen_id) + '_' + metadata_string) if save_figure: utils.save_figure(fig, figsize, utils.get_single_cell_plots_dir(), 'dff_trace_and_behavior', - str(cell_specimen_id) + '_' + dataset.metadata_string + '_dff_trace_and_behavior') + str(cell_specimen_id) + '_' + metadata_string + '_dff_trace_and_behavior') plt.close() diff --git a/visual_behavior/visualization/utils.py b/visual_behavior/visualization/utils.py index f5b8fbf7d..2ed0e57f0 100644 --- a/visual_behavior/visualization/utils.py +++ b/visual_behavior/visualization/utils.py @@ -3,9 +3,11 @@ import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns - sns.set_context('notebook', font_scale=1.5, rc={'lines.markeredgewidth': 2}) +import visual_behavior.data_access.utilities as utilities + + def get_container_plots_dir(): return r'/allen/programs/mindscope/workgroups/learning/ophys/qc_plots/container_plots' @@ -427,12 +429,18 @@ def get_metadata_string(metadata): :return: """ m = metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + # genotype = m['cre_line'].split('-')[0] + # add simple genotype + genotype = utilities.get_simple_genotype(m['full_genotype']) + # add _dox if mouse is a dox mouse + dox_mice = utilities.get_list_of_dox_mice() + if str(m['mouse_id']) in dox_mice: + genotype = genotype + '_dox' + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + genotype + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string def get_container_metadata_string(metadata): - import visual_behavior.data_access.utilities as utilities m = metadata # genotype = m['cre_line'].split('-')[0] genotype = utilities.get_simple_genotype(m['full_genotype']) From 9044ea6fcb416127e9cee71dc57669715162c5bc Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:32:13 -0700 Subject: [PATCH 165/187] container qc plots --- .../visualization/qc/container_plots.py | 35 +++++++++++-------- .../qc/run_save_all_container_plots.py | 17 +++++---- .../visualization/qc/single_cell_plots.py | 2 +- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/visual_behavior/visualization/qc/container_plots.py b/visual_behavior/visualization/qc/container_plots.py index 6cb38acd1..d4ce16ed9 100644 --- a/visual_behavior/visualization/qc/container_plots.py +++ b/visual_behavior/visualization/qc/container_plots.py @@ -41,7 +41,13 @@ def get_metadata_string(ophys_container_id): ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) dataset = loading.get_ophys_dataset(ophys_experiment_ids[0]) m = dataset.metadata.copy() - metadata_string = str(m['mouse_id']) + '_' + str(m['experiment_container_id']) + '_' + m['cre_line'].split('-')[0] + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] + # genotype = m['cre_line'].split('-')[0] + genotype = utilities.get_simple_genotype(m['full_genotype']) + # add _dox if mouse is a dox mouse + dox_mice = utilities.get_list_of_dox_mice() + if str(m['mouse_id']) in dox_mice: + genotype = genotype + '_dox' + metadata_string = str(m['mouse_id']) + '_' + str(m['ophys_container_id']) + '_' + genotype + '_' + m['targeted_structure'] + '_' + str(m['imaging_depth']) + '_' + m['session_type'] return metadata_string @@ -127,7 +133,7 @@ def plot_sdk_max_projection_images_for_container(ophys_container_id, save_figure experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 5) + figsize = (5*len(ophys_experiment_ids), 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -168,7 +174,7 @@ def plot_movie_max_projection_images_for_container(ophys_container_id, save_figu experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 5) + figsize = (5 * len(ophys_experiment_ids), 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -204,7 +210,7 @@ def plot_sdk_average_images_for_container(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 5) + figsize = (5 * len(ophys_experiment_ids), 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -240,7 +246,7 @@ def plot_movie_average_images_for_container(ophys_container_id, save_figure=True experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 5) + figsize = (5 * len(ophys_experiment_ids), 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -291,7 +297,7 @@ def plot_segmentation_masks_for_container(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 5) + figsize = (5 * len(ophys_experiment_ids), 5) fig, ax = plt.subplots(1, len(ophys_experiment_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -314,7 +320,7 @@ def plot_segmentation_mask_overlays_for_container(ophys_container_id, save_figur experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 18) + figsize = (5 * len(ophys_experiment_ids), 20) n = len(ophys_experiment_ids) fig, ax = plt.subplots(4, n, figsize=figsize) ax = ax.ravel() @@ -459,7 +465,7 @@ def plot_dff_traces_heatmaps_for_container(ophys_container_id, save_figure=True) experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 20) + figsize = (20, 5 * len(ophys_experiment_ids)) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -951,7 +957,7 @@ def plot_motion_correction_xy_shift_for_container(ophys_container_id, save_figur experiments_table = loading.get_filtered_ophys_experiment_table() ophys_experiment_ids = loading.get_ophys_experiment_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 20) + figsize = (20, 5 * len(ophys_experiment_ids)) fig, ax = plt.subplots(len(ophys_experiment_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_experiment_id in enumerate(ophys_experiment_ids): @@ -1011,17 +1017,18 @@ def plot_population_average_across_sessions(container_df, ophys_container_id, da :param save_figure: Boolean, whether or not to save figure to default QC plots directory :return: """ - dataset = loading.get_ophys_dataset(container_df.ophys_experiment_id.unique()[0]) + # dataset = loading.get_ophys_dataset(container_df.ophys_experiment_id.unique()[0]) # title = dataset.metadata_string title = get_metadata_string(ophys_container_id) # frame_rate = dataset.metadata['ophys_frame_rate'] if event_type == 'omissions': figsize = (12, 5) + container_df = container_df[container_df.omitted == True] m = title.split('_') # dataset.analysis_folder.split('_') title = str(ophys_container_id) + '_' + m[1] + '_' + m[2] + '_' + m[3] + '_' + m[4] + '_' + m[5] + '_' + m[6] elif event_type == 'changes': figsize = (12, 5) - container_df = container_df[container_df.go == True] + container_df = container_df[container_df.is_change == True] m = title.split('_') # dataset.analysis_folder.split('_') title = str(ophys_container_id) + '_' + m[1] + '_' + m[2] + '_' + m[3] + '_' + m[4] + '_' + m[5] + '_' + m[6] else: @@ -1072,7 +1079,7 @@ def plot_omission_population_average_across_sessions(ophys_container_id, save_fi :return: """ container_df = loading.get_container_response_df(ophys_container_id, data_type='dff', event_type='all') - plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='all', + plot_population_average_across_sessions(container_df, ophys_container_id, data_type='dff', event_type='omissions', save_figure=save_figure) @@ -1107,7 +1114,7 @@ def plot_running_speed_for_container(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table() ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 15) + figsize = (25, len(ophys_session_ids)*5) fig, ax = plt.subplots(len(ophys_session_ids), 1, figsize=figsize) ax = ax_to_array(ax) for i, ophys_session_id in enumerate(ophys_session_ids): @@ -1126,7 +1133,7 @@ def plot_lick_rasters_for_container(ophys_container_id, save_figure=True): experiments_table = loading.get_filtered_ophys_experiment_table() ophys_session_ids = loading.get_ophys_session_ids_for_ophys_container_id(ophys_container_id, experiments_table) - figsize = (25, 7) + figsize = (len(ophys_session_ids)*5, 7) fig, ax = plt.subplots(1, len(ophys_session_ids), figsize=figsize) ax = ax_to_array(ax) for i, ophys_session_id in enumerate(ophys_session_ids): diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 01bafd847..a0fcc1813 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -7,7 +7,7 @@ parser = argparse.ArgumentParser(description='run container qc plot generation functions on the cluster') -parser.add_argument('--env', type=str, default='visual_behavior_sdk', metavar='name of conda environment to use') +parser.add_argument('--env', type=str, default='learning_mFISH', metavar='name of conda environment to use') parser.add_argument('--scriptname', type=str, default='save_all_container_plots.py', metavar='name of script to run (must be in same folder)') parser.add_argument("--plots", type=str, default=None, metavar='plot name to generate') @@ -19,14 +19,19 @@ # container_ids = loading.get_ophys_container_ids() -from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache - -cache = VisualBehaviorOphysProjectCache.from_lims() -experiments_table = cache.get_ophys_experiment_table(passed_only=False) -experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +# from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache +# +# cache = VisualBehaviorOphysProjectCache.from_lims() +# experiments_table = cache.get_ophys_experiment_table(passed_only=False) +# experiments = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] # experiments = experiments[experiments.mouse_id.isin(['603892', '612764', '624942'])] # experiments = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] # experiments = experiments[experiments.mouse_id.isin(['582466'])] + +save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' +experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) +experiments_table = experiments_table.set_index('ophys_experiment_id') + container_ids = experiments.ophys_container_id.unique() if __name__ == "__main__": diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index be5618bbf..91fdb7695 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -466,7 +466,7 @@ def plot_cell_roi_mask_and_dff_trace(dataset, cell_roi_id, save_figure=True): figsize=(20,10) fig, ax = plt.subplots(2, 2, figsize=figsize, gridspec_kw={'width_ratios':[1,3]}) ax = ax.ravel() - ax[0] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, spacex=40, spacey=40, show_mask=True, ax=ax[0]) + ax[0] = sf.plot_cell_zoom(roi_masks, average_image, cell_roi_id, spacex=50, spacey=50, show_mask=True, ax=ax[0]) # create twinax ax1 = ax[1].twinx() # dff traces From 053c16f28eca5c9ba7663ce702f7ff82da6dafb5 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:33:39 -0700 Subject: [PATCH 166/187] multi session df matched cell figs --- scripts/create_single_cell_plots.py | 40 ++++++-- visual_behavior/data_access/loading.py | 135 +++++++++++++------------ 2 files changed, 103 insertions(+), 72 deletions(-) diff --git a/scripts/create_single_cell_plots.py b/scripts/create_single_cell_plots.py index 07417be12..e0ce9c6a3 100644 --- a/scripts/create_single_cell_plots.py +++ b/scripts/create_single_cell_plots.py @@ -31,19 +31,45 @@ matched_cells_df = utilities.get_max_matched_cells_for_learning_mFISH() # get just the matched cells for this container matched_cell_specimen_ids = matched_cells_df[matched_cells_df.ophys_container_id==ophys_container_id].cell_specimen_id.unique() - ophys_experiment_ids = experiments_table[experiments_table.ophys_container_id==ophys_container_id].index.values - # create dictionary of all datasets and stimulus_response_dfs - print('generating data dictionary') - data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) - print('data dictionary created') + + # # create dictionary of all datasets and stimulus_response_dfs + # print('generating data dictionary') + # data_dict = loading.get_data_dict(ophys_experiment_ids, data_types=['dff']) + # print('data dictionary created') + + data_type = 'dff' + output_sampling_rate = 30 + + # load change responses for each image + event_type = 'changes' + conditions = ['cell_specimen_id', 'is_change', 'image_name'] + change_image_mdf = loading.load_multi_session_df(data_type, event_type, conditions, interpolate=True, output_sampling_rate=output_sampling_rate) + change_image_mdf = change_image_mdf.merge(experiments_table, on='ophys_experiment_id') + + # load omission responses + event_type = 'omissions' + conditions = ['cell_specimen_id', 'omitted'] + omission_mdf = loading.load_multi_session_df(data_type, event_type, conditions, interpolate=True, output_sampling_rate=output_sampling_rate) + omission_mdf = omission_mdf[omission_mdf.omitted == True] + omission_mdf = omission_mdf.merge(experiments_table, on='ophys_experiment_id') for cell_specimen_id in matched_cell_specimen_ids: try: cell_specimen_id = int(cell_specimen_id) print('plotting cell_specimen_id', cell_specimen_id) - scp.plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, - experiments_table, data_type='dff', save_figure=True) + + # matched cell plots using dataset dict + # scp.plot_across_session_responses_from_dataset_dict(data_dict, ophys_container_id, cell_specimen_id, + # experiments_table, data_type='dff', save_figure=True) + + # matched cell plots using pre-computed multi session dfs + scp.plot_across_session_responses_from_multi_session_dfs(ophys_container_id, cell_specimen_id, + experiments_table, + change_image_mdf, omission_mdf, + output_sampling_rate=output_sampling_rate, + data_type=data_type, save_figure=True) + # ppf.plot_matched_roi_and_trace(ophys_container_id, cell_specimen_id, limit_to_last_familiar_second_novel=True, # use_events=use_events, filter_events=filter_events, save_figure=True) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index ba5318422..45f7e57ae 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -292,70 +292,75 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o experiment_table -- returns a dataframe with ophys_experiment_id as the index and metadata as columns. """ - if include_failed_data is True: - release_data_only = False - if release_data_only: - # get cache from lims for data released on March 25th - print('getting experiment table for March and August releases from lims') - cache = bpc.from_lims(data_release_date=['2021-03-25', '2021-08-12']) - experiments = cache.get_ophys_experiment_table() - if not release_data_only: - if from_cached_file == True: - if 'filtered_ophys_experiment_table.csv' in os.listdir(get_production_cache_dir()): - filepath = os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv') - print('loading cached experiment_table') - print('last updated on:') - import time - print(time.ctime(os.path.getctime(filepath))) - # load the cached file - experiments = pd.read_csv(filepath) - else: - print('there is no filtered_ophys_experiment_table.csv', get_production_cache_dir()) - else: - print('getting up-to-date experiment_table from lims') - # get everything in lims - cache = bpc.from_lims() - experiments = cache.get_ophys_experiment_table(passed_only=False) - print(len(experiments), 'experiments in ophys_experiment_table') - # limit to the 4 VisualBehavior project codes - # experiments = filtering.limit_to_production_project_codes(experiments) - if add_extra_columns: - print('adding extra columns') - print('NOTE: this is slow. set from_cached_file to True to load cached version of experiments_table at:') - print(get_production_cache_dir()) - # create cre_line column, set NaN session_types to None, add model output availability and location columns - experiments = reformat.reformat_experiments_table(experiments) - if include_failed_data: - print('including failed data') - pass - else: - print('limiting to passed experiments') - experiments = filtering.limit_to_passed_experiments(experiments) - experiments = filtering.remove_failed_containers(experiments) # container_workflow_state can be anything other than 'failed' - # limit to sessions that start with OPHYS - print('limiting to sessions that start with OPHYS') - # experiments = filtering.limit_to_valid_ophys_session_types(experiments) - if experiments.index.name != 'ophys_experiment_id': - # experiments = experiments.drop_duplicates(subset=['ophys_experiment_id', 'ophys_container_id']) - experiments = experiments.set_index('ophys_experiment_id') - if exclude_ai94: - print('excluding Ai94 data') - experiments = experiments[experiments.full_genotype != 'Slc17a7-IRES2-Cre/wt;Camk2a-tTA/wt;Ai94(TITL-GCaMP6s)/wt'] - if 'cre_line' not in experiments.keys(): - experiments['cre_line'] = [full_genotype.split('/')[0] for full_genotype in experiments.full_genotype.values] - # filter one more time on load to restrict to Visual Behavior project experiments ### - # experiments = filtering.limit_to_production_project_codes(experiments) - - # add new columns for conditions to analyze for platform paper ### - # experiments = utilities.add_cell_type_column(experiments) - - - ### hack because of problem container - # experiments = experiments[experiments.ophys_container_id!=1132424700] + # if include_failed_data is True: + # release_data_only = False + # if release_data_only: + # # get cache from lims for data released on March 25th + # print('getting experiment table for March and August releases from lims') + # cache = bpc.from_lims(data_release_date=['2021-03-25', '2021-08-12']) + # experiments = cache.get_ophys_experiment_table() + # if not release_data_only: + # if from_cached_file == True: + # if 'filtered_ophys_experiment_table.csv' in os.listdir(get_production_cache_dir()): + # filepath = os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv') + # print('loading cached experiment_table') + # print('last updated on:') + # import time + # print(time.ctime(os.path.getctime(filepath))) + # # load the cached file + # experiments = pd.read_csv(filepath) + # else: + # print('there is no filtered_ophys_experiment_table.csv', get_production_cache_dir()) + # else: + # print('getting up-to-date experiment_table from lims') + # # get everything in lims + # cache = bpc.from_lims() + # experiments = cache.get_ophys_experiment_table(passed_only=False) + # print(len(experiments), 'experiments in ophys_experiment_table') + # # limit to the 4 VisualBehavior project codes + # # experiments = filtering.limit_to_production_project_codes(experiments) + # if add_extra_columns: + # print('adding extra columns') + # print('NOTE: this is slow. set from_cached_file to True to load cached version of experiments_table at:') + # print(get_production_cache_dir()) + # # create cre_line column, set NaN session_types to None, add model output availability and location columns + # experiments = reformat.reformat_experiments_table(experiments) + # if include_failed_data: + # print('including failed data') + # pass + # else: + # print('limiting to passed experiments') + # experiments = filtering.limit_to_passed_experiments(experiments) + # experiments = filtering.remove_failed_containers(experiments) # container_workflow_state can be anything other than 'failed' + # # limit to sessions that start with OPHYS + # print('limiting to sessions that start with OPHYS') + # # experiments = filtering.limit_to_valid_ophys_session_types(experiments) + # if experiments.index.name != 'ophys_experiment_id': + # # experiments = experiments.drop_duplicates(subset=['ophys_experiment_id', 'ophys_container_id']) + # experiments = experiments.set_index('ophys_experiment_id') + # if exclude_ai94: + # print('excluding Ai94 data') + # experiments = experiments[experiments.full_genotype != 'Slc17a7-IRES2-Cre/wt;Camk2a-tTA/wt;Ai94(TITL-GCaMP6s)/wt'] + # if 'cre_line' not in experiments.keys(): + # experiments['cre_line'] = [full_genotype.split('/')[0] for full_genotype in experiments.full_genotype.values] + # # filter one more time on load to restrict to Visual Behavior project experiments ### + # # experiments = filtering.limit_to_production_project_codes(experiments) + # + # # add new columns for conditions to analyze for platform paper ### + # # experiments = utilities.add_cell_type_column(experiments) + # + # + # ### hack because of problem container + # # experiments = experiments[experiments.ophys_container_id!=1132424700] + # + # if overwrite_cached_file == True: + # print('overwriting pre-saved experiments table file') + # experiments.to_csv(os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv')) + + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + experiments_table = experiments_table.set_index('ophys_experiment_id') - if overwrite_cached_file == True: - print('overwriting pre-saved experiments table file') - experiments.to_csv(os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv')) return experiments @@ -3055,8 +3060,8 @@ def get_container_response_df(ophys_container_id, data_type='dff', event_type='a try: print('getting stimulus response df for', ophys_experiment_id) dataset = get_ophys_dataset(ophys_experiment_id) - sdf = get_stimulus_response_df(dataset, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, - data_type=data_type, event_type=event_type, load_from_file=False) + sdf = get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, output_sampling_rate=30, + data_type=data_type, event_type=event_type, load_from_file=True) sdf['ophys_experiment_id'] = ophys_experiment_id sdf['session_type'] = experiments_table.loc[ophys_experiment_id].session_type container_df = pd.concat([container_df, sdf]) From a7e1e94e5b0b57da82be85587a47f4050aa333b4 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:35:14 -0700 Subject: [PATCH 167/187] import --- visual_behavior/visualization/qc/run_save_all_container_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index a0fcc1813..d344734c6 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -28,6 +28,7 @@ # experiments = experiments_table[experiments_table.project_code=='LearningmFISHDevelopment'] # experiments = experiments[experiments.mouse_id.isin(['582466'])] +import pandas as pd save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') From 76a7dd75634b4029ace741ca28482da1ecdc2fc9 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:36:33 -0700 Subject: [PATCH 168/187] minor --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index d344734c6..33c86bd0a 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -31,7 +31,7 @@ import pandas as pd save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -experiments_table = experiments_table.set_index('ophys_experiment_id') +experiments = experiments_table.set_index('ophys_experiment_id') container_ids = experiments.ophys_container_id.unique() From ce2bf3c8d5d09ce95cb90247b2500fdac29f3aaf Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:37:21 -0700 Subject: [PATCH 169/187] add cell list to tuning curve heatmap --- .../ophys/population_summary_figures.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/visual_behavior/visualization/ophys/population_summary_figures.py b/visual_behavior/visualization/ophys/population_summary_figures.py index c4b4d17ee..40da3e8ad 100644 --- a/visual_behavior/visualization/ophys/population_summary_figures.py +++ b/visual_behavior/visualization/ophys/population_summary_figures.py @@ -419,7 +419,7 @@ def plot_mean_change_responses(df, vmax=0.3, colorbar=False, ax=None, save_dir=N 'change_response_matrix_' + cre_line + '_' + image_set + '_' + suffix) -def plot_tuning_curve_heatmap(df, vmax=0.3, sup_title=None, title=None, ax=None, save_dir=None, folder=None, use_events=False, +def plot_tuning_curve_heatmap(df, cell_list=None, vmax=0.3, sup_title=None, title=None, ax=None, save_dir=None, folder=None, use_events=False, colorbar=True, include_omitted=False): if 'image_name' in df.keys(): image_name = 'image_name' @@ -437,12 +437,13 @@ def plot_tuning_curve_heatmap(df, vmax=0.3, sup_title=None, title=None, ax=None, label = 'mean dF/F' suffix = suffix images = np.sort(df[image_name].unique()) - cell_list = [] - for image in images: - tmp = df[(df[image_name] == image) & (df.pref_stim == True)] - order = np.argsort(tmp.mean_response.values)[::-1] - cell_ids = list(tmp.cell_specimen_id.values[order]) - cell_list = cell_list + cell_ids + if cell_list is None: + cell_list = [] + for image in images: + tmp = df[(df[image_name] == image) & (df.pref_stim == True)] + order = np.argsort(tmp.mean_response.values)[::-1] + cell_ids = list(tmp.cell_specimen_id.values[order]) + cell_list = cell_list + cell_ids response_matrix = np.empty((len(cell_list), len(images))) for i, cell in enumerate(cell_list): responses = [] From bd52f087d2a6004d7abad13f463c327d75773218 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:38:06 -0700 Subject: [PATCH 170/187] wrong conda --- .../visualization/qc/run_save_all_container_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/visualization/qc/run_save_all_container_plots.py b/visual_behavior/visualization/qc/run_save_all_container_plots.py index 33c86bd0a..7915f3800 100644 --- a/visual_behavior/visualization/qc/run_save_all_container_plots.py +++ b/visual_behavior/visualization/qc/run_save_all_container_plots.py @@ -37,7 +37,7 @@ if __name__ == "__main__": args = parser.parse_args() - python_executable = "{}/anaconda2/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) + python_executable = "{}/anaconda3/envs/{}/bin/python".format(os.path.expanduser('~'), args.env) python_file = os.path.join(os.getcwd(), args.scriptname) for ii, container_id in enumerate(container_ids): From 1c02bdd9481ec98b7b5d33d97406525e08f2e878 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:40:48 -0700 Subject: [PATCH 171/187] load expts from file --- visual_behavior/data_access/loading.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 45f7e57ae..ca216ba31 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -2801,9 +2801,15 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o :param output_sampling_rate: :return: """ - cache = bpc.from_lims() - experiments_table = cache.get_ophys_experiment_table(passed_only=False) - experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + # cache = bpc.from_lims() + # experiments_table = cache.get_ophys_experiment_table(passed_only=False) + # experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', + # 'LearningmFISHDevelopment', + # 'omFISHGad2Meso'])] + ## This one loads from SDK + # experiments_table = utilities.get_mFISH_projects_experiments_table() + # This one loads from file as currently configured + experiments_table = get_filtered_ophys_experiment_table() print(len(experiments_table), 'expts in expt table') mouse_ids = experiments_table.mouse_id.unique() From 73c32ba9b26c34f1f6e7b14cec08d946a4a1f219 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 14:43:39 -0700 Subject: [PATCH 172/187] error --- visual_behavior/data_access/loading.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index ca216ba31..2839b0918 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -357,11 +357,13 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o # print('overwriting pre-saved experiments table file') # experiments.to_csv(os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv')) + # experiments_table = experiments.copy() + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') - return experiments + return experiments_table def get_filtered_ophys_session_table(release_data_only=False, include_failed_data=True): From 2aeea0f83ca9f918b707f14b9303e59a4d0f425a Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 10 Oct 2022 16:23:12 -0700 Subject: [PATCH 173/187] run plots for omFISH --- scripts/run_single_cell_plots.py | 2 +- visual_behavior/data_access/utilities.py | 10 +++++----- visual_behavior/visualization/qc/single_cell_plots.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 61bf784b7..2c2bb2c6a 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -14,7 +14,7 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] +experiments_table = experiments_table[experiments_table.project_code=='omFISHGad2Meso'] print(len(experiments_table)) container_ids = experiments_table.ophys_container_id.unique() diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 7e93bf80d..777986537 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1860,14 +1860,14 @@ def get_max_matched_cells_for_learning_mFISH(): Returns a dataframe with cell_specimen_id and metadata for matched cells. """ - cache = bpc.from_lims() - # experiments_table = cache.get_ophys_experiment_table(passed_only=False) - # experiments = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment'])] + # cache = bpc.from_lims() + # experiments = cache.get_ophys_experiment_table(passed_only=False) + # # experiments = experiments[experiments_table.project_code.isin(['LearningmFISHTask1A'])] # ophys_cells_table = cache.get_ophys_cells_table() save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) - experiments = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] + experiments = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) + # experiments = experiments[experiments_table.project_code == 'LearningmFISHTask1A'] ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) print(len(experiments), 'experiments') diff --git a/visual_behavior/visualization/qc/single_cell_plots.py b/visual_behavior/visualization/qc/single_cell_plots.py index 91fdb7695..0a806d8cb 100644 --- a/visual_behavior/visualization/qc/single_cell_plots.py +++ b/visual_behavior/visualization/qc/single_cell_plots.py @@ -448,10 +448,10 @@ def plot_single_cell_activity_and_behavior(dataset, cell_specimen_id, save_figur # ax[x].legend(loc='upper left', fontsize='x-small') plt.subplots_adjust(wspace=0, hspace=0.1) metadata_string = utils.get_metadata_string(dataset.metadata) - ax[0].set_title(str(cell_specimen_id) + '_' + metadata_string) + ax[0].set_title(metadata_string + '_' + str(cell_specimen_id)) if save_figure: utils.save_figure(fig, figsize, utils.get_single_cell_plots_dir(), 'dff_trace_and_behavior', - str(cell_specimen_id) + '_' + metadata_string + '_dff_trace_and_behavior') + metadata_string + '_' + str(cell_specimen_id) + '_dff_trace_and_behavior') plt.close() From b8245ddebe67e5f7617a4c1682a0d39c43674aa2 Mon Sep 17 00:00:00 2001 From: matchings Date: Fri, 14 Oct 2022 15:19:11 -0700 Subject: [PATCH 174/187] minor changes to plots and such --- scripts/run_single_cell_plots.py | 2 +- visual_behavior/data_access/loading.py | 6 +++--- visual_behavior/data_access/utilities.py | 1 + .../visualization/ophys/platform_paper_figures.py | 10 +++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/run_single_cell_plots.py b/scripts/run_single_cell_plots.py index 2c2bb2c6a..62981336b 100644 --- a/scripts/run_single_cell_plots.py +++ b/scripts/run_single_cell_plots.py @@ -14,7 +14,7 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -experiments_table = experiments_table[experiments_table.project_code=='omFISHGad2Meso'] +# experiments_table = experiments_table[experiments_table.project_code=='omFISHGad2Meso'] print(len(experiments_table)) container_ids = experiments_table.ophys_container_id.unique() diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 2839b0918..e48dcfa35 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -358,7 +358,7 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o # experiments.to_csv(os.path.join(get_production_cache_dir(), 'filtered_ophys_experiment_table.csv')) # experiments_table = experiments.copy() - + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') @@ -2809,9 +2809,9 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o # 'LearningmFISHDevelopment', # 'omFISHGad2Meso'])] ## This one loads from SDK - # experiments_table = utilities.get_mFISH_projects_experiments_table() + experiments_table = utilities.get_mFISH_projects_experiments_table() # This one loads from file as currently configured - experiments_table = get_filtered_ophys_experiment_table() + # experiments_table = get_filtered_ophys_experiment_table() print(len(experiments_table), 'expts in expt table') mouse_ids = experiments_table.mouse_id.unique() diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 777986537..4065a5c68 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1807,6 +1807,7 @@ def replace_cell_specimen_id_with_cell_roi_id(df): print('the index values of this df are not NaN, this function is not needed') return df + def get_behavior_session_ids_to_analyze(): """ Gets a list of behavior_session_ids by combining the behavior_session_ids present in the platform paper experiments table diff --git a/visual_behavior/visualization/ophys/platform_paper_figures.py b/visual_behavior/visualization/ophys/platform_paper_figures.py index 2020607a3..13d022874 100644 --- a/visual_behavior/visualization/ophys/platform_paper_figures.py +++ b/visual_behavior/visualization/ophys/platform_paper_figures.py @@ -115,10 +115,10 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t if ax is None: format_fig = True if horizontal: - figsize = (3 * n_axes_conditions, 3) - fig, ax = plt.subplots(1, n_axes_conditions, figsize=figsize, sharey=False) + figsize = (3 * n_axes_conditions, 4) + fig, ax = plt.subplots(1, n_axes_conditions, figsize=figsize, sharey=True) else: - figsize = (5, 3.5 * n_axes_conditions) + figsize = (7, 3.5 * n_axes_conditions) fig, ax = plt.subplots(n_axes_conditions, 1, figsize=figsize, sharex=True) else: format_fig = False @@ -156,6 +156,7 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t else: ax[i].set_ylabel(ylabel) ax[i].set_xlabel('') + # ax[i].legend(loc='upper left', fontsize='xx-small') # except: # print('no data for', axis, hue) if format_fig: @@ -163,7 +164,7 @@ def plot_population_averages_for_conditions(multi_session_df, data_type, event_t ax[0].set_ylabel(ylabel) else: ax[i].set_xlabel(xlabel) - # ax[0].legend(loc='upper left', fontsize='xx-small') + ax[i].legend(bbox_to_anchor=(1,1), fontsize='xx-small') if project_code: if suptitle is None: @@ -346,7 +347,6 @@ def plot_n_segmented_cells(multi_session_df, df_name, horizontal=True, save_dir= Plots the fraction of responsive cells across cre lines :param multi_session_df: dataframe of trial averaged responses for each cell for some set of conditions :param df_name: name of the type of response_df used to make multi_session_df, such as 'omission_response_df' or 'stimulus_response_df' - :param responsiveness_threshold: threshold on fraction_significant_p_value_gray_screen to determine whether a cell is responsive or not :param save_dir: directory to save figures to. if None, will not save. :param suffix: string starting with '_' to append to end of filename of saved plot :return: From abacb91b2f1f8c18eecc5606ea56250be2ebd06f Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 8 Nov 2022 20:14:45 -0800 Subject: [PATCH 175/187] paths --- visual_behavior/data_access/loading.py | 4 +++- visual_behavior/data_access/utilities.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index e48dcfa35..45b735d54 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -359,7 +359,9 @@ def get_filtered_ophys_experiment_table(include_failed_data=True, release_data_o # experiments_table = experiments.copy() - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 4065a5c68..5b32254c2 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -1866,7 +1866,9 @@ def get_max_matched_cells_for_learning_mFISH(): # # experiments = experiments[experiments_table.project_code.isin(['LearningmFISHTask1A'])] # ophys_cells_table = cache.get_ophys_cells_table() - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + experiments = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) # experiments = experiments[experiments_table.project_code == 'LearningmFISHTask1A'] ophys_cells_table = pd.read_csv(os.path.join(save_dir, 'ophys_cells_table.csv')) From 1572013d908512274afcdde3b5992fa8f8532133 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 24 Apr 2023 20:14:53 -0700 Subject: [PATCH 176/187] use BehaviorOphysExperimentDev --- visual_behavior/ophys/io/create_multi_session_df.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 292ded7c4..d2fcc8e4a 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -5,6 +5,8 @@ import visual_behavior.ophys.response_analysis.utilities as ut from visual_behavior.data_access import loading +from brain_observatory_qc.data_access.behavior_ophys_experiment_dev import BehaviorOphysExperimentDev + def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, ophys_experiment_ids=None, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, @@ -31,6 +33,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev :param mouse_id: mouse_id to use when identifying what experiment_ids to include in the multi_session_df :param conditions: columns in stimulus_response_df to group by when averaging across trials / stimulus presentations if use_extended_stimulus_presentations is True, columns available include the set of columns provided in that table (ex: engagement_state) + example: ['cell_specimen_id', 'is_change', 'image_name'] :param data_type: which timeseries in dataset object to get event triggered responses for options: 'filtered_events', 'events', 'dff', 'running_speed', 'pupil_diameter', 'lick_rate' :param event_type: how to filter stimulus presentations when creating table with loading.get_stimulus_response_df() @@ -64,8 +67,8 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - # save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') @@ -107,8 +110,10 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev for experiment_id in experiment_ids: try: print(experiment_id) + # new dev object + dataset = BehaviorOphysExperimentDev(experiment_id, skip_eye_tracking=False) # get dataset - dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) + # dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) # get stimulus_response_df print('loading stimulus response df') df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, From e8f180096dee2d215988397dec7feccd0c05d929 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 24 Apr 2023 20:49:34 -0700 Subject: [PATCH 177/187] multi_session_df attempt --- scripts/create_multi_session_df.py | 12 ++++++------ scripts/run_create_multi_session_df.py | 2 +- visual_behavior/ophys/io/create_multi_session_df.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 4f4bcb248..48bfb393d 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -26,12 +26,12 @@ use_extended_stimulus_presentations = False # set up conditions to make multi session dfs for - physio_data_types = ['dff', 'events']# 'filtered_events', 'events'] + physio_data_types = ['dff', 'events', 'filtered_events']# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] physio_conditions = [['cell_specimen_id', 'image_name'],] - # ['cell_specimen_id', 'is_change'], - # ['cell_specimen_id', 'omitted'], + ['cell_specimen_id', 'is_change'], + ['cell_specimen_id', 'omitted'], # ['cell_specimen_id', 'is_change', 'epoch'], # ['cell_specimen_id', 'omitted', 'epoch'], # ['cell_specimen_id', 'is_change', 'image_name'], @@ -43,8 +43,8 @@ # ['cell_specimen_id', 'omitted', 'pre_omitted'],] behavior_conditions = [['ophys_experiment_id', 'image_name'],] - # ['ophys_experiment_id', 'is_change'], - # ['ophys_experiment_id', 'omitted'], + ['ophys_experiment_id', 'is_change'], + ['ophys_experiment_id', 'omitted'], # ['ophys_experiment_id', 'is_change', 'epoch'], # ['ophys_experiment_id', 'omitted', 'epoch'], # ['ophys_experiment_id', 'is_change', 'image_name'], @@ -57,7 +57,7 @@ # event types corresponding to the above physio and behavior conditions - must be in same sequential order!! - event_types_for_conditions = ['all', ]#'changes', 'omissions', + event_types_for_conditions = ['all', 'changes', 'omissions',] # 'changes', 'omissions', # 'changes', 'changes', 'all', ] #'changes', # 'all', 'all', 'all'] diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index e05848ed9..e2918e519 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -9,7 +9,7 @@ python_file = r"/home/marinag/visual_behavior_analysis/scripts/create_multi_session_df.py" # conda environment to use -conda_environment = 'learning_mFISH' +conda_environment = 'visual_behavior_sdk' # build the python path # this assumes that the environments are saved in the user's home directory in a folder called 'anaconda2' diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index d2fcc8e4a..f680ab331 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -67,8 +67,8 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') From f2848702e218d102d8f8256a6c88cae12e70533b Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 24 Apr 2023 21:21:52 -0700 Subject: [PATCH 178/187] typo --- scripts/create_multi_session_df.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 48bfb393d..7bdac66f2 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -29,9 +29,9 @@ physio_data_types = ['dff', 'events', 'filtered_events']# 'filtered_events', 'events'] behavior_data_types = ['pupil_width', 'running_speed', 'lick_rate'] - physio_conditions = [['cell_specimen_id', 'image_name'],] + physio_conditions = [['cell_specimen_id', 'image_name'], ['cell_specimen_id', 'is_change'], - ['cell_specimen_id', 'omitted'], + ['cell_specimen_id', 'omitted'],] # ['cell_specimen_id', 'is_change', 'epoch'], # ['cell_specimen_id', 'omitted', 'epoch'], # ['cell_specimen_id', 'is_change', 'image_name'], @@ -42,9 +42,9 @@ # ['cell_specimen_id', 'is_change', 'hit', 'epoch'], # ['cell_specimen_id', 'omitted', 'pre_omitted'],] - behavior_conditions = [['ophys_experiment_id', 'image_name'],] + behavior_conditions = [['ophys_experiment_id', 'image_name'], ['ophys_experiment_id', 'is_change'], - ['ophys_experiment_id', 'omitted'], + ['ophys_experiment_id', 'omitted'],] # ['ophys_experiment_id', 'is_change', 'epoch'], # ['ophys_experiment_id', 'omitted', 'epoch'], # ['ophys_experiment_id', 'is_change', 'image_name'], From 2bab867d24841dde815c6c65394030909d5421dd Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 24 Apr 2023 21:30:20 -0700 Subject: [PATCH 179/187] remove skip eye tracking arg --- visual_behavior/ophys/io/create_multi_session_df.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index f680ab331..79862d807 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -111,7 +111,7 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev try: print(experiment_id) # new dev object - dataset = BehaviorOphysExperimentDev(experiment_id, skip_eye_tracking=False) + dataset = BehaviorOphysExperimentDev(experiment_id) # get dataset # dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) # get stimulus_response_df From 6ea921b881d03b29ca8c93e273c957c0c9ee27ef Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 24 Apr 2023 21:44:56 -0700 Subject: [PATCH 180/187] try original dataset object --- visual_behavior/ophys/io/create_multi_session_df.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 79862d807..a16d9b37f 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -111,9 +111,9 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev try: print(experiment_id) # new dev object - dataset = BehaviorOphysExperimentDev(experiment_id) + # dataset = BehaviorOphysExperimentDev(experiment_id) # get dataset - # dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) + dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) # get stimulus_response_df print('loading stimulus response df') df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, From 35b8fa0a2ee16fef5645b00c1818c414716187ba Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 25 Apr 2023 21:48:23 -0700 Subject: [PATCH 181/187] always save mdf --- visual_behavior/data_access/loading.py | 4 ++-- visual_behavior/ophys/io/create_multi_session_df.py | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 45b735d54..86ba00226 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -73,8 +73,8 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + # cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index a16d9b37f..5a4b665a5 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -67,26 +67,27 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - # save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') + save_mega_mdf = True if ophys_experiment_ids is None: print('mouse_id:', mouse_id, ', ophys_container_id:', ophys_container_id) experiments = experiments_table[(experiments_table.ophys_container_id == int(ophys_container_id)) & (experiments_table.mouse_id == int(mouse_id))] experiment_ids = experiments.index.values print(len(experiment_ids), 'experiments for this mouse_id and ophys_container_id') - save_mega_mdf = True + # save_mega_mdf = True else: experiment_ids = ophys_experiment_ids.copy() print('ophys_experiment_ids provided, ignoring mouse_id and ophys_container_id') print('generating multi_session_df for provided list of ophys_experiment_ids') print(len(experiment_ids), 'experiments are in the provided list') print('multi_session_df will not be automatically saved') - save_mega_mdf = False + # save_mega_mdf = False filename = loading.get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) @@ -111,9 +112,9 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev try: print(experiment_id) # new dev object - # dataset = BehaviorOphysExperimentDev(experiment_id) + dataset = BehaviorOphysExperimentDev(experiment_id) # get dataset - dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) + # dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) # get stimulus_response_df print('loading stimulus response df') df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, From 890dac0b37d3a2f96e05de331dca0d78b792c2ad Mon Sep 17 00:00:00 2001 From: Jinho Kim Date: Mon, 11 Sep 2023 13:32:54 -0700 Subject: [PATCH 182/187] fixed time stamps --- visual_behavior/data_access/utilities.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/visual_behavior/data_access/utilities.py b/visual_behavior/data_access/utilities.py index 5b32254c2..4ce4069a1 100644 --- a/visual_behavior/data_access/utilities.py +++ b/visual_behavior/data_access/utilities.py @@ -492,7 +492,8 @@ def get_sync_data(lims_data, use_acq_trigger): # print(len(frames_2p)) if lims_data.rig.values[0][0] == 'M': # if Mesoscope roi_group = get_roi_group(lims_data) # get roi_group order - frames_2p = frames_2p[roi_group::4] # resample sync times + fov_nums = get_fov_nums(lims_data) + frames_2p = frames_2p[roi_group::fov_nums] # resample sync times # print(len(frames_2p)) logger.info('stimulus frames detected in sync: {}'.format(len(vsyncs))) logger.info('ophys frames detected in sync: {}'.format(len(frames_2p))) @@ -543,7 +544,17 @@ def get_roi_group(lims_data): expt_id = int(group[plane]['experiment_id']) if expt_id == experiment_id: expt_roi_group = i - return expt_roi_group + return expt_roi_group + + +def get_fov_nums(lims_data): + ophys_session_dir = get_ophys_session_dir(lims_data) + import json + json_file = [file for file in os.listdir(ophys_session_dir) if ('SPLITTING' in file) and ('input.json' in file)] + json_path = os.path.join(ophys_session_dir, json_file[0]) + with open(json_path, 'r') as w: + jin = json.load(w) + return len(jin['plane_groups']) def get_lims_id(lims_data): From abffe2561f5419baf6ec743e696b5da2b4e49a6a Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 28 Nov 2023 16:15:19 -0800 Subject: [PATCH 183/187] switch directories --- visual_behavior/ophys/io/convert_level_1_to_level_2.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/visual_behavior/ophys/io/convert_level_1_to_level_2.py b/visual_behavior/ophys/io/convert_level_1_to_level_2.py index 315c86558..80faa9e47 100644 --- a/visual_behavior/ophys/io/convert_level_1_to_level_2.py +++ b/visual_behavior/ophys/io/convert_level_1_to_level_2.py @@ -48,9 +48,11 @@ def save_dataframe_as_h5(df, name, analysis_dir): def get_cache_dir(cache_dir=None): if not cache_dir: if platform.system() == 'Linux': - cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/visual_behavior_production_analysis' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/learning_mFISH_production_analysis' + # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/visual_behavior_production_analysis' else: - cache_dir = r'\\allen\programs\braintv\workgroups\nc-ophys\visual_behavior\visual_behavior_production_analysis' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\learning_mFISH_production_analysis' + # cache_dir = r'\\allen\programs\braintv\workgroups\nc-ophys\visual_behavior\visual_behavior_production_analysis' return cache_dir else: return cache_dir @@ -908,7 +910,7 @@ def convert_level_1_to_level_2(lims_id, cache_dir=None, plot_roi_validation=Fals analysis_dir = get_analysis_dir(lims_data, cache_on_lims_data=True, cache_dir=cache_dir) - timestamps = get_timestamps(lims_data, analysis_dir) + timestamps = get_timestamps(lims_data) pkl = get_pkl(lims_data) stimulus_timestamps = get_stimulus_timestamps(timestamps) From 80ce0a8de157fb50aae51a2533af9409bce0982f Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 20 Feb 2024 20:11:00 -0800 Subject: [PATCH 184/187] remove container_id from multi session df creation --- scripts/create_multi_session_df.py | 8 +-- visual_behavior/data_access/loading.py | 53 +++++++++---------- .../ophys/io/create_multi_session_df.py | 34 +++++++----- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/scripts/create_multi_session_df.py b/scripts/create_multi_session_df.py index 7bdac66f2..2dcec7a26 100644 --- a/scripts/create_multi_session_df.py +++ b/scripts/create_multi_session_df.py @@ -11,13 +11,13 @@ if __name__ == '__main__': # define args parser = argparse.ArgumentParser() - parser.add_argument('--ophys_container_id', type=str, help='ophys_container_id to use') + # parser.add_argument('--ophys_container_id', type=str, help='ophys_container_id to use') parser.add_argument('--mouse_id', type=str, help='mouse_id to use') args = parser.parse_args() - ophys_container_id = int(args.ophys_container_id) + # ophys_container_id = int(args.ophys_container_id) mouse_id = int(args.mouse_id) - print('mouse_id:', mouse_id, 'ophys_container_id:', ophys_container_id) + print('mouse_id:', mouse_id) # params for stim response df creation time_window = [-2, 2.1] @@ -79,7 +79,7 @@ response_window_duration = 0.5 print('creating multi_session_df for', data_type, event_type, conditions) try: # use try except so that it skips over any conditions that fail to generate for some reason - df = io.get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, ophys_experiment_ids=None, + df = io.get_multi_session_df(mouse_id, conditions, data_type, event_type, ophys_experiment_ids=None, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, response_window_duration=response_window_duration, use_extended_stimulus_presentations=use_extended_stimulus_presentations, diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 86ba00226..53eea4400 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -73,8 +73,8 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - # cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir @@ -580,6 +580,8 @@ def get_stimulus_response_df(dataset, time_window=[-2, 2.1], interpolate=True, o Note that including invalid ROIs will result in some cell traces being NaNs because traces are not computed for some types of invalid ROIs """ import mindscope_utilities.visual_behavior_ophys.data_formatting as vb_ophys + # from brain_observatory_utilities.datasets.optical_physiology.data_formatting import get_stimulus_response_df + # load stimulus response df from file if it exists otherwise generate it ophys_experiment_id = dataset.ophys_experiment_id filepath = get_stimulus_response_df_filepath_for_experiment(ophys_experiment_id, data_type, event_type, @@ -832,12 +834,6 @@ def get_ophys_dataset(ophys_experiment_id, exclude_invalid_rois=True, load_from_ object -- BehaviorOphysSession or BehaviorOphysDataset instance, which inherits attributes & methods from SDK BehaviorOphysSession """ - id_type = from_lims.get_id_type(ophys_experiment_id) - if id_type != 'ophys_experiment_id': - warnings.warn('It looks like you passed an id of type {} instead of an ophys_experiment_id'.format(id_type)) - - assert id_type == 'ophys_experiment_id', "The passed ID type is {}. It must be an ophys_experiment_id".format(id_type) - if load_from_lims: print('loading from lims, exclude_invalid_rois =', exclude_invalid_rois) # cache = bpc.from_lims() @@ -2772,22 +2768,21 @@ def add_superficial_deep_to_experiments_table(experiments_table): return experiments_table -def get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions): +def get_file_name_for_multi_session_df(data_type, event_type, mouse_id, conditions): mouse_id = 'mouse_id_'+str(mouse_id) - ophys_container_id = 'container_id_'+str(ophys_container_id) if len(conditions) == 6: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '_' + conditions[5] + '.h5' elif len(conditions) == 5: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '_' + conditions[4] + '.h5' elif len(conditions) == 4: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '_' + conditions[3] + '.h5' elif len(conditions) == 3: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[1] + '_' + conditions[2] + '.h5' elif len(conditions) == 2: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[1] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[1] + '.h5' elif len(conditions) == 1: - filename = event_type + '_' + data_type + '_' + mouse_id + '_' + ophys_container_id + '_' + conditions[0] + '.h5' + filename = event_type + '_' + data_type + '_' + mouse_id + '_' + conditions[0] + '.h5' return filename @@ -2811,26 +2806,30 @@ def load_multi_session_df(data_type, event_type, conditions, interpolate=True, o # 'LearningmFISHDevelopment', # 'omFISHGad2Meso'])] ## This one loads from SDK - experiments_table = utilities.get_mFISH_projects_experiments_table() + # experiments_table = utilities.get_mFISH_projects_experiments_table() # This one loads from file as currently configured # experiments_table = get_filtered_ophys_experiment_table() + save_dir = get_production_cache_dir() + saved_filepath = os.path.join(save_dir, 'mFISH_project_expts.csv') + experiments_table = pd.read_csv(saved_filepath, index_col=0) + print(len(experiments_table), 'expts in expt table') mouse_ids = experiments_table.mouse_id.unique() multi_session_df = pd.DataFrame() for mouse_id in mouse_ids: print('mouse_id:', mouse_id) experiments = experiments_table[(experiments_table.mouse_id == mouse_id)] - for ophys_container_id in np.sort(experiments.ophys_container_id.unique()): - try: - filename = get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) - multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) - filepath = os.path.join(multi_session_df_dir, filename) - df = pd.read_hdf(filepath, key='df') - multi_session_df = pd.concat([multi_session_df, df]) - except Exception as e: - # print(e) - print('no multi_session_df for', mouse_id, ophys_container_id) + # for ophys_container_id in np.sort(experiments.ophys_container_id.unique()): + # try: + filename = get_file_name_for_multi_session_df(data_type, event_type, mouse_id, conditions) + multi_session_df_dir = get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) + filepath = os.path.join(multi_session_df_dir, filename) + df = pd.read_hdf(filepath, key='df') + multi_session_df = pd.concat([multi_session_df, df]) + # except Exception as e: + # # print(e) + # print('no multi_session_df for', mouse_id, ophys_container_id) return multi_session_df diff --git a/visual_behavior/ophys/io/create_multi_session_df.py b/visual_behavior/ophys/io/create_multi_session_df.py index 5a4b665a5..595849a27 100644 --- a/visual_behavior/ophys/io/create_multi_session_df.py +++ b/visual_behavior/ophys/io/create_multi_session_df.py @@ -8,7 +8,7 @@ from brain_observatory_qc.data_access.behavior_ophys_experiment_dev import BehaviorOphysExperimentDev -def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, event_type, ophys_experiment_ids=None, +def get_multi_session_df(mouse_id, conditions, data_type, event_type, ophys_experiment_ids=None, time_window=[-3, 3.1], interpolate=True, output_sampling_rate=30, response_window_duration=0.5, use_extended_stimulus_presentations=False, overwrite=False): """ @@ -67,29 +67,35 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code == 'LearningmFISHTask1A'] - # save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' - experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) - experiments_table = experiments_table.set_index('ophys_experiment_id') + save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # save_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv'), index_col=0) + # limit to non-failed experiments + experiments_table = experiments_table[(experiments_table.experiment_workflow_state.isin(['passed', 'qc'])) & + (experiments_table.container_workflow_state != 'failed')] + # experiments_table = experiments_table.set_index('ophys_experiment_id') print(len(experiments_table), 'experiments in experiments table') + print(len(experiments_table.mouse_id.unique()), 'mice in experiments table') + save_mega_mdf = True if ophys_experiment_ids is None: - print('mouse_id:', mouse_id, ', ophys_container_id:', ophys_container_id) - experiments = experiments_table[(experiments_table.ophys_container_id == int(ophys_container_id)) & - (experiments_table.mouse_id == int(mouse_id))] + print('mouse_id:', mouse_id) # ', ophys_container_id:', ophys_container_id) + experiments = experiments_table[(experiments_table.mouse_id == int(mouse_id))] + #(experiments_table.ophys_container_id == int(ophys_container_id)) & + experiment_ids = experiments.index.values - print(len(experiment_ids), 'experiments for this mouse_id and ophys_container_id') + print(len(experiment_ids), 'experiments for this mouse_id') # save_mega_mdf = True else: experiment_ids = ophys_experiment_ids.copy() - print('ophys_experiment_ids provided, ignoring mouse_id and ophys_container_id') + print('ophys_experiment_ids provided, ignoring mouse_id') print('generating multi_session_df for provided list of ophys_experiment_ids') print(len(experiment_ids), 'experiments are in the provided list') print('multi_session_df will not be automatically saved') # save_mega_mdf = False - filename = loading.get_file_name_for_multi_session_df(data_type, event_type, mouse_id, ophys_container_id, conditions) + filename = loading.get_file_name_for_multi_session_df(data_type, event_type, mouse_id, conditions) mega_mdf_write_dir = loading.get_multi_session_df_dir(interpolate=interpolate, output_sampling_rate=output_sampling_rate, event_type=event_type) filepath = os.path.join(mega_mdf_write_dir, filename) print('saving to', filepath) @@ -112,14 +118,14 @@ def get_multi_session_df(mouse_id, ophys_container_id, conditions, data_type, ev try: print(experiment_id) # new dev object - dataset = BehaviorOphysExperimentDev(experiment_id) + # dataset = BehaviorOphysExperimentDev(experiment_id) # get dataset - # dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=use_extended_stimulus_presentations) + dataset = loading.get_ophys_dataset(experiment_id, get_extended_stimulus_presentations=False) # get stimulus_response_df print('loading stimulus response df') df = loading.get_stimulus_response_df(dataset, data_type=data_type, event_type=event_type, time_window=time_window, interpolate=interpolate, output_sampling_rate=output_sampling_rate, - load_from_file=False) + load_from_file=True) print('stim response df loaded') # use response_window duration from stim response df if it exists if response_window_duration in df.keys(): From f346e4f3162690041998aa786524e1edfc3a80d2 Mon Sep 17 00:00:00 2001 From: matchings Date: Tue, 20 Feb 2024 20:14:27 -0800 Subject: [PATCH 185/187] remove container_id from run script --- scripts/run_create_multi_session_df.py | 39 ++++++++++++-------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/scripts/run_create_multi_session_df.py b/scripts/run_create_multi_session_df.py index e2918e519..809696e80 100644 --- a/scripts/run_create_multi_session_df.py +++ b/scripts/run_create_multi_session_df.py @@ -27,7 +27,6 @@ # define the job record output folder stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/multi_session_dfs" - # cache = VisualBehaviorOphysProjectCache.from_lims() # experiments_table = cache.get_ophys_experiment_table(passed_only=False) # experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', 'LearningmFISHDevelopment', @@ -37,14 +36,12 @@ save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' import pandas as pd -experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv')) -# experiments_table = experiments_table[experiments_table.project_code.isin(['LearningmFISHTask1A', -# 'LearningmFISHDevelopment', -# 'omFISHGad2Meso'])] -# experiments_table = experiments_table[(experiments_table.project_code.isin(['omFISHGad2Meso']))& -# (experiments_table.session_type=='OPHYS_2_images_A_passive')] -# experiments_table = experiments_table[(experiments_table.project_code.isin(['LearningmFISHDevelopment']))] +experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv'), index_col=0) +# limit to non-failed experiments +experiments_table = experiments_table[(experiments_table.experiment_workflow_state.isin(['passed', 'qc'])) & + (experiments_table.container_workflow_state != 'failed')] print(len(experiments_table), 'experiments') +print(len(experiments_table.mouse_id.unique()), 'mice') # experiments_table = loading.get_filtered_ophys_experiment_table() # experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] @@ -52,20 +49,20 @@ # call the `sbatch` command to run the jobs. for mouse_id in experiments_table.mouse_id.unique(): print('mouse_id:', mouse_id) - ophys_container_ids = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() - print('there are', len(ophys_container_ids),'ophys_container_ids for this mouse') - for ophys_container_id in ophys_container_ids: - print('ophys_container_id:', ophys_container_id) + # ophys_container_ids = experiments_table[experiments_table.mouse_id==mouse_id].ophys_container_id.unique() + # print('there are', len(ophys_container_ids),'ophys_container_ids for this mouse') + # for ophys_container_id in ophys_container_ids: + # print('ophys_container_id:', ophys_container_id) # instantiate a Slurm object - slurm = Slurm(mem='120g', # '24g' - cpus_per_task=1, - time='20:00:00', - partition='braintv', - job_name='multi_session_df_'+str(mouse_id)+'_'+str(ophys_container_id), - output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', - ) - + slurm = Slurm(mem='120g', # '24g' + cpus_per_task=1, + time='20:00:00', + partition='braintv', + job_name='multi_session_df_'+str(mouse_id), #+'_'+str(ophys_container_id), + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', + ) - slurm.sbatch(python_executable+' '+python_file+' --mouse_id '+str(mouse_id)+' --ophys_container_id'+' '+str(ophys_container_id)) + # slurm.sbatch( python_executable + ' ' + python_file + ' --mouse_id ' + str(mouse_id) + ' --ophys_container_id' + ' ' + str(ophys_container_id)) + slurm.sbatch(python_executable+' '+python_file+' --mouse_id '+str(mouse_id)) From a3aebbe9285e98a1b1ae1d9507dc13d8af212f44 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 26 Feb 2024 18:08:16 -0800 Subject: [PATCH 186/187] Auto stash before merge of "finalize_fig_2" and "origin/finalize_fig_2" --- visual_behavior/ophys/response_analysis/cell_metrics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/visual_behavior/ophys/response_analysis/cell_metrics.py b/visual_behavior/ophys/response_analysis/cell_metrics.py index 1bb2abab5..7a610b178 100644 --- a/visual_behavior/ophys/response_analysis/cell_metrics.py +++ b/visual_behavior/ophys/response_analysis/cell_metrics.py @@ -681,7 +681,8 @@ def get_cell_metrics_dir(interpolate=False, output_sampling_rate=None): :param output_sampling_rate: sampling rate used to create interpolated traces; if interpolate is False, output_sampling_rate is None :return: """ - base_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_cache/cell_metrics' + # base_dir = r'//allen/programs/braintv/workgroups/nc-ophys/visual_behavior/platform_paper_cache/cell_metrics' + base_dir = r'\\allen\programs\braintv\workgroups\nc-ophys\visual_behavior\platform_paper_cache\cell_metrics' if interpolate: save_dir = os.path.join(base_dir, 'interpolated_' + str(output_sampling_rate) + 'Hz') else: From f9f6b2f458891e6e7f5b0167aec070bed48bcc98 Mon Sep 17 00:00:00 2001 From: matchings Date: Mon, 17 Jun 2024 22:34:12 -0700 Subject: [PATCH 187/187] update convert for LAMF --- scripts/convert_level_1_to_level_2.py | 22 +- .../run_convert_level_1_to_level_2_slurm.py | 51 +++++ visual_behavior/data_access/loading.py | 4 +- .../dataset/visual_behavior_ophys_dataset.py | 22 +- .../ophys/io/convert_level_1_to_level_2.py | 204 ++++++++++-------- .../ophys/experiment_summary_figures.py | 7 +- .../visualization/ophys/summary_figures.py | 4 +- 7 files changed, 207 insertions(+), 107 deletions(-) create mode 100644 scripts/run_convert_level_1_to_level_2_slurm.py diff --git a/scripts/convert_level_1_to_level_2.py b/scripts/convert_level_1_to_level_2.py index 952b70bff..d4f0b212e 100644 --- a/scripts/convert_level_1_to_level_2.py +++ b/scripts/convert_level_1_to_level_2.py @@ -1,14 +1,24 @@ #!/usr/bin/env python -import matplotlib -matplotlib.use('Agg') +import os +import argparse +import numpy as np +import pandas as pd +import visual_behavior.data_access.loading as loading +import visual_behavior.ophys.io.create_multi_session_df as io from visual_behavior.ophys.io.convert_level_1_to_level_2 import convert_level_1_to_level_2 if __name__ == '__main__': - import sys - experiment_id = sys.argv[1] - cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/visual_behavior_production_analysis' - ophys_data = convert_level_1_to_level_2(experiment_id, cache_dir) + # define args + parser = argparse.ArgumentParser() + # parser.add_argument('--ophys_container_id', type=str, help='ophys_container_id to use') + parser.add_argument('--ophys_experiment_id', type=str, help='ophys_experiment_id to use') + args = parser.parse_args() + # ophys_container_id = int(args.ophys_container_id) + ophys_experiment_id = int(args.ophys_experiment_id) + + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + ophys_data = convert_level_1_to_level_2(ophys_experiment_id, cache_dir) diff --git a/scripts/run_convert_level_1_to_level_2_slurm.py b/scripts/run_convert_level_1_to_level_2_slurm.py new file mode 100644 index 000000000..1366866aa --- /dev/null +++ b/scripts/run_convert_level_1_to_level_2_slurm.py @@ -0,0 +1,51 @@ +import os +import argparse +from simple_slurm import Slurm + +import visual_behavior.data_access.loading as loading +from allensdk.brain_observatory.behavior.behavior_project_cache import VisualBehaviorOphysProjectCache + +# python file to execute on cluster +python_file = r"/home/marinag/visual_behavior_analysis/scripts/convert_level_1_to_level_2.py" + +# conda environment to use +conda_environment = 'learning_mfish' + +python_executable = "{}/anaconda3/envs/{}/bin/python".format(os.path.expanduser('~'), conda_environment) + +# define the job record output folder +stdout_location = r"/allen/programs/mindscope/workgroups/learning/ophys/cluster_jobs/convert" + +save_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' +# import pandas as pd +# experiments_table = pd.read_csv(os.path.join(save_dir, 'mFISH_project_expts.csv'), index_col=0) +# # limit to non-failed experiments +# experiments_table = experiments_table[(experiments_table.experiment_workflow_state.isin(['passed', 'qc'])) & +# (experiments_table.container_workflow_state != 'failed')] +# print(len(experiments_table), 'experiments') +# print(len(experiments_table.mouse_id.unique()), 'mice') + +# experiments_table = loading.get_filtered_ophys_experiment_table() +# experiments_table = experiments_table[experiments_table.project_code=='LearningmFISHTask1A'] + +# experiment_ids = experiments_table.index.unique() + +experiment_ids = [1372472098, 1372472096, 1372159256, 1372159254, 1371522715, + 1371522713, 1371073935, 1371073933, 1370814965, 1370814962, + 1370605604, 1370605601, 1367808840, 1367808838, 1367257322, + 1367257319, 1367058338, 1367058336, 1366815095, 1366815092] + +# call the `sbatch` command to run the jobs. +for experiment_id in experiment_ids: + print('ophys_experiment_id:', experiment_id) + + slurm = Slurm(mem='50g', # '24g' + cpus_per_task=1, + time='20:00:00', + partition='braintv', + job_name='convert'+str(experiment_id), #+'_'+str(ophys_container_id), + output=f'{stdout_location}/{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out', + ) + + # slurm.sbatch( python_executable + ' ' + python_file + ' --mouse_id ' + str(mouse_id) + ' --ophys_container_id' + ' ' + str(ophys_container_id)) + slurm.sbatch(python_executable+' '+python_file+' --ophys_experiment_id '+str(experiment_id)) diff --git a/visual_behavior/data_access/loading.py b/visual_behavior/data_access/loading.py index 53eea4400..d8d6df208 100644 --- a/visual_behavior/data_access/loading.py +++ b/visual_behavior/data_access/loading.py @@ -73,8 +73,8 @@ def get_platform_analysis_cache_dir(): def get_production_cache_dir(): """Get directory containing a manifest file that includes all VB production data, including failed experiments""" - cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' - # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + # cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' return cache_dir diff --git a/visual_behavior/ophys/dataset/visual_behavior_ophys_dataset.py b/visual_behavior/ophys/dataset/visual_behavior_ophys_dataset.py index 39e11f736..24a7f7ef4 100644 --- a/visual_behavior/ophys/dataset/visual_behavior_ophys_dataset.py +++ b/visual_behavior/ophys/dataset/visual_behavior_ophys_dataset.py @@ -59,17 +59,17 @@ def __init__(self, experiment_id, cache_dir=None, **kwargs): self.cache_dir = self.get_cache_dir() self.roi_metrics = self.get_roi_metrics() self.append_omitted_to_stim_metadata = True - if self.roi_metrics.cell_specimen_id.values[0] is None: - self.cell_matching = False - else: - self.cell_matching = True + # if self.roi_metrics.cell_specimen_id.values[0] is None: + # self.cell_matching = False + # else: + # self.cell_matching = True def get_cache_dir(self): if self.cache_dir is None: if platform.system() == 'Linux': - cache_dir = r'/allen/aibs/informatics/swdb2018/visual_behavior' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' else: - cache_dir = r'\\allen\aibs\informatics\swdb2018\visual_behavior' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' logger.info('using default cache_dir: {}'.format(cache_dir)) else: @@ -333,6 +333,15 @@ def get_cell_indices(self): cell_indices = LazyLoadable('_cell_indices', get_cell_indices) + def get_valid_cell_indices(self): + roi_metrics = self.roi_metrics.copy() + valid_cell_indices = roi_metrics[roi_metrics.valid_roi == True].cell_index.values + + self._valid_cell_indices = np.sort(valid_cell_indices) + return self._valid_cell_indices + + valid_cell_indices = LazyLoadable('_valid_cell_indices', get_valid_cell_indices) + def get_cell_specimen_id_for_cell_index(self, cell_index): roi_metrics = self.roi_metrics cell_specimen_id = roi_metrics[roi_metrics.cell_index == cell_index].id.values[0] @@ -453,6 +462,7 @@ def construct_and_load(cls, experiment_id, cache_dir=None, **kwargs): obj.get_roi_mask_array() obj.get_cell_specimen_ids() obj.get_cell_indices() + obj.get_valid_cell_indices() obj.get_max_projection() obj.get_average_image() obj.get_motion_correction() diff --git a/visual_behavior/ophys/io/convert_level_1_to_level_2.py b/visual_behavior/ophys/io/convert_level_1_to_level_2.py index 80faa9e47..40cf26404 100644 --- a/visual_behavior/ophys/io/convert_level_1_to_level_2.py +++ b/visual_behavior/ophys/io/convert_level_1_to_level_2.py @@ -48,10 +48,12 @@ def save_dataframe_as_h5(df, name, analysis_dir): def get_cache_dir(cache_dir=None): if not cache_dir: if platform.system() == 'Linux': - cache_dir = r'/allen/programs/mindscope/workgroups/learning/learning_mFISH_production_analysis' + cache_dir = r'/allen/programs/mindscope/workgroups/learning/ophys/learning_project_cache' + # cache_dir = r'/allen/programs/mindscope/workgroups/learning/learning_mFISH_production_analysis' # cache_dir = r'/allen/programs/braintv/workgroups/nc-ophys/visual_behavior/visual_behavior_production_analysis' else: - cache_dir = r'\\allen\programs\mindscope\workgroups\learning\learning_mFISH_production_analysis' + cache_dir = r'\\allen\programs\mindscope\workgroups\learning\ophys\learning_project_cache' + # cache_dir = r'\\allen\programs\mindscope\workgroups\learning\learning_mFISH_production_analysis' # cache_dir = r'\\allen\programs\braintv\workgroups\nc-ophys\visual_behavior\visual_behavior_production_analysis' return cache_dir else: @@ -189,12 +191,12 @@ def get_processed_dir(lims_data): return processed_dir -def get_segmentation_dir(lims_data): - processed_dir = get_processed_dir(lims_data) - segmentation_folder = [file for file in os.listdir(processed_dir) if 'segmentation' in file] - segmentation_folder.sort() - segmentation_dir = os.path.join(processed_dir, segmentation_folder[-1]) - return segmentation_dir +# def get_segmentation_dir(lims_data): +# processed_dir = get_processed_dir(lims_data) +# segmentation_folder = [file for file in os.listdir(processed_dir) if 'segmentation' in file] +# segmentation_folder.sort() +# segmentation_dir = os.path.join(processed_dir, segmentation_folder[-1]) +# return segmentation_dir def get_stimulus_timestamps(timestamps): @@ -477,7 +479,7 @@ def get_roi_locations(lims_data): else: mask = roi["mask"] roi_locations_list.append([roi["id"], roi["x"], roi["y"], roi["width"], roi["height"], roi["valid"], mask]) - roi_locations = pd.DataFrame(data=roi_locations_list, columns=['id', 'x', 'y', 'width', 'height', 'valid', 'mask']) + roi_locations = pd.DataFrame(data=roi_locations_list, columns=['id', 'x', 'y', 'width', 'height', 'valid_roi', 'mask_matrix']) return roi_locations @@ -494,32 +496,35 @@ def add_roi_ids_to_roi_metrics(roi_metrics, roi_locations): def get_roi_metrics(lims_data): - # objectlist.txt contains metrics associated with segmentation masks - segmentation_dir = get_segmentation_dir(lims_data) - roi_metrics = pd.read_csv(os.path.join(segmentation_dir, 'objectlist.txt')) - # get roi_locations and add unfiltered cell index - roi_locations = get_roi_locations(lims_data) - roi_names = np.sort(roi_locations.id.values) - roi_locations['unfiltered_cell_index'] = [np.where(roi_names == id)[0][0] for id in roi_locations.id.values] - # add cell ids to roi_metrics from roi_locations - roi_metrics = add_roi_ids_to_roi_metrics(roi_metrics, roi_locations) - # merge roi_metrics and roi_locations - roi_metrics['id'] = roi_metrics.roi_id.values - roi_metrics = pd.merge(roi_metrics, roi_locations, on='id') - unfiltered_roi_metrics = roi_metrics - # remove invalid roi_metrics - roi_metrics = roi_metrics[roi_metrics.valid == True] - # hack to get rid of cases with 2 rois at the same location - for roi_id in roi_metrics.roi_id.values: - roi_data = roi_metrics[roi_metrics.roi_id == roi_id] - if len(roi_data) > 1: - ind = roi_data.index - roi_metrics = roi_metrics.drop(index=ind.values) - # add filtered cell index - cell_index = [np.where(np.sort(roi_metrics.roi_id.values) == id)[0][0] for id in - roi_metrics.roi_id.values] - roi_metrics['cell_index'] = cell_index - return roi_metrics, unfiltered_roi_metrics + roi_metrics = get_roi_locations(lims_data) + roi_metrics['roi_id'] = roi_metrics['id'] + + # # objectlist.txt contains metrics associated with segmentation masks + # segmentation_dir = get_segmentation_dir(lims_data) + # roi_metrics = pd.read_csv(os.path.join(segmentation_dir, 'objectlist.txt')) + # # get roi_locations and add unfiltered cell index + # roi_locations = get_roi_locations(lims_data) + # roi_names = np.sort(roi_locations.id.values) + # roi_locations['unfiltered_cell_index'] = [np.where(roi_names == id)[0][0] for id in roi_locations.id.values] + # # add cell ids to roi_metrics from roi_locations + # roi_metrics = add_roi_ids_to_roi_metrics(roi_metrics, roi_locations) + # # merge roi_metrics and roi_locations + # roi_metrics['id'] = roi_metrics.roi_id.values + # roi_metrics = pd.merge(roi_metrics, roi_locations, on='id') + # unfiltered_roi_metrics = roi_metrics + # # remove invalid roi_metrics + # roi_metrics = roi_metrics[roi_metrics.valid == True] + # # hack to get rid of cases with 2 rois at the same location + # for roi_id in roi_metrics.roi_id.values: + # roi_data = roi_metrics[roi_metrics.roi_id == roi_id] + # if len(roi_data) > 1: + # ind = roi_data.index + # roi_metrics = roi_metrics.drop(index=ind.values) + # # add filtered cell index + # cell_index = [np.where(np.sort(roi_metrics.roi_id.values) == id)[0][0] for id in + # roi_metrics.roi_id.values] + # roi_metrics['cell_index'] = cell_index + return roi_metrics def save_roi_metrics(roi_metrics, lims_data): @@ -621,7 +626,7 @@ def get_cell_specimen_ids_from_lims(mouse_id): def add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir): - # roi_ids = get_roi_ids(roi_metrics) + roi_ids = get_roi_ids(roi_metrics) mouse_id = int(lims_data['external_specimen_id']) # Sql query lims to see if there are matching cell ids ids = get_cell_specimen_ids_from_lims(mouse_id) @@ -646,13 +651,16 @@ def add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir): def get_cell_specimen_ids(roi_metrics): roi_ids = get_roi_ids(roi_metrics) - if roi_metrics.cell_specimen_id.values[0] is None: - cell_specimen_ids = roi_ids - else: - # make sure cell_specimen_ids are retrieved in the same order as roi_ids, as these correspond to order of trace and roi maskindices - cell_specimen_ids = [int(roi_metrics[roi_metrics.roi_id == roi_id].cell_specimen_id.values[0]) for roi_id in - roi_ids] - cell_specimen_ids = np.asarray(cell_specimen_ids) + + # if roi_metrics.cell_specimen_id.values[0] is None: + # cell_specimen_ids = roi_ids + # else: + # make sure cell_specimen_ids are retrieved in the same order as roi_ids, as these correspond to order of trace and roi maskindices + # cell_specimen_ids = [int(roi_metrics[roi_metrics.roi_id == roi_id].cell_specimen_id.values[0]) for roi_id in + # roi_ids] + # cell_specimen_ids = np.asarray(cell_specimen_ids) + + cell_specimen_ids = roi_metrics.roi_id.values return cell_specimen_ids @@ -672,30 +680,40 @@ def get_cell_index_for_cell_specimen_id(cell_specimen_id, cell_specimen_ids): def get_fov_dims(experiment_id): - from allensdk.brain_observatory.behavior.session_apis.data_io.behavior_ophys_lims_api import BehaviorOphysLimsApi - api = BehaviorOphysLimsApi(experiment_id) - image_metadata = api.get_metadata() + # from allensdk.brain_observatory.behavior.session_apis.data_io.behavior_ophys_lims_api import BehaviorOphysLimsApi + # api = BehaviorOphysLimsApi(experiment_id) + # image_metadata = api.get_metadata() + + max_projection = get_max_projection(lims_data) + + h = image_metadata['field_of_view_height'] + w = image_metadata['field_of_view_width'] + return image_metadata def get_roi_masks(roi_metrics, lims_data): # make roi_dict with ids as keys and roi_mask_array - jin = get_input_extract_traces_json(lims_data) - - try: - h = jin["image"]["height"] - w = jin["image"]["width"] - except Exception as e: - print(e) - image_metadata = get_fov_dims(lims_data['lims_id'].iloc[0]) - h = image_metadata['field_of_view_height'] - w = image_metadata['field_of_view_width'] + # jin = get_input_extract_traces_json(lims_data) + # try: + # h = jin["image"]["height"] + # w = jin["image"]["width"] + # except Exception as e: + # print(e) + # image_metadata = get_fov_dims(lims_data['lims_id'].iloc[0]) + # h = image_metadata['field_of_view_height'] + # w = image_metadata['field_of_view_width'] + + # get size from max projection + size = get_max_projection(lims_data).shape + h = size[0] + w = size[1] cell_specimen_ids = get_cell_specimen_ids(roi_metrics) roi_masks = {} for i, id in enumerate(cell_specimen_ids): m = roi_metrics[roi_metrics.id == id].iloc[0] - mask = np.asarray(m['mask']) + mask = np.asarray(m['mask_matrix']) binary_mask = np.zeros((h, w), dtype=np.uint8) binary_mask[int(m.y):int(m.y) + int(m.height), int(m.x):int(m.x) + int(m.width)] = mask roi_masks[int(id)] = binary_mask @@ -714,8 +732,8 @@ def get_corrected_fluorescence_traces(roi_metrics, lims_data): str(get_lims_id(lims_data)) + '_demixed_traces.h5') g = h5py.File(file_path) corrected_fluorescence_traces = np.asarray(g['data']) - valid_roi_indices = np.sort(roi_metrics.unfiltered_cell_index.values) - corrected_fluorescence_traces = corrected_fluorescence_traces[valid_roi_indices] + # valid_roi_indices = np.sort(roi_metrics.unfiltered_cell_index.values) + # corrected_fluorescence_traces = corrected_fluorescence_traces[valid_roi_indices] return corrected_fluorescence_traces @@ -733,24 +751,24 @@ def get_dff_traces(roi_metrics, lims_data): dff_traces = np.asarray(g['data']) # print(dff_traces.shape) # print(roi_metrics.unfiltered_cell_index) - valid_roi_indices = np.sort(roi_metrics.unfiltered_cell_index.values) - dff_traces = dff_traces[valid_roi_indices] + # valid_roi_indices = np.sort(roi_metrics.unfiltered_cell_index.values) + # dff_traces = dff_traces[valid_roi_indices] print(dff_traces.shape) # find cells with NaN traces - bad_cell_indices = [] - final_dff_traces = [] - for i, dff in enumerate(dff_traces): - if np.isnan(dff).any(): - logger.info('NaN trace detected, removing cell_index: %d', i) - bad_cell_indices.append(i) - elif np.amax(dff) > 20: - logger.info('outlier trace detected, removing cell_index %d', i) - bad_cell_indices.append(i) - else: - final_dff_traces.append(dff) - dff_traces = np.asarray(final_dff_traces) - print(dff_traces.shape) - roi_metrics = roi_metrics[roi_metrics.cell_index.isin(bad_cell_indices) == False] + # bad_cell_indices = [] + # final_dff_traces = [] + # for i, dff in enumerate(dff_traces): + # if np.isnan(dff).any(): + # logger.info('NaN trace detected, removing cell_index: %d', i) + # bad_cell_indices.append(i) + # elif np.amax(dff) > 20: + # logger.info('outlier trace detected, removing cell_index %d', i) + # bad_cell_indices.append(i) + # else: + # final_dff_traces.append(dff) + # dff_traces = np.asarray(final_dff_traces) + # print(dff_traces.shape) + # roi_metrics = roi_metrics[roi_metrics.cell_index.isin(bad_cell_indices) == False] # reset cell index after removing bad cells cell_index = [np.where(np.sort(roi_metrics.roi_id.values) == id)[0][0] for id in roi_metrics.roi_id.values] @@ -805,8 +823,10 @@ def save_motion_correction(motion_correction, lims_data): def get_max_projection(lims_data): + files = os.listdir(os.path.join(get_processed_dir(lims_data))) + max_image = [file for file in files if 'maximum_projection.png' in file] # max_projection = mpimg.imread(os.path.join(get_processed_dir(lims_data), 'max_downsample_4Hz_0.png')) - max_projection = mpimg.imread(os.path.join(get_segmentation_dir(lims_data), 'maxInt_a13a.png')) + max_projection = mpimg.imread(os.path.join(get_processed_dir(lims_data), max_image[0])) return max_projection @@ -818,14 +838,18 @@ def save_max_projections(lims_data): # mpimg.imsave(os.path.join(get_analysis_dir(lims_data), 'max_intensity_projection.png'), arr=max_projection, # cmap='gray') # contrast enhanced one - max_projection = mpimg.imread(os.path.join(get_segmentation_dir(lims_data), 'maxInt_a13a.png')) + files = os.listdir(os.path.join(get_processed_dir(lims_data))) + max_image = [file for file in files if 'maximum_projection.png' in file] + max_projection = mpimg.imread(os.path.join(get_processed_dir(lims_data), max_image[0])) save_data_as_h5(max_projection, 'max_projection', analysis_dir) mpimg.imsave(os.path.join(get_analysis_dir(lims_data), 'max_intensity_projection.png'), arr=max_projection, cmap='gray') def get_average_image(lims_data): - average_image = mpimg.imread(os.path.join(get_segmentation_dir(lims_data), 'avgInt_a1X.png')) + files = os.listdir(os.path.join(get_processed_dir(lims_data))) + avg_image = [file for file in files if 'average_projection.png' in file] + average_image = mpimg.imread(os.path.join(get_processed_dir(lims_data), avg_image[0])) return average_image @@ -854,7 +878,7 @@ def run_roi_validation(lims_data, cache_dir): roi_df = get_roi_locations(lims_data) roi_metrics, unfiltered_roi_metrics = get_roi_metrics(lims_data) - roi_metrics = add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir) + # roi_metrics = add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir) roi_masks = get_roi_masks(roi_metrics, lims_data) dff_traces, roi_metrics = get_dff_traces(roi_metrics, lims_data) # cell_specimen_ids = get_cell_specimen_ids(roi_metrics) @@ -914,20 +938,24 @@ def convert_level_1_to_level_2(lims_id, cache_dir=None, plot_roi_validation=Fals pkl = get_pkl(lims_data) stimulus_timestamps = get_stimulus_timestamps(timestamps) - core_data = get_core_data(pkl, stimulus_timestamps) - save_core_data_components(core_data, lims_data, stimulus_timestamps) + try: + core_data = get_core_data(pkl, stimulus_timestamps) + save_core_data_components(core_data, lims_data, stimulus_timestamps) - metadata = get_metadata(lims_data, timestamps, core_data) - save_metadata(metadata, lims_data) + metadata = get_metadata(lims_data, timestamps, core_data) + save_metadata(metadata, lims_data) + + trials = get_trials(core_data) + save_trials(trials, lims_data) + except: + print('could not save core data for behavior task') - trials = get_trials(core_data) - save_trials(trials, lims_data) stimulus_template, stimulus_metadata = get_visual_stimulus_data(pkl) save_visual_stimulus_data(stimulus_template, stimulus_metadata, lims_data) - roi_metrics, unfiltered_roi_metrics = get_roi_metrics(lims_data) - roi_metrics = add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir) + roi_metrics = get_roi_metrics(lims_data) + # roi_metrics = add_cell_specimen_ids_to_roi_metrics(lims_data, roi_metrics, cache_dir) dff_traces, roi_metrics = get_dff_traces(roi_metrics, lims_data) save_dff_traces(dff_traces, roi_metrics, lims_data) @@ -936,7 +964,7 @@ def convert_level_1_to_level_2(lims_id, cache_dir=None, plot_roi_validation=Fals save_roi_masks(roi_masks, lims_data) save_roi_metrics(roi_metrics, lims_data) - save_unfiltered_roi_metrics(unfiltered_roi_metrics, lims_data) + # save_unfiltered_roi_metrics(unfiltered_roi_metrics, lims_data) corrected_fluorescence_traces = get_corrected_fluorescence_traces(roi_metrics, lims_data) save_corrected_fluorescence_traces(corrected_fluorescence_traces, roi_metrics, lims_data) diff --git a/visual_behavior/visualization/ophys/experiment_summary_figures.py b/visual_behavior/visualization/ophys/experiment_summary_figures.py index 3efb9a5b7..08b45ae34 100644 --- a/visual_behavior/visualization/ophys/experiment_summary_figures.py +++ b/visual_behavior/visualization/ophys/experiment_summary_figures.py @@ -461,8 +461,8 @@ def plot_roi_masks(dataset, save=False): metrics = np.empty(len(dataset.cell_indices)) metrics[:] = -1 - cell_list = dataset.cell_indices - plot_metrics_mask(dataset, metrics, cell_list, 'roi masks', max_image=True, cmap='hls', ax=ax[1], save=False, + cell_list = dataset.valid_cell_indices + plot_metrics_mask(dataset.roi_mask_array, metrics, cell_list, 'roi masks', max_projection=None, cmap='hls', ax=ax[1], save=False, colorbar=False) plt.suptitle(dataset.analysis_folder, fontsize=16, x=0.5, y=1., horizontalalignment='center') @@ -555,7 +555,8 @@ def plot_experiment_summary_figure(analysis, save_dir=None): metrics = np.empty(len(analysis.dataset.cell_indices)) metrics[:] = -1 cell_list = analysis.dataset.cell_indices - plot_metrics_mask(analysis.dataset, metrics, cell_list, 'cell masks', max_image=True, cmap='hls', ax=ax, save=False, + max_projection = analysis.dataset.max_projection + plot_metrics_mask(analysis.dataset.roi_mask_array, metrics, cell_list, 'cell masks', max_projection=max_projection, cmap='hls', ax=ax, save=False, colorbar=False) # ax.imshow(analysis.dataset.max_projection, cmap='gray', vmin=0, vmax=np.amax(analysis.dataset.max_projection)) ax.set_title(analysis.dataset.experiment_id) diff --git a/visual_behavior/visualization/ophys/summary_figures.py b/visual_behavior/visualization/ophys/summary_figures.py index 1c7de972d..a83bfef3e 100644 --- a/visual_behavior/visualization/ophys/summary_figures.py +++ b/visual_behavior/visualization/ophys/summary_figures.py @@ -90,8 +90,8 @@ def plot_roi_validation(roi_names, id = int(id) x = roi_df[roi_df.id == id]['x'].values[0] y = roi_df[roi_df.id == id]['y'].values[0] - valid = roi_df[roi_df.id == id]['valid'].values[0] - ax[0].imshow(roi_df[roi_df.id == id]['mask'].values[0]) + valid = roi_df[roi_df.id == id]['valid_roi'].values[0] + ax[0].imshow(roi_df[roi_df.id == id]['mask_matrix'].values[0]) ax[0].set_title(str(id) + ', ' + str(valid) + ', x: ' + str(x) + ', y: ' + str(y)) ax[0].grid(False)