Repository: mne-tools/mne-python

Issue:
BUG: pick drop channels on raw without preload
do:

``` Python
raw = mne.io.Raw(fname)
raw.pick_channels(raw.ch_names[:4])
raw[:2]
```

and enjoy 

```
--> 435                                            rlims=(first_pick, last_pick)).data
    436                             if np.isrealobj(one):
    437                                 dtype = np.float

/Users/dengemann/anaconda/lib/python2.7/site-packages/mne/io/tag.pyc in read_tag(fid, pos, shape, rlims)
    341             elif tag.type == FIFF.FIFFT_FLOAT:
    342                 tag.data = _fromstring_rows(fid, tag.size, dtype=">f4",
--> 343                                             shape=shape, rlims=rlims)
    344             elif tag.type == FIFF.FIFFT_DOUBLE:
    345                 tag.data = _fromstring_rows(fid, tag.size, dtype=">f8",

/Users/dengemann/anaconda/lib/python2.7/site-packages/mne/io/tag.pyc in _fromstring_rows(fid, tag_size, dtype, shape, rlims)
    157             raise ValueError('Only implemented for 2D matrices')
    158         if not np.prod(shape) == tag_size / item_size:
--> 159             raise ValueError('Wrong shape specified')
    160         if not len(rlims) == 2:
    161             raise ValueError('rlims must have two elements')

ValueError: Wrong shape specified
```

@adykstra @agramfort 



Discussion:
@agramfort is this fixable or does it depend on the fid? In the latter case we can only allow picking for preload=True.

cc @Eric89GXL 

If your `pick_channels` mixin actually removes entries from `raw.info` (it does, right?), this creates a problem for the reading functions, since they expect `raw.info` to correctly specify the contents of _the file as it is on hard drive_. Changing `raw.info` breaks this relationship.

Hmm . . . 

```
self.info = pick_info(self.info, idx, copy=False)
```

Would it be OK to simply drop this line?

No, because things like the `__getitem__` / `[...]`/ `raw.ch_names` expect `raw.info` to reflect what's in `raw._data` (or what comes out of the raw reading functions, or what channels are present).

Then at least for now, I guess we only allow pick_ or drop_channels when preload is True, as @dengemann suggested?

Is there a reason for raw.info to reflect different things in different contexts?

I think the idea is that `*.info` stores all of the information about the data contained in a measurement, including the channel data a user has access to. Traditionally (i.e., before the mixin), the data accessed through a `Raw` object in Python was the same as the data provided by the file Python was accessing under the hood, so there was no actually no reflection of these two different ideas for what `raw.info` means. The `pick_channels` call forces `raw.info` to reflect either what is available as part of the data in the class (as it does now), _or_ what's available in the file (as it does not), since they are no longer identical.

Forcing `preload=True` will get around this issue, since there would no longer be a "file" to pull from. Alternatively, you could store the original set of channels in something like `raw._orig_channels` so the file-IO works (thus splitting the within-file functionality `_orig_channels` from the within-python one `info`), and then sub-select appropriately. I personally wouldn't bother until a compelling use-case came up, though, since it sounds like a pain to get right.

oh boy... how about we drop support of drop_channels for Raw? or maybe just Raw which is non preloaded? that would be my pragmatic way to handle this pb...

>  how about we drop support of drop_channels for Raw?

-1 too useful

> or maybe just Raw which is non preloaded? that would be my pragmatic way to handle this pb...

+1 best cost/benefit ratio

> or maybe just Raw which is non preloaded? that would be my pragmatic way
> to handle this pb...
> 
> +1 best cost/benefit ratio

let's go for this then. any volunteer?

I could take of it at the sprint . . . in the midst of grant writing at the moment.

> I could take of it at the sprint . . . in the midst of grant writing at
> the moment.
> 
> let's do this. I doubt we will be able to release this week anyway...
> unless @dengemann can pull it off (disclaimer it is not a suggestion)


Resolving patch:
diff --git a/mne/io/channels.py b/mne/io/channels.py
--- a/mne/io/channels.py
+++ b/mne/io/channels.py
@@ -203,6 +203,10 @@ def _pick_drop_channels(self, idx):
         from .fiff.raw import _BaseRaw
         from .. import Epochs
         from . import Evoked
+        if isinstance(self, _BaseRaw):
+            if not self._preloaded:
+                raise RuntimeError('Raw data must be preloaded to drop or pick'
+                                   ' channels')
 
         inst_has = lambda attr: getattr(self, attr, None) is not None
 


Test patch:
diff --git a/mne/io/fiff/tests/test_raw.py b/mne/io/fiff/tests/test_raw.py
--- a/mne/io/fiff/tests/test_raw.py
+++ b/mne/io/fiff/tests/test_raw.py
@@ -955,38 +955,30 @@ def test_pick_channels_mixin():
     """Test channel-picking functionality
     """
     # preload is True
+
     raw = Raw(fif_fname, preload=True)
     ch_names = raw.ch_names[:3]
 
     ch_names_orig = raw.ch_names
-    dummy = raw.pick_channels(ch_names, copy=True) # copy is True
+    dummy = raw.pick_channels(ch_names, copy=True)  # copy is True
     assert_equal(ch_names, dummy.ch_names)
     assert_equal(ch_names_orig, raw.ch_names)
     assert_equal(len(ch_names_orig), raw._data.shape[0])
 
-    raw.pick_channels(ch_names, copy=False) # copy is False
+    raw.pick_channels(ch_names, copy=False)  # copy is False
     assert_equal(ch_names, raw.ch_names)
     assert_equal(len(ch_names), len(raw.cals))
     assert_equal(len(ch_names), raw._data.shape[0])
 
-    # preload is False
     raw = Raw(fif_fname, preload=False)
-    ch_names = raw.ch_names[:3]
-
-    ch_names_orig = raw.ch_names
-    dummy = raw.pick_channels(ch_names, copy=True) # copy is True
-    assert_equal(ch_names, dummy.ch_names)
-    assert_equal(ch_names_orig, raw.ch_names)
-
-    raw.pick_channels(ch_names, copy=False) # copy is False
-    assert_equal(ch_names, raw.ch_names)
-    assert_equal(len(ch_names), len(raw.cals))
+    assert_raises(RuntimeError, raw.pick_channels, ch_names)
+    assert_raises(RuntimeError, raw.drop_channels, ch_names)
 
 
 def test_equalize_channels():
     """Test equalization of channels
     """
-    raw1 = Raw(fif_fname)
+    raw1 = Raw(fif_fname, preload=True)
 
     raw2 = raw1.copy()
     ch_names = raw1.ch_names[2:]