Ejemplo n.º 1
0
def test_montage_file_lookup(subject, localization, montage, rhino_root):
    finder = PathFinder(subject,
                        localization=localization,
                        montage=montage,
                        rootdir=rhino_root)
    path = finder.find('pairs')
    assert path is not None
Ejemplo n.º 2
0
def save_coords_for_blender(subject_id: str, localization: int, outdir: str,
                            localization_file: Optional[str] = None,
                            rootdir: Optional[str] = "/"):
    """
        Generates a CSV file with metadata about contacts in the subject's
        montage

    Parameters
    ----------
    subject_id: str
        ID of the subject
    localization: int
        Localization number of the subject to use
    outdir: str
        Directory to save the resulting file
    localization_file: str, default None
        Default is to look up the localization file to use, but if specified,
        uses the passed file location
    rootdir: str
        Mount point for RHINO

    Returns
    -------
    saved_results_path: str

    """
    use_matlab = False
    if localization_file is None:
        finder = PathFinder(subject=subject_id, localization=localization,
                            rootdir=rootdir)
        try:
            localization_file = finder.find('localization')
        except FileNotFoundError:
            use_matlab = True

    if use_matlab or not os.path.exists(localization_file):
        # get coordinates from matlab talstruct
        subject_localization = subject_id
        if localization != 0:
            subject_localization = "_".join([subject_id, str(localization)])
        final_df = extract_coordinates_from_talstructs(subject_localization,
                                                       rootdir=rootdir)
    else:
        # get coordinates from the new localization.json file
        final_df = extract_coordinates_from_localization_file(localization_file)

    # Scale coordinates
    for col in ['x', 'y', 'z']:
        final_df[col] = OBJ_SCALE_FACTOR * final_df[col]

    final_df = add_orientation_contact_for_depth_electrodes(final_df)
    final_df = final_df[["contact_name", "contact_type", "x", "y", "z",
                         "atlas", "orient_to"]]
    final_df.to_csv(outdir + "/electrode_coordinates.csv", index=False)

    return os.path.join(outdir, "electrode_coordinates.csv")
Ejemplo n.º 3
0
    def load(self, **kwargs):
        """Overrides the generic load method so as to accept keyword arguments
        to pass along to :meth:`as_timeseries`.

        """
        if "events" in kwargs:
            events = kwargs["events"]
        else:
            if self.session is None:
                raise ValueError(
                    "A session must be specified to load an entire session of "
                    "EEG data!")

            finder = PathFinder(subject=self.subject,
                                experiment=self.experiment,
                                session=self.session,
                                rootdir=self.rootdir)

            events_file = finder.find("task_events")
            all_events = EventReader.fromfile(events_file, self.subject,
                                              self.experiment, self.session)

            # Select only a single event with a valid eegfile just to get the
            # filename
            valid = all_events[(all_events["eegfile"].notnull())
                               & (all_events["eegfile"].str.len() > 0)]
            events = pd.DataFrame(valid.iloc[0]).T.reset_index(drop=True)

            # Set relative start and stop times if necessary. If they were
            # already specified, these will allow us to subset the session.
            if "rel_start" not in kwargs:
                kwargs["rel_start"] = 0
            if "rel_stop" not in kwargs:
                kwargs["rel_stop"] = -1

        if not len(events):
            raise ValueError("No events found! Hint: did filtering events "
                             "result in at least one?")
        elif len(events["subject"].unique()) > 1:
            raise ValueError("Loading multiple sessions of EEG data requires "
                             "using events from only a single subject.")

        if "rel_start" not in kwargs or "rel_stop" not in kwargs:
            raise exc.IncompatibleParametersError(
                "rel_start and rel_stop must be given with events")

        # info = EEGMetaReader.fromfile(path, subject=self.subject)
        # sample_rate = info["sample_rate"]
        # dtype = info["data_format"]

        self.scheme = kwargs.get("scheme", None)

        events = self._eegfile_absolute(events.copy())
        return self.as_timeseries(events, kwargs["rel_start"],
                                  kwargs["rel_stop"])
