PythonのCounterでリストの各要素の出現個数をカウント

Modified: | Tags: Python, リスト

Pythonでリストやタプルの全要素の個数は組み込み関数len()、各要素の個数(要素ごとの出現回数)はcount()メソッドで取得できる。さらに、Python標準ライブラリcollectionsのCounterクラスを使うと、出現回数が多い順に要素を取得可能。

サンプルはリストだが、タプルでも同様の処理が可能。

文字列に含まれる特定の文字・文字列の数をカウントしたい場合は以下の記事を参照。

全要素数をカウント: len()

リストやタプルの全要素数をカウントするには、組み込み関数len()を使う。

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']

print(len(l))
# 7

各要素の個数(要素ごとの出現回数)をカウント: count()メソッド

各要素の個数(要素ごとの出現回数)をカウントするには、リストやタプルなどのcount()メソッドを使う。

要素として存在しない値を引数に渡すと0が返る。

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']

print(l.count('a'))
# 4

print(l.count('b'))
# 1

print(l.count('c'))
# 2

print(l.count('d'))
# 0

各要素の出現回数を一括で取得したい場合は次のcollections.Counterが便利。

collections.Counterの使い方

Python標準ライブラリcollectionsにCounterクラスがある。

collections.Counter()にリストやタプルを渡すと、Counterオブジェクトが生成される。Counterは辞書型dictのサブクラスで、キーに要素、値に出現回数という形のデータを持つ。

import collections

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']

c = collections.Counter(l)
print(c)
# Counter({'a': 4, 'c': 2, 'b': 1})

print(type(c))
# <class 'collections.Counter'>

print(issubclass(type(c), dict))
# True

キーとして要素を指定するとその個数を取得できる。要素として存在しない値を指定すると0を返す。

print(c['a'])
# 4

print(c['b'])
# 1

print(c['c'])
# 2

print(c['d'])
# 0

keys(), values(), items()などの辞書型のメソッドも使える。

print(c.keys())
# dict_keys(['a', 'b', 'c'])

print(c.values())
# dict_values([4, 1, 2])

print(c.items())
# dict_items([('a', 4), ('b', 1), ('c', 2)])

これらのメソッドはdict_keys型などのオブジェクトを返す。for文で回したりする場合はそのまま使用できる。リストに変換したい場合はlist()を使えばよい。

出現回数順に要素を取得: most_common()メソッド

Counterにはmost_common()メソッドがあり、(要素, 出現回数)という形のタプルを出現回数順に並べたリストを返す。

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']

c = collections.Counter(l)
print(c)
# Counter({'a': 4, 'c': 2, 'b': 1})

print(c.most_common())
# [('a', 4), ('c', 2), ('b', 1)]

出現回数が最も多いものは[0]、最も少ないものは[-1]のようにインデックスを指定して取得できる。要素だけ、出現回数だけを取得したい場合はさらにインデックスを指定すればOK。

print(c.most_common()[0])
# ('a', 4)

print(c.most_common()[-1])
# ('b', 1)

print(c.most_common()[0][0])
# a

print(c.most_common()[0][1])
# 4

出現回数の少ない順に並べ替えたい場合は増分を-1としたスライスを使う。

print(c.most_common()[::-1])
# [('b', 1), ('c', 2), ('a', 4)]

most_common()メソッドに引数nを指定すると、出現回数の多いn個の要素のみを返す。省略するとすべての要素。

print(c.most_common(2))
# [('a', 4), ('c', 2)]

(要素, 出現回数)のタプルではなく、出現回数順に並べた要素・出現回数のリストが個別に欲しい場合は、以下のようにして分解できる。

values, counts = zip(*c.most_common())

print(values)
# ('a', 'c', 'b')

print(counts)
# (4, 2, 1)

組み込み関数zip()を利用して二次元リスト(ここではタプルのリスト)を転置して、アンパックして取り出している。詳しくは以下の記事を参照。

重複しない要素(一意な要素)の個数(種類)をカウント

リストやタプルに重複しない要素(一意な要素)が何個あるか(何種類あるか)をカウントする場合、上述のCounterset()を使う。

Counterオブジェクトの要素数が元のリストの重複しない要素の個数に等しい。Counterオブジェクトの要素数はlen()で取得できる。

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']
c = collections.Counter(l)

print(len(c))
# 3

集合型setのコンストラクタset()も使える。Counterオブジェクトが必要ない場合はこちらのほうが楽。

set型は重複した要素をもたないデータ型で、set()にリストを渡すと重複する値は無視されて一意な値のみが要素となるset型のオブジェクトを返す。これの要素数をlen()で取得する。

print(set(l))
# {'a', 'c', 'b'}

print(len(set(l)))
# 3

リストの重複要素の判定や抽出・削除については以下の記事を参照。

条件を満たす要素の個数をカウント

リストやタプルに特定の条件を満たす要素が何個あるかカウントするには、リスト内包表記またはジェネレーター式を利用する。

例として、以下の数値のリストに対して負の値の要素の数をカウントする。

l = list(range(-5, 6))
print(l)
# [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]

