Exemplo n.º 1
0
def test_bdf(_bids_validate):
    """Test write_raw_bids conversion for Biosemi data."""
    output_path = _TempDir()
    data_path = op.join(base_path, 'edf', 'tests', 'data')
    raw_fname = op.join(data_path, 'test.bdf')

    raw = mne.io.read_raw_bdf(raw_fname)
    with pytest.warns(UserWarning, match='No line frequency found'):
        write_raw_bids(raw, bids_basename, output_path, overwrite=False)
    _bids_validate(output_path)

    # Test also the reading of channel types from channels.tsv
    # the first channel in the raw data is not MISC right now
    test_ch_idx = 0
    assert coil_type(raw.info, test_ch_idx) != 'misc'

    # we will change the channel type to MISC and overwrite the channels file
    bids_fname = bids_basename + '_eeg.bdf'
    channels_fname = _find_matching_sidecar(bids_fname, output_path,
                                            'channels.tsv')
    channels_dict = _from_tsv(channels_fname)
    channels_dict['type'][test_ch_idx] = 'MISC'
    _to_tsv(channels_dict, channels_fname)

    # Now read the raw data back from BIDS, with the tampered TSV, to show
    # that the channels.tsv truly influences how read_raw_bids sets ch_types
    # in the raw data object
    raw = read_raw_bids(bids_fname, output_path)
    assert coil_type(raw.info, test_ch_idx) == 'misc'

    # Test cropped assertion error
    raw = mne.io.read_raw_bdf(raw_fname)
    raw.crop(0, raw.times[-2])
    with pytest.raises(AssertionError, match='cropped'):
        write_raw_bids(raw, bids_basename, output_path)
Exemplo n.º 2
0
def _write_tsv(fname, dictionary, overwrite=False, verbose=None):
    """Write an ordered dictionary to a .tsv file."""
    if op.exists(fname) and not overwrite:
        raise FileExistsError(f'"{fname}" already exists. '
                              'Please set overwrite to True.')
    _to_tsv(dictionary, fname)

    logger.info(f"Writing '{fname}'...")
Exemplo n.º 3
0
def _update_electrodes_tsv(electrodes_tsv_fpath, elec_labels_anat, atlas_depth):
    electrodes_tsv = _from_tsv(electrodes_tsv_fpath)

    if atlas_depth not in electrodes_tsv.keys():
        electrodes_tsv[atlas_depth] = ["n/a"] * len(elec_labels_anat)
    for i in range(len(elec_labels_anat)):
        ch_name = electrodes_tsv["name"][i]
        print(ch_name, elec_labels_anat[i])
        electrodes_tsv[atlas_depth][i] = elec_labels_anat[i]
    _to_tsv(electrodes_tsv, electrodes_tsv_fpath)

    return electrodes_tsv
Exemplo n.º 4
0
def test_handle_events_reading():
    """Test reading events from a BIDS events.tsv file."""
    # We can use any `raw` for this
    raw = mne.io.read_raw_fif(raw_fname)

    # Create an arbitrary events.tsv file, to test we can deal with 'n/a'
    events = {'onset': [11, 12, 13], 'duration': ['n/a', 'n/a', 'n/a']}
    tmp_dir = _TempDir()
    events_fname = op.join(tmp_dir, 'sub-01_task-test_events.json')
    _to_tsv(events, events_fname)

    raw = _handle_events_reading(events_fname, raw)
    events, event_id = mne.events_from_annotations(raw)
