¿Cómo puedo crear un promedio a partir de una matriz Ruby?
¿Cómo conseguiría encontrar un promedio de una matriz?
Si tengo el array:
[0,4,8,2,5,0,2,6]
El promedio me daría 3.375.
Gracias!
16 answers
Prueba esto:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Tenga en cuenta el .to_f
, que usted 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 Array
como otro comentarista ha sugerido, pero debe evitar la división de enteros o sus resultados serán incorrectos. Además, esto no es generalmente aplicable a todos los tipos de elementos posibles (obviamente, un promedio solo tiene sentido para las 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 has visto inject
antes, no es tan mágico como podría parecer. Itera sobre cada elemento y luego le aplica un valor acumulador. El acumulador es entonces entregado al siguiente elemento. En este caso, nuestro acumulador es simplemente un entero que refleja la suma de todos los elementos anteriores.
Edit: El comentarista Dave Ray propuso una buena mejora.
Editar: La propuesta del comentarista Glenn Jackman, usando arr.inject(:+).to_f
, también es agradable, pero tal vez un poco demasiado inteligente si no sabes lo que está pasando. El :+
es un símbolo; cuando se pasa a inject, aplica el método nombrado por el símbolo (en este caso, la operación de adición) a cada elemento contra el valor acumulador.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-10 14:40:33
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 usa instance_eval
sería:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-07 14:31:22
Creo que la respuesta más simple es
list.reduce(:+).to_f / list.size
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-24 20:16:57
Esperaba Matemáticas.promedio (valores), pero no hay tal suerte.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-02 13:05:46
Ruby versions >= 2.4 tiene un método Enumerable#sum.
Y para obtener el promedio de coma flotante, puede usar Entero#fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-01 08:10:26
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-07 10:37:14
Permítanme traer algo a la competencia que resuelve la división por cero problema:
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
Debo admitir, sin embargo, que "try" 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 es cero. El promedio de nada es nada, no 0. Así que ese es el comportamiento esperado. Sin embargo, si cambia a:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
El resultado para matrices vacías no será una excepción como esperaba, sino que devuelve NaN... He nunca había visto eso en Ruby. ;- ) Parece ser un comportamiento especial de la clase Float...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-21 11:56:56
Algunos benchmarking de las mejores soluciones (en orden de las más eficientes):
Gran matriz:
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)
Pequeños arreglos:
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-23 14:36:39
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-23 11:57:02
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.tamaño al final.
Para resolver esto puramente funcionalmente necesitamos llevar un registro 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 un array, podríamos usar desestructuring 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, añade algunos puts:
[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(:/)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-02 12:22:13
No tiene ruby en este pc, pero algo hasta este punto debería funcionar:
values = [0,4,8,2,5,0,2,6]
total = 0.0
values.each do |val|
total += val
end
average = total/values.size
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 07:53:04
a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-27 14:01:37
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-21 21:44:24
arr = [0,4,8,2,5,0,2,6]
average = arr.inject(&:+).to_f / arr.size
=> 3.375
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-28 13:29:02
Podrías intentar algo como lo siguiente:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-07 17:17:19
[1,2].tap { |a| @asize = a.size }.inject(:+).to_f/@asize
Corto pero usando la variable de instancia
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-01 20:10:04