def write_behavior_nwb(session_data, nwb_filepath): nwb_filepath_inprogress = nwb_filepath+'.inprogress' nwb_filepath_error = nwb_filepath+'.error' # Clean out files from previous runs: for filename in [nwb_filepath_inprogress, nwb_filepath_error, nwb_filepath]: if os.path.exists(filename): os.remove(filename) try: json_session = BehaviorSession(api=BehaviorJsonApi(session_data)) lims_api = BehaviorLimsApi( behavior_session_id=session_data['behavior_session_id']) lims_session = BehaviorSession(api=lims_api) logging.info("Comparing a BehaviorSession created from JSON " "with a BehaviorSession created from LIMS") assert sessions_are_equal(json_session, lims_session, reraise=True) BehaviorNwbApi(nwb_filepath_inprogress).save(json_session) logging.info("Comparing a BehaviorSession created from JSON " "with a BehaviorSession created from NWB") nwb_api = BehaviorNwbApi(nwb_filepath_inprogress) nwb_session = BehaviorSession(api=nwb_api) assert sessions_are_equal(json_session, nwb_session, reraise=True) os.rename(nwb_filepath_inprogress, nwb_filepath) return {'output_path': nwb_filepath} except Exception as e: os.rename(nwb_filepath_inprogress, nwb_filepath_error) raise e
def get_behavior_session(self, behavior_session_id: int) -> BehaviorSession: """Returns a BehaviorSession object that contains methods to analyze a single behavior session. :param behavior_session_id: id that corresponds to a behavior session :type behavior_session_id: int :rtype: BehaviorSession """ return BehaviorSession(BehaviorLimsApi(behavior_session_id))
def test_behavior_session_list_data_attributes_and_methods(): # Test that data related methods/attributes/properties for # BehaviorSession are returned properly. # This test will need to be updated if: # 1. Data being returned by class has changed # 2. Inheritance of class has changed expected = { 'behavior_session_id', 'get_performance_metrics', 'get_reward_rate', 'get_rolling_performance_df', 'licks', 'metadata', 'raw_running_speed', 'rewards', 'running_speed', 'stimulus_presentations', 'stimulus_templates', 'stimulus_timestamps', 'task_parameters', 'trials' } behavior_session = BehaviorSession(api=MagicMock()) obt = behavior_session.list_data_attributes_and_methods() assert any(expected ^ set(obt)) is False
def test_behavior_session_list_data_attributes_and_methods(monkeypatch): """Test that data related methods/attributes/properties for BehaviorSession are returned properly.""" def dummy_init(self): pass with monkeypatch.context() as ctx: ctx.setattr(BehaviorSession, '__init__', dummy_init) bs = BehaviorSession() obt = bs.list_data_attributes_and_methods() expected = { 'behavior_session_id', 'get_performance_metrics', 'get_reward_rate', 'get_rolling_performance_df', 'licks', 'metadata', 'raw_running_speed', 'rewards', 'running_speed', 'stimulus_presentations', 'stimulus_templates', 'stimulus_timestamps', 'task_parameters', 'trials' } assert any(expected ^ set(obt)) is False
def write_behavior_nwb(session_data, nwb_filepath): nwb_filepath_inprogress = nwb_filepath + '.inprogress' nwb_filepath_error = nwb_filepath + '.error' # Clean out files from previous runs: for filename in [ nwb_filepath_inprogress, nwb_filepath_error, nwb_filepath ]: if os.path.exists(filename): os.remove(filename) try: json_session = BehaviorSession.from_json(session_data) behavior_session_id = session_data['behavior_session_id'] lims_session = BehaviorSession.from_lims(behavior_session_id) logging.info("Comparing a BehaviorSession created from JSON " "with a BehaviorSession created from LIMS") assert sessions_are_equal(json_session, lims_session, reraise=True) nwbfile = lims_session.to_nwb() with NWBHDF5IO(nwb_filepath_inprogress, 'w') as nwb_file_writer: nwb_file_writer.write(nwbfile) logging.info("Comparing a BehaviorSession created from JSON " "with a BehaviorSession created from NWB") nwb_session = BehaviorSession.from_nwb_path(nwb_filepath_inprogress) assert sessions_are_equal(json_session, nwb_session, reraise=True) os.rename(nwb_filepath_inprogress, nwb_filepath) return {'output_path': nwb_filepath} except Exception as e: if os.path.isfile(nwb_filepath_inprogress): os.rename(nwb_filepath_inprogress, nwb_filepath_error) raise e
def get_behavior_session(self, behavior_session_id: int) -> BehaviorSession: """get a BehaviorSession by specifying behavior_session_id Parameters ---------- behavior_session_id: int the id of the behavior_session Returns ------- BehaviorSession Notes ----- entries in the _behavior_session_table represent (1) ophys_sessions which have a many-to-one mapping between nwb files and behavior sessions. (file_id is NaN) AND (2) behavior only sessions, which have a one-to-one mapping with nwb files. (file_id is not Nan) In the case of (1) this method returns an object which is just behavior data which is shared by all experiments in 1 session. This is extracted from the nwb file for the first-listed ophys_experiment. """ row = self._behavior_session_table.query( f"behavior_session_id=={behavior_session_id}") if row.shape[0] != 1: raise RuntimeError("The behavior_session_table should have " "1 and only 1 entry for a given " "behavior_session_id. For " f"{behavior_session_id} " f" there are {row.shape[0]} entries.") row = row.squeeze() has_file_id = not pd.isna(row[self.cache.file_id_column]) if not has_file_id: oeid = row.ophys_experiment_id[0] row = self._ophys_experiment_table.query(f"index=={oeid}") file_id = str(int(row[self.cache.file_id_column])) data_path = self._get_data_path(file_id=file_id) return BehaviorSession.from_nwb_path(str(data_path))
def test_eye_tracking(dilation_frames, z_threshold, monkeypatch): """A very long test just to test that eye tracking arguments are sent to EyeTrackingTable factory method from BehaviorOphysExperiment.from_lims""" expected = EyeTrackingTable(eye_tracking=pd.DataFrame([1, 2, 3])) EyeTrackingTable_mock = create_autospec(EyeTrackingTable) EyeTrackingTable_mock.from_data_file.return_value = expected etf = create_autospec(EyeTrackingFile, instance=True) sf = create_autospec(SyncFile, instance=True) with monkeypatch.context() as ctx: ctx.setattr( 'allensdk.brain_observatory.behavior.' 'behavior_ophys_experiment.db_connection_creator', create_autospec(db_connection_creator, instance=True)) ctx.setattr(SyncFile, 'from_lims', lambda db, ophys_experiment_id: sf) ctx.setattr( StimulusTimestamps, 'from_sync_file', lambda sync_file: create_autospec(StimulusTimestamps, instance=True)) ctx.setattr( BehaviorSessionId, 'from_lims', lambda db, ophys_experiment_id: create_autospec(BehaviorSessionId, instance=True)) ctx.setattr(ImagingPlaneGroup, 'from_lims', lambda lims_db, ophys_experiment_id: None) ctx.setattr( BehaviorOphysMetadata, 'from_lims', lambda lims_db, ophys_experiment_id, is_multiplane: create_autospec(BehaviorOphysMetadata, instance=True)) ctx.setattr( 'allensdk.brain_observatory.behavior.' 'behavior_ophys_experiment.calculate_monitor_delay', create_autospec(calculate_monitor_delay)) ctx.setattr( DateOfAcquisitionOphys, 'from_lims', lambda lims_db, ophys_experiment_id: create_autospec( DateOfAcquisitionOphys, instance=True)) ctx.setattr( BehaviorSession, 'from_lims', lambda lims_db, behavior_session_id, stimulus_timestamps, monitor_delay, date_of_acquisition: BehaviorSession( behavior_session_id=None, stimulus_timestamps=None, running_acquisition=None, raw_running_speed=None, running_speed=None, licks=None, rewards=None, stimuli=None, task_parameters=None, trials=None, metadata=None, date_of_acquisition=None, )) ctx.setattr( OphysTimestamps, 'from_sync_file', lambda sync_file: create_autospec(OphysTimestamps, instance=True)) ctx.setattr( Projections, 'from_lims', lambda lims_db, ophys_experiment_id: create_autospec(Projections, instance=True)) ctx.setattr( CellSpecimens, 'from_lims', lambda lims_db, ophys_experiment_id, ophys_timestamps, segmentation_mask_image_spacing, events_params, exclude_invalid_rois: create_autospec(BehaviorSession, instance=True)) ctx.setattr( RigidMotionTransformFile, 'from_lims', lambda db, ophys_experiment_id: create_autospec( RigidMotionTransformFile, instance=True)) ctx.setattr(EyeTrackingFile, 'from_lims', lambda db, ophys_experiment_id: etf) ctx.setattr( EyeTrackingTable, 'from_data_file', lambda data_file, sync_file, z_threshold, dilation_frames: EyeTrackingTable_mock.from_data_file( data_file=data_file, sync_file=sync_file, z_threshold=z_threshold, dilation_frames=dilation_frames)) ctx.setattr( EyeTrackingRigGeometry, 'from_lims', lambda lims_db, ophys_experiment_id: create_autospec( EyeTrackingRigGeometry, instance=True)) boe = BehaviorOphysExperiment.from_lims( ophys_experiment_id=1, eye_tracking_z_threshold=z_threshold, eye_tracking_dilation_frames=dilation_frames) obtained = boe.eye_tracking assert obtained.equals(expected.value) EyeTrackingTable_mock.from_data_file.assert_called_with( data_file=etf, sync_file=sf, z_threshold=z_threshold, dilation_frames=dilation_frames)
def __init__(self, api=None, eye_tracking_z_threshold: float = 3.0, eye_tracking_dilation_frames: int = 2, events_filter_scale: float = 2.0, events_filter_n_time_steps: int = 20): """ Parameters ---------- api : object, optional The backend api used by the session object to get behavior ophys data, by default None. eye_tracking_z_threshold : float, optional The z-threshold when determining which frames likely contain outliers for eye or pupil areas. Influences which frames are considered 'likely blinks'. By default 3.0 eye_tracking_dilation_frames : int, optional Determines the number of adjacent frames that will be marked as 'likely_blink' when performing blink detection for `eye_tracking` data, by default 2 events_filter_scale : float, optional Stdev of halfnorm distribution used to convolve ophys events with a 1d causal half-gaussian filter to smooth it for visualization, by default 2.0 events_filter_n_time_steps : int, optional Number of time steps to use for convolution of ophys events """ BehaviorSession.__init__(self, api=api) ParamsMixin.__init__(self, ignore={'api'}) # eye_tracking processing params self._eye_tracking_z_threshold = eye_tracking_z_threshold self._eye_tracking_dilation_frames = eye_tracking_dilation_frames # events processing params self._events_filter_scale = events_filter_scale self._events_filter_n_time_steps = events_filter_n_time_steps # LazyProperty constructor provided by LazyPropertyMixin LazyProperty = self.LazyProperty # Initialize attributes to be lazily evaluated self._ophys_session_id = LazyProperty(self.api.get_ophys_session_id) self._ophys_experiment_id = LazyProperty( self.api.get_ophys_experiment_id) self._max_projection = LazyProperty(self.api.get_max_projection, wrappers=[ImageApi.deserialize]) self._average_projection = LazyProperty( self.api.get_average_projection, wrappers=[ImageApi.deserialize]) self._ophys_timestamps = LazyProperty(self.api.get_ophys_timestamps, settable=True) self._dff_traces = LazyProperty(self.api.get_dff_traces, settable=True) self._events = LazyProperty(self.api.get_events, settable=True) self._cell_specimen_table = LazyProperty( self.api.get_cell_specimen_table, settable=True) self._corrected_fluorescence_traces = LazyProperty( self.api.get_corrected_fluorescence_traces, settable=True) self._motion_correction = LazyProperty(self.api.get_motion_correction, settable=True) self._segmentation_mask_image = LazyProperty( self.get_segmentation_mask_image) self._eye_tracking = LazyProperty( self.api.get_eye_tracking, settable=True, z_threshold=self._eye_tracking_z_threshold, dilation_frames=self._eye_tracking_dilation_frames) self._eye_tracking_rig_geometry = LazyProperty( self.api.get_eye_tracking_rig_geometry)
def from_json(cls, session_data: dict, eye_tracking_z_threshold: float = 3.0, eye_tracking_dilation_frames: int = 2, events_filter_scale: float = 2.0, events_filter_n_time_steps: int = 20, exclude_invalid_rois=True, skip_eye_tracking=False) -> \ "BehaviorOphysExperiment": """ Parameters ---------- session_data eye_tracking_z_threshold See `BehaviorOphysExperiment.from_nwb` eye_tracking_dilation_frames See `BehaviorOphysExperiment.from_nwb` events_filter_scale See `BehaviorOphysExperiment.from_nwb` events_filter_n_time_steps See `BehaviorOphysExperiment.from_nwb` exclude_invalid_rois Whether to exclude invalid rois skip_eye_tracking Used to skip returning eye tracking data """ def _is_multi_plane_session(): imaging_plane_group_meta = ImagingPlaneGroup.from_json( dict_repr=session_data) return cls._is_multi_plane_session( imaging_plane_group_meta=imaging_plane_group_meta) def _get_motion_correction(): rigid_motion_transform_file = RigidMotionTransformFile.from_json( dict_repr=session_data) return MotionCorrection.from_data_file( rigid_motion_transform_file=rigid_motion_transform_file) def _get_eye_tracking_table(sync_file: SyncFile): eye_tracking_file = EyeTrackingFile.from_json( dict_repr=session_data) eye_tracking_table = EyeTrackingTable.from_data_file( data_file=eye_tracking_file, sync_file=sync_file, z_threshold=eye_tracking_z_threshold, dilation_frames=eye_tracking_dilation_frames) return eye_tracking_table sync_file = SyncFile.from_json(dict_repr=session_data) is_multiplane_session = _is_multi_plane_session() meta = BehaviorOphysMetadata.from_json( dict_repr=session_data, is_multiplane=is_multiplane_session) monitor_delay = calculate_monitor_delay( sync_file=sync_file, equipment=meta.behavior_metadata.equipment) behavior_session = BehaviorSession.from_json( session_data=session_data, monitor_delay=monitor_delay) if is_multiplane_session: ophys_timestamps = OphysTimestampsMultiplane.from_sync_file( sync_file=sync_file, group_count=meta.ophys_metadata.imaging_plane_group_count, plane_group=meta.ophys_metadata.imaging_plane_group) else: ophys_timestamps = OphysTimestamps.from_sync_file( sync_file=sync_file) projections = Projections.from_json(dict_repr=session_data) cell_specimens = CellSpecimens.from_json( dict_repr=session_data, ophys_timestamps=ophys_timestamps, segmentation_mask_image_spacing=projections.max_projection.spacing, events_params=EventsParams( filter_scale=events_filter_scale, filter_n_time_steps=events_filter_n_time_steps), exclude_invalid_rois=exclude_invalid_rois) motion_correction = _get_motion_correction() if skip_eye_tracking: eye_tracking_table = None eye_tracking_rig_geometry = None else: eye_tracking_table = _get_eye_tracking_table(sync_file=sync_file) eye_tracking_rig_geometry = EyeTrackingRigGeometry.from_json( dict_repr=session_data) return BehaviorOphysExperiment( behavior_session=behavior_session, cell_specimens=cell_specimens, ophys_timestamps=ophys_timestamps, metadata=meta, projections=projections, motion_correction=motion_correction, eye_tracking_table=eye_tracking_table, eye_tracking_rig_geometry=eye_tracking_rig_geometry, date_of_acquisition=behavior_session._date_of_acquisition)
def from_nwb(cls, nwbfile: NWBFile, eye_tracking_z_threshold: float = 3.0, eye_tracking_dilation_frames: int = 2, events_filter_scale: float = 2.0, events_filter_n_time_steps: int = 20, exclude_invalid_rois=True) -> "BehaviorOphysExperiment": """ Parameters ---------- nwbfile eye_tracking_z_threshold : float, optional The z-threshold when determining which frames likely contain outliers for eye or pupil areas. Influences which frames are considered 'likely blinks'. By default 3.0 eye_tracking_dilation_frames : int, optional Determines the number of adjacent frames that will be marked as 'likely_blink' when performing blink detection for `eye_tracking` data, by default 2 events_filter_scale : float, optional Stdev of halfnorm distribution used to convolve ophys events with a 1d causal half-gaussian filter to smooth it for visualization, by default 2.0 events_filter_n_time_steps : int, optional Number of time steps to use for convolution of ophys events exclude_invalid_rois Whether to exclude invalid rois """ def _is_multi_plane_session(): imaging_plane_group_meta = ImagingPlaneGroup.from_nwb( nwbfile=nwbfile) return cls._is_multi_plane_session( imaging_plane_group_meta=imaging_plane_group_meta) behavior_session = BehaviorSession.from_nwb(nwbfile=nwbfile) projections = Projections.from_nwb(nwbfile=nwbfile) cell_specimens = CellSpecimens.from_nwb( nwbfile=nwbfile, segmentation_mask_image_spacing=projections.max_projection.spacing, events_params=EventsParams( filter_scale=events_filter_scale, filter_n_time_steps=events_filter_n_time_steps), exclude_invalid_rois=exclude_invalid_rois) eye_tracking_rig_geometry = EyeTrackingRigGeometry.from_nwb( nwbfile=nwbfile) eye_tracking_table = EyeTrackingTable.from_nwb( nwbfile=nwbfile, z_threshold=eye_tracking_z_threshold, dilation_frames=eye_tracking_dilation_frames) motion_correction = MotionCorrection.from_nwb(nwbfile=nwbfile) is_multiplane_session = _is_multi_plane_session() metadata = BehaviorOphysMetadata.from_nwb( nwbfile=nwbfile, is_multiplane=is_multiplane_session) if is_multiplane_session: ophys_timestamps = OphysTimestampsMultiplane.from_nwb( nwbfile=nwbfile) else: ophys_timestamps = OphysTimestamps.from_nwb(nwbfile=nwbfile) date_of_acquisition = DateOfAcquisitionOphys.from_nwb(nwbfile=nwbfile) return BehaviorOphysExperiment( behavior_session=behavior_session, cell_specimens=cell_specimens, eye_tracking_rig_geometry=eye_tracking_rig_geometry, eye_tracking_table=eye_tracking_table, motion_correction=motion_correction, metadata=metadata, ophys_timestamps=ophys_timestamps, projections=projections, date_of_acquisition=date_of_acquisition)
def from_lims(cls, ophys_experiment_id: int, eye_tracking_z_threshold: float = 3.0, eye_tracking_dilation_frames: int = 2, events_filter_scale: float = 2.0, events_filter_n_time_steps: int = 20, exclude_invalid_rois=True, skip_eye_tracking=False) -> \ "BehaviorOphysExperiment": """ Parameters ---------- ophys_experiment_id eye_tracking_z_threshold See `BehaviorOphysExperiment.from_nwb` eye_tracking_dilation_frames See `BehaviorOphysExperiment.from_nwb` events_filter_scale See `BehaviorOphysExperiment.from_nwb` events_filter_n_time_steps See `BehaviorOphysExperiment.from_nwb` exclude_invalid_rois Whether to exclude invalid rois skip_eye_tracking Used to skip returning eye tracking data """ def _is_multi_plane_session(): imaging_plane_group_meta = ImagingPlaneGroup.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db) return cls._is_multi_plane_session( imaging_plane_group_meta=imaging_plane_group_meta) def _get_motion_correction(): rigid_motion_transform_file = RigidMotionTransformFile.from_lims( ophys_experiment_id=ophys_experiment_id, db=lims_db) return MotionCorrection.from_data_file( rigid_motion_transform_file=rigid_motion_transform_file) def _get_eye_tracking_table(sync_file: SyncFile): eye_tracking_file = EyeTrackingFile.from_lims( db=lims_db, ophys_experiment_id=ophys_experiment_id) eye_tracking_table = EyeTrackingTable.from_data_file( data_file=eye_tracking_file, sync_file=sync_file, z_threshold=eye_tracking_z_threshold, dilation_frames=eye_tracking_dilation_frames) return eye_tracking_table lims_db = db_connection_creator( fallback_credentials=LIMS_DB_CREDENTIAL_MAP) sync_file = SyncFile.from_lims(db=lims_db, ophys_experiment_id=ophys_experiment_id) stimulus_timestamps = StimulusTimestamps.from_sync_file( sync_file=sync_file) behavior_session_id = BehaviorSessionId.from_lims( db=lims_db, ophys_experiment_id=ophys_experiment_id) is_multiplane_session = _is_multi_plane_session() meta = BehaviorOphysMetadata.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db, is_multiplane=is_multiplane_session) monitor_delay = calculate_monitor_delay( sync_file=sync_file, equipment=meta.behavior_metadata.equipment) date_of_acquisition = DateOfAcquisitionOphys.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db) behavior_session = BehaviorSession.from_lims( lims_db=lims_db, behavior_session_id=behavior_session_id.value, stimulus_timestamps=stimulus_timestamps, monitor_delay=monitor_delay, date_of_acquisition=date_of_acquisition) if is_multiplane_session: ophys_timestamps = OphysTimestampsMultiplane.from_sync_file( sync_file=sync_file, group_count=meta.ophys_metadata.imaging_plane_group_count, plane_group=meta.ophys_metadata.imaging_plane_group) else: ophys_timestamps = OphysTimestamps.from_sync_file( sync_file=sync_file) projections = Projections.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db) cell_specimens = CellSpecimens.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db, ophys_timestamps=ophys_timestamps, segmentation_mask_image_spacing=projections.max_projection.spacing, events_params=EventsParams( filter_scale=events_filter_scale, filter_n_time_steps=events_filter_n_time_steps), exclude_invalid_rois=exclude_invalid_rois) motion_correction = _get_motion_correction() if skip_eye_tracking: eye_tracking_table = None eye_tracking_rig_geometry = None else: eye_tracking_table = _get_eye_tracking_table(sync_file=sync_file) eye_tracking_rig_geometry = EyeTrackingRigGeometry.from_lims( ophys_experiment_id=ophys_experiment_id, lims_db=lims_db) return BehaviorOphysExperiment( behavior_session=behavior_session, cell_specimens=cell_specimens, ophys_timestamps=ophys_timestamps, metadata=meta, projections=projections, motion_correction=motion_correction, eye_tracking_table=eye_tracking_table, eye_tracking_rig_geometry=eye_tracking_rig_geometry, date_of_acquisition=date_of_acquisition)
def test_cache_clear_no_warning(self, caplog): caplog.clear() bs = BehaviorSession(api=DummyApiCache()) bs.cache_clear() assert len(caplog.record_tuples) == 0
def setup_class(cls): cls.behavior_session = BehaviorSession(api=DummyApi())