スポンサーリンク

Python CollectionsのCounterの基本的な使い方

Pythonは標準ライブラリが非常に豊富です。バッテリー同胞って言われているように、標準ライブラリだけでも十分に活用ができるプログラミング言語です。

本記事ではそんな豊富な標準ライブラリの中からCollectionsというライブラリのCounterの基本的な使い方を紹介します!

ぜひ参考にしてください!

Counterの基本的な使い方

そもそもCollectionsとは?

Counterの活用方法の紹介に入る前にまずはCollectionsに関して軽くお話しておきます。

Collectionsはコンテナ型データと呼ばれるもので、汎用的なdictlisttuplesetに代わる特殊なコンテナ型データです。

簡単に説明するとdictlisttuplesetを強化したものだと思えばいいと思います。ですので、通常のコンテナ型データと同じ操作ができ、さらにそこから強化された機能を使うことができます。

通常のdictlisttuplesetだと実現が難しいこともCollectionsを使えば簡単に解決できる場合も多々見受けられます。

では早速Counterの活用方法を紹介します。

Counterの基本的な使い方

Counterは名前から想像がつくように要素をカウントしてくれる機能を持ったオブジェクトです。

Counterは辞書のサブクラスに該当するためCounterを使ってカウントした値は辞書型と同じようにKeyとValueを持ったCounterオブジェクトが返ってきます。

初期化の方法

まずはCounterオブジェクトの初期化の方法を紹介します。

実際にコードを書きながら紹介してきます。
まずは最もシンプルかつ一番多く使うであろうパターンです。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

# リストに含まれた要素の数を数える
count_list = Counter(list_)
print(count_list)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

使い方は非常に簡単です。リストをCounterに渡してあげるだけです。

上記のサンプルコードの場合だとリストに含まれる要素がそれぞれ何個ずつ含まれているかをカウントした結果をCounterオブジェクトにして返しています。

ちなみにリストだけでなく文字列を渡すこともできます。

from collections import Counter

# Appleの中にそれぞれの文字が何個含まれているかを数える
str_ = 'Apple'
count_str = Counter(str_)
print(count_str)
# Counter({'p': 2, 'A': 1, 'l': 1, 'e': 1})

辞書や文字列の場合は集計した上でCounterオブジェクトが返ってきました。

ですが特定の集計結果からCounterオブジェクトを生成したい場合もあると思います。

安心してください。辞書や引数を渡してCounterオブジェクトを作成することも可能です。

# 辞書型を使って初期化する
dict_ = {
    'hoge': 4,
    'huga': 8,
    'puga': 1,
    'poge': 5
}
count_dict = Counter(dict_)
print(f'Init with dict  -> {count_dict}')
# Counter({'huga': 8, 'poge': 5, 'hoge': 4, 'puga': 1})

# 引数を渡して初期化する
count_args = Counter(hoge=10, puga=3)
print(f'Init with args  -> {count_args}')
# Counter({'hoge': 10, 'puga': 3})

少し気をつけるべきは数字以外の値を持ったデータをCounterに渡してもエラーが発生しないことです。

# hugaの値が文字列になっている辞書をCounterに渡す
dict_2 = {
    'hoge': 4,
    'huga': 'hugahuga',
    'puga': 1,
    'poge': 5
}
count_dict_2 = Counter(dict_2)
print(count_dict_2)
# Counter({'hoge': 4, 'huga': 'hugahuga', 'puga': 1, 'poge': 5})

# 引数に文字列を渡す
count_args_2 = Counter(hoge=10, puga='pugapuga')
print(count_args_2)
# Counter({'hoge': 10, 'puga': 'pugapuga'})

ですのでCounterオブジェクトがもつデータのキーに対する値には必ず数字が入っているわけではないです。

初期化する際に渡す値をバリデーションした上で渡す、Counterオブジェクトから値を取得した後にバリデーションをする、エラーハンドリングの処理をしっかりと実装するなどひと工夫が必要です。

  • 文字列、リストを渡してCounterオブジェクト初期化するとカウントされた結果が返ってくる
  • 辞書、キーワード引数からCounterオブジェクトを生成する場合は値に数字以外が入っていてもエラーは発生しない

初期化をしてCounterオブジェクトを生成したらもちろん使いたいですよね。
続いてはCounterオブジェクトから値を取得する方法を紹介します。

Counterオブジェクトから値を操作する方法

Counterオブジェクトは辞書型のサブクラスなので辞書と同じように値を操作することができます。

まずは値にアクセスする方法を紹介します。

実際にコードを動かして見ると下記のようになります。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

count = Counter(list_)
print(count)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

# hogeの値を取得する
hoge_number = count['hoge']
print(hoge_number)
# 6

# get関数を使って取得
hoge_number_2 = count.get('hoge')
print(hoge_number_2)
# 6

