如何从 Ruby 数组创建平均值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1341271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I create an average from a Ruby array?
提问by dotty
How would get find an average from an array?
如何从数组中找到平均值?
If I have the array:
如果我有数组:
[0,4,8,2,5,0,2,6]
Averaging would give me 3.375.
平均会给我 3.375。
回答by John Feminella
Try this:
尝试这个:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Note the .to_f, which you'll want for avoiding any problems from integer division. You can also do:
请注意.to_f,您需要使用它来避免整数除法带来的任何问题。你也可以这样做:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
You can define it as part of Arrayas another commenter has suggested, but you need to avoid integer division or your results will be wrong. Also, this isn't generally applicable to every possible element type (obviously, an average only makes sense for things that can be averaged). But if you want to go that route, use this:
您可以将其定义Array为另一位评论者建议的一部分,但您需要避免整数除法,否则您的结果将是错误的。此外,这通常不适用于所有可能的元素类型(显然,平均值仅对可以平均的事物有意义)。但是,如果您想走那条路,请使用以下命令:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
If you haven't seen injectbefore, it's not as magical as it might appear. It iterates over each element and then applies an accumulator value to it. The accumulator is then handed to the next element. In this case, our accumulator is simply an integer that reflects the sum of all the previous elements.
如果你以前没见过inject,它并不像看起来那么神奇。它遍历每个元素,然后对其应用累加器值。然后将累加器交给下一个元素。在这种情况下,我们的累加器只是一个整数,它反映了所有先前元素的总和。
Edit:Commenter Dave Ray proposed a nice improvement.
编辑:评论者 Dave Ray 提出了一个很好的改进。
Edit:Commenter Glenn Hymanman's proposal, using arr.inject(:+).to_f, is nice too but perhaps a bit too clever if you don't know what's going on. The :+is a symbol; when passed to inject, it applies the method named by the symbol (in this case, the addition operation) to each element against the accumulator value.
编辑:评论者格伦Hyman曼的提议,使用arr.inject(:+).to_f, 也很好,但如果你不知道发生了什么,可能有点太聪明了。的:+是一个符号; 当传递给 inject 时,它会将由符号命名的方法(在本例中为加法运算)应用到针对累加器值的每个元素。
回答by Corban Brook
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
A version of this that does not use instance_evalwould be:
一个不使用的版本instance_eval是:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
回答by Shu Wu
I believe the simplest answer is
我相信最简单的答案是
list.reduce(:+).to_f / list.size
回答by Denny Abraham
I was hoping for Math.average(values), but no such luck.
我希望 Math.average(values),但没有这样的运气。
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
回答by Santhosh
Ruby versions >= 2.4 has an Enumerable#summethod.
Ruby 版本 >= 2.4 有一个Enumerable#sum方法。
And to get floating point average, you can use Integer#fdiv
要获得浮点平均值,您可以使用Integer#fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
For older versions:
对于旧版本:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
回答by stevenspiel
Some benchmarking of top solutions (in order of most efficient):
顶级解决方案的一些基准测试(按最有效的顺序):
Large Array:
大阵列:
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)
Small Arrays:
小阵列:
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)
回答by astropanic
class Array
def sum
inject( nil ) { |sum,x| sum ? sum+x : x }
end
def mean
sum.to_f / size.to_f
end
end
[0,4,8,2,5,0,2,6].mean
回答by hurikhan77
Let me bring something into competition which solves the division by zero problem:
让我把一些东西带入竞争,解决除零问题:
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
I must admit, however, that "try" is a Rails helper. But you can easily solve this:
然而,我必须承认,“try”是一个 Rails 助手。但是你可以轻松解决这个问题:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
BTW: I think it is correct that the average of an empty list is nil. The average of nothing is nothing, not 0. So that is expected behavior. However, if you change to:
顺便说一句:我认为空列表的平均值为零是正确的。没有的平均值是什么,而不是 0。所以这是预期的行为。但是,如果您更改为:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
the result for empty Arrays won't be an exception as I had expected but instead it returns NaN... I've never seen that before in Ruby. ;-) Seems to be a special behavior of the Float class...
空数组的结果不会像我预期的那样异常,而是返回 NaN ......我以前从未在 Ruby 中看到过这种情况。;-) 似乎是 Float 类的特殊行为......
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
回答by bjelli
what I don't like about the accepted solution
我不喜欢接受的解决方案
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
is that it does not really work in a purely functional way. we need a variable arr to compute arr.size at the end.
是它并没有真正以纯粹的功能方式工作。最后我们需要一个变量 arr 来计算 arr.size 。
to solve this purely functionally we need to keep track of two values: the sum of all elements, and the number of elements.
为了纯粹从功能上解决这个问题,我们需要跟踪两个值:所有元素的总和和元素的数量。
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh improved on this solution: instead of the argument r being an array, we could use destructuring to immediatly pick it apart into two variables
Santhosh 改进了这个解决方案:我们可以使用解构将其分解为两个变量,而不是参数 r 是一个数组
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
if you want to see how it works, add some puts:
如果你想看看它是如何工作的,添加一些看跌期权:
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
r2 = [ sum + ele, size + 1 ]
puts "adding #{ele} gives #{r2}"
r2
end.inject(:/)
adding 5 gives [5.0, 1]
adding 6 gives [11.0, 2]
adding 7 gives [18.0, 3]
adding 8 gives [26.0, 4]
=> 6.5
We could also use a struct instead of an array to contain the sum and the count, but then we have to declare the struct first:
我们也可以使用结构体而不是数组来包含总和和计数,但是我们必须首先声明结构体:
R=Struct.new(:sum, :count)
[5, 6, 7, 8].inject( R.new(0.0, 0) ) do |r,ele|
r.sum += ele
r.count += 1
r
end.inject(:/)
回答by Boris Stitnicky
For public amusement, yet another solution:
对于公共娱乐,另一种解决方案:
a = 0, 4, 8, 2, 5, 0, 2, 6
a.reduce [ 0.0, 0 ] do |(s, c), e| [ s + e, c + 1 ] end.reduce :/
#=> 3.375

