3
$\begingroup$

Assume I have a set of vectors

  set = {{0,1,1,0,1}, {1,0,1,1,1}, {1,1,1,1,0},{1,0,0,1,0}}. 

I want to delete the vectors which have the last entry as 0. In the above example, it is the 3rd and 4th vectors.

Can I use DeleteCases in a better way than following?

 Do[If[set[[j]][[5]] == 0, set[[j]] = ConstantArray[0, Length[set[[1]]]]], 
 {j, 1, Length[set]}];

 set =  DeleteCases[set, ConstantArray[0, Length[set[[1]]]]];
$\endgroup$
6
$\begingroup$
DeleteCases[set, {___, 0}]
{{0, 1, 1, 0, 1}, {1, 0, 1, 1, 1}}

In this case Pick also works:

Pick[set, Last /@ set, 1]
{{0, 1, 1, 0, 1}, {1, 0, 1, 1, 1}}

You asked about positions other than last. It is possible to generate a working pattern using Blank, e.g. {_, _, 0, _, _}, and this process can be automated, but that is not usually the first approach I would recommend.

You can use individual part extraction in either Select or Cases/DeleteCases, or you can create a "mask" for use with Pick as I showed above. Notable differences are that Select only operates at a single level, while Cases and Pick generalize to deeper structures. Pick is often somewhat faster than other methods, especially if one uses fast numeric functions where possible.

Picking according to the second column:

Pick[set, set[[All, 2]], 1]
{{0, 1, 1, 0, 1}, {1, 1, 1, 1, 0}}

Suppose however that your keep elements are not all the same, like 1 in this example above:

set2 = {{3, 1, 2, 1, 4}, {4, 1, 2, 2, 4}, {2, 3, 0, 0, 1}, {3, 1, 0, 4, 4}};

Pick[set2, Unitize @ set2[[All, 3]], 1]
{{3, 1, 2, 1, 4}, {4, 1, 2, 2, 4}}

Note here that Unitize is needed to make all keep elements into 1. While it is possible to use e.g. Positive instead this is not as efficient because in Mathematica Booleans True and False cannot be packed, while machine-size integers can. It is for this reason that I do not recommend these apparent equivalents:

(* Not recommended where performance matters *)

Pick[set2, Positive @ set2[[All, 3]]]

Pick[set2, set2[[All, 3]], _?Positive]
$\endgroup$
  • $\begingroup$ Thanks. Can you modify this to any index instead of just the last one? $\endgroup$ – cleanplay Jan 9 at 1:29
  • 1
    $\begingroup$ @cleanplay Please see the addendum to my answer. $\endgroup$ – Mr.Wizard Jan 9 at 1:45
  • $\begingroup$ Wizard, thanks again. Can I use Pick over multiple indices in the columns of set. For example, to find vectors with both 2nd and 3rd index as 1, I tried Pick[set, set[[All, 2 ;; 3]], {1, 1}] but that doesn't recognize the pattern in the last argument $\endgroup$ – cleanplay Jan 10 at 19:26
6
$\begingroup$

Or...

Select[set, Last[#] != 0 &]

or...

Select[set, #[[-1]] != 0 &]

or...

DeleteCases[set, x_ /; Last[x] == 0]
$\endgroup$
1
$\begingroup$

Another way is to use Delete in conjunction with Position:

Delete[set, Position[set[[All, -1]], 0]]

If I cared about speed for long lists, I'd probably use

Delete[set, Partition[Random`Private`PositionsOf[set[[All, -1]], 0], 1]]
$\endgroup$
  • 2
    $\begingroup$ In version 10.1 under Windows x64 your PositionsOf code is significantly slower than Pick, at least the way I tested it, e.g. set = RandomInteger[3, {1*^6, 5}]. Are you seeing different behavior? $\endgroup$ – Mr.Wizard Jan 9 at 9:11
  • 1
    $\begingroup$ @Mr.Wizard Now that you say it, it becomes clear to me that Pick should be faster. OP asked for using DeleteCases which sort of drove me to using Delete. But significance lies somewhat in the eye of the beholder. On my Haswell QuadCore with Mathematica 11.3 under macOS, Delete[set, Partition[RandomPrivatePositionsOf[set[[All, -1]], 0], 1]] requires 0.030 seconds while Pick[set, set[[All, -1]], 1] needs only 0.022. This difference is really small if you compare the timings to the timings of pattern-matching based methods. $\endgroup$ – Henrik Schumacher Jan 9 at 20:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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