Skip to content

Commit

Permalink
fix doc and logging mark_channels (#1293)
Browse files Browse the repository at this point in the history
* fix doc and logging mark_channels

* sane behavior, add changelog

* Update doc/whats_new.rst

Co-authored-by: Daniel McCloy <[email protected]>

* Update mne_bids/write.py

Co-authored-by: Daniel McCloy <[email protected]>

* Update mne_bids/write.py

Co-authored-by: Daniel McCloy <[email protected]>

* fix set logic

---------

Co-authored-by: Daniel McCloy <[email protected]>
  • Loading branch information
sappelhoff and drammock authored Aug 19, 2024
1 parent 648867f commit 283d859
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 11 deletions.
1 change: 1 addition & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Detailed list of changes

- :func:`mne_bids.read_raw_bids` no longer warns about unit changes in channels upon reading, as that information is taken from ``channels.tsv`` and judged authorative, by `Stefan Appelhoff`_ (:gh:`1282`)
- MEG OPM channels are now experimentally included, by `Amaia Benitez`_ (:gh:`1222`)
- :func:`mne_bids.mark_channels` will no longer create a ``status_description`` column filled with ``n/a`` in the ``channels.tsv`` file, by `Stefan Appelhoff`_ (:gh:`1293`)

🛠 Requirements
^^^^^^^^^^^^^^^
Expand Down
36 changes: 25 additions & 11 deletions mne_bids/write.py
Original file line number Diff line number Diff line change
Expand Up @@ -2460,18 +2460,24 @@ def mark_channels(bids_path, *, ch_names, status, descriptions=None, verbose=Non
type (e.g., only EEG or MEG data) is present in the dataset, it will be
selected automatically.
ch_names : str | list of str
The names of the channel(s) to mark with a ``status`` and possibly a
The names of the channel(s) to mark with a ``status`` and optionally a
``description``. Can be an empty list to indicate all channel names.
status : 'good' | 'bad' | list of str
The status of the channels ('good', or 'bad'). Default is 'bad'. If it
is a list, then must be a list of 'good', or 'bad' that has the same
length as ``ch_names``.
The status of the channels ('good', or 'bad'). If it is a list, then must be a
list of 'good', or 'bad' that has the same length as ``ch_names``.
descriptions : None | str | list of str
Descriptions of the reasons that lead to the exclusion of the
Descriptions of the reasons that lead to the marking ('good' or 'bad') of the
channel(s). If a list, it must match the length of ``ch_names``.
If ``None``, no descriptions are added.
%(verbose)s
Notes
-----
If the 'status' or 'status_description' columns were not present in the
corresponding tsv file before using this function, they may be created with default
values ('good' for status, 'n/a' for status_description) for all channels that are
not differently specified (by using ``ch_names``, ``status``, and ``descriptions``).
Examples
--------
Mark a single channel as bad.
Expand Down Expand Up @@ -2526,7 +2532,9 @@ def mark_channels(bids_path, *, ch_names, status, descriptions=None, verbose=Non
# set descriptions based on how it's passed in
if isinstance(descriptions, str):
descriptions = [descriptions] * len(ch_names)
write_descriptions = True
elif not descriptions:
write_descriptions = False
descriptions = [None] * len(ch_names)

# make sure statuses is a list of strings
Expand All @@ -2543,18 +2551,24 @@ def mark_channels(bids_path, *, ch_names, status, descriptions=None, verbose=Non
f"({len(ch_names)})."
)

if not all(status in ["good", "bad"] for status in status):
if not set(status).issubset({"good", "bad"}):
raise ValueError(
'Setting the status of a channel must only be "good", or "bad".'
)

# Read sidecar and create required columns if they do not exist.
if "status" not in tsv_data:
logger.info('No "status" column found in input file. Creating.')
logger.info(
'No "status" column found in channels file.'
'Creating it with default value "good".'
)
tsv_data["status"] = ["good"] * len(tsv_data["name"])

if "status_description" not in tsv_data:
logger.info('No "status_description" column found in input file. Creating.')
if "status_description" not in tsv_data and write_descriptions:
logger.info(
'No "status_description" column found in input file. '
'Creating it with default value "n/a".'
)
tsv_data["status_description"] = ["n/a"] * len(tsv_data["name"])

# Now actually mark the user-requested channels as bad.
Expand All @@ -2565,13 +2579,13 @@ def mark_channels(bids_path, *, ch_names, status, descriptions=None, verbose=Non
idx = tsv_data["name"].index(ch_name)
logger.info(
f"Processing channel {ch_name}:\n"
f" status: bad\n"
f" status: {status_}\n"
f" description: {description}"
)
tsv_data["status"][idx] = status_

# only write if the description was passed in
if description is not None:
if description:
tsv_data["status_description"][idx] = description

_write_tsv(channels_fname, tsv_data, overwrite=True)
Expand Down

0 comments on commit 283d859

Please sign in to comment.