[Python] Counterチートシート()

目的
「PythonのCounter」の学習
親記事
https://zenn.dev/taka_noiri/scraps/3ff6117cccf4e2
対象読者
三ヶ月後くらいの自分
学び
- Counterの使用方法
- Counterをいつ使用するか
Counterチートシート
Pythonのcollections
モジュールに含まれるCounter
クラスは、データの頻度を効率的にカウントし、分析するタスクを簡単かつ効率的に実行するための強力なツールです。
目次
Counter
とは?
Counter
は、Pythonのcollections
モジュールに含まれるクラスで、イテラブルなデータ(リスト、文字列、タプルなど)の各要素の出現回数をカウントするためのツールです。内部的には辞書(dict
)を拡張しており、キーとして要素、バリューとしてその出現回数を保持します。
基本的な使い方
インポート方法
from collections import Counter
要素のカウント
from collections import Counter
# リスト内の要素をカウント
fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
fruit_counts = Counter(fruits)
print(fruit_counts)
# 出力: Counter({'apple': 3, 'banana': 2, 'orange': 1})
# 文字列内の文字をカウント
text = "hello world"
char_counts = Counter(text)
print(char_counts)
# 出力: Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
主なメソッドと機能
most_common([n])
1. 最も出現回数の多い要素を取得します。n
を指定すると上位n
個を返します。
from collections import Counter
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
word_counts = Counter(words)
top_two = word_counts.most_common(2)
print(top_two)
# 出力: [('apple', 3), ('banana', 2)]
elements()
2. 要素を出現回数分だけ繰り返して返します。カウントが0以下の要素は無視されます。
from collections import Counter
c = Counter({'a': 2, 'b': 1, 'c': 0})
elements = list(c.elements())
print(elements)
# 出力: ['a', 'a', 'b']
update(iterable_or_mapping)
3. カウンターを更新します。イテラブルなオブジェクトやマッピングを渡すことで、要素のカウントを増加させます。
from collections import Counter
c = Counter({'a': 2, 'b': 1})
c.update({'a': 1, 'c': 3})
print(c)
# 出力: Counter({'c': 3, 'a': 3, 'b': 1})
subtract(iterable_or_mapping)
4. 指定した要素のカウントを減少させます。
from collections import Counter
c = Counter({'a': 3, 'b': 1, 'c': 3})
c.subtract({'a': 1, 'c': 2})
print(c)
# 出力: Counter({'a': 2, 'c': 1, 'b': 1})
elements()
5. カウンター内の要素を反復可能な形式で返します。カウントが0以下の要素は無視されます。
from collections import Counter
c = Counter({'a': 2, 'b': 1, 'c': 0})
elements = list(c.elements())
print(elements)
# 出力: ['a', 'a', 'b']
clear()
6. カウンターを空にします。
from collections import Counter
c = Counter({'a': 2, 'b': 1})
c.clear()
print(c)
# 出力: Counter()
&=
演算子によるカウンターの共通部分
7. &=
演算子を使用すると、2つの Counter
オブジェクトの共通部分を取得できます。これは各要素について最小のカウントを保持します。
from collections import Counter
c1 = Counter({'a': 3, 'b': 1, 'c': 2})
c2 = Counter({'a': 1, 'b': 2, 'c': 2})
c1 &= c2
print(c1)
# 出力: Counter({'a': 1, 'c': 2, 'b': 1})
説明:
-
c1 &= c2
はc1
とc2
の共通部分を計算し、c1
を更新します。 - 各要素について、
c1
とc2
のカウントのうち小さい方の値がc1
に設定されます。 - この操作により、両方の
Counter
に存在する要素のみが残り、そのカウントは両方のカウンターで最小の値になります。
用途例:
- 複数のデータセット間で共通の要素とその出現回数を知りたい場合。
- アルゴリズム問題で、複数の条件を同時に満たす要素を効率的に抽出したい場合。
アルゴリズム問題での活用例
1. アナグラム判定
2つの文字列がアナグラム(同じ文字を同じ回数含む)であるかを判定します。
from collections import Counter
def isAnagram(s: str, t: str) -> bool:
return Counter(s) == Counter(t)
# 使用例
print(isAnagram("listen", "silent")) # 出力: True
print(isAnagram("hello", "billion")) # 出力: False
2. 重複要素の検出
リスト内に重複する要素が存在するかを判定します。
from collections import Counter
def containsDuplicate(nums):
num_counts = Counter(nums)
for count in num_counts.values():
if count > 1:
return True
return False
# 使用例
print(containsDuplicate([1, 2, 3, 1])) # 出力: True
print(containsDuplicate([1, 2, 3, 4])) # 出力: False
3. 単語の出現回数カウント
文章内の単語の出現回数をカウントし、頻出単語を取得します。
from collections import Counter
def topKFrequent(words, k):
counts = Counter(words)
return [word for word, _ in counts.most_common(k)]
# 使用例
print(topKFrequent(["i", "love", "leetcode", "i", "love", "coding"], 2))
# 出力: ['i', 'love']
4. 文字列の最小ウィンドウ問題(まだわかっていない)
指定した条件を満たす最小のウィンドウを文字列から見つける問題にCounter
が役立ちます。
from collections import Counter
def minWindow(s: str, t: str) -> str:
if not t or not s:
return ""
dict_t = Counter(t)
required = len(dict_t)
l, r = 0, 0
formed = 0
window_counts = {}
ans = float("inf"), None, None
while r < len(s):
character = s[r]
window_counts[character] = window_counts.get(character, 0) + 1
if character in dict_t and window_counts[character] == dict_t[character]:
formed += 1
while l <= r and formed == required:
character = s[l]
if r - l + 1 < ans[0]:
ans = (r - l + 1, l, r)
window_counts[character] -= 1
if character in dict_t and window_counts[character] < dict_t[character]:
formed -= 1
l += 1
r += 1
return "" if ans[0] == float("inf") else s[ans[1]: ans[2] + 1]
# 使用例
print(minWindow("ADOBECODEBANC", "ABC")) # 出力: "BANC"
5. 負数も含む要素のカウント
Counter
は負の数やゼロもカウント可能です。
from collections import Counter
def findPairs(nums, k):
if k < 0:
return 0
counts = Counter(nums)
result = 0
for num in counts:
if k > 0 and (num + k) in counts:
result += 1
elif k == 0 and counts[num] > 1:
result += 1
return result
# 使用例
print(findPairs([3, 1, 4, 1, 5], 2)) # 出力: 2
print(findPairs([1, 2, 3, 4, 5], 1)) # 出力: 4
Counter
の利点と注意点
利点
- 簡潔なコード: 少ないコードで要素のカウントが可能。
- 高速な処理: 内部的にハッシュテーブルを使用しており、カウント操作が高速。
-
豊富なメソッド:
most_common()
,elements()
,update()
,subtract()
など、頻度分析に役立つメソッドが豊富。 - 柔軟性: リスト、文字列、タプルなど、さまざまなイテラブルなデータを扱える。
注意点
-
ハッシュ可能なオブジェクトのみ:
Counter
はハッシュ可能なオブジェクト(例:数値、文字列、タプルなど)のみをカウントできます。リストや辞書などのミュータブルなオブジェクトは使用できません。from collections import Counter # 有効 c = Counter([(1, 2), (3, 4), (1, 2)]) print(c) # 出力: Counter({(1, 2): 2, (3, 4): 1}) # 無効(エラー) # c = Counter([[1, 2], [3, 4], [1, 2]]) # TypeError: unhashable type: 'list'
-
大規模データの扱い: 非常に大きなデータセットを扱う場合、メモリ消費に注意が必要です。
Counter
は内部的に辞書を使用しているため、大量のユニークな要素が存在するとメモリ使用量が増加します。 -
順序の保証: Python 3.7以降、辞書は挿入順を保持しますが、
Counter
自体は順序を保証しません。要素を順序付けて処理する必要がある場合は、most_common()
などのメソッドを使用してください。
Counter
と他のデータ構造との比較
Counter
vs dict
-
Counter
の利点:- 存在しないキーにアクセスすると自動的に
0
を返します。 - 頻度分析に特化したメソッド(
most_common()
など)が利用可能。
from collections import Counter c = Counter({'a': 2, 'b': 1}) print(c['c']) # 出力: 0 d = {'a': 2, 'b': 1} print(d.get('c', 0)) # 出力: 0 # print(d['c']) # KeyError
- 存在しないキーにアクセスすると自動的に
-
dict
の利点:- 任意の値を保持でき、
Counter
に特化した機能が不要な場合に柔軟。 -
Counter
はdict
を継承しているため、dict
の機能も利用可能。
- 任意の値を保持でき、
Counter
vs defaultdict
-
Counter
の利点:- 要素のカウントに特化し、カウント関連のメソッドが豊富。
- デフォルト値が自動的に
0
に設定される点はdefaultdict
と共通。
-
defaultdict
の利点:- 任意のデフォルト値を設定可能。
-
Counter
はdefaultdict(int)
としても機能するが、カウント以外の用途には向かない。
from collections import defaultdict, Counter # defaultdictの例 dd = defaultdict(int) dd['a'] += 1 print(dd) # 出力: defaultdict(<class 'int'>, {'a': 1}) # Counterの例 c = Counter() c['a'] += 1 print(c) # 出力: Counter({'a': 1})
よくある質問
Counter
の比較はどのように行われますか?
Q1. Counter
オブジェクト同士を比較すると、各要素のカウントが一致するかどうかが評価されます。
from collections import Counter
c1 = Counter({'a': 2, 'b': 1})
c2 = Counter({'b': 1, 'a': 2})
c3 = Counter({'a': 1, 'b': 2})
print(c1 == c2) # 出力: True
print(c1 == c3) # 出力: False
Counter
はイテラブル以外のデータ型を扱えますか?
Q2. Counter
はイテラブルなデータ型(リスト、文字列、タプルなど)を扱うことが前提です。イテラブルでないデータ型(例えば、整数)は直接カウントできません。
Counter
で要素を削除する方法は?
Q3. Counter
から特定の要素を削除するには、del
キーワードを使用します。また、カウントが0以下になる要素は自動的に削除されます。
from collections import Counter
c = Counter({'a': 2, 'b': 1, 'c': 3})
del c['b']
print(c)
# 出力: Counter({'c': 3, 'a': 2})
c.subtract({'a': 2, 'c': 1})
print(c)
# 出力: Counter({'c': 2})
Counter
はソートできますか?
Q4. Counter
自体はソートをサポートしていませんが、most_common()
メソッドを使用して出現頻度の高い順に要素を取得できます。逆順や特定の基準でソートしたい場合は、sorted()
関数と組み合わせて使用します。
from collections import Counter
c = Counter({'a': 3, 'b': 1, 'c': 2})
# 出現頻度の高い順
print(c.most_common())
# 出力: [('a', 3), ('c', 2), ('b', 1)]
# アルファベット順にソート
sorted_items = sorted(c.items())
print(sorted_items)
# 出力: [('a', 3), ('b', 1), ('c', 2)]
Counter
のカウントをリセットする方法は?
Q5. Counter
のカウントをリセットするには、clear()
メソッドを使用します。
from collections import Counter
c = Counter({'a': 2, 'b': 1})
c.clear()
print(c)
# 出力: Counter()
まとめ
PythonのCounter
クラスは、データの頻度を効率的にカウント・分析するための強力なツールです。アルゴリズム問題では、文字列操作、配列の頻度分析、アナグラム判定など、さまざまな場面で活用できます。以下のポイントを押さえておくと、面接での活用がスムーズになります。
-
基本操作をマスター: 要素のカウント、
most_common()
、elements()
などの基本メソッドを理解する。 -
応用問題に挑戦: アナグラム判定や重複要素の検出など、実際の問題で
Counter
を使ってみる。 -
効率性を理解:
Counter
の時間・空間計算量を理解し、他のデータ構造との比較を行う。 -
コードの可読性:
Counter
を使うことで、コードがより簡潔で読みやすくなることを活用する。