Exemple #1
0
    def __init__(self, api):
        super(ExtendedBehaviorSession, self).__init__(api)
        self.api = api

        self.trial_response_df = LazyProperty(self.api.get_trial_response_df)
        self.flash_response_df = LazyProperty(self.api.get_flash_response_df)
        self.image_index = LazyProperty(self.api.get_image_index_names)
        self.roi_masks = LazyProperty(self.get_roi_masks)
Exemple #2
0
    def __init__(self, api=None):

        self.api = api

        self.ophys_experiment_id = LazyProperty(
            self.api.get_ophys_experiment_id)
        self.max_projection = LazyProperty(self.get_max_projection)
        self.stimulus_timestamps = LazyProperty(
            self.api.get_stimulus_timestamps)
        self.ophys_timestamps = LazyProperty(self.api.get_ophys_timestamps)
        self.metadata = LazyProperty(self.api.get_metadata)
        self.dff_traces = LazyProperty(self.api.get_dff_traces)
        self.cell_specimen_table = LazyProperty(
            self.api.get_cell_specimen_table)
        self.running_speed = LazyProperty(self.api.get_running_speed)
        self.running_data_df = LazyProperty(self.api.get_running_data_df)
        self.stimulus_presentations = LazyProperty(
            self.api.get_stimulus_presentations)
        self.stimulus_templates = LazyProperty(self.api.get_stimulus_templates)
        self.licks = LazyProperty(self.api.get_licks)
        self.rewards = LazyProperty(self.api.get_rewards)
        self.task_parameters = LazyProperty(self.api.get_task_parameters)
        self.trials = LazyProperty(self.api.get_trials)
        self.corrected_fluorescence_traces = LazyProperty(
            self.api.get_corrected_fluorescence_traces)
        self.average_projection = LazyProperty(self.get_average_projection)
        self.motion_correction = LazyProperty(self.api.get_motion_correction)
        self.segmentation_mask_image = LazyProperty(
            self.get_segmentation_mask_image)
