23
$\begingroup$

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?

$\endgroup$
1

3 Answers 3

19
$\begingroup$

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.)

$\endgroup$
9
  • 2
    $\begingroup$ 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. $\endgroup$ Commented May 1, 2012 at 11:54
  • 1
    $\begingroup$ @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. $\endgroup$
    – Szabolcs
    Commented May 1, 2012 at 11:57
  • 1
    $\begingroup$ @gwr I agree that it's really confusing. If it weren't for Trace, I would have believed it was a bug ... $\endgroup$
    – Szabolcs
    Commented May 1, 2012 at 12:00
  • 7
    $\begingroup$ Then I really wonder why Pick does not have a 4th argument for level specification... $\endgroup$ Commented May 1, 2012 at 12:07
  • 2
    $\begingroup$ @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. $\endgroup$ Commented May 1, 2012 at 12:30
19
$\begingroup$

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.

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

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]
$\endgroup$
2
  • $\begingroup$ 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 !=. $\endgroup$
    – gwr
    Commented May 1, 2012 at 12:09
  • $\begingroup$ @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[_]...}] $\endgroup$
    – celtschk
    Commented May 2, 2012 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.