Repository: mne-tools/mne-python

Issue:
Function "read_raw_edf"  have some channels which dosen't apear in version 0.23.0
I used args "exclude"  to get the specific channel data before version 0.23.0.
Since it's much faster than read all data and pick channel data.
But when I upgrade mne to 0.24.0 , it would have bug .
There have some channels that dosent apear in version 0.23.0.
It will appear this warning , and I cannot read them in raw edf data. 

RuntimeWarning: Channel names are not unique, found duplicates for: {'Impedance', 'Rate', 'Sum', 'Pressure', 'Fast', '(Imp)', 'Leg'}. Applying running numbers for duplicates.


And this is my function  to get specific channel data in 0.23.0.


```
def get_channel_name(edfname): ## Read  edf channel name
    with open(edfname,'r') as f:
        _ = f.read(96+120+36)
        channel_num = int(f.read(4))
        return  [f.read(16).strip() for i in range(channel_num )]

def get_channel_data(edfname,need_channels):
    ch_names = get_channel_name(edfname)
    exclude_channels = ['' if the_ch in need_channels else the_ch  for the_ch in ch_names ]
    data = mne.io.read_raw_edf(edfname,exclude=channel_list,verbose=False)
    return data.get_data() , data.info['sfreq'] , data.ch_names
```
Function "get_channel_name" is copy from function "_read_edf_header" in version 0.23.0.



Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

I would not recommend copying and using internal code with all these magic numbers (`96+120+36`), this is very brittle and likely to break anytime. In addition, you are opening the file in text mode instead of binary (should be `'rb'`).

In any case, this still works for me in 0.24. However, a better approach is to first load the data with `preload=False`. This won't load the data, but you will get the header information, including channel names. You can then use `raw["ch_names"]` to

