How to classify an ordered ascending {0, 5, 5, 10, 19, 22, 23} into several classes {{0,9},{10,19}, ...}?
data = {0, 5, 5, 10, 19, 22, 23, 24, 25, 33, 34, 40, 40, 42, 53, 62, 69, 74, 91, 91};
GatherBy
GatherBy[data, Quotient[#, 10] &]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
Split
Split[data, SameQ @@ Quotient[{##}, 10] &]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
GroupBy
Values @ GroupBy[Quotient[#, 10] &] @ data
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
BinLists
DeleteCases[{}] @ BinLists[data, 10]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
Or use the second column of you class
as bin limits:
binlims = {Join[{-Infinity}, class[[All, 2]], {Infinity}]};
DeleteCases[{}] @ BinLists[data, binlims]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
#
always binds itself to the most direct&
, and is equivalent to#1
, so#[[1]] <= # <= #[[2]]
is erroneous.TakeWhile
scans from the first element of the list. You're giving everyclass
the same list. As a result, e.g., for{10, 19}
, since the first element0
is not within this range, it immediately halts and returns{}
.
As an alternative, you should take elements and drop (pass) the rest to next class
:
FoldPairList[
TakeDrop[
#1, LengthWhile[#1, u \[Function] Between[u, #2]]
(* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
or `Between[#2] `, the operator form *)
] &, data, class]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {}, {91, 91}}
Other approaches
Level[SplitBy[data, Between /@ class], {-2}]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53}, {62, 69}, {74}, {91, 91}}
For Edit
We can get class
es from lower bounds l
:
class = Partition[Flatten[{l, \[Infinity]}], 2, 1]
{{17, 24}, {24, 31}, {31, 38}, {38, 45}, {45, 52}, {52, 59}, {59, 66}, {66, \[Infinity]}}
Then similarly,
FoldPairList[
TakeDrop[#1,
LengthWhile[#1, u \[Function] #2[[1]] <= u < #2[[2]]]
] &, data, class]
{{17, 18, 19, 20, 20, 20, 21, 21, 22, 22, 22, 22, 23}, {24, 24, 26, 27, 28, 28, 29, 29, 29, 30, 30}, {32, 32, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 36}, {38, 38, 39, 39, 41, 42, 42, 42, 43, 44, 44, 44}, {45, 45, 45, 46, 46, 47, 47, 49, 49, 51, 51, 51, 51, 51}, {52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58}, {59, 60, 61, 61, 62, 62, 63}, {69}}
Of course, it's always better to use built-in functions than implementing by yourself.
Just a quick benchmark of kglr's methods in version 10.1.
f1[data_] := GatherBy[data, Quotient[#, 10] &];
f2[data_] := Split[data, SameQ @@ Quotient[{##}, 10] &];
f3[data_] := Values@GroupBy[Quotient[#, 10] &]@data;
f4[data_] := DeleteCases[{}]@BinLists[data, 10];
f5[data_] := SplitBy[data, Quotient[#, 10] &];
Needs["GeneralUtilities`"]
BenchmarkPlot[{f1, f2, f3, f4, f5}, Sort@RandomInteger[5 #, #] &,
"IncludeFits" -> True]