Ejemplo n.º 4
0
def test_session_params(rhino_root, use_basename):
    subject = 'TJ012'
    eeg_basename = 'TJ012_20Apr10_1329'
    if use_basename:
        finder = PathFinder(subject=subject,
                            eeg_basename=eeg_basename,
                            rootdir=rhino_root)
    else:
        finder = PathFinder(subject=subject, rootdir=rhino_root)
    path = finder.find('sources')
    assert (eeg_basename in path) == use_basename
Ejemplo n.º 5
0
def get_bad_channel_names(subject, montage, just_bad=None, rhino_root='/'):
    finder = PathFinder(rootdir=rhino_root, subject=subject, montage=montage)
    fn = finder.find('electrode_categories')
    with open(fn, 'r') as fh:
        lines = [mystr.replace('\n', '') for mystr in fh.readlines()]

    if just_bad is True:
        bidx = len(lines)
        try:
            bidx = [s.lower().replace(':', '').strip()
                    for s in lines].index('bad electrodes')
        except:
            try:
                bidx = [s.lower().replace(':', '').strip()
                        for s in lines].index('broken leads')
            except:
                lines = []
        lines = lines[bidx:]
    return lines
Ejemplo n.º 6
0
    def as_timeseries(self, events: pd.DataFrame,
                      rel_start: Union[float, int],
                      rel_stop: Union[float, int]) -> EEGContainer:
        """Read the timeseries.

        Parameters
        ----------
        events
            Events to read EEG data from
        rel_start
            Relative start times in ms
        rel_stop
            Relative stop times in ms

        Returns
        -------
        A time series with shape (channels, epochs, time). By default, this
        returns data as it was physically recorded (e.g., if recorded with a
        common reference, each channel will be a contact's reading referenced
        to the common reference, a.k.a. "monopolar channels").

        Raises
        ------
        RereferencingNotPossibleError
            When rereferencing is not possible.

        """
        eegs = []

        # sanity check on the offsets

        if rel_start != 0 and rel_stop != -1 and rel_start > rel_stop:
            raise ValueError('rel_start must precede rel_stop')

        for filename in events["eegfile"].unique():
            # select subset of events for this basename
            ev = events[events["eegfile"] == filename]

            # determine experiment, session, dtype, and sample rate
            experiment = ev["experiment"].unique()[0]
            session = ev["session"].unique()[0]
            basename = os.path.basename(filename)
            finder = PathFinder(subject=self.subject,
                                experiment=experiment,
                                session=session,
                                eeg_basename=basename,
                                rootdir=self.rootdir)
            sources = EEGMetaReader.fromfile(finder.find("sources"),
                                             subject=self.subject)
            sample_rate = sources["sample_rate"]
            dtype = sources["data_format"]
            is_scalp = dtype in (".bdf", ".raw", ".mff")

            # Convert events to epochs (onset & offset times)
            if rel_start == 0 and rel_stop == -1 and len(events) == 1:
                epochs = [(0, None)]
            else:
                epochs = convert.events_to_epochs(ev, rel_start, rel_stop,
                                                  sample_rate)

            # Scalp EEG reader requires onsets, rel_start (in sec), and rel_
            # stop (in sec) to cut data into epochs
            if is_scalp:
                on_off_epochs = epochs  # The onset & offset times will still
                # be passed to the EEGContainer later
                if rel_start == 0 and rel_stop == -1 and len(events) == 1:
                    epochs = None
                else:
                    epochs = np.zeros((len(ev), 3), dtype=int)
                    epochs[:, 0] = ev["eegoffset"]
                    epochs = dict(epochs=epochs, tmin=rel_start / 1000.,
                                  tmax=rel_stop / 1000.)

            root = get_root_dir(self.rootdir)
            eeg_filename = os.path.join(root, filename.lstrip("/"))
            reader_class = self._get_reader_class(filename)
            reader = reader_class(filename=eeg_filename,
                                  dtype=dtype,
                                  epochs=epochs,
                                  scheme=self.scheme,
                                  clean=self.clean)
            # if scalp EEG, info is an MNE Info object; if iEEG, info is a list
            # of contacts
            data, info = reader.read()

            attrs = {}
            if is_scalp:
                # Pass MNE info and events as extra attributes, to be able to
                # fully reconstruct MNE Raw/Epochs objects
                attrs["mne_info"] = info
                channels = info["ch_names"]
                if epochs is not None:
                    # Crop out any events/epoch times that ran beyond the
                    # bounds of the EEG recording
                    te_pre = info["truncated_events_pre"] \
                        if info["truncated_events_pre"] > 0 else None
                    te_post = -info["truncated_events_post"] \
                        if info["truncated_events_post"] > 0 else None
                    on_off_epochs = on_off_epochs[te_pre:te_post]
                    ev = ev[te_pre:te_post]
                # Pass the onset & offset time epoch list to EEGContainer, NOT
                # the MNE-formatted epoch list
                epochs = on_off_epochs
            elif self.scheme is not None:
                data, channels = reader.rereference(data, info)
            else:
                channels = ["CH{}".format(n + 1) for n in range(data.shape[1])]

            eegs.append(
                EEGContainer(
                    data,
                    sample_rate,
                    epochs=epochs,
                    events=ev,
                    channels=channels,
                    tstart=rel_start,
                    attrs=attrs
                )
            )

        eegs = EEGContainer.concatenate(eegs)
        eegs.attrs["rereferencing_possible"] = reader.rereferencing_possible
        return eegs
