Example #1
0
    def from_nwb(cls, nwbfile: NWBFile) -> "Presentations":
        # Note: using NwbApi class because ecephys uses this method
        # TODO figure out how behavior and ecephys can share this method
        nwbapi = NwbApi.from_nwbfile(nwbfile=nwbfile)
        df = nwbapi.get_stimulus_presentations()

        df['is_change'] = is_change_event(stimulus_presentations=df)
        df = cls._postprocess(presentations=df, fill_omitted_values=False)
        return Presentations(presentations=df)
def test_is_change_mult_omission():
    """Test case for multiple omission"""
    stimulus_presentations = pd.DataFrame({
        'image_name': ['A', 'B', 'C', 'D'],
        'omitted': [False, True, True, False]
    })

    obtained = is_change_event(stimulus_presentations=stimulus_presentations)
    expected = pd.Series([False, False, False, True], name='is_change')
    pd.testing.assert_series_equal(obtained, expected)
def test_is_change_event_no_change():
    """Test case for no change"""
    stimulus_presentations = pd.DataFrame({
        'image_name': ['A', 'A', 'A'],
        'omitted': [False, False, False]
    })

    obtained = is_change_event(stimulus_presentations=stimulus_presentations)
    expected = pd.Series([False, False, False], name='is_change')
    pd.testing.assert_series_equal(obtained, expected)
Example #4
0
    def get_stimulus_presentations(self) -> pd.DataFrame:
        """Get stimulus presentation data.

        NOTE: Uses timestamps that do not account for monitor delay.

        :returns: pd.DataFrame --
            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_timestamps = self.get_stimulus_timestamps()
        data = self._behavior_stimulus_file()
        raw_stim_pres_df = get_stimulus_presentations(data,
                                                      stimulus_timestamps)

        # Fill in nulls for image_name
        # This makes two assumptions:
        #   1. Nulls in `image_name` should be "gratings_<orientation>"
        #   2. Gratings are only present (or need to be fixed) when all
        #      values for `image_name` are null.
        if pd.isnull(raw_stim_pres_df["image_name"]).all():
            if ~pd.isnull(raw_stim_pres_df["orientation"]).all():
                raw_stim_pres_df["image_name"] = (
                    raw_stim_pres_df["orientation"].apply(
                        lambda x: f"gratings_{x}"))
            else:
                raise ValueError("All values for 'orentation' and 'image_name'"
                                 " are null.")

        stimulus_metadata_df = get_stimulus_metadata(data)

        idx_name = raw_stim_pres_df.index.name
        stimulus_index_df = (raw_stim_pres_df.reset_index().merge(
            stimulus_metadata_df.reset_index(),
            on=["image_name"]).set_index(idx_name))
        stimulus_index_df = (stimulus_index_df[[
            "image_set", "image_index", "start_time", "phase",
            "spatial_frequency"
        ]].rename(columns={
            "start_time": "timestamps"
        }).sort_index().set_index("timestamps", drop=True))
        stim_pres_df = raw_stim_pres_df.merge(stimulus_index_df,
                                              left_on="start_time",
                                              right_index=True,
                                              how="left")
        if len(raw_stim_pres_df) != len(stim_pres_df):
            raise ValueError("Length of `stim_pres_df` should not change after"
                             f" merge; was {len(raw_stim_pres_df)}, now "
                             f" {len(stim_pres_df)}.")

        stim_pres_df['is_change'] = is_change_event(
            stimulus_presentations=stim_pres_df)

        # Sort columns then drop columns which contain only all NaN values
        return stim_pres_df[sorted(stim_pres_df)].dropna(axis=1, how='all')
Example #5
0
    def from_stimulus_file(
            cls,
            stimulus_file: StimulusFile,
            stimulus_timestamps: StimulusTimestamps,
            limit_to_images: Optional[List] = None) -> "Presentations":
        """Get stimulus presentation data.

        :param stimulus_file
        :param limit_to_images
            Only return images given by these image names
        :param stimulus_timestamps


        :returns: pd.DataFrame --
            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_timestamps = stimulus_timestamps.value
        data = stimulus_file.data
        raw_stim_pres_df = get_stimulus_presentations(data,
                                                      stimulus_timestamps)

        # Fill in nulls for image_name
        # This makes two assumptions:
        #   1. Nulls in `image_name` should be "gratings_<orientation>"
        #   2. Gratings are only present (or need to be fixed) when all
        #      values for `image_name` are null.
        if pd.isnull(raw_stim_pres_df["image_name"]).all():
            if ~pd.isnull(raw_stim_pres_df["orientation"]).all():
                raw_stim_pres_df["image_name"] = (
                    raw_stim_pres_df["orientation"].apply(
                        lambda x: f"gratings_{x}"))
            else:
                raise ValueError("All values for 'orentation' and 'image_name'"
                                 " are null.")

        stimulus_metadata_df = get_stimulus_metadata(data)

        idx_name = raw_stim_pres_df.index.name
        stimulus_index_df = (raw_stim_pres_df.reset_index().merge(
            stimulus_metadata_df.reset_index(),
            on=["image_name"]).set_index(idx_name))
        stimulus_index_df = (stimulus_index_df[[
            "image_set", "image_index", "start_time", "phase",
            "spatial_frequency"
        ]].rename(columns={
            "start_time": "timestamps"
        }).sort_index().set_index("timestamps", drop=True))
        stim_pres_df = raw_stim_pres_df.merge(stimulus_index_df,
                                              left_on="start_time",
                                              right_index=True,
                                              how="left")
        if len(raw_stim_pres_df) != len(stim_pres_df):
            raise ValueError("Length of `stim_pres_df` should not change after"
                             f" merge; was {len(raw_stim_pres_df)}, now "
                             f" {len(stim_pres_df)}.")

        stim_pres_df['is_change'] = is_change_event(
            stimulus_presentations=stim_pres_df)

        # Sort columns then drop columns which contain only all NaN values
        stim_pres_df = \
            stim_pres_df[sorted(stim_pres_df)].dropna(axis=1, how='all')
        if limit_to_images is not None:
            stim_pres_df = \
                stim_pres_df[stim_pres_df['image_name'].isin(limit_to_images)]
            stim_pres_df.index = pd.Int64Index(range(stim_pres_df.shape[0]),
                                               name=stim_pres_df.index.name)
        stim_pres_df = cls._postprocess(presentations=stim_pres_df)
        return Presentations(presentations=stim_pres_df)
Example #6
0
 def get_stimulus_presentations(self) -> pd.DataFrame:
     df = super().get_stimulus_presentations()
     df['is_change'] = is_change_event(stimulus_presentations=df)
     return df