Dropping n consecutive terms from a list periodically
The simplest (and probably fastest) way is to use Partition
with the appropriate offset:
list = Range@100;
Flatten@Partition[list, 3, 5]
(* {1, 2, 3, 6, 7, 8, 11, 12, 13, 16, 17, 18, 21, 22, 23, 26, 27, 28,
31, 32, 33, 36, 37, 38, 41, 42, 43, 46, 47, 48, 51, 52, 53, 56, 57,
58, 61, 62, 63, 66, 67, 68, 71, 72, 73, 76, 77, 78, 81, 82, 83, 86,
87, 88, 91, 92, 93, 96, 97, 98} *)
The logic is: "Take 3, drop 2, take 3, drop 2,... " till the end of the list (the argument 5
is just 3+2
). You can change these numbers as desired.
I am rather amused that my f2
is considerably faster than Partition
(here as f1
).
Now with an additional method I'll name f4
.
Third try. I'll name this function f5
. It is optimized for short take
sequences and it is quite fast in its element. It is in a way based on your original method.
f1[list_, take_, skip_] := Flatten @ Partition[list, take, take + skip, 1, {}]
f2[list_, take_, skip_] :=
list[[ SparseArray[PadRight[#, Length@list, #] & @
UnitStep @ Range[take - 1, -skip, -1]]["AdjacencyLists"] ]]
f4[list_, take_, skip_] := list ~Part~ With[{n = Length@list, m = take + skip},
Drop[Tuples[{Range[0, n, m], Range[take]}] ~Total~ {2}, Min[0, Mod[n, m] - take]]
]
f5[list_, take_, skip_] :=
list ~Part~ Flatten[Range[Range@take, Length@list, take + skip], {2, 1}]
Test:
a = RandomInteger[99, 1*^6];
First @ Timing @ Do[#[a, 3, 2], {100}] & /@ {f1, f2, f4, f5}
SameQ @@ (#[a, 3, 2] & /@ {f1, f2, f4, f5})
{3.962, 0.983, 0.952, 0.702} True
Timings in version 10.1, including the Pick
variation of f2
that rcollyer posted as f3
which only became practical in Mathematica 8.
Needs["GeneralUtilities`"]
BenchmarkPlot[
Cases[{f1, f2, f3, f4, f5}, f_ :> (f[#, 3, 2] &)],
RandomInteger[99, #] &,
5
]
The same benchmark with (f[#, 88, 7] &)
:
And finally (f[#, 7, 88] &)
:
This is around six to nine times slower than @rm-rf 's :) But you can use it for more complicated patterns:
pickpat[a_List, pattern_List: {1, 1, 1, 0, 0}] :=
Module[{patarray},
patarray = Flatten@ConstantArray[pattern, Ceiling[Length@a/Length@pattern]];
Pick[a, patarray[[1 ;; Length@a]], 1]
]
so
pickpat[Range@100]
(*{1, 2, 3, 6, 7, 8, 11, 12, 13, 16, 17, 18, 21, 22, 23, 26, 27, 28,
31, 32, 33, 36, 37, 38, 41, 42, 43, 46, 47, 48, 51, 52, 53, 56, 57,
58, 61, 62, 63, 66, 67, 68, 71, 72, 73, 76, 77, 78, 81, 82, 83, 86,
87, 88, 91, 92, 93, 96, 97, 98}*)
but, say if you wanted to drop the 2nd, 4th, 5th and 7th elements of a list you could call it like this:
pickpat[Range@100, {1,0,1,0,0,1,0}]
(*{1, 3, 6, 8, 10, 13, 15, 17, 20, 22, 24, 27, 29, 31, 34, 36, 38, 41,
43, 45, 48, 50, 52, 55, 57, 59, 62, 64, 66, 69, 71, 73, 76, 78, 80,
83, 85, 87, 90, 92, 94, 97, 99}*)