¿Cómo conseguiría encontrar un promedio de una matriz?
Si tengo la matriz:
[0,4,8,2,5,0,2,6]
El promedio me daría 3.375.
¿Cómo conseguiría encontrar un promedio de una matriz?
Si tengo la matriz:
[0,4,8,2,5,0,2,6]
El promedio me daría 3.375.
Respuestas:
Prueba esto:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Tenga en cuenta .to_flo que querrá para evitar cualquier problema de la división de enteros. También puedes hacer:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Puede definirlo como parte de lo Arrayque sugirió otro comentarista, pero debe evitar la división de enteros o sus resultados serán incorrectos. Además, esto generalmente no es aplicable a todos los tipos de elementos posibles (obviamente, un promedio solo tiene sentido para cosas que se pueden promediar). Pero si quieres ir por esa ruta, usa esto:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Si no lo has visto injectantes, no es tan mágico como podría parecer. Se itera sobre cada elemento y luego le aplica un valor acumulador. El acumulador se entrega al siguiente elemento. En este caso, nuestro acumulador es simplemente un número entero que refleja la suma de todos los elementos anteriores.
Editar: El comentarista Dave Ray propuso una buena mejora.
Editar: La propuesta del comentarista Glenn Jackman, usando arr.inject(:+).to_f, también es buena, pero tal vez un poco demasiado inteligente si no sabes lo que está sucediendo. El :+es un símbolo; cuando se pasa a inyectar, aplica el método nombrado por el símbolo (en este caso, la operación de suma) a cada elemento contra el valor del acumulador.
arr.inject(0.0) { |sum,el| sum + el } / arr.size.
injectinterfaz, mencionada en la documentación. El to_procoperador es &.
Array#injectes excesivo aquí. Solo úsalo #sum. Por ejemploarr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Una versión de esto que no use instance_evalsería:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_evalle permite ejecutar el código mientras solo especifica auna vez, para que pueda encadenarse con otros comandos. Es decir, en random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f } lugar derandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
selfdentro de ese bloque, te encontrarás con problemas). instance_evalEs más para metaprogramación o DSL.
Creo que la respuesta más simple es
list.reduce(:+).to_f / list.size
reducees un método de Enumerablemezcla utilizado por Array. Y a pesar de su nombre, estoy de acuerdo con @ShuWu ... a menos que esté usando Rails que implementa sum.
Esperaba Math.average (valores), pero no tuve tanta suerte.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
summétodo, por lo que parece ser una respuesta correcta después de 6 años, digna del premio Nostradamus.
Las versiones de Ruby> = 2.4 tienen un método de suma # Enumerable .
Y para obtener el promedio de coma flotante, puede usar Integer # fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Para versiones anteriores:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
Algunos puntos de referencia de las mejores soluciones (en orden de la más eficiente):
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
nillugar de 0?
Permítanme traer algo a la competencia que resuelve el problema de división por cero:
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
Sin embargo, debo admitir que "intentar" es un ayudante de Rails. Pero puedes resolver esto fácilmente:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
Por cierto: creo que es correcto que el promedio de una lista vacía sea nulo. El promedio de nada es nada, no 0. Entonces, ese es el comportamiento esperado. Sin embargo, si cambia a:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
el resultado para las matrices vacías no será una excepción como esperaba, sino que devuelve NaN ... Nunca lo había visto antes en Ruby. ;-) Parece ser un comportamiento especial de la clase Float ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
lo que no me gusta de la solución aceptada
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
es que realmente no funciona de una manera puramente funcional. necesitamos una variable arr para calcular arr.size al final.
Para resolver esto de manera puramente funcional, necesitamos hacer un seguimiento de dos valores: la suma de todos los elementos y el número de elementos.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh mejoró esta solución: en lugar de que el argumento r sea una matriz, podríamos usar la desestructuración para separarlo inmediatamente en dos variables
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
si quieres ver cómo funciona, agrega algunas opciones:
[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
También podríamos usar una estructura en lugar de una matriz para contener la suma y el recuento, pero luego tenemos que declarar la estructura primero:
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.methoden rubí, ¡gracias por esto!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Para diversión pública, otra solución más:
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.Estaba haciendo lo mismo con bastante frecuencia, así que pensé que era prudente extender la Arrayclase con un averagemétodo simple . No funciona para nada más que una matriz de números como enteros o flotantes o decimales, pero es útil cuando lo usa correctamente.
Estoy usando Ruby on Rails, así que coloqué esto config/initializers/array.rbpero puedes colocarlo en cualquier lugar que esté incluido en el arranque, etc.
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
Resuelve dividir por cero, división entera y es fácil de leer. Se puede modificar fácilmente si elige que una matriz vacía devuelva 0.
También me gusta esta variante, pero es un poco más prolija.
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
Este método puede ser útil.
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
Corto pero usando variable de instancia
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_sizelugar de crear una variable de instancia.
Podría intentar algo como lo siguiente:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0