Exemple #3
0
class BehaviorOphysSession(LazyPropertyMixin):
    """Represents data from a single Visual Behavior Ophys imaging session.  LazyProperty attributes access the data only on the first demand, and then memoize the result for reuse.
    
    Attributes:
        ophys_experiment_id : int (LazyProperty)
            Unique identifier for this experimental session
        max_projection : allensdk.brain_observatory.behavior.image_api.Image (LazyProperty)
            2D max projection image
        stimulus_timestamps : numpy.ndarray (LazyProperty)
            Timestamps associated the stimulus presentations on the monitor 
        ophys_timestamps : numpy.ndarray (LazyProperty)
            Timestamps associated with frames captured by the microscope
        metadata : dict (LazyProperty)
            A dictionary of session-specific metadata
        dff_traces : pandas.DataFrame (LazyProperty)
            The traces of dff organized into a dataframe; index is the cell roi ids
        cell_specimen_table : pandas.DataFrame (LazyProperty)
            Cell roi information organized into a dataframe; index is the cell roi ids
        running_speed : allensdk.brain_observatory.running_speed.RunningSpeed (LazyProperty)
            NamedTuple with two fields
                timestamps : numpy.ndarray
                    Timestamps of running speed data samples
                values : np.ndarray
                    Running speed of the experimental subject (in cm / s).
        running_data_df : pandas.DataFrame (LazyProperty)
            Dataframe containing various signals used to compute running speed
        stimulus_presentations : pandas.DataFrame (LazyProperty)
            Table whose rows are stimulus presentations (i.e. a given image, for a given duration, typically 250 ms) and whose columns are presentation characteristics.
        stimulus_templates : dict (LazyProperty)
            A dictionary containing the stimulus images presented during the session keys are data set names, and values are 3D numpy arrays.
        licks : pandas.DataFrame (LazyProperty)
            A dataframe containing lick timestamps
        rewards : pandas.DataFrame (LazyProperty)
            A dataframe containing timestamps of delivered rewards
        task_parameters : dict (LazyProperty)
            A dictionary containing parameters used to define the task runtime behavior
        trials : pandas.DataFrame (LazyProperty)
            A dataframe containing behavioral trial start/stop times, and trial data
        corrected_fluorescence_traces : pandas.DataFrame (LazyProperty)
            The motion-corrected fluorescence traces organized into a dataframe; index is the cell roi ids
        average_projection : allensdk.brain_observatory.behavior.image_api.Image (LazyProperty)
            2D image of the microscope field of view, averaged across the experiment
        motion_correction : pandas.DataFrame LazyProperty
            A dataframe containing trace data used during motion correction computation
    """
    @classmethod
    def from_lims(cls, ophys_experiment_id):
        return cls(api=BehaviorOphysLimsApi(ophys_experiment_id))

    @classmethod
    def from_nwb_path(cls, nwb_path, **api_kwargs):
        api_kwargs["filter_invalid_rois"] = api_kwargs.get(
            "filter_invalid_rois", True)
        return cls(
            api=BehaviorOphysNwbApi.from_path(path=nwb_path, **api_kwargs))

    def __init__(self, api=None):

        self.api = api

        self.ophys_experiment_id = LazyProperty(
            self.api.get_ophys_experiment_id)
        self.max_projection = LazyProperty(self.get_max_projection)
        self.stimulus_timestamps = LazyProperty(
            self.api.get_stimulus_timestamps)
        self.ophys_timestamps = LazyProperty(self.api.get_ophys_timestamps)
        self.metadata = LazyProperty(self.api.get_metadata)
        self.dff_traces = LazyProperty(self.api.get_dff_traces)
        self.cell_specimen_table = LazyProperty(
            self.api.get_cell_specimen_table)
        self.running_speed = LazyProperty(self.api.get_running_speed)
        self.running_data_df = LazyProperty(self.api.get_running_data_df)
        self.stimulus_presentations = LazyProperty(
            self.api.get_stimulus_presentations)
        self.stimulus_templates = LazyProperty(self.api.get_stimulus_templates)
        self.licks = LazyProperty(self.api.get_licks)
        self.rewards = LazyProperty(self.api.get_rewards)
        self.task_parameters = LazyProperty(self.api.get_task_parameters)
        self.trials = LazyProperty(self.api.get_trials)
        self.corrected_fluorescence_traces = LazyProperty(
            self.api.get_corrected_fluorescence_traces)
        self.average_projection = LazyProperty(self.get_average_projection)
        self.motion_correction = LazyProperty(self.api.get_motion_correction)
        self.segmentation_mask_image = LazyProperty(
            self.get_segmentation_mask_image)

    def get_roi_masks(self, cell_specimen_ids=None):
        """ Obtains boolean masks indicating the location of one or more cell's ROIs in this session.

        Parameters
        ----------
        cell_specimen_ids : array-like of int, optional
            ROI masks for these cell specimens will be returned. The default behavior is to return masks for all 
            cell specimens.
        
        Returns
        -------
        result : xr.DataArray
            dimensions are:
                - cell_specimen_id : which cell's roi is described by this mask?
                - row : index within the underlying image
                - column : index within the image
            values are 1 where an ROI was present, otherwise 0.

        """

        if cell_specimen_ids is None:
            cell_specimen_ids = self.cell_specimen_table.index.values
        elif isinstance(cell_specimen_ids, int) or np.issubdtype(
                type(cell_specimen_ids), np.integer):
            cell_specimen_ids = np.array([int(cell_specimen_ids)])
        else:
            cell_specimen_ids = np.array(cell_specimen_ids)

        cell_roi_ids = self.cell_specimen_table.loc[cell_specimen_ids,
                                                    "cell_roi_id"].values
        result = self._get_roi_masks_by_cell_roi_id(cell_roi_ids)
        if "cell_roi_id" in result.dims:
            result = result.rename({"cell_roi_id": "cell_specimen_id"})
            result.coords["cell_specimen_id"] = cell_specimen_ids

        return result

    def _get_roi_masks_by_cell_roi_id(self, cell_roi_ids=None):
        """ Obtains boolean masks indicating the location of one or more ROIs in this session.

        Parameters
        ----------
        cell_roi_ids : array-like of int, optional
            ROI masks for these rois will be returned. The default behavior is to return masks for all rois.
        
        Returns
        -------
        result : xr.DataArray
            dimensions are:
                - roi_id : which roi is described by this mask?
                - row : index within the underlying image
                - column : index within the image
            values are 1 where an ROI was present, otherwise 0.

        Notes
        -----
        This method helps Allen Institute scientists to look at sessions that have not yet had cell specimen ids assigned.
        You probably want to use get_roi_masks instead.


        """
        if cell_roi_ids is None:
            cell_roi_ids = self.cell_specimen_table["cell_roi_id"].unique()
        elif isinstance(cell_roi_ids, int) or np.issubdtype(
                type(cell_roi_ids), np.integer):
            cell_roi_ids = np.array([int(cell_roi_ids)])
        else:
            cell_roi_ids = np.array(cell_roi_ids)

        table = self.cell_specimen_table.copy()
        table.set_index("cell_roi_id", inplace=True)
        table = table.loc[cell_roi_ids, :]

        full_image_shape = table.iloc[0]["image_mask"].shape

        output = np.zeros(
            (len(cell_roi_ids), full_image_shape[0], full_image_shape[1]),
            dtype=np.uint8)
        for ii, (_, row) in enumerate(table.iterrows()):
            output[ii, :, :] = _translate_roi_mask(row["image_mask"],
                                                   int(row["y"]),
                                                   int(row["x"]))

        segmentation_mask_image = self.api.get_segmentation_mask_image()
        spacing = segmentation_mask_image.GetSpacing()
        unit = segmentation_mask_image.GetMetaData('unit')

        return xr.DataArray(data=output,
                            dims=("cell_roi_id", "row", "column"),
                            coords={
                                "cell_roi_id":
                                cell_roi_ids,
                                "row":
                                np.arange(full_image_shape[0]) * spacing[0],
                                "column":
                                np.arange(full_image_shape[1]) * spacing[1]
                            },
                            attrs={
                                "spacing": spacing,
                                "unit": unit
                            }).squeeze(drop=True)

    @legacy('Consider using "dff_traces" instead.')
    def get_dff_traces(self, cell_specimen_ids=None):

        if cell_specimen_ids is None:
            cell_specimen_ids = self.get_cell_specimen_ids()

        csid_table = self.cell_specimen_table.reset_index()[[
            'cell_specimen_id'
        ]]
        csid_subtable = csid_table[csid_table['cell_specimen_id'].isin(
            cell_specimen_ids)].set_index('cell_specimen_id')
        dff_table = csid_subtable.join(self.dff_traces, how='left')
        dff_traces = np.vstack(dff_table['dff'].values)
        timestamps = self.ophys_timestamps

        assert (len(cell_specimen_ids), len(timestamps)) == dff_traces.shape
        return timestamps, dff_traces

    @legacy()
    def get_cell_specimen_indices(self, cell_specimen_ids):
        return [
            self.cell_specimen_table.index.get_loc(csid)
            for csid in cell_specimen_ids
        ]

    @legacy(
        'Consider using "cell_specimen_table[\'cell_specimen_id\']" instead.')
    def get_cell_specimen_ids(self):
        cell_specimen_ids = self.cell_specimen_table.index.values

        if np.isnan(cell_specimen_ids.astype(float)).sum() == len(
                self.cell_specimen_table):
            raise ValueError(
                f'cell_specimen_id values not assigned for {self.ophys_experiment_id}'
            )
        return cell_specimen_ids

    def deserialize_image(self, sitk_image):
        '''
        Convert SimpleITK image returned by the api to an Image class:

        Args:
            sitk_image (SimpleITK image): image object returned by the api

        Returns
            img (allensdk.brain_observatory.behavior.image_api.Image)
        '''
        img = ImageApi.deserialize(sitk_image)
        return img

    def get_max_projection(self):
        """ Returns an image whose values are the maximum obtained values at each pixel of the ophys movie over time.

        Returns
        ----------
        allensdk.brain_observatory.behavior.image_api.Image:
            array-like interface to max projection image data and metadata
        """
        return self.deserialize_image(self.api.get_max_projection())

    def get_average_projection(self):
        """ Returns an image whose values are the average obtained values at each pixel of the ophys movie over time.

        Returns
        ----------
        allensdk.brain_observatory.behavior.image_api.Image:
            array-like interface to max projection image data and metadata
        """
        return self.deserialize_image(self.api.get_average_projection())

    def get_segmentation_mask_image(self):
        """ Returns an image with value 1 if the pixel was included in an ROI, and 0 otherwise

        Returns
        ----------
        allensdk.brain_observatory.behavior.image_api.Image:
            array-like interface to segmentation_mask image data and metadata
        """
        masks = self._get_roi_masks_by_cell_roi_id()
        mask_image_data = masks.any(dim='cell_roi_id').astype(int)
        mask_image = Image(data=mask_image_data.values,
                           spacing=masks.attrs['spacing'],
                           unit=masks.attrs['unit'])
        return mask_image

    def get_reward_rate(self):
        response_latency_list = []
        for _, t in self.trials.iterrows():
            valid_response_licks = [
                l for l in t.lick_times if l -
                t.change_time > self.task_parameters['response_window_sec'][0]
            ]
            response_latency = float('inf') if len(
                valid_response_licks
            ) == 0 else valid_response_licks[0] - t.change_time
            response_latency_list.append(response_latency)
        reward_rate = calculate_reward_rate(
            response_latency=response_latency_list,
            starttime=self.trials.start_time.values)
        reward_rate[np.isinf(reward_rate)] = float('nan')
        return reward_rate

    def get_rolling_performance_df(self):

        # Indices to build trial metrics dataframe:
        trials_index = self.trials.index
        not_aborted_index = self.trials[np.logical_not(
            self.trials.aborted)].index

        # Initialize dataframe:
        performance_metrics_df = pd.DataFrame(index=trials_index)

        # Reward rate:
        performance_metrics_df['reward_rate'] = pd.Series(
            self.get_reward_rate(), index=self.trials.index)

        # Hit rate raw:
        hit_rate_raw = get_hit_rate(hit=self.trials.hit,
                                    miss=self.trials.miss,
                                    aborted=self.trials.aborted)
        performance_metrics_df['hit_rate_raw'] = pd.Series(
            hit_rate_raw, index=not_aborted_index)

        # Hit rate with trial count correction:
        hit_rate = get_trial_count_corrected_hit_rate(
            hit=self.trials.hit,
            miss=self.trials.miss,
            aborted=self.trials.aborted)
        performance_metrics_df['hit_rate'] = pd.Series(hit_rate,
                                                       index=not_aborted_index)

        # False-alarm rate raw:
        false_alarm_rate_raw = get_false_alarm_rate(
            false_alarm=self.trials.false_alarm,
            correct_reject=self.trials.correct_reject,
            aborted=self.trials.aborted)
        performance_metrics_df['false_alarm_rate_raw'] = pd.Series(
            false_alarm_rate_raw, index=not_aborted_index)

        # False-alarm rate with trial count correction:
        false_alarm_rate = get_trial_count_corrected_false_alarm_rate(
            false_alarm=self.trials.false_alarm,
            correct_reject=self.trials.correct_reject,
            aborted=self.trials.aborted)
        performance_metrics_df['false_alarm_rate'] = pd.Series(
            false_alarm_rate, index=not_aborted_index)

        # Rolling-dprime:
        rolling_dprime = get_rolling_dprime(hit_rate, false_alarm_rate)
        performance_metrics_df['rolling_dprime'] = pd.Series(
            rolling_dprime, index=not_aborted_index)

        return performance_metrics_df

    def get_performance_metrics(self, engaged_trial_reward_rate_threshold=2):
        performance_metrics = {}
        performance_metrics['trial_count'] = len(self.trials)
        performance_metrics['go_trial_count'] = self.trials.go.sum()
        performance_metrics['catch_trial_count'] = self.trials.catch.sum()
        performance_metrics['hit_trial_count'] = self.trials.hit.sum()
        performance_metrics['miss_trial_count'] = self.trials.miss.sum()
        performance_metrics[
            'false_alarm_trial_count'] = self.trials.false_alarm.sum()
        performance_metrics[
            'correct_reject_trial_count'] = self.trials.correct_reject.sum()
        performance_metrics[
            'auto_rewarded_trial_count'] = self.trials.auto_rewarded.sum()
        performance_metrics[
            'rewarded_trial_count'] = self.trials.reward_time.apply(
                lambda x: not np.isnan(x)).sum()
        performance_metrics['total_reward_count'] = len(self.rewards)
        performance_metrics['total_reward_volume'] = self.rewards.volume.sum()

        rolling_performance_df = self.get_rolling_performance_df()
        engaged_trial_mask = (rolling_performance_df['reward_rate'] >
                              engaged_trial_reward_rate_threshold)
        performance_metrics['maximum_reward_rate'] = np.nanmax(
            rolling_performance_df['reward_rate'].values)
        performance_metrics['engaged_trial_count'] = (engaged_trial_mask).sum()
        performance_metrics['mean_hit_rate'] = rolling_performance_df[
            'hit_rate'].mean()
        performance_metrics[
            'mean_hit_rate_uncorrected'] = rolling_performance_df[
                'hit_rate_raw'].mean()
        performance_metrics['mean_hit_rate_engaged'] = rolling_performance_df[
            'hit_rate'][engaged_trial_mask].mean()
        performance_metrics['mean_false_alarm_rate'] = rolling_performance_df[
            'false_alarm_rate'].mean()
        performance_metrics[
            'mean_false_alarm_rate_uncorrected'] = rolling_performance_df[
                'false_alarm_rate_raw'].mean()
        performance_metrics[
            'mean_false_alarm_rate_engaged'] = rolling_performance_df[
                'false_alarm_rate'][engaged_trial_mask].mean()
        performance_metrics['mean_dprime'] = rolling_performance_df[
            'rolling_dprime'].mean()
        performance_metrics['mean_dprime_engaged'] = rolling_performance_df[
            'rolling_dprime'][engaged_trial_mask].mean()
        performance_metrics['max_dprime'] = rolling_performance_df[
            'rolling_dprime'].max()
        performance_metrics['max_dprime_engaged'] = rolling_performance_df[
            'rolling_dprime'][engaged_trial_mask].max()

        return performance_metrics