Exemplo n.º 5
0
def test_handle_events_reading(tmpdir):
    """Test reading events from a BIDS events.tsv file."""
    # We can use any `raw` for this
    raw = _read_raw_fif(raw_fname)

    # Create an arbitrary events.tsv file, to test we can deal with 'n/a'
    # make sure we can deal w/ "#" characters
    events = {
        'onset': [11, 12, 'n/a'],
        'duration': ['n/a', 'n/a', 'n/a'],
        'trial_type': ["rec start", "trial #1", "trial #2!"]
    }
    events_fname = tmpdir.mkdir('bids1') / 'sub-01_task-test_events.json'
    _to_tsv(events, events_fname)

    raw = _handle_events_reading(events_fname, raw)
    events, event_id = mne.events_from_annotations(raw)

    # Test with a `stim_type` column instead of `trial_type`.
    events = {
        'onset': [11, 12, 'n/a'],
        'duration': ['n/a', 'n/a', 'n/a'],
        'stim_type': ["rec start", "trial #1", "trial #2!"]
    }
    events_fname = tmpdir.mkdir('bids2') / 'sub-01_task-test_events.json'
    _to_tsv(events, events_fname)

    with pytest.warns(RuntimeWarning, match='This column should be renamed'):
        raw = _handle_events_reading(events_fname, raw)
    events, event_id = mne.events_from_annotations(raw)

    # Test with same `trial_type` referring to different `value`
    events = {
        'onset': [11, 12, 13],
        'duration': ['n/a', 'n/a', 'n/a'],
        'trial_type': ["event1", "event1", "event2"],
        'value': [1, 2, 3]
    }
    events_fname = tmpdir.mkdir('bids3') / 'sub-01_task-test_events.json'
    _to_tsv(events, events_fname)

    raw = _handle_events_reading(events_fname, raw)
    events, event_id = mne.events_from_annotations(raw)

    assert len(events) == 3
    assert 'event1/1' in event_id
    assert 'event1/2' in event_id
    # The event with unique value mapping should not be renamed
    assert 'event2' in event_id

    # Test without any kind of event description.
    events = {'onset': [11, 12, 'n/a'], 'duration': ['n/a', 'n/a', 'n/a']}
    events_fname = tmpdir.mkdir('bids4') / 'sub-01_task-test_events.json'
    _to_tsv(events, events_fname)

    raw = _handle_events_reading(events_fname, raw)
    events, event_id = mne.events_from_annotations(raw)
    ids = list(event_id.keys())
    assert len(ids) == 1
    assert ids == ['n/a']
Exemplo n.º 6
0
def test_tsv_handler(tmp_path):
    """Test the TSV handling."""
    # create some dummy data
    d = odict(a=[1, 2, 3, 4], b=['five', 'six', 'seven', 'eight'])
    assert _contains_row(d, {'a': 1, 'b': 'five'})
    d2 = odict(a=[5], b=['nine'])
    d = _combine_rows(d, d2)
    assert 5 in d['a']
    d2 = odict(a=[5])
    d = _combine_rows(d, d2)
    assert 'n/a' in d['b']
    d2 = odict(a=[5], b=['ten'])
    d = _combine_rows(d, d2, drop_column='a')
    # make sure that the repeated data was dropped
    assert 'nine' not in d['b']
    print(_tsv_to_str(d))

    d_path = tmp_path / 'output.tsv'

    # write the data to an output tsv file
    _to_tsv(d, d_path)
    # now read it back
    d = _from_tsv(d_path)
    # test reading the file in with the incorrect number of datatypes raises
    # an Error
    with pytest.raises(ValueError):
        d = _from_tsv(d_path, dtypes=[str])
    # we can also pass just a single data type and it will be applied to all
    # columns
    d = _from_tsv(d_path, str)

    # remove any rows with 2 or 5 in them
    d = _drop(d, [2, 5], 'a')
    assert 2 not in d['a']

    # test combining data with differing numbers of columns
    d = odict(a=[1, 2], b=['three', 'four'])
    d2 = odict(a=[4], b=['five'], c=[3.1415])
    # raise error if a new column is tried to be added
    with pytest.raises(KeyError):
        d = _combine_rows(d, d2)
    d2 = odict(a=[5])
    d = _combine_rows(d, d2)
    assert d['b'] == ['three', 'four', 'n/a']
    assert _contains_row(d, {'a': 5})

    # test reading a single column
    _to_tsv(odict(a=[1, 2, 3, 4]), d_path)
    d = _from_tsv(d_path)
    assert d['a'] == ['1', '2', '3', '4']
