# GatherBy using Conditional bins

If I had a dataset such as:

s={{1,1.1,1},{2,1.2,2},{1,2.3,1},{3,2.2,2},{1,3.5,1},{4,3.6,2},{1,4.2,3},{2,4.4,1}}


I want to gather sublists based on the 2nd term of each sublist, such that first group has 2nd term of each sublists between 0 and 2, the second group has 2nd term of each sublists between 2 and 3.7, the third group has 2nd term of each sublists between 3.7 and 5. My output should look like this:

output:= {{{1,1.1,1},{2,1.2,2}},{{1,2.3,1},{3,2.2,2},{1,3.5,1},{4,3.6,2}},{{1,4.2,3},{2,4.4,1}}}


I suspect it should be something of the sort:

r = {0, 2, 3.7, 5};
GatherBy[s, {#[] > #1 &, #[] < #2}] & /@ MapThread[Most@r, Rest@r]

• I mean, GatherBy[s, #[] < 2.5 &] does what you want. Can you provide an example for which that fails so that we have a better idea of what you're trying to do? – march Sep 20 '16 at 5:04
• if there are more than 2 groups, then I need to define more conditions. – brama Sep 20 '16 at 10:55
• @brama it is a different question then. – Kuba Sep 20 '16 at 11:16
• possible duplicate: mathematica.stackexchange.com/q/120696/121 – Mr.Wizard Sep 20 '16 at 13:45
• – Mr.Wizard Sep 21 '16 at 6:56

divisions = {2, 3.7, 5}


Then use GatherBy and use the sorted position of an element in this list as the gathering function

GatherBy[s, Position[Sort[Append[divisions, #[]]], #[]] &]

(* {{{1, 1.1, 1}, {2, 1.2, 2}}, {{1, 2.3, 1}, {3, 2.2, 2}, {1,
3.5, 1}, {4, 3.6, 2}}, {{1, 4.2, 3}, {2, 4.4, 1}}} *)

• Also GatherBy[s, First@Ordering@Ordering@Prepend[r, #[]] &] – Jacob Akkerboom Sep 21 '16 at 9:33

Maybe something like this

ineqFu[a_, b_, max_, expr_, data_] :=
If[
a == 0,
expr <= data[[b]]
,
If[
b == max,
data[[a]] < expr
,
data[[a]] < expr <= data[[b]]
]
]

max = Length@bounds + 1;
tokenizedIneqs =
ineqFu[##, max, token, r] & @@@
Partition[Range[0, max], 2, 1];
funkyTown =
Which @@@
(Function@
Evaluate[Riffle[tokenizedIneqs, Range[max]]] /.
token :> #[]);
result = GatherBy[s, funkyTown];


We then have

result === output


True

• Sorry for the confusion, but what I meant by my earlier comment is that if the dataset were larger and if there are multiple values for the #[] (such that the bins are 0 to 2.5, 2.5 to 4.5, 4.5 to 6.2, 6.2 to 7.4, and so on), how can I gather for the above bins? so my solution will be {{{x11,1.1,y11},{x21,2,y21},...{xn1,2.4,yn1}},{{x12,2.6,y12},{x22,2.8,y22},...{xn2,4.4,yn2}},..{{x1m,6.3,y1m},{x2m,6.8,y2m},...{xnm,7.3,ynm}}} – brama Sep 20 '16 at 11:57
• @brama If I understand you correctly (big if), then my code does what you ask. Note that the my variable result stores the result/output/solution, the reason I show the output of result[[All,All,2]] instead is because it is easier for me to see from this that the code works. – Jacob Akkerboom Sep 20 '16 at 12:38
• I am not afraid that's not what I want. Please see the edited question. Hope it is clearer. – brama Sep 20 '16 at 12:51
• @brama I hope I did not confuse you earlier by slightly adapting the input (data/s). But without adapting my code, the result of my code equals the desired output. – Jacob Akkerboom Sep 20 '16 at 13:06
• @brama I see that you have edited your data (s), but you have not made your output correspond to this yet. I mean there is a 1 in output that should be a 1.2. – Jacob Akkerboom Sep 20 '16 at 13:07
s = {{1, 1.1, 1}, {2, 1.2, 2}, {1, 2.3, 1}, {3, 2.2, 2}, {1, 3.5, 1}, {4, 3.6, 2}, {1, 4.2, 3}, {2, 4.4, 1}};
divisions = {2, 3.7, 5};

condGather[list_, divi_] :=
Block[{second, f, s = list, divisions = divi, els, ord},
second := #[] &;
f[x_] := Block[{divs, part, int, cond, which},
divs = Insert[Insert[divisions, -Infinity, 1], Infinity, -1];
part = Partition[divs, 2, 1];
int = IntervalMemberQ[#1, x] & /@ Interval /@ part;
cond = Riffle[int, Range[Length@divs - 1]];
which = Which[##] & @@ cond
];
els = f /@ second /@ s;
ord = Length /@ SplitBy[els, Max];
FoldPairList[TakeDrop, s, ord]
]

condGather[s, divisions]


{{{1, 1.1, 1}, {2, 1.2, 2}}, {{1, 2.3, 1}, {3, 2.2, 2}, {1, 3.5, 1}, {4, 3.6, 2}}, {{1, 4.2, 3}, {2, 4.4, 1}}}

TableForm[%, TableDepth -> 2] 