[1, 1, 1, 2, 3].mode
=> 1
['cat', 'dog', 'snake', 'dog'].mode
=> dog
Commencez par créer un hash mappant chaque valeur du tableau sur sa fréquence…
arr = [1, 1, 1, 2, 3]
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
#=> {1=>3, 2=>1, 3=>1}
… Puis utilisez le tableau des fréquences pour trouver l'élément avec la fréquence la plus élevée:
arr.max_by { |v| freq[v] }
#=> 1
array.max_by { |i| array.count(i) }
Mike: J'ai trouvé une méthode plus rapide. Essaye ça:
class Array
def mode4
group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0]
end
end
La sortie de référence:
user system total real
mode1 24.340000 0.070000 24.410000 ( 24.526991)
mode2 0.200000 0.000000 0.200000 ( 0.195348)
mode3 0.120000 0.000000 0.120000 ( 0.118200)
mode4 0.050000 0.010000 0.060000 ( 0.056315)
mode1 = 76
mode2 = 76
mode3 = [[76, 18]]
mode4 = 76
arr = [ 1, 3, 44, 3 ]
most_frequent_item = arr.uniq.max_by{ |i| arr.count( i ) }
puts most_frequent_item
#=> 3
Inutile de penser aux mappages de fréquences.
Ceci est une copie de cette question: Ruby - Eléments uniques dans Array
Voici la solution de cette question:
group_by { |n| n }.values.max_by(&:size).first
Cette version semble être encore plus rapide que la réponse de Nilesh C. Voici le code que j'ai utilisé pour le comparer (OS X 10.6 Core 2 2,4 GHz Mo).
Félicitations à Mike Woodhouse pour le code de référence (original):
class Array
def mode1
group_by { |n| n }.values.max_by(&:size).first
end
def mode2
freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h }
max = freq.values.max # we're only interested in the key(s) with the highest frequency
freq.select { |k, f| f == max } # extract the keys that have the max frequency
end
end
arr = Array.new(1_0000) { |i| Rand(100000) } # something to test with
Benchmark.bm(30) do |r|
(1..2).each do |i| r.report("mode#{i}") { 100.times do arr.send("mode#{i}").inspect; end }; end
end
Et voici les résultats du benchmark:
user system total real
mode1 1.830000 0.010000 1.840000 ( 1.876642)
mode2 2.280000 0.010000 2.290000 ( 2.382117)
mode1 = 70099
mode2 = [[70099, 3], [70102, 3], [51694, 3], [49685, 3], [38410, 3], [90815, 3], [30551, 3], [34720, 3], [58373, 3]]
Comme vous pouvez le constater, cette version est environ 20% plus rapide avec l’avertissement de ne pas tenir compte des liens. J'aime aussi la brièveté, je l'utilise personnellement tel quel, sans appliquer de patch pour le singe partout. :)
si vous essayez d'éviter d'apprendre #inject (ce que vous ne devriez pas faire ...)
words = ['cat', 'dog', 'snake', 'dog']
count = Hash.new(0)
words.each {|Word| count[Word] += 1}
count.sort_by { |k,v| v }.last
mais si je lisais cette réponse avant, maintenant je ne saurais rien sur #inject and man, vous devez savoir sur #inject.
idx = {}
[2,2,1,3,1].each { |i| idx.include?(i) ? idx[i] += 1 : idx[i] = 1}
Ceci est juste un indexeur simple. Vous pouvez remplacer le tableau [2,2,1 ..] par tout type d'identifiant basé sur un symbole/une chaîne, cela ne fonctionnerait pas avec les objets, vous auriez besoin d'introduire un peu plus de complexité, mais c'est assez simple.
en relisant vos questions, cette solution est un peu trop élaborée car elle va vous renvoyer un index de toutes les occurrences, pas seulement celle qui en contient le plus.
Voici une autre version qui vous donne les liens comme un mode devrait:
def mode
group_by {|x| x}.group_by {|k,v| v.size}.sort.last.last.map(&:first)
end
En d'autres termes, regroupez les valeurs, puis regroupez ces paires kv par le nombre de valeurs, puis triez ces paires kv, prenez le dernier groupe de tailles (le plus élevé), puis déroulez ses valeurs. J'aime group_by
.
def mode(array)
count = [] # Number of times element is repeated in array
output = []
array.compact!
unique = array.uniq
j=0
unique.each do |i|
count[j] = array.count(i)
j+=1
end
k=0
count.each do |i|
output[k] = unique[k] if i == count.max
k+=1
end
return output.compact.inspect
end
p mode([3,3,4,5]) #=> [3]
p mode([1,2,3]) #=> [1,2,3]
p mode([0,0,0,0,0,1,2,3,3,3,3,3]) #=> [0,3]
p mode([-1,-1,nil,nil,nil,0]) #=> [-1]
p mode([-2,-2,3,4,5,6,7,8,9,10,1000]) #=> [-2]