Exemplo n.º 7
0
def test_read_participants_data(tmpdir):
    """Test reading information from a BIDS sidecar.json file."""
    bids_path = _bids_path.copy().update(root=tmpdir, datatype='meg')
    raw = _read_raw_fif(raw_fname, verbose=False)

    # if subject info was set, we don't roundtrip birthday
    # due to possible anonymization in mne-bids
    subject_info = {
        'hand': 1,
        'sex': 2,
    }
    raw.info['subject_info'] = subject_info
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)
    raw = read_raw_bids(bids_path=bids_path)
    print(raw.info['subject_info'])
    assert raw.info['subject_info']['hand'] == 1
    assert raw.info['subject_info']['sex'] == 2
    assert raw.info['subject_info'].get('birthday', None) is None
    assert raw.info['subject_info']['his_id'] == f'sub-{bids_path.subject}'
    assert 'participant_id' not in raw.info['subject_info']

    # if modifying participants tsv, then read_raw_bids reflects that
    participants_tsv_fpath = tmpdir / 'participants.tsv'
    participants_tsv = _from_tsv(participants_tsv_fpath)
    participants_tsv['hand'][0] = 'n/a'
    _to_tsv(participants_tsv, participants_tsv_fpath)
    raw = read_raw_bids(bids_path=bids_path)
    assert raw.info['subject_info']['hand'] == 0
    assert raw.info['subject_info']['sex'] == 2
    assert raw.info['subject_info'].get('birthday', None) is None

    # make sure things are read even if the entries don't make sense
    participants_tsv = _from_tsv(participants_tsv_fpath)
    participants_tsv['hand'][0] = 'righty'
    participants_tsv['sex'][0] = 'malesy'
    _to_tsv(participants_tsv, participants_tsv_fpath)
    with pytest.warns(RuntimeWarning, match='Unable to map'):
        raw = read_raw_bids(bids_path=bids_path)
        assert raw.info['subject_info']['hand'] is None
        assert raw.info['subject_info']['sex'] is None

    # make sure to read in if no participants file
    raw = _read_raw_fif(raw_fname, verbose=False)
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)
    os.remove(participants_tsv_fpath)
    with pytest.warns(RuntimeWarning, match='Participants file not found'):
        raw = read_raw_bids(bids_path=bids_path)
        assert raw.info['subject_info'] is None
Exemplo n.º 8
0
def test_handle_coords_reading():
    """Test reading coordinates from BIDS files."""
    bids_root = _TempDir()

    data_path = op.join(testing.data_path(), 'EDF')
    raw_fname = op.join(data_path, 'test_reduced.edf')

    raw = mne.io.read_raw_edf(raw_fname)

    # ensure we are writing 'ecog'/'ieeg' data
    raw.set_channel_types({ch: 'ecog' for ch in raw.ch_names})

    # set a `random` montage
    ch_names = raw.ch_names
    elec_locs = np.random.random((len(ch_names), 3)).astype(float)
    ch_pos = dict(zip(ch_names, elec_locs))
    montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame="mri")
    raw.set_montage(montage)
    write_raw_bids(raw, bids_basename, bids_root, overwrite=True)

    # read in the data and assert montage is the same
    bids_fname = bids_basename + "_ieeg.edf"
    raw_test = read_raw_bids(bids_fname, bids_root)

    # obtain the sensor positions
    orig_locs = raw.info['dig'][1]
    test_locs = raw_test.info['dig'][1]
    assert orig_locs == test_locs
    assert not object_diff(raw.info['chs'], raw_test.info['chs'])

    # test error message if electrodes don't match
    electrodes_fname = _find_matching_sidecar(bids_fname,
                                              bids_root,
                                              "electrodes.tsv",
                                              allow_fail=True)
    electrodes_dict = _from_tsv(electrodes_fname)
    # pop off 5 channels
    for key in electrodes_dict.keys():
        for i in range(5):
            electrodes_dict[key].pop()
    _to_tsv(electrodes_dict, electrodes_fname)
    with pytest.raises(RuntimeError, match='Channels do not correspond'):
        raw_test = read_raw_bids(bids_fname, bids_root)
Exemplo n.º 9
0
def test_handle_scans_reading():
    """Test reading data from a BIDS scans.tsv file."""
    bids_root = _TempDir()
    raw = _read_raw_fif(raw_fname)
    suffix = "meg"

    # write copy of raw with line freq of 60
    # bids basename and fname
    bids_path = BIDSPath(subject='01',
                         session='01',
                         task='audiovisual',
                         run='01',
                         datatype=suffix,
                         root=bids_root)
    bids_path = write_raw_bids(raw, bids_path, overwrite=True)
    raw_01 = read_raw_bids(bids_path)

    # find sidecar scans.tsv file and alter the
    # acquisition time to not have the optional microseconds
    scans_path = BIDSPath(subject=bids_path.subject,
                          session=bids_path.session,
                          root=bids_root,
                          suffix='scans',
                          extension='.tsv')
    scans_tsv = _from_tsv(scans_path)
    acq_time_str = scans_tsv['acq_time'][0]
    acq_time = datetime.strptime(acq_time_str, '%Y-%m-%dT%H:%M:%S.%fZ')
    acq_time = acq_time.replace(tzinfo=timezone.utc)
    new_acq_time = acq_time_str.split('.')[0]
    assert acq_time == raw_01.info['meas_date']
    scans_tsv['acq_time'][0] = new_acq_time
    _to_tsv(scans_tsv, scans_path)

    # now re-load the data and it should be different
    # from the original date and the same as the newly altered date
    raw_02 = read_raw_bids(bids_path)
    new_acq_time += '.0Z'
    new_acq_time = datetime.strptime(new_acq_time, '%Y-%m-%dT%H:%M:%S.%fZ')
    new_acq_time = new_acq_time.replace(tzinfo=timezone.utc)
    assert raw_02.info['meas_date'] == new_acq_time
    assert new_acq_time != raw_01.info['meas_date']