Ejemplo n.º 7
0
    def as_timeseries(self, events: pd.DataFrame, rel_start: Union[float, int],
                      rel_stop: Union[float, int]) -> EEGContainer:
        """Read the timeseries.

        Parameters
        ----------
        events
            Events to read EEG data from
        rel_start
            Relative start times in ms
        rel_stop
            Relative stop times in ms

        Returns
        -------
        A time series with shape (channels, epochs, time). By default, this
        returns data as it was physically recorded (e.g., if recorded with a
        common reference, each channel will be a contact's reading referenced to
        the common reference, a.k.a. "monopolar channels").

        Raises
        ------
        RereferencingNotPossibleError
            When rereferencing is not possible.

        """
        eegs = []

        for filename in events["eegfile"].unique():
            # select subset of events for this basename
            ev = events[events["eegfile"] == filename]

            # determine experiment, session, dtype, and sample rate
            experiment = ev["experiment"].unique()[0]
            session = ev["session"].unique()[0]
            finder = PathFinder(subject=self.subject,
                                experiment=experiment,
                                session=session,
                                rootdir=self.rootdir)
            sources = EEGMetaReader.fromfile(finder.find("sources"),
                                             subject=self.subject)
            sample_rate = sources["sample_rate"]
            dtype = sources["data_format"]

            # convert events to epochs
            if rel_stop < 0:
                # We're trying to load to the end of the session. Only allow
                # this in cases where we're trying to load a whole session.
                if len(events) > 1:
                    raise ValueError("rel_stop must not be negative")
                epochs = [(0, None)]
            else:
                epochs = convert.events_to_epochs(ev, rel_start, rel_stop,
                                                  sample_rate)

            root = get_root_dir(self.rootdir)
            eeg_filename = os.path.join(root, filename.lstrip("/"))
            reader_class = self._get_reader_class(filename)
            reader = reader_class(filename=eeg_filename,
                                  dtype=dtype,
                                  epochs=epochs,
                                  scheme=self.scheme)
            data, contacts = reader.read()

            if self.scheme is not None:
                data, labels = reader.rereference(data, contacts)
                channels = labels
            else:
                channels = ["CH{}".format(n + 1) for n in range(data.shape[1])]

            eegs.append(
                EEGContainer(
                    data,
                    sample_rate,
                    epochs=epochs,
                    events=ev,
                    channels=channels,
                    tstart=rel_start,
                ))

        eegs = EEGContainer.concatenate(eegs)
        eegs.attrs["rereferencing_possible"] = reader.rereferencing_possible
        return eegs