Wie würde man einen Durchschnitt aus einem Array finden?
Wenn ich das Array habe:
[0,4,8,2,5,0,2,6]
Die Mittelung würde mir 3,375 geben.
Wie würde man einen Durchschnitt aus einem Array finden?
Wenn ich das Array habe:
[0,4,8,2,5,0,2,6]
Die Mittelung würde mir 3,375 geben.
Antworten:
Versuche dies:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Beachten Sie die .to_f
, die Sie benötigen, um Probleme durch die Ganzzahldivision zu vermeiden. Sie können auch tun:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Sie können es als Teil von definieren, wie es ein Array
anderer Kommentator vorgeschlagen hat, aber Sie müssen die Ganzzahldivision vermeiden, da sonst Ihre Ergebnisse falsch sind. Dies gilt auch nicht generell für jeden möglichen Elementtyp (offensichtlich macht ein Durchschnitt nur für Dinge Sinn, die gemittelt werden können). Wenn Sie diesen Weg gehen möchten, verwenden Sie Folgendes:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Wenn Sie noch nie gesehen inject
haben, ist es nicht so magisch, wie es scheinen mag. Es iteriert über jedes Element und wendet dann einen Akkumulatorwert darauf an. Der Akku wird dann an das nächste Element übergeben. In diesem Fall ist unser Akkumulator einfach eine Ganzzahl, die die Summe aller vorherigen Elemente widerspiegelt.
Edit: Kommentator Dave Ray schlug eine nette Verbesserung vor.
Bearbeiten: Der Vorschlag von Kommentator Glenn Jackman arr.inject(:+).to_f
ist auch nett, aber vielleicht ein bisschen zu klug, wenn Sie nicht wissen, was los ist. Das :+
ist ein Symbol; Wenn es zum Injizieren übergeben wird, wendet es die durch das Symbol benannte Methode (in diesem Fall die Additionsoperation) auf jedes Element gegen den Akkumulatorwert an.
arr.inject(0.0) { |sum,el| sum + el } / arr.size
.
inject
Schnittstelle, die in der Dokumentation erwähnt wird. Der to_proc
Betreiber ist &
.
Array#inject
ist dies hier übertrieben. Einfach benutzen #sum
. ZBarr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Eine Version davon, die nicht verwendet wird, instance_eval
wäre:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
Mit dieser Option können Sie den Code ausführen, während Sie ihn nur a
einmal angeben , damit er mit anderen Befehlen verkettet werden kann. Dh random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
stattrandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
self
in diesem Block zuzugreifen , treten Probleme auf.) instance_eval
Dies gilt eher für Metaprogrammierung oder DSL.
Ich glaube, die einfachste Antwort ist
list.reduce(:+).to_f / list.size
reduce
ist eine Methode des Enumerable
Mixins von Array
. Und trotz seines Namens stimme ich dem @ShuWu zu ... es sei denn, Sie verwenden Rails, die implementiert werden sum
.
Ich hatte auf Math.average (Werte) gehofft, aber kein solches Glück.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
Methode, so ist dies eine richtige Antwort nach 6 Jahren zu sein scheint, würdig der Nostradamus Auszeichnung.
Ruby-Versionen> = 2.4 haben eine Aufzählbare # Summe Methode.
Und um den Gleitkomma-Durchschnitt zu erhalten, können Sie Integer # fdiv verwenden
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Für ältere Versionen:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
Einige Benchmarking von Top-Lösungen (in der Reihenfolge der effizientesten):
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 haben?
Lassen Sie mich etwas in den Wettbewerb bringen, das das Problem der Division durch Null löst:
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
Ich muss jedoch zugeben, dass "try" ein Rails-Helfer ist. Aber Sie können dies leicht lösen:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
Übrigens: Ich denke, es ist richtig, dass der Durchschnitt einer leeren Liste Null ist. Der Durchschnitt von nichts ist nichts, nicht 0. Das ist also erwartetes Verhalten. Wenn Sie jedoch zu Folgendem wechseln:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
Das Ergebnis für leere Arrays ist keine Ausnahme, wie ich erwartet hatte, sondern gibt NaN zurück ... Das habe ich in Ruby noch nie gesehen. ;-) Scheint ein besonderes Verhalten der Float-Klasse zu sein ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
Was ich an der akzeptierten Lösung nicht mag
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
ist, dass es nicht wirklich rein funktional funktioniert. Wir brauchen eine Variable arr, um arr.size am Ende zu berechnen.
Um dies rein funktional zu lösen, müssen wir zwei Werte verfolgen: die Summe aller Elemente und die Anzahl der Elemente.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh hat diese Lösung verbessert: Anstatt das Argument r als Array zu verwenden, könnten wir die Destrukturierung verwenden, um es sofort in zwei Variablen zu zerlegen
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
Wenn Sie sehen möchten, wie es funktioniert, fügen Sie einige Puts hinzu:
[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
Wir könnten auch eine Struktur anstelle eines Arrays verwenden, um die Summe und die Anzahl zu enthalten, aber dann müssen wir zuerst die Struktur deklarieren:
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
in Rubin verwendet sehe , danke dafür!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Zur öffentlichen Unterhaltung noch eine andere Lösung:
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
.Ich habe ziemlich oft das Gleiche gemacht, deshalb fand ich es klug, die Array
Klasse nur mit einem einfachen zu erweiternaverage
Methode zu erweitern. Es funktioniert nur für ein Array von Zahlen wie Integer oder Floats oder Decimals, aber es ist praktisch, wenn Sie es richtig verwenden.
Ich verwende Ruby on Rails, also habe ich dies platziert, config/initializers/array.rb
aber Sie können es überall platzieren, was beim Booten usw. enthalten ist.
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
Löst Division durch Null, Integer Division und ist leicht zu lesen. Kann leicht geändert werden, wenn ein leeres Array 0 zurückgibt.
Ich mag diese Variante auch, aber sie ist etwas wortreicher.
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
Diese Methode kann hilfreich sein.
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
Kurz, aber mit Instanzvariable
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
lieber eine Instanzvariable erstellen.
Sie könnten Folgendes ausprobieren:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0