Exemplo n.º 10
0
    with open(output_electrodes_tsv_fpath, "w", encoding="utf-8") as f:
        f.write("name\tx\ty\tz\n")
        for i, name in enumerate(electrodes_tsv.keys()):
            f.write("%s\t%.6f\t%.6f\t%.6f\n" % (
                name,
                electrodes_tsv[name][0],
                electrodes_tsv[name][1],
                electrodes_tsv[name][2],
            ))

    # Output labeled .mat files with atlas, white matter, and brainmask information
    electrodes_tsv, electrodes_json = apply_atlas(bids_root,
                                                  output_electrodes_tsv_fpath,
                                                  inv_affine, fs_patient_dir,
                                                  fs_lut_fpath)

    # save sidecar electrodes tsv
    _to_tsv(electrodes_tsv, output_electrodes_tsv_fpath)

    # save sidecar electrodes json
    with open(str(output_electrodes_tsv_fpath).replace(".tsv", ".json"),
              "w") as fout:
        json.dump(electrodes_json, fout, indent=4)

    # create a coordsystem JSON file
    output_coordsyste_fpath = str(output_electrodes_tsv_fpath).replace(
        "electrodes.tsv", "coordsystem.json")
    _write_coordsystem_json(fname=output_coordsyste_fpath,
                            unit="mm",
                            img_fname=mri_img_fpath)
Exemplo n.º 11
0
    assert labelsxyz.shape[0] == len(labels)

    # run coordinate transformation
    modified_coords = np.array(
        [
            transform(
                coords,
                ct_nifti_img,
                mri_nifti_img,
                mapping_transformation_file,
                coordinate_type=coordinate_type,
            )
            for coords in labelsxyz
        ]
    )

    # resave the coordinate transformed data
    for i, (x, y, z) in enumerate(modified_coords):
        electrodes_tsv["x"][i] = x
        electrodes_tsv["y"][i] = y
        electrodes_tsv["z"][i] = z
    _to_tsv(electrodes_tsv, mri_coords_fpath)

    # resave the coordinate system file
    mri_coordsystem_fpath = mri_coords_fpath.replace(
        "electrodes.tsv", "coordsystem.json"
    )
    unit = "mm"
    img_fname = mri_nifti_img
    _write_coordsystem_json(mri_coordsystem_fpath, unit, img_fname=img_fname)
