Repository: mne-tools/mne-python

Issue:
Allow more types for "picks"
#### Describe the new feature or enhancement

I'm not exactly sure if a bug or feature request, so I'll go with my gut:

Most methods that have `picks` as argument accept a list. They also accept a tuple even if not listed in the docs (bug?). However, most of the times I want to provide a set or a frozenset, since I want to make sure that there are no repetitions at any point (e.g., no two "Pz" by mistake), and their order is irrelevant. For such case, a set might be the most correct native data structure. Giving a set or a frozenset as `picks` will raise an exception `TypeError: picks must be a list of int or list of str, got a data type of object`

#### Describe your proposed implementation

Relax current type restrictions for `picks` to at least allow sets and frozensets.

#### Describe possible alternatives

Document better why this is not possible even if a priori intuitive.


Thanks!



Discussion:
> However, most of the times I want to provide a set or a frozenset, since I want to make sure that there are no repetitions at any point (e.g., no two "Pz" by mistake), and their order is irrelevant. 

Careful, the order does matter, assuming you want to preserve the original order in the data:
```
>>> raw = mne.io.read_raw_fif(mne.datasets.sample.data_path() / 'MEG' / 'sample' / 'sample_audvis_raw.fif')
>>> raw.copy().pick([0, 1]).ch_names[0]
'MEG 0113'
>>> raw.copy().pick([1, 0]).ch_names[0]
'MEG 0112'
```

`set` in 3.7+ does preserve order, though, so this might be less important nowadays.

> Relax current type restrictions for picks to at least allow sets and frozensets.

I would rather have users pass `list(picks)` instead. I think it makes clearer that it's really supposed to be `array-like`
Thanks for the reply. True, there are cases in which the order does matter. I was looking mainly at Epochs.average. Then I see several ways to proceed:

1. Allow to pass a set and call list(picks) internally, since there's no reason a priori reason why a set could not be passed.
2. Allow to pass a set and call list(picks) internally, but issue a runtime warning about the ordering issues.

I still think that sets should be allowed and that 1. is the better option. It still sounds a bit weird to me to impose this constraint. If I'm implementing e.g, logic to select channels via set intersections, among others, and I pass the set to MNE, it's an unexpected behavior that it raises an error. Sure someone can make a mistake regarding order in <3.7, but I think it's reasonable to think that it's the user the one that should be careful in this case.

Also, independently of above, should the documentation say `array-like` instead of `list` then?

 
>  It still sounds a bit weird to me to impose this constraint. If I'm implementing e.g, logic to select channels via set intersections, among others, and I pass the set to MNE, it's an unexpected behavior that it raises an error.

One problem is that we actually do treat set-like differently in MNE. In `pick_channels`, we treat it as a set, and the order there does not matter, at least by default.

Another problem is that a `set` is not array-like in the same way as `list` and `tuple`:
```
>>> np.array(list([1, 2]))
array([1, 2])
>>> np.array(tuple([1, 2]))
array([1, 2])
>>> np.array(set([1, 2]))
array({1, 2}, dtype=object)
```
So I think we shouldn't change behavior, and instead just update it to say `array-like of int`
Interesting, so for methods such as `pick_channels` and `Epochs.average`, should they accept both "array-like" and a "set-like"? Then update the docs to reflect these changes.
I would change `Epochs.average` to say `str | array-like | slice | None` (i.e., change `list` to `array-like`). `pick_channels` just says "list of str", which I think is good enough (even if it's incomplete). We often allow `tuple` for things we say must be `list` so I think people get used to it quickly enough.
OK that can work then. Maybe this can be closed then. Thanks!
it seems we should close after amending the docstrings as suggested by
@larsoner


Message ID: ***@***.***>
>

I'll have a look at changing the docstrings as suggested above.

Resolving patch:
diff --git a/mne/utils/docs.py b/mne/utils/docs.py
--- a/mne/utils/docs.py
+++ b/mne/utils/docs.py
@@ -2345,7 +2345,7 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
     Restrict sensor channels (MEG, EEG, etc.) to this list of channel names.
 """
 
-_picks_types = 'str | list | slice | None'
+_picks_types = 'str | array-like | slice | None'
 _picks_header = f'picks : {_picks_types}'
 _picks_desc = 'Channels to include.'
 _picks_int = ('Slices and lists of integers will be interpreted as channel '


Test patch: