Jak znaleźć średnią z tablicy?
Jeśli mam tablicę:
[0,4,8,2,5,0,2,6]
Uśrednianie dałoby mi 3,375.
Jak znaleźć średnią z tablicy?
Jeśli mam tablicę:
[0,4,8,2,5,0,2,6]
Uśrednianie dałoby mi 3,375.
Odpowiedzi:
Spróbuj tego:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Zwróć uwagę na to .to_f
, co chcesz uniknąć problemów z dzieleniem liczb całkowitych. Możesz także:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Możesz zdefiniować go jako część, Array
jak sugerował inny komentator, ale musisz unikać dzielenia liczb całkowitych, w przeciwnym razie wyniki będą błędne. Ponadto nie ma to ogólnie zastosowania do każdego możliwego typu elementu (oczywiście średnia ma sens tylko dla rzeczy, które można uśrednić). Ale jeśli chcesz wybrać tę trasę, skorzystaj z tego:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Jeśli jeszcze tego nie widziałeś inject
, nie jest to tak magiczne, jak mogłoby się wydawać. Iteruje po każdym elemencie, a następnie stosuje do niego wartość akumulatora. Akumulator jest następnie przekazywany do następnego elementu. W tym przypadku nasz akumulator jest po prostu liczbą całkowitą, która odzwierciedla sumę wszystkich poprzednich elementów.
Edycja: Komentator Dave Ray zaproponował miłą poprawę.
Edycja: Używanie propozycji komentatora Glenna Jackmana arr.inject(:+).to_f
jest również miłe, ale może zbyt sprytne, jeśli nie wiesz, co się dzieje. To :+
jest symbol; przekazany do wtrysku, stosuje metodę nazwaną przez symbol (w tym przypadku operację dodawania) do każdego elementu w stosunku do wartości akumulatora.
arr.inject(0.0) { |sum,el| sum + el } / arr.size
.
inject
interfejsu, o której mowa w dokumentacji. to_proc
Operator &
.
Array#inject
tutaj jest przesada. Po prostu użyj #sum
. Np.arr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Wersja tego, która nie używa instance_eval
, to:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
pozwala uruchomić kod, określając tylko a
jeden raz, dzięki czemu można go połączyć z innymi poleceniami. Tj. random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
Zamiastrandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
self
wewnątrz tego bloku, możesz napotkać problemy.) instance_eval
To więcej w przypadku metaprogramowania lub DSL.
Uważam, że najprostsza odpowiedź brzmi
list.reduce(:+).to_f / list.size
reduce
jest to metoda Enumerable
miksu używana przez Array
. I pomimo jego nazwy, zgadzam się z @ShuWu ... chyba że używasz Railsów, które implementują sum
.
Miałem nadzieję, że Math. Średnia (wartości), ale bez takiego szczęścia.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
metodę, więc wydaje się, że jest to poprawna odpowiedź po 6 latach, godna nagrody Nostradamus.
Wersje Ruby> = 2.4 mają metodę Enumerable # sum .
Aby uzyskać średnią zmiennoprzecinkową, możesz użyć liczby całkowitej # fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
W przypadku starszych wersji:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
Niektóre testy porównawcze najlepszych rozwiązań (w kolejności najbardziej wydajnych):
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
zamiast 0?
Pozwól, że przyniosę coś do rywalizacji, która rozwiązuje problem dzielenia przez zero:
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
Muszę jednak przyznać, że „try” to pomocnik Railsów. Ale możesz to łatwo rozwiązać:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
BTW: Myślę, że to prawda, że średnia pustej listy wynosi zero. Średnia niczego to nic, a nie 0. To jest oczekiwane zachowanie. Jeśli jednak zmienisz na:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
wynik dla pustych tablic nie będzie wyjątkiem, jak się spodziewałem, ale zwraca NaN ... Nigdy wcześniej tego nie widziałem w Ruby. ;-) Wydaje się, że jest to szczególne zachowanie klasy Float ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
czego nie lubię w zaakceptowanym rozwiązaniu
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
jest to, że tak naprawdę nie działa w czysto funkcjonalny sposób. potrzebujemy zmiennej arr do obliczenia arr.size na końcu.
aby rozwiązać to czysto funkcjonalnie, musimy śledzić dwie wartości: sumę wszystkich elementów i liczbę elementów.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh ulepszył to rozwiązanie: zamiast argumentu r będącego tablicą, moglibyśmy użyć destrukcji, aby natychmiast rozdzielić ją na dwie zmienne
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
jeśli chcesz zobaczyć, jak to działa, dodaj kilka putów:
[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
Możemy również użyć struktury zamiast tablicy do przechowywania sumy i liczby, ale najpierw musimy zadeklarować strukturę:
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
użyłem rubinu, dzięki za to!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Dla rozrywki publicznej jeszcze jedno rozwiązanie:
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
Array#average
.Często robiłem to samo, więc pomyślałem, że rozsądnie byłoby po prostu rozszerzyć Array
klasę za pomocą prostej average
metody. Nie działa na nic poza tablicą liczb takich jak liczby całkowite, zmiennoprzecinkowe lub dziesiętne, ale jest przydatny, gdy używasz go poprawnie.
Korzystam z Ruby on Rails, więc umieściłem go, config/initializers/array.rb
ale możesz go umieścić w dowolnym miejscu na starcie itp.
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]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375
Rozwiązuje dzielenie przez zero, dzielenie liczb całkowitych i jest łatwy do odczytania. Można go łatwo zmodyfikować, jeśli wybierzesz opcję zwracania pustej tablicy 0.
Ten wariant też mi się podoba, ale jest trochę bardziej niewygodny.
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
Ta metoda może być pomocna.
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
Krótka, ale przy użyciu zmiennej instancji
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
tworzyć zmienną instancji.
Możesz spróbować czegoś takiego:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0