Ruby: 配列のモードとメジアンを算出する方法(翻訳)
Rubyで配列の平均値を求める方法については以前も別記事『Calculate a mean average from a Ruby array』で取り上げました。しかし、普段あまり目にしませんがそれでも有用な平均が他にも2種類あります。
メジアン(中央値)は次のように求める
a = [1, 3, 2, 4, 6, 5, 7, 8]
sorted = a.sort # required
#=> [1, 2, 3, 4, 5, 6, 7, 8]
midpoint = a.length / 2 # integer division
#=> 4
if a.length.even?
# メジアンは中間の前後にある2つの値の平均
sorted[midpoint-1, 2].sum / 2.0
else
sorted[midpoint]
end
#=> 4.5
モード(最頻値)は次のように求める
Array#tally
を使い、それからソートする。
a = [1, 3, 3, 4, 6, 5, 7, 8]
tallied = a.tally
#=> {1=>1, 3=>2, 4=>1, 6=>1, 5=>1, 7=>1, 8=>1}
top_pair = tallied.sort_by { |_,v| v }.last(2)
#=> [[8, 1], [3, 2]]
if top_pair.size == 1
top_pair[0][0] # 要素が1個だけなら、それがモード
elsif top_pair[0][1] == top_pair[1][1]
nil # 個数が同じならモードは存在しない
else
top_pair[1][0]
end
#=> 3
そうする理由
#tally
メソッドはRuby 2.7でEnumerable
に追加されました。inject
を用いる実装を見たことがあるかもしれませんが、こちらはパフォーマンスが落ちます。
以前平均値の計算について記事を書いたときに、Rubyのネイティブメソッドを使った場合と、自分で実装した場合のパフォーマンスを比較しました。
他には?
これらの計算をメソッドにまとめておくと良いと思います。
この種の計算を頻繁に行う場合や、パフォーマンスが重要な場合には、enumerable-statistics
gemをチェックしてみましょう。これはArrayとEnumerableにミックスインされたいくつかの統計的なサマリー算出のネイティブ実装版です。
これらのメソッドはC言語で実装されていて、Rubyでプログラムされたアルゴリズムよりもはるかに高速です。
概要
原著者の許諾を得て翻訳・公開いたします。