指定したキーの値が取得できていることがわかると思います。
辞書型のサブクラスなのでget関数でも取得が可能です。

ただし存在しないキーを指定した時は辞書型と違う動きをします。

実際に動かしてみると下記のようになります。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

count = Counter(list_)
print(count)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

# 存在しないキーにアクセス
pege_number = count['pege']
print(pege_number)
# 0

# get関数を使って存在しないキーにアクセスかつデフォルト値を設定
pege_number_2 = count.get('pege', 99)
print(pege_number_2)
# 99

上記のコードを見てわかるように存在しないキーにアクセスしようとしてもエラーは発生せず0が返ってきます。
get関数を使ってデフォルト値を設定した場合はデフォルト値が返ってきます。

続いては値を削除する方法です。

こちらも通常の辞書と同じ操作で削除が可能です。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

count = Counter(list_)
print(count)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

# hogeを削除する
del count['hoge']
print(count)
Counter({'fuga': 5, 'puga': 2, 'poge': 1})

続いては値を変更する方法です。

こちらも通常の辞書と同じ方法で変更が可能です。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

count = Counter(list_)
print(count)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

# hogeに10加算する
count['hoge'] += 10
print(count)
# Counter({'hoge': 16, 'fuga': 5, 'puga': 2, 'poge': 1})

# hogeの値を77に変更する
count['hoge'] = 77
print(count)
# Counter({'hoge': 77, 'fuga': 5, 'puga': 2, 'poge': 1})

# pikaというキーを作成して値に99を入れる
count['pika'] += 99
print(count)
# Counter({'pika': 99, 'hoge': 10, 'fuga': 5, 'puga': 2, 'poge': 1})

+=を使えば値の加算、=を使えば値の上書き、存在しないキーを指定したらそのキーの要素を作成します。

  • Counterオブジェクトの値の操作は基本的に通常の辞書型と同じ
  • 値にアクセスする際に存在しないキーを指定すると0が返ってくる

これでCounterオブジェクトの値を操作する方法はわかったと思います。

ただ値を取得しただけだと、実際の集計結果にはどのような要素が含まれているのか等Counterオブジェクトの全体がわからないと思います。

ですので続いてはCounterオブジェクトの要素の操作方法を紹介します。

Counterオブジェクトの要素を操作する方法

続いてはCounterオブジェクトの要素の操作方法です。

まずはCounterオブジェクトから全ての要素を取得する方法です。
要素を全部取得するにはmost_common()関数を使います。

実際にコードを書いてみると下記のようになります。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

# count list
count = Counter(list_)
print(count)

# 全ての要素を取得する
all_elms = count.most_common()
print(all_elms)
# [('hoge', 6), ('fuga', 5), ('puga', 2), ('poge', 1)]

# 上位2つの要素を取得する
all_elms_top2 = count.most_common(2)
print(f'Top 2 elements-> {all_elms_top2}')

most_common()を使うとそれぞれの要素がタプルに格納されたリストが返ってきます。
リストに格納される要素はカウントされた数字の降順で格納されます。

またmost_common()にはint型の引数を渡すことができます。
引数を渡すとその上位からその数分の要素を取得して値を返します。

ですので集計結果の上位XX位のデータの要素を取得したい場合に有効活用できます。

ちなみに同一数値があった場合は下記のような動きをします。

from collections import Counter

list_ = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

# count list
count_2 = Counter(list_)

# 集計結果の2番目が同一数値になるように調整
count_2['fuga'] = 3
count_2['puga'] = 3
print(count_2)
# Counter({'hoge': 6, 'fuga': 3, 'puga': 3, 'poge': 1})

# 上位2つの要素を取得
top2_elms = count_2.most_common(2)
print(top2_elms)
# [('hoge', 6), ('fuga', 3)]

同一順位は判定してくれないので同じ値があった場合は手前にある要素のみが取得されます。

ですのでmost_common()に引数を渡した場合はシンプルにリストの先頭から指定した数の要素を取得してくる動きをすることがわかると思います。

ですので、most_common()を使って上位のランキング付けをしようとしても場合によっては想定した結果と異なるので注意が必要です。

most_common()はあくまで先頭から要素をとってくる機能でランキング付けの機能ではない

これで要素全体に対する操作はわかったと思います。

続いてはCounterオブジェクトそのものの操作方法を紹介します。

Counterオブジェクトそのものの操作方法

Counterオブジェクトは異なるCounterオブジェクトの結合したり、差分を取得したりすることが可能です。

Counterオブジェクト本体の操作はset型と非常に似ています。

まずはCounterオブジェクトの結合を紹介します。
結合は別々に集計されたCounterオブジェクトを1つにまとめる際に使います。

イメージしやすい場面だと、学校のクラスごとに好きな給食に投票してもらって、その結果を学校全体にまとめる、とか。(学校でPythonが活用されているかは一旦無視で!笑)