Exemplo n.º 12
0
def test_bads_reading():
    bids_root = _TempDir()
    bids_path = _bids_path.copy().update(root=bids_root)
    data_path = BIDSPath(subject=subject_id,
                         session=session_id,
                         datatype='meg',
                         root=bids_root).mkdir().directory
    ch_path = (bids_path.copy().update(suffix='channels', extension='.tsv'))
    channels_fname = op.join(data_path, ch_path.basename)

    raw_bids_fname = (bids_path.copy().update(root=bids_root,
                                              datatype='meg',
                                              suffix='meg',
                                              extension='.fif'))
    raw = _read_raw_fif(raw_fname, verbose=False)

    ###########################################################################
    # bads in FIF only, no `status` column in channels.tsv
    bads = ['EEG 053', 'MEG 2443']
    raw.info['bads'] = bads
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)

    # Delete `status` column
    tsv_data = _from_tsv(channels_fname)
    del tsv_data['status'], tsv_data['status_description']
    _to_tsv(tsv_data, fname=channels_fname)

    raw = read_raw_bids(bids_path=bids_path, verbose=False)
    assert raw.info['bads'] == bads

    ###########################################################################
    # bads in `status` column in channels.tsv, no bads in raw.info['bads']
    bads = ['EEG 053', 'MEG 2443']
    raw.info['bads'] = bads
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)

    # Remove info['bads'] from the raw file.
    raw = _read_raw_fif(raw_bids_fname, preload=True, verbose=False)
    raw.info['bads'] = []
    raw.save(raw_bids_fname, overwrite=True, verbose=False)

    raw = read_raw_bids(bids_path=bids_path, verbose=False)
    assert type(raw.info['bads']) is list
    assert set(raw.info['bads']) == set(bads)

    ###########################################################################
    # Different bads in `status` column and raw.info['bads']
    bads_bids = ['EEG 053', 'MEG 2443']
    bads_raw = ['MEG 0112', 'MEG 0131']

    raw.info['bads'] = bads_bids
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)

    # Replace info['bads'] in the raw file.
    raw = _read_raw_fif(raw_bids_fname, preload=True, verbose=False)
    raw.info['bads'] = bads_raw
    raw.save(raw_bids_fname, overwrite=True, verbose=False)

    with pytest.warns(RuntimeWarning, match='conflicting information'):
        raw = read_raw_bids(bids_path=bids_path, verbose=False)
    assert type(raw.info['bads']) is list
    assert set(raw.info['bads']) == set(bads_bids)
Exemplo n.º 13
0
def append_original_fname_to_scans(
    orig_fname: str,
    bids_root: Union[str, Path],
    bids_fname: str,
    overwrite: bool = True,
    verbose: bool = True,
):
    """Append the original filename to *scans.tsv in BIDS data structure.

    This will also create a sidecar *scans.json file alongside to document
    a description of the added column in the scans.tsv file.

    Parameters
    ----------
    orig_fname : str
        The original base filename that will be added into the
        'original_filename' columnn.
    bids_root : str | Path
        The root to the BIDS dataset.
    bids_fname : str | BIDSPath
        The BIDS filename of the BIDSified dataset. This should
        correspond to a specific 'filename' in the *scans.tsv file.
    overwrite : bool
        Whether or not to overwrite the row.
    verbose : bool
    """
    # create a BIDS path object noting that you only need
    # subject and session to define the *scans.tsv file
    entities = get_entities_from_fname(bids_fname)
    bids_path = BIDSPath(entities["subject"], entities["session"], root=bids_root)
    scans_fpath = bids_path.copy().update(suffix="scans", extension=".tsv")

    # make sure the path actually exists
    if not scans_fpath.fpath.exists():
        raise OSError(
            f"Scans.tsv file {scans_fpath} does not "
            f"exist. Please check the path to ensure it is "
            f"valid."
        )
    scans_tsv = _from_tsv(scans_fpath)

    # new filenames
    filenames = scans_tsv["filename"]
    ind = [i for i, fname in enumerate(filenames) if str(bids_fname) in fname]

    if len(ind) > 1:  # pragma: no cover
        msg = (
            "This should not happen. All scans should "
            "be uniquely identifiable from scans.tsv file. "
            "The current scans file has these filenames: "
            f"{filenames}."
        )
        logger.exception(msg)
        raise RuntimeError(msg)
    if len(ind) == 0:
        msg = (
            f"No filename, {bids_fname} found. "
            f"Scans.tsv has these files: {filenames}."
        )
        logger.exception(msg)
        raise RuntimeError(msg)

    # write scans.json
    scans_json_path = _replace_ext(scans_fpath, "json")
    scans_json = {
        "original_filename": "The original filename of the converted BIDs dataset. "
        "Provides possibly ictal/interictal, asleep/awake and "
        "clinical seizure grouping (i.e. SZ2PG, etc.)."
    }
    _write_json(scans_json_path, scans_json, overwrite=True, verbose=verbose)

    # write in original filename
    if "original_filename" not in scans_tsv.keys():
        scans_tsv["original_filename"] = ["n/a"] * len(filenames)
    if scans_tsv["original_filename"][ind[0]] == "n/a" or overwrite:
        scans_tsv["original_filename"][ind[0]] = orig_fname
    else:
        logger.warning(
            "Original filename has already been written here. "
            f"Skipping for {bids_fname}. It is written as "
            f"{scans_tsv['original_filename'][ind[0]]}."
        )
        return

    # write the scans out
    _to_tsv(scans_tsv, scans_fpath)