如何从Ruby数组创建平均值?

时间:2009-08-27 13:54:25

标签: ruby-on-rails ruby

如何从阵列中找到平均值?

如果我有阵列:

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

平均值会给我3.375。

谢谢!

24 个答案:

答案 0 :(得分:244)

试试这个:

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提出了一个很好的改进。

编辑:评论员格伦·杰克曼的提议,使用arr.inject(:+).to_f,也很不错,但如果你不知道发生了什么,也许有点太聪明了。 :+是一个符号;当传递给inject时,它将符号命名的方法(在本例中为加法运算)应用于累加器值。

答案 1 :(得分:106)

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

答案 2 :(得分:88)

我相信最简单的答案是

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

答案 3 :(得分:45)

我希望获得Math.average(值),但没有这样的运气。

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

答案 4 :(得分:28)

Ruby版本> = 2.4具有Enumerable#sum方法。

要获得浮点平均值,您可以使用Integer#fdiv

file = open("read_it.txt" , "r")
for line in file:
            line = line.strip('\n')
            print(line)

total=[]
GTIN=''
while True:
    GTIN=(input("please enter GTIN for product"))
    if GTIN.isnumeric() and len(GTIN)==8 and GTIN in open('read_it.txt').read():
        total.append(GTIN)

    Quantity=""
    while True:
        Quantity=(input("Please enter the Quantity"))
        if Quantity.isdigit():
            break
        else:
            print("enter a number!")
    else:
        print("Product Not Found")
        break

    with open("read_it.txt", "r") as text_file:
        for items in text_file:
            line = items.strip('\r\n').split(",")
            if GTIN in items:
                allprice=[]
                product = line[1]
                indprice = line[2]
                finprice = float(indprice)* float(Quantity)
                print(GTIN,product,Quantity,"£",indprice,"£",finprice)
                finprice=int(finprice)
                total.append(finprice)
                allprice.append(finprice)
                break
# if user press key :
#     final = sum(allprice)
#     print(final)

对于旧版本:

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

arr.sum.fdiv(arr.size)
# => 3.375

答案 5 :(得分:6)

顶级解决方案的一些基准测试(按照最有效的顺序):

大数组:

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)

答案 6 :(得分:4)

为了公共娱乐,还有另一种解决方案:

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

答案 7 :(得分:4)

让我把一些东西带入竞争中,解决零问题的划分:

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

BTW:我认为空列表的平均值为零是正确的。没有什么是平均值,不是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

答案 8 :(得分:3)

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

答案 9 :(得分:3)

无需重复数组(例如,非常适合单线):

[1, 2, 3, 4].then { |a| a.sum.to_f / a.size }

答案 10 :(得分:3)

我不喜欢接受的解决方案

arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5

它实际上并不是以纯粹的功能方式工作。 我们需要一个变量arr来计算最后的arr.size。

要从纯粹的功能上解决这个问题,我们需要跟踪两个 values:所有元素的总和,以及元素的数量。

[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

我们也可以使用struct而不是数组来包含sum和count,但是我们必须首先声明struct:

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(:/)

答案 11 :(得分:2)

另一个简单的解决方案

arr = [0,4,8,2,5,0,2,6]
arr.sum(0.0) / arr.size

答案 12 :(得分:2)

这台电脑上没有红宝石,但这种程度的东西应该有效:

values = [0,4,8,2,5,0,2,6]
total = 0.0
values.each do |val|
 total += val
end

average = total/values.size

答案 13 :(得分:1)

arr = [0,4,8,2,5,0,2,6] average = arr.inject(&:+).to_f / arr.size => 3.375

答案 14 :(得分:1)

添加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

答案 15 :(得分:1)

此方法可能会有所帮助。

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])

答案 16 :(得分:1)

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

答案 17 :(得分:1)

a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length

答案 18 :(得分:0)

您可以尝试以下内容:

2.0.0-p648 :009 >   a = [1,2,3,4,5]
 => [1, 2, 3, 4, 5]
2.0.0-p648 :010 > (a.sum/a.length).to_f
 => 3.0

答案 19 :(得分:0)

print array.sum / array.count是我做的方式

答案 20 :(得分:0)

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

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类,这种做法很危险,并且可能对您的系统造成不良影响。

对于我们来说,红宝石语言提供了一个很好的功能来克服此问题,即优化,这是在红宝石上修补猴子的安全方法。

为简化起见,使用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

答案 21 :(得分:0)

我真的很喜欢定义一个 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,只需删除两个.compact

答案 22 :(得分:0)

比 .inject 更快的解决方案是:

<块引用>

arr.sum(0.0) / arr.size

参考这篇文章:https://andycroll.com/ruby/calculate-a-mean-average-from-a-ruby-array/

答案 23 :(得分:-1)

[1,2].tap { |a| @asize = a.size }.inject(:+).to_f/@asize

短但使用实例变量