実際にコードを書くと下記のようになります。

from collections import Counter

list_1 = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

list_2 = [
    'fuga', 'fuga', 'poge', 'puga', 'pika', 'pika', 'hoge',
    'hoge', 'fuga', 'fuga', 'fuga', 'pika', 'pika', 'pika'
]

# count list
count_1 = Counter(list_1)
print(count_1)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

count_2 = Counter(list_2)
print(count_2)
# Counter({'fuga': 5, 'pika': 5, 'hoge': 2, 'poge': 1, 'puga': 1})

joined_cnt = count_1 + count_2
print(joined_cnt)
# Counter({'fuga': 10, 'hoge': 8, 'pika': 5, 'puga': 3, 'poge': 2})

Counterオブジェクトを結合する時にはシンプルに演算子を使います。

結合して新しく生成されたCounterオブジェクトの要素は、数字は加算されて、存在しない要素は新しく追加されています。

足し算ができるということは引き算もできます。

実際にコードを書いてみると下記のようになります。

from collections import Counter

list_1 = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

list_2 = [
    'fuga', 'fuga', 'poge', 'puga', 'pika', 'pika', 'hoge',
    'hoge', 'fuga', 'fuga', 'fuga', 'pika', 'pika', 'pika'
]

# count list
count_1 = Counter(list_1)
print(count_1)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

count_2 = Counter(list_2)
print(count_2)
# Counter({'fuga': 5, 'pika': 5, 'hoge': 2, 'poge': 1, 'puga': 1})

# diff counter obj
diff_cnt = count_1 - count_2
print(f'Diff of count objects -> {diff_cnt}')
# Counter({'hoge': 4, 'puga': 1})

引き算の場合は足し算の逆で要素の値は減算されます。
また減算の結果値が0もしくはマイナスになった場合は要素そのものが削除されます。

また積集合と和集合の取得も可能です。
実際にコードを書くと下記のようになります。

from collections import Counter

list_1 = [
    'hoge', 'fuga', 'poge', 'puga', 'fuga', 'fuga', 'hoge',
    'hoge', 'puga', 'fuga', 'fuga', 'hoge', 'hoge', 'hoge'
]

list_2 = [
    'fuga', 'fuga', 'poge', 'puga', 'pika', 'pika', 'hoge',
    'hoge', 'fuga', 'fuga', 'fuga', 'pika', 'pika', 'pika'
]

# count list
count_1 = Counter(list_1)
print(count_1)
# Counter({'hoge': 6, 'fuga': 5, 'puga': 2, 'poge': 1})

count_2 = Counter(list_2)
print(count_2)
# Counter({'fuga': 5, 'pika': 5, 'hoge': 2, 'poge': 1, 'puga': 1})

# 積集合(共通部分の取得)
intersection_cnt = count_1 & count_2
print(f'Intersection of count objects -> {intersection_cnt}')
# Counter({'fuga': 5, 'hoge': 2, 'poge': 1, 'puga': 1})

# 和集合(全体の集合)
union_cnt = count_1 & count_2
print(f'Union of count objects -> {diff_cnt}')
# Counter({'hoge': 6, 'fuga': 5, 'pika': 5, 'puga': 2, 'poge': 1})

積集合は共通部分の取得します。ですので新しく生成されるCounterオブジェクトには2つのCounterオブジェクト両方で持っている要素の中から値の小さい要素が取得されます。

一方で和集合は2つのCounterオブジェクトに含まれる全ての要素を取得して新しいCounterオブジェクトを生成します。

ここで注意すべきなのは和集合の場合は演算子による結合とは異なり値の加算は行われず、値の大きい要素のみが取得されます。

  • Counterオブジェクトはset型のように演算操作ができる

これでCounterの基本的な使い方は理解できたと思います。

まとめ

Counterは辞書型のように要素や値へのアクセスができて、Set型のように集合演算が可能だということがわかったと思います。

Counterの性質を理解して実装することがよりよいプログラムを作る上で非常に役に立つと思います。

データを使った分析を行うようなプログラムを作成する際には活躍するかもしれません。

下記の記事では実際にCounterを使うためのより具体的な実装を紹介しています。興味がある方はぜひ参考にしてください。

みなさまの快適なPythonライフの役に立ちますと幸いです。

Python
スポンサーリンク
ibukishをフォローする
スポンサーリンク
ibukish Lab+

コメント

  1. […] 【PythonのCollectionsを使いこなせ!】Counterの基本的な使い方Pythonを使って集計の処理をしたいと考えていますか?本記事は集計の基本であるカウントをするためのPythonライブラリ(Collection […]

  2. […] 【PythonのCollectionsを使いこなせ!】Counterの基本的な使い方Pythonを使って集計の処理をしたいと考えていますか?本記事は集計の基本であるカウントをするためのPythonライブラリ(Collection […]

タイトルとURLをコピーしました