# Is there a bug in Pick?

I wanted to use the Pick function with a condition. But there seems to be an issue here. Take a look at this:

selection = {0,1.2,3,0.,5};
Pick[{1,2,3,4,5},selection,elem_ /; elem =!= 0]


In Mathematica 8 it will give {1,2,3,4,5} instead of {2,3,4,5}. Please note, that the Pick function works nicely with

Pick[{1,2,3,4,5},selection,elem_ /; elem === 0]


Giving {1} as a result. Is this a bug or am I missing something?

-

This confused me as well, but using Trace revealed what is going on:

Trace@Pick[{1, 2, 3, 4, 5}, selection, elem_ /; elem =!= 0]

{{selection,{0,1.2,3,0.,5}},
Pick[{1,2,3,4,5},{0,1.2,3,0.,5},elem_/;elem=!=0],
{{0,1.2,3,0.,5}=!=0,True},
{1,2,3,4,5}}


The key is the 4th line: note that the pattern is applied to the full list, at level 0. The full list selection does match (because it is not structurally equivalent to 0) thus the full first argument is picked out.

The reason why we don't see this behavior with Equal (i.e. ==) is that {0, 1.2, 3, 0., 5} != 0 stays unevaluated.

(I did not find a way to restrict at which levels Pick operates, but it is possible to tweak the pattern instead, e.g. elem_?NumericQ/;elem=!=0, possibly with a performance hit.)

-
Does it mean that the patt in Pick[list, sel, patt] is not mapped over list and sel in this case? That is contradicting the help, I think. – István Zachar May 1 '12 at 11:54
@István It seems that Pick compares list and sel at every level, successively, including level 0. Once a level matches, it picks everything from there. If it doesn't, it looks at deeper levels. Another example would be Pick[{1, 2, {x, y, z}}, {1, 2, {a, b, c}}, a_ /; Length[a] == 3]. The full list is picked. If we change 1, 2 to 1, 2, 3 in both lists to make them length 4, only the last element will be picked. – Szabolcs May 1 '12 at 11:57
@gwr I agree that it's really confusing. If it weren't for Trace, I would have believed it was a bug ... – Szabolcs May 1 '12 at 12:00
Then I really wonder why Pick does not have a 4th argument for level specification... – István Zachar May 1 '12 at 12:07
@Szabolcs +1. As to Cases: "once an element has matched, it will still look at its sub-elements" - this actually happens the other way around: Cases uses a depth-first traversal (which causes some other effects as well), so it sees the parts before it sees the expression as a whole. – Leonid Shifrin May 1 '12 at 12:30

Here is a version which avoids any extra performance overhead associated with Condition etc:

Pick[{1, 2, 3, 4, 5}, selection, Except[_List | 0]]


I did not benchmark, but for large lists I'd expect it to be significantly faster than the versions based on Condition and / or PatternTest.

-
Except[selection | 0] also works. – kglr May 1 '12 at 13:02
@kguler Yes, but I think this might be slower, since _List pattern must be faster to match. – Leonid Shifrin May 1 '12 at 13:03
Yes...I was trying to avoid mis-picks when selection contains lists. – kglr May 1 '12 at 13:12
@kguler Yes, I agree, it is better in that sense. – Leonid Shifrin May 1 '12 at 13:21

Edit: Assuming that you do not differ between 0 and 0.

The unequal sign is !=

Pick[{1, 2, 3, 4, 5}, selection, elem_ /; elem != 0]


gives

{2, 3, 5}

Alternatively you can use the True selection mechanism:

 Pick[{1, 2, 3, 4, 5}, !PossibleZeroQ[#] & /@ selection]

-
Thank you, Markus, but I wanted to explicitedly make a difference between a Null-entry as in a sparse array and a numerical Value of 0. That is why I used =!= instead of !=. – gwr May 1 '12 at 12:09
@gwr: If your goal is to find the elements which are explicitly in the sparse array, you can use DeleteCases[#[[1,1]]&/@ArrayRules[yoursparsearray],Verbatim[_]] or, if the array might be multi-dimensional, DeleteCases[#[[1]]&/@ArrayRules[yoursparsearray],{Verbatim[_]...}] – celtschk May 2 '12 at 13:52