- either construct the `exclude` argument and then load the data again with `preload=True` or
- drop channels you do not need with `raw.drop_channels()` and then load the data with `raw.load_data()`.
@cbrnr  Thanks for reply this issue.
I would try to use new method you provide to get the specfic channels data.
Because of the channels I need have different frequence so I use exclude .
And I found out why I received a warning about duplicate channel names.
In function "_read_edf_header" in v0.24.0
```
        ch_names = []
        ch_types = []
        for ch_idx, this_label in enumerate(ch_labels):
            # if no channel type, then we will default to eeg
            ch_type = None
            ch_name = this_label

            # space is found, so the prefix is the channel type. 'Annotations'
            # is also a keyword we search for
            if (this_label.count(' ') == 1) and \
                    ('Annotations' not in this_label):
                ch_type, ch_name = this_label.split(' ')

                # channel types should be upper case for easy comparison
                ch_type = ch_type.upper()
```
It would split channels name by space and cause some channels have the same name.
Maybe the EDF files I owned is not in typical format....
My channel names contain a lot of spaces.
Anyway, thank you for answering my question.


 
Glad I could help and that you could solve your issue! EDF channel names are poorly specified, and you are correct that from 0.24 we now detect if they contain spaces. We use this information to deduce channel types, which works for common cases such as `"EEG Fz"`. Apparently, this should only be the case if we detect [known prefixes](https://mne.tools/stable/generated/mne.io.read_raw_edf.html#:~:text=for%20EDF%20files.-,Notes,-It%20is%20worth).

Could you please post the channel names you have in (one of) your files? If these are unique, the warning about duplicate names should not appear, but if it does this might indicate that we're not doing the correct thing. Thanks!
My channels contains :
E1 ,
E1 Impedance,
O1 ,
O1 Impedance

Then the channles "Impedance" would be duplicated. 

But I think this is my own file format problem.
I even need to write another header reader to solve this problem because It have some channel names longer than 16.


Thanks @0220220313! I think at least partly MNE-Python is doing the wrong thing here, there shouldn't be any duplicate channels names because these are unknown prefixes. Could you share one of your files so that I can reproduce the problem? If you have some channel names that are too long this would be a separate issue.
I am glad to help you.
Here is the example EDF .
[example.zip](https://github.com/mne-tools/mne-python/files/7622205/example.zip)


Some of the origin channel names in UI of my device are longer then 16 , they would be wrote only 16  chars in edf.
Since the final char of channel name would be drop by function "_read_edf_header " , if the channel name lenth is 16 chars.
I need to wite another function to read channel names.
Therefore, I think there is something wrong with my device when exporting EDF files.

Thanks! You are correct that EDF channel labels are 16 characters at most. If you have longer labels originally, then yes, I think you should try to shorten the names if possible so that nothing is lost when you export to EDF.

However, the first few channel labels in your example file are:
```
1
1 Impedance
1-2
1-2 (Imp)
1-F
2
2 Impedance
```

MNE-Python reads these as:
```
1
Impedance-0
1-2
(Imp)-0
1-F
2
Impedance-1
```

So even though unknown prefixes should be ignored, they are actually removed. It seems like labels are split on the first space character, and the first part (the prefix) gets removed no matter what. That's why suddenly there are duplicates, which should not happen (`Impedance-0` and `Impedance-1` should be `1 Impedance` and `2 Impedance`).

I will take a look and try to fix this.
Thanks,hope this problem can be resolved smoothly.

Resolving patch:
diff --git a/mne/io/edf/edf.py b/mne/io/edf/edf.py
--- a/mne/io/edf/edf.py
+++ b/mne/io/edf/edf.py
@@ -27,6 +27,22 @@
 from ...annotations import Annotations
 
 
+# common channel type names mapped to internal ch types
+CH_TYPE_MAPPING = {
+    'EEG': FIFF.FIFFV_EEG_CH,
+    'SEEG': FIFF.FIFFV_SEEG_CH,
+    'ECOG': FIFF.FIFFV_ECOG_CH,
+    'DBS': FIFF.FIFFV_DBS_CH,
+    'EOG': FIFF.FIFFV_EOG_CH,
+    'ECG': FIFF.FIFFV_ECG_CH,
+    'EMG': FIFF.FIFFV_EMG_CH,
+    'BIO': FIFF.FIFFV_BIO_CH,
+    'RESP': FIFF.FIFFV_RESP_CH,
+    'MISC': FIFF.FIFFV_MISC_CH,
+    'SAO2': FIFF.FIFFV_BIO_CH,
+}
+
+
 @fill_doc
 class RawEDF(BaseRaw):
     """Raw object from EDF, EDF+ or BDF file.
@@ -59,6 +75,16 @@ class RawEDF(BaseRaw):
     exclude : list of str
         Channel names to exclude. This can help when reading data with
         different sampling rates to avoid unnecessary resampling.
+    infer_types : bool
+        If True, try to infer channel types from channel labels. If a channel
+        label starts with a known type (such as 'EEG') followed by a space and
+        a name (such as 'Fp1'), the channel type will be set accordingly, and
+        the channel will be renamed to the original label without the prefix.
+        For unknown prefixes, the type will be 'EEG' and the name will not be
+        modified. If False, do not infer types and assume all channels are of
+        type 'EEG'.
+
+        .. versionadded:: 0.24.1
     %(preload)s
     %(verbose)s
 
@@ -108,11 +134,12 @@ class RawEDF(BaseRaw):
 
     @verbose
     def __init__(self, input_fname, eog=None, misc=None, stim_channel='auto',
-                 exclude=(), preload=False, verbose=None):
+                 exclude=(), infer_types=False, preload=False, verbose=None):
         logger.info('Extracting EDF parameters from {}...'.format(input_fname))
         input_fname = os.path.abspath(input_fname)
         info, edf_info, orig_units = _get_info(input_fname, stim_channel, eog,
-                                               misc, exclude, preload)
+                                               misc, exclude, infer_types,
+                                               preload)
         logger.info('Creating raw.info structure...')
 
         # Raw attributes
@@ -187,7 +214,7 @@ def __init__(self, input_fname, eog=None, misc=None,
         logger.info('Extracting EDF parameters from {}...'.format(input_fname))
         input_fname = os.path.abspath(input_fname)
         info, edf_info, orig_units = _get_info(input_fname, stim_channel, eog,
-                                               misc, exclude, preload)
+                                               misc, exclude, True, preload)
         logger.info('Creating raw.info structure...')
 
         # Raw attributes
@@ -322,7 +349,7 @@ def _read_segment_file(data, idx, fi, start, stop, raw_extras, filenames,
     return tal_data
 
 
-def _read_header(fname, exclude):
+def _read_header(fname, exclude, infer_types):
     """Unify EDF, BDF and GDF _read_header call.
 
     Parameters
@@ -341,7 +368,7 @@ def _read_header(fname, exclude):
     ext = os.path.splitext(fname)[1][1:].lower()
     logger.info('%s file detected' % ext.upper())
     if ext in ('bdf', 'edf'):
-        return _read_edf_header(fname, exclude)
+        return _read_edf_header(fname, exclude, infer_types)
     elif ext == 'gdf':
         return _read_gdf_header(fname, exclude), None
     else:
@@ -349,12 +376,12 @@ def _read_header(fname, exclude):
             f'Only GDF, EDF, and BDF files are supported, got {ext}.')
 
 
-def _get_info(fname, stim_channel, eog, misc, exclude, preload):
+def _get_info(fname, stim_channel, eog, misc, exclude, infer_types, preload):
     """Extract information from EDF+, BDF or GDF file."""
     eog = eog if eog is not None else []
     misc = misc if misc is not None else []
 
-    edf_info, orig_units = _read_header(fname, exclude)
+    edf_info, orig_units = _read_header(fname, exclude, infer_types)
 
     # XXX: `tal_ch_names` to pass to `_check_stim_channel` should be computed
     #      from `edf_info['ch_names']` and `edf_info['tal_idx']` but 'tal_idx'
@@ -391,20 +418,6 @@ def _get_info(fname, stim_channel, eog, misc, exclude, preload):
     chs = list()
     pick_mask = np.ones(len(ch_names))
 
-    # common channel type names mapped to internal ch types
-    ch_type_mapping = {
-        'EEG': FIFF.FIFFV_EEG_CH,
-        'SEEG': FIFF.FIFFV_SEEG_CH,
-        'ECOG': FIFF.FIFFV_ECOG_CH,
-        'DBS': FIFF.FIFFV_DBS_CH,
-        'EOG': FIFF.FIFFV_EOG_CH,
-        'ECG': FIFF.FIFFV_ECG_CH,
-        'EMG': FIFF.FIFFV_EMG_CH,
-        'BIO': FIFF.FIFFV_BIO_CH,
-        'RESP': FIFF.FIFFV_RESP_CH,
-        'MISC': FIFF.FIFFV_MISC_CH,
-        'SAO2': FIFF.FIFFV_BIO_CH,
-    }
     chs_without_types = list()
 
     for idx, ch_name in enumerate(ch_names):
@@ -425,8 +438,8 @@ def _get_info(fname, stim_channel, eog, misc, exclude, preload):
         # if the edf info contained channel type information
         # set it now
         ch_type = ch_types[idx]
-        if ch_type is not None and ch_type in ch_type_mapping:
-            chan_info['kind'] = ch_type_mapping.get(ch_type)
+        if ch_type is not None and ch_type in CH_TYPE_MAPPING:
+            chan_info['kind'] = CH_TYPE_MAPPING.get(ch_type)
             if ch_type not in ['EEG', 'ECOG', 'SEEG', 'DBS']:
                 chan_info['coil_type'] = FIFF.FIFFV_COIL_NONE
                 pick_mask[idx] = False
@@ -448,7 +461,7 @@ def _get_info(fname, stim_channel, eog, misc, exclude, preload):
             chan_info['ch_name'] = ch_name
             ch_names[idx] = chan_info['ch_name']
             edf_info['units'][idx] = 1
-        elif ch_type not in ch_type_mapping:
+        elif ch_type not in CH_TYPE_MAPPING:
             chs_without_types.append(ch_name)
         chs.append(chan_info)
 
@@ -568,7 +581,7 @@ def _edf_str(x):
     return x.decode('latin-1').split('\x00')[0]
 
 
-def _read_edf_header(fname, exclude):
+def _read_edf_header(fname, exclude, infer_types):
     """Read header information from EDF+ or BDF file."""
     edf_info = {'events': []}
 
@@ -653,31 +666,28 @@ def _read_edf_header(fname, exclude):
         channels = list(range(nchan))
 
         # read in 16 byte labels and strip any extra spaces at the end
-        ch_labels = [fid.read(16).strip().decode('latin-1')
-                     for ch in channels]
+        ch_labels = [fid.read(16).strip().decode('latin-1') for _ in channels]
 
         # get channel names and optionally channel type
         # EDF specification contains 16 bytes that encode channel names,
         # optionally prefixed by a string representing channel type separated
         # by a space
-        ch_names = []
-        ch_types = []
-        for ch_idx, this_label in enumerate(ch_labels):
-            # if no channel type, then we will default to eeg
-            ch_type = None
-            ch_name = this_label
-
-            # space is found, so the prefix is the channel type. 'Annotations'
-            # is also a keyword we search for
-            if (this_label.count(' ') == 1) and \
-                    ('Annotations' not in this_label):
-                ch_type, ch_name = this_label.split(' ')
-
-                # channel types should be upper case for easy comparison
-                ch_type = ch_type.upper()
-
-            ch_names.append(ch_name)
-            ch_types.append(ch_type)
+        if infer_types:
+            ch_types, ch_names = [], []
+            for ch_label in ch_labels:
+                ch_type, ch_name = 'EEG', ch_label  # default to EEG
+                parts = ch_label.split(' ')
+                if len(parts) > 1:
+                    if parts[0].upper() in CH_TYPE_MAPPING:
+                        ch_type = parts[0].upper()
+                        ch_name = ' '.join(parts[1:])
+                        logger.info(f"Channel '{ch_label}' recognized as type "
+                                    f"{ch_type} (renamed to '{ch_name}').")
+                ch_types.append(ch_type)
+                ch_names.append(ch_name)
+        else:
+            ch_types, ch_names = ['EEG'] * nchan, ch_labels
+
         exclude = _find_exclude_idx(ch_names, exclude)
         tal_idx = _find_tal_idx(ch_names)
         exclude = np.concatenate([exclude, tal_idx])
@@ -1232,7 +1242,7 @@ def _find_tal_idx(ch_names):
 
 @fill_doc
 def read_raw_edf(input_fname, eog=None, misc=None, stim_channel='auto',
-                 exclude=(), preload=False, verbose=None):
+                 exclude=(), infer_types=False, preload=False, verbose=None):
     """Reader function for EDF or EDF+ files.
 
     Parameters
@@ -1264,6 +1274,16 @@ def read_raw_edf(input_fname, eog=None, misc=None, stim_channel='auto',
         Channel names to exclude. This can help when reading data with
         different sampling rates to avoid unnecessary resampling. A str is
         interpreted as a regular expression.
+    infer_types : bool
+        If True, try to infer channel types from channel labels. If a channel
+        label starts with a known type (such as 'EEG') followed by a space and
+        a name (such as 'Fp1'), the channel type will be set accordingly, and
+        the channel will be renamed to the original label without the prefix.
+        For unknown prefixes, the type will be 'EEG' and the name will not be
+        modified. If False, do not infer types and assume all channels are of
+        type 'EEG'.
+
+        .. versionadded:: 0.24.1
     %(preload)s
     %(verbose)s
 
@@ -1325,13 +1345,13 @@ def read_raw_edf(input_fname, eog=None, misc=None, stim_channel='auto',
     if ext != 'edf':
         raise NotImplementedError(f'Only EDF files are supported, got {ext}.')
     return RawEDF(input_fname=input_fname, eog=eog, misc=misc,
-                  stim_channel=stim_channel, exclude=exclude, preload=preload,
-                  verbose=verbose)
+                  stim_channel=stim_channel, exclude=exclude,
+                  infer_types=infer_types, preload=preload, verbose=verbose)
 
 
 @fill_doc
 def read_raw_bdf(input_fname, eog=None, misc=None, stim_channel='auto',
-                 exclude=(), preload=False, verbose=None):
+                 exclude=(), infer_types=False, preload=False, verbose=None):
     """Reader function for BDF files.
 
     Parameters
@@ -1363,6 +1383,16 @@ def read_raw_bdf(input_fname, eog=None, misc=None, stim_channel='auto',
         Channel names to exclude. This can help when reading data with
         different sampling rates to avoid unnecessary resampling. A str is
         interpreted as a regular expression.
+    infer_types : bool
+        If True, try to infer channel types from channel labels. If a channel
+        label starts with a known type (such as 'EEG') followed by a space and
+        a name (such as 'Fp1'), the channel type will be set accordingly, and
+        the channel will be renamed to the original label without the prefix.
+        For unknown prefixes, the type will be 'EEG' and the name will not be
+        modified. If False, do not infer types and assume all channels are of
+        type 'EEG'.
+
+        .. versionadded:: 0.24.1
     %(preload)s
     %(verbose)s
 
@@ -1417,8 +1447,8 @@ def read_raw_bdf(input_fname, eog=None, misc=None, stim_channel='auto',
     if ext != 'bdf':
         raise NotImplementedError(f'Only BDF files are supported, got {ext}.')
     return RawEDF(input_fname=input_fname, eog=eog, misc=misc,
-                  stim_channel=stim_channel, exclude=exclude, preload=preload,
-                  verbose=verbose)
+                  stim_channel=stim_channel, exclude=exclude,
+                  infer_types=infer_types, preload=preload, verbose=verbose)
 
 
 @fill_doc


Test patch:
diff --git a/mne/export/tests/test_export.py b/mne/export/tests/test_export.py
--- a/mne/export/tests/test_export.py
+++ b/mne/export/tests/test_export.py
@@ -72,28 +72,25 @@ def test_double_export_edf(tmp_path):
     """Test exporting an EDF file multiple times."""
     rng = np.random.RandomState(123456)
     format = 'edf'
-    ch_types = ['eeg', 'eeg', 'stim', 'ecog', 'ecog', 'seeg',
-                'eog', 'ecg', 'emg', 'dbs', 'bio']
-    ch_names = np.arange(len(ch_types)).astype(str).tolist()
-    info = create_info(ch_names, sfreq=1000,
-                       ch_types=ch_types)
-    data = rng.random(size=(len(ch_names), 1000)) * 1.e-5
+    ch_types = ['eeg', 'eeg', 'stim', 'ecog', 'ecog', 'seeg', 'eog', 'ecg',
+                'emg', 'dbs', 'bio']
+    info = create_info(len(ch_types), sfreq=1000, ch_types=ch_types)
+    data = rng.random(size=(len(ch_types), 1000)) * 1e-5
 
     # include subject info and measurement date
-    subject_info = dict(first_name='mne', last_name='python',
-                        birthday=(1992, 1, 20), sex=1, hand=3)
-    info['subject_info'] = subject_info
+    info['subject_info'] = dict(first_name='mne', last_name='python',
+                                birthday=(1992, 1, 20), sex=1, hand=3)
     raw = RawArray(data, info)
 
     # export once
-    temp_fname = op.join(str(tmp_path), f'test.{format}')
+    temp_fname = tmp_path / f'test.{format}'
     raw.export(temp_fname, add_ch_type=True)
-    raw_read = read_raw_edf(temp_fname, preload=True)
+    raw_read = read_raw_edf(temp_fname, infer_types=True, preload=True)
 
     # export again
     raw_read.load_data()
     raw_read.export(temp_fname, add_ch_type=True, overwrite=True)
-    raw_read = read_raw_edf(temp_fname, preload=True)
+    raw_read = read_raw_edf(temp_fname, infer_types=True, preload=True)
 
     # stim channel should be dropped
     raw.drop_channels('2')
@@ -170,7 +167,7 @@ def test_rawarray_edf(tmp_path):
     temp_fname = op.join(str(tmp_path), f'test.{format}')
 
     raw.export(temp_fname, add_ch_type=True)
-    raw_read = read_raw_edf(temp_fname, preload=True)
+    raw_read = read_raw_edf(temp_fname, infer_types=True, preload=True)
 
     # stim channel should be dropped
     raw.drop_channels('2')
@@ -254,8 +251,7 @@ def test_export_raw_edf(tmp_path, dataset, format):
         raw = read_raw_fif(fname)
 
     # only test with EEG channels
-    raw.pick_types(eeg=True, ecog=True, seeg=True,
-                   eog=True, ecg=True, emg=True)
+    raw.pick_types(eeg=True, ecog=True, seeg=True)
     raw.load_data()
     orig_ch_names = raw.ch_names
     temp_fname = op.join(str(tmp_path), f'test.{format}')
@@ -269,9 +265,8 @@ def test_export_raw_edf(tmp_path, dataset, format):
         raw.export(temp_fname, physical_range=(0, 1e6))
 
     if dataset == 'test':
-        orig_ch_names = [ch.split(' ')[1] for ch in raw.ch_names]
         with pytest.warns(RuntimeWarning, match='Data has a non-integer'):
-            raw.export(temp_fname, add_ch_type=False)
+            raw.export(temp_fname)
     elif dataset == 'misc':
         with pytest.warns(RuntimeWarning, match='EDF format requires'):
             raw.export(temp_fname)
diff --git a/mne/io/edf/tests/test_edf.py b/mne/io/edf/tests/test_edf.py
--- a/mne/io/edf/tests/test_edf.py
+++ b/mne/io/edf/tests/test_edf.py
@@ -181,7 +181,7 @@ def test_edf_data_broken(tmp_path):
 
 def test_duplicate_channel_labels_edf():
     """Test reading edf file with duplicate channel names."""
-    EXPECTED_CHANNEL_NAMES = ['F1-Ref-0', 'F2-Ref', 'F1-Ref-1']
+    EXPECTED_CHANNEL_NAMES = ['EEG F1-Ref-0', 'EEG F2-Ref', 'EEG F1-Ref-1']
     with pytest.warns(RuntimeWarning, match='Channel names are not unique'):
         raw = read_raw_edf(duplicate_channel_labels_path, preload=False)
 
@@ -431,7 +431,7 @@ def test_bdf_multiple_annotation_channels():
 def test_edf_lowpass_zero():
     """Test if a lowpass filter of 0Hz is mapped to the Nyquist frequency."""
     raw = read_raw_edf(edf_stim_resamp_path)
-    assert raw.ch_names[100] == 'LDAMT_01-REF'
+    assert raw.ch_names[100] == 'EEG LDAMT_01-REF'
     assert_allclose(raw.info["lowpass"], raw.info["sfreq"] / 2)
 
 
@@ -531,7 +531,7 @@ def test_hp_lp_reversed(fname, lo, hi, warns, monkeypatch):
 def test_degenerate():
     """Test checking of some bad inputs."""
     for func in (read_raw_edf, read_raw_bdf, read_raw_gdf,
-                 partial(_read_header, exclude=())):
+                 partial(_read_header, exclude=(), infer_types=False)):
         with pytest.raises(NotImplementedError, match='Only.*txt.*'):
             func(edf_txt_stim_channel_path)
 
