Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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
names(df2) <- c('mean.energy')
plot(df2$mean.energy, type="s",
    xlab="Off-axis distance (mm)", ylab="Mean Energy (MeV)")

enter image description here

share|improve this question
    
Define "equal" - interval span, number of elements, what? –  rasher Mar 26 at 6:25
1  
Take a look at BinLists and BinCount. –  rasher Mar 26 at 6:46
1  
possibly related? mathematica.stackexchange.com/a/3996/1089 –  chris Mar 26 at 8:34
    
see also: Histogram3D using sums instead of counts –  kguler May 4 at 21:55
1  
add comment

3 Answers

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.

share|improve this answer
    
@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. –  rasher May 4 at 22:35
    
I've made a mistake, I'm sorry :) –  Kuba May 4 at 22:43
    
@Kuba: No worries, I'd forgotten about this post - curious why it came alive ;-) –  rasher May 4 at 22:47
    
OP made an edit :) –  Kuba May 4 at 22:48
add comment

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}
]

enter image description here

share|improve this answer
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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