如何从数组中找到平均值?
如果我有数组:
[0,4,8,2,5,0,2,6]
平均会给我3.375。
如何从数组中找到平均值?
如果我有数组:
[0,4,8,2,5,0,2,6]
平均会给我3.375。
Answers:
试试这个:
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
也是不错的,但是如果您不知道发生了什么,也许有点太聪明。的:+
是一个符号; 当传递给注入时,它将对累加器值应用由符号命名的方法(在本例中为加法运算)到每个元素。
arr.inject(0.0) { |sum,el| sum + el } / arr.size
。
inject
是文档中提到的接口的一部分。该to_proc
运营商&
。
Array#inject
在这里过大了。只需使用#sum
。例如arr.sum.to_f / arr.size
a = [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
一次即可运行代码,因此可以将其与其他命令链接在一起。即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
self
该块内的实例变量或方法,则会遇到问题。)instance_eval
元编程或DSL的使用更多。
我相信最简单的答案是
list.reduce(:+).to_f / list.size
reduce
是的方法Enumerable
所使用的mixin Array
。尽管它有名字,但我同意@ShuWu ...,除非您使用的是实现了的Rails sum
。
Ruby版本> = 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
最佳解决方案的一些基准测试(以最有效的顺序):
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?
让我带些东西来解决零分问题:
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
但是,我必须承认,“尝试”是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
我对接受的解决方案不满意的地方
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(:/)
如果要查看其工作原理,请添加一些看跌期权:
[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
用在红宝石中,谢谢!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Array#average
。我经常做同样的事情,所以我认为Array
用一个简单的average
方法扩展类是审慎的做法。除整数,浮点数或小数之类的数字数组外,它对其他任何功能均不起作用,但正确使用它非常方便。
我正在使用Ruby on Rails,因此已将其放置在其中,config/initializers/array.rb
但是您可以将其放置在引导等包含的任何位置。
config/initializers/array.rb
class Array
# Will only work for an Array of numbers like Integers, Floats or Decimals.
#
# Throws various errors when trying to call it on an Array of other types, like Strings.
# Returns nil for an empty Array.
#
def average
return nil if self.empty?
self.sum / self.size
end
end
a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375
解决除以零的整数除法,并且易于阅读。如果选择一个空数组返回0,则可以轻松修改。
我也喜欢这个变体,但是有点罗word。
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375
arr = [0,4,8,2,5,0,2,6]
average = arr.inject(&:+).to_f / arr.size
# => 3.375
此方法可能会有所帮助。
def avg(arr)
val = 0.0
arr.each do |n|
val += n
end
len = arr.length
val / len
end
p avg([0,4,8,2,5,0,2,6])
[1,2].tap { |a| @asize = a.size }.inject(:+).to_f/@asize
简短但使用实例变量
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
而不是创建一个实例变量。