@@ -552,14 +552,37 @@ def test_exclude():
 @testing.requires_testing_data
 def test_ch_types():
     """Test reading of channel types from EDF channel label."""
-    raw = read_raw_edf(edf_chtypes_path)
-
-    # get the channel types for all channels
-    ch_types = raw.get_channel_types()
-    assert all(x in ch_types for x in ['eeg', 'ecg'])
-
-    # test certain channels were correctly detected as a channel type
-    test_ch_names = {"Fp1-Ref": "eeg", "P10-Ref": "eeg", "DC01": "eeg",
-                     "ECG1": "ecg", "ECG2": "ecg"}
-    assert all(raw.get_channel_types(picks=pick)[0] == ch_type
-               for pick, ch_type in test_ch_names.items())
+    raw = read_raw_edf(edf_chtypes_path)  # infer_types=False
+
+    labels = ['EEG Fp1-Ref', 'EEG Fp2-Ref', 'EEG F3-Ref', 'EEG F4-Ref',
+              'EEG C3-Ref', 'EEG C4-Ref', 'EEG P3-Ref', 'EEG P4-Ref',
+              'EEG O1-Ref', 'EEG O2-Ref', 'EEG F7-Ref', 'EEG F8-Ref',
+              'EEG T7-Ref', 'EEG T8-Ref', 'EEG P7-Ref', 'EEG P8-Ref',
+              'EEG Fz-Ref', 'EEG Cz-Ref', 'EEG Pz-Ref', 'POL E', 'POL PG1',
+              'POL PG2', 'EEG A1-Ref', 'EEG A2-Ref', 'POL T1', 'POL T2',
+              'ECG ECG1', 'ECG ECG2', 'EEG F9-Ref', 'EEG T9-Ref', 'EEG P9-Ref',
+              'EEG F10-Ref', 'EEG T10-Ref', 'EEG P10-Ref', 'SaO2 X9',
+              'SaO2 X10', 'POL DC01', 'POL DC02', 'POL DC03', 'POL DC04',
+              'POL $A1', 'POL $A2']
+
+    # by default all types are 'eeg'
+    assert(all(t == 'eeg' for t in raw.get_channel_types()))
+    assert raw.ch_names == labels
+
+    raw = read_raw_edf(edf_chtypes_path, infer_types=True)
+
+    labels = ['Fp1-Ref', 'Fp2-Ref', 'F3-Ref', 'F4-Ref', 'C3-Ref', 'C4-Ref',
+              'P3-Ref', 'P4-Ref', 'O1-Ref', 'O2-Ref', 'F7-Ref', 'F8-Ref',
+              'T7-Ref', 'T8-Ref', 'P7-Ref', 'P8-Ref', 'Fz-Ref', 'Cz-Ref',
+              'Pz-Ref', 'POL E', 'POL PG1', 'POL PG2', 'A1-Ref', 'A2-Ref',
+              'POL T1', 'POL T2', 'ECG1', 'ECG2', 'F9-Ref', 'T9-Ref', 'P9-Ref',
+              'F10-Ref', 'T10-Ref', 'P10-Ref', 'X9', 'X10', 'POL DC01',
+              'POL DC02', 'POL DC03', 'POL DC04', 'POL $A1', 'POL $A2']
+    types = ['eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg',
+             'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg',
+             'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'ecg',
+             'ecg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'bio', 'bio',
+             'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg']
+
+    assert raw.get_channel_types() == types
+    assert raw.ch_names == labels
diff --git a/mne/io/nihon/tests/test_nihon.py b/mne/io/nihon/tests/test_nihon.py
--- a/mne/io/nihon/tests/test_nihon.py
+++ b/mne/io/nihon/tests/test_nihon.py
@@ -26,7 +26,7 @@ def test_nihon_eeg():
     assert raw._data.shape == raw_edf._data.shape
     assert raw.info['sfreq'] == raw.info['sfreq']
     # ch names and order are switched in the EDF
-    edf_ch_names = {x: x.replace('-Ref', '')
+    edf_ch_names = {x: x.split(' ')[1].replace('-Ref', '')
                     for x in raw_edf.ch_names}
     raw_edf.rename_channels(edf_ch_names)
     assert raw.ch_names == raw_edf.ch_names