Pythonでシーケンス内の最頻出アイテムを特定する方法|collections.Counterの使い方を徹底解説
問題
シーケンス(リストや文字列など)の中で、最も頻繁に出現する要素を特定したい場面は、データ分析において非常によくあります。Pythonでは、標準ライブラリの collections.Counter を使うことで、この処理を簡単かつ効率的に行えます。
解決策
Counter を使えば、シーケンス内の各アイテムの出現回数を自動的に集計できます。さらに most_common() メソッドを組み合わせれば、最も頻度の高いアイテムを瞬時に取り出せます。
Counterとは?
「Counter」は、各キーに対して整数のカウント値を保持するマッピング型です。既存のキーを更新すると、そのカウントが加算されていきます。このオブジェクトは、ハッシュ可能なオブジェクトの出現回数を数えたり、マルチセット(重複を許す集合)として利用したりするために設計されています。
データ分析を行う際には、「Counter」はまさに強力な相棒となる存在です。Pythonにかなり前から搭載されているため、多くの方にとっては復習になるかもしれません。まずは collections モジュールから Counter をインポートしましょう。
from collections import Counter
通常の辞書の問題点
従来の辞書(dict)では、存在しないキーにアクセスすると KeyError が発生します。Pythonの辞書は、指定されたキーが見つからない場合にキーエラーを返します。
# 空の辞書を定義
d = {}
# 存在しないキーにアクセス
d['mystring']
# エラーメッセージ
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-12-1e03564507c6> in <module>
----> 5 d['mystring']
KeyError: 'mystring'KeyErrorを回避するには?
Counterは辞書のサブクラスであり、非常に辞書に近い振る舞いをします。しかし、存在しないキーにアクセスした場合でもKeyErrorを発生させる代わりに、単に「0」を返してくれるのが大きな特徴です。
# Counterを定義
c = Counter()
# 存在しないキーを参照
print(f"Output\n{c['mystring']}")出力
0
c['mystring'] += 1
print(f"Output\n{c}")出力
Counter({'mystring': 1})例
print(f"Output\n{type(c)}")出力
<class 'collections.Counter'>
シーケンス内の最頻出アイテムを取得する
Counterのもう一つの便利な点は、リストなどのオブジェクトを渡すだけで自動的にカウントしてくれることです。自前でループを書いてカウンターを構築する手間が省けます。
Counter('Peas porridge hot peas porridge cold peas porridge in the pot nine days old'.split())出力
Counter({'Peas': 1,
'porridge': 3,
'hot': 1,
'peas': 2,
'cold': 1,
'in': 1,
'the': 1,
'pot': 1,
'nine': 1,
'days': 1,
'old': 1})split() メソッドは、文字列を空白文字で区切って単語のリストに変換します。そしてCounterがそのリストを走査し、すべての単語をカウントしてくれます。
most_common()メソッドで頻出アイテムを取得
さらに、文中で最も多く使われている単語を抽出することもできます。most_common() メソッドを使うと、頻度の高い順にアイテムを取得できます。
count = Counter('Peas porridge hot peas porridge cold peas porridge in the pot nine days old'.split())
print(f"Output\n{count.most_common(1)}")出力
[('porridge', 3)]例:上位2件・上位3件を取得
print(f"Output\n{count.most_common(2)}")出力
[('porridge', 3), ('peas', 2)]print(f"Output\n{count.most_common(3)}")出力
[('porridge', 3), ('peas', 2), ('Peas', 1)]ご覧のとおり、戻り値はタプルのリストになっています。タプルの第1要素が単語、第2要素がその出現回数です。
Counter同士の演算
Counterインスタンスのあまり知られていない機能として、さまざまな数学的演算を使って複数のCounterを簡単に組み合わせられる点があります。
string = 'Peas porridge hot peas porridge cold peas porridge in the pot nine days old'
another_string = 'Peas peas hot peas peas peas cold peas'
a = Counter(string.split())
b = Counter(another_string.split())
# カウントの足し算
add = a + b
print(f"Output\n{add}")出力
Counter({'peas': 7, 'porridge': 3, 'Peas': 2, 'hot': 2, 'cold': 2, 'in': 1, 'the': 1, 'pot': 1, 'nine': 1, 'days': 1, 'old': 1})# カウントの引き算
sub = a - b
print(f"Output\n{sub}")出力
Counter({'porridge': 3, 'in': 1, 'the': 1, 'pot': 1, 'nine': 1, 'days': 1, 'old': 1})減算の場合、結果が0以下になる要素は自動的に除外される点にも注目してください。
Counterの内部構造とelements()メソッド
最後に、Counterはデータをコンテナに格納する際に非常に賢い挙動を示します。上記の例のように、同じ単語をまとめてグループ化して保持するため、これらをひとまとまりとして扱えます。これは一般に「マルチセット」と呼ばれる概念です。
elements() メソッドを使うと、単語を1つずつ取り出すことができます。元の順序は保持されませんが、同じ単語はすべてまとめて展開されます。
例
print(f"Output\n{list(a.elements())}")出力
['Peas', 'porridge', 'porridge', 'porridge', 'hot', 'peas', 'peas', 'cold', 'in', 'the', 'pot', 'nine', 'days', 'old']
values()とitems()でカウント情報を取得
Counterは辞書のサブクラスなので、values() や items() といった辞書のメソッドもそのまま利用できます。
例
print(f"Output\n{list(a.values())}")出力
[1, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1]
例
print(f"Output\n{list(a.items())}")出力
[('Peas', 1), ('porridge', 3), ('hot', 1), ('peas', 2), ('cold', 1), ('in', 1), ('the', 1), ('pot', 1), ('nine', 1), ('days', 1), ('old', 1)]まとめ
Pythonの collections.Counter を使えば、シーケンス内の要素の出現回数を簡単に集計でき、most_common() メソッドで最頻出アイテムを即座に特定できます。KeyErrorを気にせずカウントを更新できる点や、Counter同士の加減算が可能な点も、データ分析の現場で大いに役立ちます。ぜひ日々のコーディングに取り入れてみてください。
-
PythonでAPIの結果を可視化する方法|GitHub APIとPlotlyを使った実践チュートリアル
はじめにAPIを活用する最大のメリットの一つは、常に最新のライブデータを取得できる点です。データが頻繁に更新される場合でも、API経由なら最新の状態を確実に取得できます。APIプログラムは、特定の情報をリクエストするために専用のURLを使用します。たとえば「SpotifyやYouTube Musicで2020年に最も再生された楽曲トップ100」といった具合です。リクエストされたデータは、JSONやCSVなど、プログラムで扱いやすい形式で返却されます。Pythonを使えば、ほぼあらゆるURLに対してAPI呼び出しを記述できます。本記事では、GitHubのAPIからデータを抽出し、その結果をグラフ
-
Pythonで解く「最大の水を溜められるコンテナ」問題 ― 二ポインタ法による効率的な実装
問題の概要n個の非負整数 a1, a2, ..., an が与えられ、それぞれの値は座標 (i, a[i]) 上の点を表すものとします。i番目の縦線は、端点 (i, a[i]) と (i, 0) を結ぶ線分です。この中から2本の線を選び、x軸とともにコンテナ(容器)を形成したときに、最も多くの水を溜められる組み合わせを見つけるのがこの問題の目的です。例えば、配列が [1,8,6,2,5,4,8,3,7] の場合を考えてみましょう。図の網掛け部分では、高さが7、横幅が7区間あるため、合計面積は 7 × 7 = 49 となります。これが求める出力です。解法のアプローチ(二ポインタ法)この問題は「二