如何从数组中找到平均值?
如果我有数组:
[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
,也很好,但如果您不知道发生了什么,可能有点太聪明了。 :+
是一个符号;当传递给注入时,它会将符号命名的方法(在本例中为加法运算)应用于每个元素的累加器值。
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
是 Array
使用的 Enumerable
mixin 的一种方法。尽管它的名字,我同意@ShuWu ...除非您使用的是实现sum
的Rails。
我希望 Math.average(values),但没有这样的运气。
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
方法,因此这似乎是 6 年后的正确答案,值得诺查丹玛斯奖。
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)
无需重复数组(例如,非常适合单行):
[1, 2, 3, 4].then { |a| a.sum.to_f / a.size }
.then
!
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
然而,我必须承认,“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
对于公共娱乐,还有另一种解决方案:
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
我不喜欢接受的解决方案
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
,谢谢!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
这台电脑上没有红宝石,但这种程度的东西应该可以工作:
values = [0,4,8,2,5,0,2,6]
total = 0.0
values.each do |val|
total += val
end
average = total/values.size
另一个简单的解决方案
arr = [0,4,8,2,5,0,2,6]
arr.sum(0.0) / arr.size
您可以根据需要选择以下解决方案之一。
蛮力
[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 的一种安全方法。
为简化起见,您可以使用 Refinements 对 Array
类进行猴子修补,并且更改将仅在使用细化的类的范围内可用! :)
您可以在您正在学习的课程中使用细化,然后您就可以开始了。
改进
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
添加数组#average。
我经常做同样的事情,所以我认为用一个简单的 average
方法扩展 Array
类是明智的。除了像整数、浮点数或小数这样的数字数组之外,它对任何东西都不起作用,但是当你正确使用它时它很方便。
我正在使用 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.to_d / self.size
end
end
[1, 2, 3, 4].average
。它返回 2
而不是 2.5
。如果有人需要 Monkey Patch 的完整解决方案,可以在这里找到:stackoverflow.com/a/65337711/7644846
size
之前将 sum
转换为 BigDecimal
来修复它。现在,[1, 2, 3, 4].average #=> 0.25e1
。再次感谢。
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,则可以轻松修改。
我也喜欢这个变体,但它有点罗嗦。
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])
print array.sum / array.count 是我如何做到的
我真的很喜欢定义一个 mean()
方法,以便我的代码更具表现力。
我通常想默认忽略 nil
,所以这是我定义的
def mean(arr)
arr.compact.inject{ |sum, el| sum + el }.to_f / arr.compact.size
end
mean([1, nil, 5])
=> 3.0
如果您想保留 nil
,只需删除 both .compact
。
比 .inject 更快的解决方案是:
arr.sum(0.0) / arr.size
请参阅这篇文章以获取参考:https://andycroll.com/ruby/calculate-a-mean-average-from-a-ruby-array/
[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
而不是创建实例变量。
您可以尝试以下方法:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0
a = [1, 2, 3, 4]
。平均值应为 2.5
而不是 2.0
。它应该是 a.sum.to_f / a.length.to_f
。
arr.inject(0.0) { |sum,el| sum + el } / arr.size
。inject
接口的一部分。to_proc
运算符是&
。Array#inject
在这里是多余的。只需使用#sum
。例如arr.sum.to_f / arr.size