Repository: mne-tools/mne-bids

Issue:
Handling empty room data
It would be nice to be able to handle data that is specified to be as empty room data automatically.
To do this I propose adding an extra argument to `raw_to_bids`. Something along the lines of (this is the docstring for it):
```python
"""
    emptyroom : False | bool | string
        Whether or not the supplied file is for the empty room measurement
        If False we do nothing.
        If True the file is used as an empty room file and handled appropriately
        If a path is specified it is assumed that this file is the empty room file path
"""
```
If the value is True then it will process the file in such a way as to generate all the empty room data (also see code snippet in #48 ).
The data for the recording time can all be extracted from the raw files, so it can all be done automatically.

The path specified is to allow for the `AssociatedEmptyRoom` value in the sidecar .json to be set.
I couldn't think of a better way to specify this value than setting it as a string here. It's reusing the argument however for two (similar) but different objectives. This is also the only part of this that I don't think can really be done automatically (unless you add another argument like 'has_emptyroom' which essentially generates the empty room path. Not sure whether it would be better to get this to happen externally or not...)



Discussion:
sounds good, but I would say:

```py
emptyroom : bool | str
```

and specify in the docstring that it can be boolean or string
@jasmainak any thoughts on how this could be implemented considering the changes in #106 ?
It would still be good to be able to handle this. I have all the code to do it, the only question really is how to pass the info to `raw_to_bids` (or `write_raw_bids`).
One possible solution is renaming the participant in the raw object as `emptyroom`. This would be fine as there shouldn't be any participant data anyway, so it doesn't matter if the subject ID is `emptyroom`. The problem with this is that people using MNE-BIDS would need to do
`raw.info['subject_info']['his_id'] = 'emptyroom'` for their files to specify something is emptyroom data, as opposed to `write_raw_bids(~args, emptyroom=True)` which would be a fair bit more obvious...
*although*... since we are taking the file name as the argument, we could potentially just have something like `fname = make_bids_filename(suffix='emptyroom.fif')`, and pass this in, because none of the other values are used for empty room data, it is all determined from the file/uses preset values.
@sappelhoff is empty room data something that is relevant in the EEG data? Or is this a purely MEG problem?
It's actually much easier than that -- BIDS specification stipulates that the subject name for empty room data be `'emptyroom'`. So, we could simply do a check on the filename and don't need an extra param :)
oh yeah, that's true.
Well once #106 goes through I will write this as a PR
empty room data is not relevant for EEG :-) (or at least I am not aware of any practice or paper)
so with the new changes to the API, are we still fine with just having it so that the data is specified as empty room data if the `subject` is `emptyroom` and the `task` is `noise`? (as specified by the `bids_basename`?)
The one thing is that the `session` gets set as the date, which I think would be annoying to have to do manually. This can of course be done automatically, but the output name will be different to the `bids_basename`. I don't really see this as an issue, but just something to note...
I think because of how this works we'll also need to add an example to show how to specify something as empty room (as it may not be obvious otherwise).

The final point is that because we will be inferring if something is empty room data from the name, we now will have no way of specifying the path to the empty room data (to allow for the `AssociatedEmptyRoom` property in the sidecar to have a correct value).
I can't really think of a way to do this with the current API without doing some post-processing of the BIDS folder to find any other runs with the same date and modify the sidecar (not that I want to implement something like that right now...)
@jasmainak any thoughts on the above? I was thinking of trying to make a PR for this soon... I guess I can skip the last point about specifying the empty room file for another file.
+1 for modifying the MNE sample data example to show also how to add an `emptyroom` subject. I would prefer if we didn't hack / change the bids_basename that the user provided.
so you want the user to have to look at the date that the recording was and provide the name explicitly, either by passing in `subject='20181218'` to `make_bids_basename`, or by manually entering in that as part of the BIDS name?
I feel like if we are writing an example of how to do it you can just have something along the lines of:
```
To specify data is empty room data simply provide the `task` as `noise` and `subject` as `emptyroom`.
The session date is automatically determined from the date of the recording
```
Otherwise we would also need to add sanitization into the value entered for the session in case people enter an incorrect value.
Yes it’s more a documentation issue in my mind. The skeleton already
exists. And add one line of code to existing example to show how it’s done.

I’m a bit lost about the date. Does BIDS stipulate something for empty room
and date?

On Tue 18 Dec 2018 at 00:00, monkeyman192 <notifications@github.com> wrote:

> so you want the user to have to look at the date that the recording was
> and provide the name explicitly, either by passing in subject='20181218'
> to make_bids_basename, or by manually entering in that as part of the
> BIDS name?
> I feel like if we are writing an example of how to do it you can just have
> something along the lines of:
>
> To specify data is empty room data simply provide the `task` as `noise` and `subject` as `emptyroom`.
> The session date is automatically determined from the date of the recording
>
> Otherwise we would also need to add sanitization into the value entered
> for the session in case people enter an incorrect value.
>
> —
> You are receiving this because you were mentioned.
>
>
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-bids/issues/50#issuecomment-448096950>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/APHioj07qW9KSplpUf-6Aw8RMDEcQy0cks5u6HZQgaJpZM4VMCt1>
> .
>
-- 
Sent from my iPhone

> Does BIDS stipulate something for empty room and date?

I guess I can answer this with an example. The sidecar.json should look something like:

