如何从数组中求平均值?

如果我有一个数组:

[0,4,8,2,5,0,2,6]

平均得到3.375。


当前回答

我非常喜欢定义一个mean()方法,这样我的代码更有表现力。

默认情况下我通常会忽略nil,这是我定义的

def mean(arr)
  arr.compact.inject{ |sum, el| sum + el }.to_f / arr.compact.size
end

mean([1, nil, 5])
=> 3.0

如果您想保留nils,只需删除两个.compact。

其他回答

让我在竞争中引入一些可以解决零除问题的东西

a = [1,2,3,4,5,6,7,8]
a.reduce(:+).try(:to_f).try(:/,a.size) #==> 4.5

a = []
a.reduce(:+).try(:to_f).try(:/,a.size) #==> nil

但是,我必须承认,“try”是一个Rails助手。但你可以很容易地解决这个问题:

class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end

顺便说一句:我认为空列表的平均值为零是正确的。零的平均值是零,不是0。这就是预期行为。但是,如果您更改为:

class Array;def avg;reduce(0.0,:+).try(:/,size);end;end

空数组的结果不会像我预期的那样是一个异常,而是返回NaN…我在Ruby中从未见过这种情况。;-)似乎是Float类的特殊行为…

0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float

一些顶级解决方案的基准测试(按效率最高的顺序排列):

大阵:

array = (1..10_000_000).to_a

Benchmark.bm do |bm|
  bm.report { array.instance_eval { reduce(:+) / size.to_f } }
  bm.report { array.sum.fdiv(array.size) }
  bm.report { array.sum / array.size.to_f }
  bm.report { array.reduce(:+).to_f / array.size }
  bm.report { array.reduce(:+).try(:to_f).try(:/, array.size) }
  bm.report { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size }
  bm.report { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) }
end


    user     system      total        real
0.480000   0.000000   0.480000   (0.473920)
0.500000   0.000000   0.500000   (0.502158)
0.500000   0.000000   0.500000   (0.508075)
0.510000   0.000000   0.510000   (0.512600)
0.520000   0.000000   0.520000   (0.516096)
0.760000   0.000000   0.760000   (0.767743)
1.530000   0.000000   1.530000   (1.534404)

小数组:

array = Array.new(10) { rand(0.5..2.0) }

Benchmark.bm do |bm|
  bm.report { 1_000_000.times { array.reduce(:+).to_f / array.size } }
  bm.report { 1_000_000.times { array.sum / array.size.to_f } }
  bm.report { 1_000_000.times { array.sum.fdiv(array.size) } }
  bm.report { 1_000_000.times { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size } }
  bm.report { 1_000_000.times { array.instance_eval { reduce(:+) / size.to_f } } }
  bm.report { 1_000_000.times { array.reduce(:+).try(:to_f).try(:/, array.size) } }
  bm.report { 1_000_000.times { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) } }
end


    user     system      total        real
0.760000   0.000000   0.760000   (0.760353)
0.870000   0.000000   0.870000   (0.876087)
0.900000   0.000000   0.900000   (0.901102)
0.920000   0.000000   0.920000   (0.920888)
0.950000   0.000000   0.950000   (0.952842)
1.690000   0.000000   1.690000   (1.694117)
1.840000   0.010000   1.850000   (1.845623)

我认为最简单的答案是

list.reduce(:+).to_f / list.size

您可以根据需要选择以下解决方案之一。

Bruteforce

[0,4,8,2,5,0,2,6].sum.to_f / [0,4,8,2,5,0,2,6].size.to_f

=> 3.375

方法

def avg(array)
  array.sum.to_f / array.size.to_f
end  

avg([0,4,8,2,5,0,2,6])
=> 3.375

猴子打补丁

class Array
  def avg
    sum.to_f / size.to_f
  end
end

[0,4,8,2,5,0,2,6].avg
=> 3.375

但我不建议对Array类进行猴子补丁,这种做法是危险的,可能会对您的系统造成不良影响。

为了我们的好处,ruby语言提供了一个很好的特性来克服这个问题,即Refinements,这是一种安全的方法来对ruby进行monkey补丁。

为了简化,使用细化,您可以猴子修补数组类,并且更改将只在使用细化的类范围内可用!:)

您可以在您正在处理的类中使用细化,并且您已经准备好了。

细化

module ArrayRefinements
  refine Array do
    def avg
      sum.to_f / size.to_f
    end
  end
end

class MyClass
  using ArrayRefinements

  def test(array)
    array.avg
  end
end

MyClass.new.test([0,4,8,2,5,0,2,6])
=> 3.375

打印数组。求和/数组。计数是我做到的方式