Repository: mne-tools/mne-python

Issue:
raw.add_channels() creates duplicates in raw.info
I am using mne v0.15 for my anaIysis and I want to add a new stimulus channel to an existing raw file based on a list of events. However, raw.add_channels() seems to create duplicates in raw.info.

I wrote the following function which is based on the discussion [here:](https://github.com/mne-tools/mne-python/issues/4208)

    def add_events_to_stim_channel(raw, events, ch_name='ET_events'):

        picks = mne.pick_channels(raw.info['ch_names'], include=[ch_name])

        # if stim channel does not exist yet add new channel
        if picks.shape[0] == 0:
            info = mne.create_info([ch_name], raw.info['sfreq'], ['stim'])
            stim_data = np.zeros((1, raw.n_times))

            for evt in events:
                if evt is None:
                    print "No events found!"
                    return
                stim_data[0][evt[0]] = evt[2]

            stim_raw = mne.io.RawArray(stim_data, info)
            print 'stim_raw', stim_raw.info['bads']
            # add_channels adds duplicates to raw.info['bads'] and raw.info['dig']
            raw.add_channels([stim_raw], force_update_info=True)

        # if stim channel already exists add events to stim_channel
        else:
            raw.add_events(events, stim_channel=ch_name)

After using this function, all marked bad channels are listed twice in raw.info['bads'] and also all entries in raw.info['dig'] are present twice. I am not sure if any other entries are affected.

Is it possible that, for example, duplicates in raw.info['dig'] may affect future analysis?


Discussion:
can you provide a full gist to replicate and reveal the issue?
Here is the link to the [gist](https://gist.github.com/ckiefer0/b00fb36936dd1ec441eb7d7a2f30c7e3). The code as well as the raw file can also be found [here](https://www.dropbox.com/sh/1hh26djlqo7txpi/AAAXzsIpYshbGEJAwbtdHVeTa?dl=0).
the simplest way to replicate is this:

```
import os.path as op
import mne
from mne.io.meas_info import _merge_info

raw_fname = op.join(mne.datasets.sample.data_path(), 'MEG',
                    'sample', 'sample_audvis_raw.fif')

raw = mne.io.Raw(raw_fname, preload=True)

print(raw.info['bads'])
print(len(raw.info['dig']))

info = mne.create_info(["STI XXX"], raw.info['sfreq'], ['stim'])
info_out = _merge_info([raw.info, info], force_update_to_first=True)
print(info_out)
```

indeeds bads get duplicated. For the digs It does not seem so.

@larsoner do you have time to look?

Resolving patch:
diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py
--- a/mne/io/meas_info.py
+++ b/mne/io/meas_info.py
@@ -1527,6 +1527,8 @@ def _merge_dict_values(dicts, key, verbose=None):
 
     Fork for {'dict', 'list', 'array', 'other'}
     and consider cases where one or all are of the same type.
+
+    Does special things for "projs", "bads", and "meas_date".
     """
     values = [d[key] for d in dicts]
     msg = ("Don't know how to merge '%s'. Make sure values are "
@@ -1545,8 +1547,12 @@ def _where_isinstance(values, kind):
     # list
     if _check_isinstance(values, list, all):
         lists = (d[key] for d in dicts)
-        return (_uniquify_projs(_flatten(lists)) if key == 'projs'
-                else _flatten(lists))
+        if key == 'projs':
+            return _uniquify_projs(_flatten(lists))
+        elif key == 'bads':
+            return sorted(set(_flatten(lists)))
+        else:
+            return _flatten(lists)
     elif _check_isinstance(values, list, any):
         idx = _where_isinstance(values, list)
         if len(idx) == 1:


Test patch:
diff --git a/mne/io/tests/test_meas_info.py b/mne/io/tests/test_meas_info.py
--- a/mne/io/tests/test_meas_info.py
+++ b/mne/io/tests/test_meas_info.py
@@ -337,6 +337,22 @@ def test_merge_info():
     info_d['hpi_meas'] = [{'f1': 3, 'f2': 5}]
     pytest.raises(ValueError, _merge_info, [info_a, info_d])
 
+    info_0 = read_info(raw_fname)
+    info_0['bads'] = ['MEG 2443', 'EEG 053']
+    # XXX eventually this should not be in meas, but a property of BaseRaw
+    info_0['buffer_size_sec'] = None
+    assert len(info_0['chs']) == 376
+    assert len(info_0['dig']) == 146
+    info_1 = create_info(["STI XXX"], info_0['sfreq'], ['stim'])
+    assert info_1['bads'] == []
+    info_out = _merge_info([info_0, info_1], force_update_to_first=True)
+    assert len(info_out['chs']) == 377
+    assert len(info_out['bads']) == 2
+    assert len(info_out['dig']) == 146
+    assert len(info_0['chs']) == 376
+    assert len(info_0['bads']) == 2
+    assert len(info_0['dig']) == 146
+
 
 def test_check_consistency():
     """Test consistency check of Info objects."""