`sub-emptyroom_ses-20181026_task-noise_run-emptyroom_meg.json`
I see, I think I would start with 2 lines of python code to pull the date from `raw.info` and use it to make the filename. Not sure we need to modify mne-bids for that
A PR to show this in an example would be very welcome though !
ok, that's how I was doing it, I just wanted to see your thoughts on doing it that way since we had been expecting people to provide the entire name, but I didn't think that was appropriate for this situation.
Anyway, I'll make a PR tomorrow for it.

Resolving patch:
diff --git a/examples/convert_mne_sample.py b/examples/convert_mne_sample.py
--- a/examples/convert_mne_sample.py
+++ b/examples/convert_mne_sample.py
@@ -18,6 +18,8 @@
 
 import os.path as op
 
+from datetime import datetime
+
 import mne
 from mne.datasets import sample
 
@@ -36,13 +38,26 @@
 output_path = op.join(data_path, '..', 'MNE-sample-data-bids')
 
 ###############################################################################
-# Finally, we specify the raw_file and events_data
+# Specify the raw_file and events_data and run the BIDS conversion.
 
 raw = mne.io.read_raw_fif(raw_fname)
 bids_basename = 'sub-01_ses-01_task-audiovisual_run-01'
 write_raw_bids(raw, bids_basename, output_path, events_data=events_data,
                event_id=event_id, overwrite=True)
 
+###############################################################################
+# Specify some empty room data and run BIDS conversion on it.
+er_raw_fname = op.join(data_path, 'MEG', 'sample', 'ernoise_raw.fif')
+er_raw = mne.io.read_raw_fif(er_raw_fname)
+# For empty room data we need to specify that the subject ID is
+# 'emptyroom', and that the task is 'noise'.
+# We also need to specify the recording date in the format YYYYMMDD for the
+# session id.
+er_date = datetime.fromtimestamp(
+    er_raw.info['meas_date'][0]).strftime('%Y%m%d')
+er_bids_basename = 'sub-emptyroom_ses-{0}_task-noise'.format(er_date)
+write_raw_bids(er_raw, er_bids_basename, output_path, overwrite=True)
+
 ###############################################################################
 # Now let's see the structure of the BIDS folder we created.
 print_dir_tree(output_path)
diff --git a/mne_bids/mne_bids.py b/mne_bids/mne_bids.py
--- a/mne_bids/mne_bids.py
+++ b/mne_bids/mne_bids.py
@@ -641,6 +641,21 @@ def write_raw_bids(raw, bids_basename, output_path, events_data=None,
     kind = _handle_kind(raw)
 
     bids_fname = bids_basename + '_%s%s' % (kind, ext)
+
+    # check whether the info provided indicates that the data is emptyroom
+    # data
+    emptyroom = False
+    if subject_id == 'emptyroom' and task == 'noise':
+        emptyroom = True
+        # check the session date provided is consistent with the value in raw
+        meas_date = raw.info.get('meas_date', None)
+        if meas_date is not None:
+            er_date = datetime.fromtimestamp(
+                raw.info['meas_date'][0]).strftime('%Y%m%d')
+            if er_date != session_id:
+                raise ValueError("Date provided for session doesn't match "
+                                 "session date.")
+
     data_path = make_bids_folders(subject=subject_id, session=session_id,
                                   kind=kind, output_path=output_path,
                                   overwrite=False, verbose=verbose)
@@ -689,12 +704,12 @@ def write_raw_bids(raw, bids_basename, output_path, events_data=None,
                overwrite, verbose)
 
     # TODO: Implement coordystem.json and electrodes.tsv for EEG and  iEEG
-    if kind == 'meg':
+    if kind == 'meg' and not emptyroom:
         _coordsystem_json(raw, unit, orient, manufacturer, coordsystem_fname,
                           overwrite, verbose)
 
     events = _read_events(events_data, raw)
-    if len(events) > 0:
+    if len(events) > 0 and not emptyroom:
         _events_tsv(events, raw, events_fname, event_id, overwrite, verbose)
 
     make_dataset_description(output_path, name=" ", verbose=verbose)


Test patch:
diff --git a/mne_bids/tests/test_mne_bids.py b/mne_bids/tests/test_mne_bids.py
--- a/mne_bids/tests/test_mne_bids.py
+++ b/mne_bids/tests/test_mne_bids.py
@@ -16,6 +16,8 @@
 import pytest
 from glob import glob
 
+from datetime import datetime
+
 import pandas as pd
 
 import mne
@@ -63,6 +65,19 @@ def test_fif():
     write_raw_bids(raw, bids_basename, output_path, events_data=events_fname,
                    event_id=event_id, overwrite=False)
 
+    # write the same data but pretend it is empty room data:
+    er_date = datetime.fromtimestamp(
+        raw.info['meas_date'][0]).strftime('%Y%m%d')
+    er_bids_basename = 'sub-emptyroom_ses-{0}_task-noise'.format(str(er_date))
+    write_raw_bids(raw, er_bids_basename, output_path, overwrite=False)
+    assert op.exists(op.join(
+        output_path, 'sub-emptyroom', 'ses-{0}'.format(er_date), 'meg',
+        'sub-emptyroom_ses-{0}_task-noise_meg.json'.format(er_date)))
+    # test that an incorrect date raises an error.
+    er_bids_basename_bad = 'sub-emptyroom_ses-19000101_task-noise'
+    with pytest.raises(ValueError, match='Date provided'):
+        write_raw_bids(raw, er_bids_basename_bad, output_path, overwrite=False)
+
     # give the raw object some fake participant data
     raw = mne.io.read_raw_fif(raw_fname)
     raw.anonymize()