Ruby 배열에서 평균을 만들려면 어떻게합니까?


209

배열에서 평균을 어떻게 구할 수 있습니까?

배열이있는 경우 :

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

평균화하면 3.375가 표시됩니다.


11
그 숫자의 평균으로 21.75를 얻는다면, 뭔가 잘못되었습니다 ...
ceejayoz

2
dotty, 어떻게 21.75를 얻었는지 모르지만 해당 데이터 세트의 평균 / 평균은 3.375이고 합계는 27입니다. 어떤 종류의 집계 함수가 21.75를 산출하는지 잘 모르겠습니다. 다시 한 번 확인하고 평균이 실제로 당신이 추구하는 것인지 확인하십시오!
Paul Sasik

2
나는 어디에서 21.75를 얻었는지 전혀 모른다. 계산기에서 0 + 48 + 2 + 5 + 0 + 2 + 6와 같은 것을 눌러야합니다!
dotty

16
여기에는 루비 온 레일 (Ruby-on-rails)이라는 태그가 붙어 있기 때문에 ActiveRecord 배열을 평균화하는 경우 활성 레코드 계산이 필요합니다. Person.average (: age, : country => 'Brazil')은 브라질 인의 평균 연령을 반환합니다. 정말 멋진!
Kyle Heironimus

답변:


259

이 시도:

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전에 보지 못했다면 , 그것은 마술처럼 보이지 않습니다. 각 요소를 반복 한 다음 누산기 값을 적용합니다. 그런 다음 누산기가 다음 요소로 전달됩니다. 이 경우 누적 기는 단순히 이전의 모든 요소의 합을 반영하는 정수입니다.

편집자 : Commenter Dave Ray는 훌륭한 개선을 제안했습니다.

편집 : Commenter Glenn Jackman의 제안은을 사용하여 arr.inject(:+).to_f훌륭하지만 어떤 일이 일어나고 있는지 모른다면 약간 영리합니다. 는 :+심볼이고; 주입하도록 전달되면 누적 기 값에 대해 기호로 명명 된 메소드 (이 경우 추가 연산)가 각 요소에 적용됩니다.


6
to_f와?를 제거 할 수 있습니다. 주입 할 초기 값을 전달하여 연산자 : arr.inject(0.0) { |sum,el| sum + el } / arr.size.
Dave Ray

103
. 또는 : arr.inject (+) to_f / arr.size # => 3.375
글렌 잭맨

5
Array 클래스에 추가 할 수 있다고 생각하지는 않습니다. Arrays가 포함 할 수있는 모든 유형에 대해 일반화 할 수는 없기 때문입니다.
Sarah Mei

8
@John : 정확히 Symbol # to_proc 변환이 아닙니다 inject. 설명서에서 언급 한 인터페이스 의 일부입니다 . to_proc연산자이다 &.

21
Rails를 사용하고 있다면 Array#inject여기서 과잉입니다. 그냥 사용하십시오 #sum. 예arr.sum.to_f / arr.size
nickh

113
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

4
나는 그것이 너무 영리하다고 생각하지 않습니다. 관용적으로 문제를 해결한다고 생각합니다. 즉, 정확히 사용하는 reduce를 사용합니다. 프로그래머는 무엇이 옳고, 왜 옳은지를 이해하고 전파해야합니다. 평균과 같은 사소한 작업의 경우에는 "영리한"작업이 필요하지 않습니다. 그러나 사소한 경우에 대한 "감소"가 무엇인지 이해함으로써 훨씬 복잡한 문제에 적용 할 수 있습니다. 공감.
pduey

3
여기서 instance_eval이 필요한 이유는 무엇입니까?
tybro0103

10
instance_evala한 번만 지정하면서 코드를 실행할 수 있으므로 다른 명령과 연결될 수 있습니다. 즉 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 Manns

2
나는이 방법으로 instance_eval을 사용하는 것이 이상하게 보인다는 것을 알지 못하며,이 접근법을 나쁜 생각, IMO로 만드는 많은 어려움이 있습니다. (예를 들어, self해당 블록 내부의 변수 또는 메소드에 액세스하고 인스턴스를 시도하면 문제가 발생할 수 있습니다.) instance_eval메타 프로그래밍 또는 DSL에 더 적합합니다.
Ajedi32

1
@ Ajedi32 동의합니다. 응용 프로그램 코드에서 이것을 사용하지 마십시오. 그러나 내 repl에 붙여 넣을 수있어서 매우 좋았습니다 (:
animatedgif

94

가장 간단한 대답은

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

1
그것을 찾는 데 시간이 걸렸습니다 .에 의해 사용되는 mixin reduce의 방법입니다 . 당신은 어느 구현 레일을 사용하지 않는 그리고 그 이름에도 불구하고, I합니다 ... @ShuWu에 동의 . EnumerableArraysum
Tom Harrison

나는 여기에서 해결책을 보았습니다. 매우 깔끔하게 보인다는 것을 알고 있지만 앞으로 코드를 읽을 경우 횡설수설처럼 보일 것입니다. 깨끗한 솔루션에 감사드립니다!
atmosx

내 시스템에서 이것은 허용되는 답변보다 3 배 빠릅니다.
sergio

48

나는 Math.average (values)를 원했지만 그런 행운은 없습니다.

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

3
Rails가 #sum을 추가 한 것을 몰랐습니다! 지적 해 주셔서 감사합니다.
데니 아브라함

11
크리스마스 2016 (루비 2.4) 후, 배열 됩니다sum이 노스트라 다무스의 수상의 가치 육년 후 정답 인 것으로 나타나도록, 방법.
steenslag

38

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

9

가장 효율적인 순서로 최고 솔루션의 일부 벤치마킹 :

큰 배열 :

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)

