# Calculate mean of values in bins

Problem description: I have a list of $\{x,y\}$ pairs. I'd like to divide $x$ into equal[*] bins, say $bx_1, bx_2, \ldots$, calculate $\left<y\right>$ for every bin and then plot the bin values versus the means. I.e., plot $bx_i$ versus $\left<y\right>_i$ with ListPlot[].

Question: I've already done it manually, but I was wondering whether:

1. exists a builtin function in Mathematica that does what I want.
2. exists a builtin function that I could use in my own implementation (e.g., HistogramList[]?).

[*] Bins with the same interval. Equally spaced intervals.

EDIT: Largely off-topic, but in R it turns out to be very easy with the fields package:

library('fields')
df <- read.table('my.dat')    # V1 -> off-axis distance, V2 -> energy
st <- stats.bin(x=df$V1, y=df$V2, N=100)
df2 <- as.data.frame(st$stats["mean",]) # Plot mean energy for every distance bin # EDIT: Actually I should plot against centers of st, but anyway. names(df2) <- c('mean.energy') plot(df2$mean.energy, type="s",
xlab="Off-axis distance (mm)", ylab="Mean Energy (MeV)")


-
Define "equal" - interval span, number of elements, what? – ciao Mar 26 '14 at 6:25
Take a look at BinLists and BinCount. – ciao Mar 26 '14 at 6:46
possibly related? mathematica.stackexchange.com/a/3996/1089 – chris Mar 26 '14 at 8:34
– kglr May 4 '14 at 21:55
– Murta May 4 '14 at 23:28

I'll throw this out, used it for a similar problem, since I didn't recall and simple built-in to do this:

(* test data *)
test = RandomInteger[20, {100, 2}];

(* number of bins *)
numbins = 10;

(* setup *)
{t1, t2} = Transpose@test;
{bins, bincnt} = ConstantArray[0, {2, numbins}];

{min, max} = Through[{Min, Max}[t1]];

intervals =
Interval/@Partition[Table[x, {x, min, max, Subtract[max, min]/numbins}], 2, 1];

binpos = (Pick[Range@numbins, IntervalMemberQ[intervals, #]] & /@ t1)[[All, 1]];

Inner[(bincnt[[#]]++; bins[[#]] += #2) &, binpos, t2];

(* results *)
Transpose[{List @@@ intervals, bins, bincnt}]
mean = bins/bincnt

(*

{{{{0, 2}}, 149, 13}, {{{2, 4}}, 133, 11}, {{{4, 6}}, 101, 9}, {{{6, 8}}, 44, 8},
{{{8, 10}}, 87, 12}, {{{10, 12}}, 116, 10}, {{{12, 14}}, 127, 12},
{{{14, 16}}, 80, 8} {{{16, 18}}, 83, 8}, {{{18, 20}}, 86, 9}}

{149/13, 133/11, 101/9, 11/2, 29/4, 58/5, 127/12, 10, 83/8, 86/9}

*)


Example result shows a list of lists, each with the interval, total, and count, and the mean of the bins.

-
@Kuba: ? do you mean if a value (e.g. 2) falls on two intervals (e.g. [1,2] and [2,3])? Taken care of by selection of first interval. – ciao May 4 '14 at 22:35
I've made a mistake, I'm sorry :) – Kuba May 4 '14 at 22:43
@Kuba: No worries, I'd forgotten about this post - curious why it came alive ;-) – ciao May 4 '14 at 22:47
OP made an edit :) – Kuba May 4 '14 at 22:48

Here is another option, using GatherBy, Floor and Rules.

binListsBy[data_List,binSize_,binIndex_Integer,aggrateFunctions_,nullValue_:Null]:=Module[{max,min},
{min,max}=Floor[Through@{Min,Max}@data[[All,binIndex]],binSize];
binListsBy[data,{min,max,binSize},binIndex,aggrateFunctions,nullValue]
]

binListsBy[data_List,{min_,max_,binSize_},binIndex_Integer,aggrateFunctions_,nullValue_:Null]:=
Module[{intervals,findInterval,binRule},

intervals=Partition[Range[min,max+binSize,binSize],2,1];
findInterval={Floor[#,binSize],Floor[#,binSize]+binSize}&;
binRule=findInterval@#[[1,binIndex]] -> {findInterval@#[[1,binIndex]],aggrateFunctions@@Transpose@#}&/@GatherBy[data,findInterval[#[[binIndex]]]&];

binRule=Dispatch@Join[binRule,{{a_,b_}:> {{a,b},nullValue}}];
intervals/.binRule
]

test = RandomInteger[{20,100}, {100, 2}];
binedList=binListsBy[test,20,1,N@Mean[#2]/Length[#1]&,0]


{{{20,40},5.36111},{{40,60},3.18519},{{60,80},4.10938},{{80,100},6.91837},{{100,120},59.}}

Using ListPlot:

ListPlot[Transpose@{binedList[[All,1,2]],binedList[[All,-1]]}
,Joined->True
,InterpolationOrder->0
,Frame->True
,FrameLabel->{{"Mean Energy (MeV)"},{"Off-axis distance (mm)"}}
,PlotStyle->{Thick,Darker@Green}
]


-

This is another version which uses BinList[] as per @rasher's suggestion:

binAndMean[xdata_List, ydata_List, nbins_Integer] :=
Module[{xbins, bins},
xbins = Array[# &, nbins + 1, {Min@xdata, Max@xdata}];
bins = BinLists[Transpose[{xdata, ydata}], {xbins}, {{-Infinity, +Infinity}}];
Table[Last /@ Flatten[bins[[k]], 1] // Mean, {k, 1, Length@bins}]]


I could have made the function accept a list of {x,y} pairs, but I found this more convenient because I already have my data as separate lists.

-