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
# 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)")

enter image description here

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

3 Answers 3

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

enter image description here

share|improve this answer

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

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.