벤치 마크가 약간 잘못되었습니다. 벤치 마크 / IP는 실제로 이러한 종류의 비교에 더 좋습니다. 또한 더 현실적인 결과를 얻으려면 음수와 양수 및 부동 소수점으로 무작위로 채워진 배열을 사용하는 것이 좋습니다. instance_eval이 array.sum.fdiv보다 느립니다. 수레의 경우 약 8 배. 정수의 경우 약 x1.12입니다. 또한 OS마다 다른 결과가 나타납니다. 내 Mac에서 이러한 방법 중 일부는 내 리눅스 물방울보다 느린로 2 배
konung

또한 sum 방법은 합계를 계산하는 대신 범위에서 가우스 공식을 사용합니다.
Santhosh

4
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

2
정수 나누기 때문에 잘못된 값을 반환합니다. 예를 들어 [2,3] .mean을 사용해보십시오. 2.5 대신 2를 반환합니다.
John Feminella

1
빈 배열의 합 nil이 0 이 아닌 왜 됩니까?
앤드류 그림

1
[]와 [0]의 차이를 얻을 수 있기 때문입니다. 그리고 내가 생각하는 진정한 의미가 to_i을 활용하거나 0 위의 무기 호를 대체 할 수 원하는 사람들
astropanic

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

그러나 "시도"는 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이 아니라고 생각합니다. 아무것도 아닌 평균은 0이 아니라 아무것도 아닙니다. 따라서 예상되는 동작입니다. 그러나 다음과 같이 변경하면

class Array;def avg;reduce(0.0,:+).try(:/,size);end;end

빈 배열에 대한 결과는 예상대로 예외가 아니지만 대신 NaN을 반환합니다 ... 루비에서는 전에 본 적이 없습니다. ;-) Float 클래스의 특별한 동작 인 것 같습니다 ...

0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float

4

수용된 솔루션에 대해 내가 싫어하는 것

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

실제로 순전히 기능적인 방식으로 작동하지 않는다는 것입니다. 끝에 arr.size를 계산하려면 변수 arr이 필요합니다.

이 기능을 순전히 기능적으로 해결하려면 모든 요소의 합과 요소 수의 두 가지 값을 추적해야합니다.

[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루비에서 처음 사용 된 것을 보았습니다 . 감사합니다!
Epigene

주입 방법으로 전달 된 배열은 분산 될 수 있습니다. arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Santhosh

@ 산토시 : 예, 훨씬 더 읽기 쉽습니다! 나는이 비록 "분산"을 호출하지 것이다, 나는 "destructuring"부를 것이다 tony.pitluga.com/2011/08/08/destructuring-with-ruby.html
bjelli

3

공공 오락을위한 또 다른 해결책 :

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

1
이것이 투표에서 더 높았다면 나는 그것을 이해하지 못했을 것입니다! 아주 좋아요
매트 스티븐스

Clear는 영리한 것보다 낫습니다 .이 코드는 명확하지 않습니다.
Sebastian Palma

2

이 PC에는 루비가 없지만이 정도까지는 효과가 있습니다.

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

average = total/values.size

2

추가하십시오 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

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

4
정수 나누기 때문에 잘못된 값을 반환합니다. 예를 들어, a가 [2, 3]이면 예상 결과는 2.5이지만 2를 반환합니다.
John Feminella

1
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375

0으로 나누고 정수 나누기를 풀고 읽기 쉽습니다. 빈 배열이 0을 반환하도록 선택하면 쉽게 수정할 수 있습니다.

나는이 변형도 좋아하지만 조금 더 장황합니다.

a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375

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

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

1
여기에 스택 오버플로를 환영합니다. 질문의 원본 포스터는 3.375로 답변을 원하고 솔루션은 3을 제공합니다. i, e
27/8

귀하의 의견에 감사드립니다. 나는 질문의 원래 포스터가 3.375로 답을 원한다는 것을 알고 있으며 변수 'var'에 부동 값 (즉, 0.0)을 주었을 때이 방법이하는 일을 알고 있습니다. Munim Munna 나는 실제로 비슷한 대답이 있다는 것에 동의해야합니다.
Kishor Budhathoki

0

어레이를 반복 할 필요없이 (예 : 단일 라이너에 적합) :

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

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

짧지 만 인스턴스 변수 사용


2
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size인스턴스 변수를 만드는 것이 아니라 할 것입니다.
Andrew Grimm

-1

다음과 같은 것을 시도해 볼 수 있습니다.

a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.