各要素に対してリスト内包表記で条件式を適用すると、ブール型boolTrue, False)を要素とするリストが得られる。ブール型boolは整数型intのサブクラスで、True1False0として扱われるため、sum()で合計を算出するとTrueの個数(条件を満たす要素の個数)がカウントできる。

print([i < 0 for i in l])
# [True, True, True, True, True, False, False, False, False, False, False]

print(sum([i < 0 for i in l]))
# 5

ここで、リスト内包表記の[]()に置き換えるとジェネレーター式となる。リスト内包表記は全要素を処理したリストを生成するのに対して、ジェネレータ式は要素を逐次処理していくため、メモリ効率がよい。

ジェネレーター式を唯一の引数とするときは()を省略できるため、後者のように書ける。

print(sum((i < 0 for i in l)))
# 5

print(sum(i < 0 for i in l))
# 5

Falseの数(条件を満たさない要素の数)をカウントしたい場合はnotを使う。なお、>のほうがnotより優先順位が高い(先に演算される)ので、以下の例の(i < 0)の括弧()はなくてもよい。

print([not (i < 0) for i in l])
# [False, False, False, False, False, True, True, True, True, True, True]

print(sum(not (i < 0) for i in l))
# 6

もちろん、条件そのものを変えてもよい。

print(sum(i >= 0 for i in l))
# 6

その他の例をいくつか示す。

数値のリストに対して奇数の要素の数を取得する例。

print([i % 2 == 1 for i in l])
# [True, False, True, False, True, False, True, False, True, False, True]

print(sum(i % 2 == 1 for i in l))
# 6

文字列のリストに対する条件の例。

l = ['apple', 'orange', 'banana']

print([s.endswith('e') for s in l])
# [True, True, False]

print(sum(s.endswith('e') for s in l))
# 2

出現回数の条件でカウントする場合はcollections.Counterを使う。items()(要素, 出現回数)のタプルを取得し、出現回数で条件を指定する。

出現回数が2個以上の要素を抽出する例と、その総数をカウントする例。この例の場合、aが4個、cが2個で計6個。

l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']
c = collections.Counter(l)

print(c.items())
# dict_items([('a', 4), ('b', 1), ('c', 2)])

print([i for i in l if c[i] >= 2])
# ['a', 'a', 'a', 'a', 'c', 'c']

print([i[1] for i in c.items() if i[1] >= 2])
# [4, 2]

print(sum(i[1] for i in c.items() if i[1] >= 2))
# 6

出現回数が2個以上の要素の種類を抽出する例と、その数をカウントする例。この例の場合、acの2種類。

print([i[0] for i in c.items() if i[1] >= 2])
# ['a', 'c']

print([i[1] >= 2 for i in c.items()])
# [True, False, True]

print(sum(i[1] >= 2 for i in c.items()))
# 2

文字列の単語の出現個数をカウント

具体的な例として、文字列に登場する単語の出現個数を数えてみる。

まず、不必要なカンマ,.replace()メソッドで空文字列と置換し、削除する。さらにsplit()メソッドで空白で区切ってリスト化する。

s = 'government of the people, by the people, for the people.'

s_remove = s.replace(',', '').replace('.', '')

print(s_remove)
# government of the people by the people for the people

word_list = s_remove.split()

print(word_list)
# ['government', 'of', 'the', 'people', 'by', 'the', 'people', 'for', 'the', 'people']

リスト化できれば、各単語の出現回数や出現する単語の種類を取得したり、collections.Countermost_common()で最も出現回数の多い単語を取得したりできる。

print(word_list.count('people'))
# 3

print(len(set(word_list)))
# 6

c = collections.Counter(word_list)

print(c)
# Counter({'the': 3, 'people': 3, 'government': 1, 'of': 1, 'by': 1, 'for': 1})

print(c.most_common()[0][0])
# the

以上はごくごくシンプルな処理なので、より複雑な自然言語処理にはNLTKなどのライブラリを利用するほうがいい。

また、日本語のテキストの場合は単語の区切りがはっきりしていないのでsplit()では分割できない。例えばJanomeというライブラリを使うと実現可能。以下の記事を参照。

文字列の文字の出現個数をカウント

文字列もシーケンス型なので、count()メソッドを使ったり、collections.Counter()のコンストラクタの引数に渡したりすることができる。

s = 'supercalifragilisticexpialidocious'

print(s.count('p'))
# 2

c = collections.Counter(s)

print(c)
# Counter({'i': 7, 's': 3, 'c': 3, 'a': 3, 'l': 3, 'u': 2, 'p': 2, 'e': 2, 'r': 2, 'o': 2, 'f': 1, 'g': 1, 't': 1, 'x': 1, 'd': 1})

出現回数の多い文字トップ5を取得する例。

print(c.most_common(5))
# [('i', 7), ('s', 3), ('c', 3), ('a', 3), ('l', 3)]

values, counts = zip(*c.most_common(5))

print(values)
# ('i', 's', 'c', 'a', 'l')

関連カテゴリー

関連記事