4

I have this array:

[288.563044, 329.835918, 578.622569, 712.359026, 866.614253, 890.066321, 1049.78037, 1070.29897, 2185.443662, 2492.245562, 4398.300227, 13953.264379]

How do I calculate the interquartile mean from this?

That Wikipedia link explains it best, but I basically need to remove the bottom and top 25% leaving only the middle 50%, of which I'll need to average the numbers.

But that's assuming the number of array items is divisible by 4. Here's how to calculate it when it's not divisible by four.

So how would I do that as well?

Shpigford
  • 24,748
  • 58
  • 163
  • 252

3 Answers3

4

This is a partial solution for an array with a number of elements that is a multiple of 4. I'll put the full one when I figure it out.

arr = [288.563044, 329.835918, 578.622569, 712.359026, 866.614253, 890.066321, 1049.78037,    1070.29897, 2185.443662, 2492.245562, 4398.300227, 13953.264379].sort!
length = arr.size
mean = arr.sort[(length/4)..-(length/4+1)].inject(:+)/(length/2)

I think this is a better solution.

def interquartile_mean(array)
   arr = array.sort
   length = arr.size
   quart = (length/4.0).floor
   fraction = 1-((length/4.0)-quart)
   new_arr = arr[quart..-(quart + 1)]
   (fraction*(new_arr[0]+new_arr[-1]) + new_arr[1..-2].inject(:+))/(length/2.0)
end
Jesse Pollak
  • 1,600
  • 1
  • 15
  • 20
  • No need for the bang on `sort` here since you're operating on the modified array and returning a result, all without using the array again. Also, just pointing out for future reference, `Array#length` and `Array#size` (aliases) are faster than `count`. :) – coreyward Jan 13 '12 at 20:39
  • I'm not sure I'd `sort!` an array that doesn't belong to the method, though. I'll +1 anyway, although this question has disappointed me on two levels. – Dave Newton Jan 13 '12 at 20:51
  • Good thinking, I'll fix that. – Jesse Pollak Jan 13 '12 at 20:52
2

The simple case array_size mod 4 = 0:

xs = [5, 8, 4, 38, 8, 6, 9, 7, 7, 3, 1, 6]
q = xs.size / 4
ys = xs.sort[q...3*q]
mean = ys.inject(0, :+) / ys.size.to_f
#=> 6.5

The general case (array_size >= 4):

xs = [1, 3, 5, 7, 9, 11, 13, 15, 17]
q = xs.size / 4.0
ys = xs.sort[q.ceil-1..(3*q).floor]
factor = q - (ys.size/2.0 - 1)
mean = (ys[1...-1].inject(0, :+) + (ys[0] + ys[-1]) * factor) / (2*q)
#=> 9.0

However, if you don't try to code it yourself this won't help much...

tokland
  • 66,169
  • 13
  • 144
  • 170
1

An improvement on tokland's answer that augments the Array class and fixes an edge case (method as written blows up with array size of 4).

class Array
  def interquartile_mean
     a = sort
     l = size
     quart = (l.to_f / 4).floor
     t = a[quart..-(quart + 1)]
     t.inject{ |s, e| s + e }.to_f / t.size
  end
end
bouchard
  • 820
  • 10
  • 27