如何从数组中获取平均值?
如果我有以下数组:
[0,4,8,2,5,0,2,6]
取平均值的话会得到 3.375。
如何从数组中获取平均值?
如果我有以下数组:
[0,4,8,2,5,0,2,6]
取平均值的话会得到 3.375。
试试这个:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
请注意.to_f
,当避免整数除法问题时需要使用它。您也可以执行以下操作:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
您可以按照另一位评论者的建议将其定义为Array
的一部分,但是您需要避免使用整数除法,否则结果将会出错。此外,这并不适用于每种可能的元素类型(显然,平均值只对可以平均的事物有意义)。但如果您想要这样做,请使用以下方法:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
如果您之前没有见过inject
,它并没有看起来那么神奇。它遍历每个元素,然后对其应用累加器值。然后将累加器传递给下一个元素。在这种情况下,我们的累加器只是反映所有先前元素的总和的整数。
编辑:评论者Dave Ray提出了一个不错的改进。
编辑:评论者Glenn Jackman的建议,使用arr.inject(:+).to_f
,也很好,但如果您不知道发生了什么,则可能有点太聪明了。 :+
是一个符号;当传递给inject时,它将该符号命名的方法(在本例中为加法运算)应用于每个元素与累加器值。
inject
方法中传递一个初始值来消除to_f
和?
操作符:arr.inject(0.0) { |sum,el| sum + el } / arr.size
。 - Dave Rayinject
接口的一部分,在文档中有提到。to_proc
运算符是&
。 - ChuckArray#inject
在这里就有点过头了。只需要使用#sum
即可。例如:arr.sum.to_f / arr.size
。 - nickha = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
不使用 instance_eval
的版本如下:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
允许您只指定一次 a
并运行代码,因此可以与其他命令链接在一起。例如,使用 instance_eval
可以这样写:random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
,而不是这样写:random = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
。 - Benjamin Mannsinstance_eval
类似于使用tap
,yield_self
等方法来处理一系列的方法调用:只要适当且有帮助,就请使用它!在这种情况下,我绝对相信它是有用的。 - Tyler Rick我认为最简单的答案是
list.reduce(:+).to_f / list.size
reduce
是由Array
使用的Enumerable
mixin的一个方法。尽管它的名字如此,但我同意@ShuWu的看法...除非你正在使用实现了sum
的Rails。 - Tom Harrison我希望使用 Math.average(values) 进行计算,但没有这样的运气。
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
的方法,因此在6年之后这似乎是一个正确的答案,值得获得诺斯特拉达姆斯奖。请注意,此处使用的动词是“will”,表示未来进行时。 - steenslagRuby版本>=2.4具有Enumerable#sum方法。
要获取浮点平均值,可以使用Integer#fdiv。
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
针对旧版本:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
无需重复数组(例如,非常适合一行代码):
[1, 2, 3, 4].then { |a| a.sum.to_f / a.size }
.then
! - Victor以下是一些排名最高的解决方案的基准测试结果(按效率排序):
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)
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
nil
的总和而不是0? - Andrew Grimm让我介绍一种解决除零问题的竞争方案:
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
顺便说一句:我认为空列表的平均值是nil是正确的。没有任何数据的平均值应该是无,而不是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
我不喜欢被接受的解决方案的原因
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
问题在于它并不是以纯函数的方式运作。
我们需要一个变量 arr 来计算最终的 arr.size。
要完全以函数式的方式解决这个问题,我们需要跟踪两个值:所有元素的总和和元素数量。
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh改进了这个解决方案: 不用将参数r定义为数组,我们可以使用解构语法立即将其拆分成两个变量
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
如果您想看看它是如何工作的,请添加一些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
我们也可以使用结构体而不是数组来包含总和和计数,但这样我们必须先声明结构体:
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(:/)
end.method
的使用,谢谢您! - Epigenearr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
- Santhosh