Python
2022.08.25
PythonのCounterとは?使えるメソッド、演算子まで徹底解説!
2023.11.18

Python配列(list)辞書型(dict)を使うようになった時、知っておくと便利な機能にCounterがあります。


データサイエンス、Web、ホビー、いずれの場合でもCounterの知識があるとデータ整理のストレスが低減するでしょう。


Counterに関する基礎をご紹介します。


1.PythonのCounterとは?

まずはCounterの概要をご紹介します。

①  Counterとは

Counterは、Pythonに標準で用意されているサブクラスの一つ。


Counter()にリスト型やタプル型のデータを渡すと、  Counterオブジェクトを返してくれます。

返り値には、キーとして元データの要素、キーの値には出現回数がセットされます。

②count()メソッドとの違い

すでに配列やタプルを学習された方は、count()メソッドをご存知の方もいらっしゃるでしょう。


count()とCounter、どちらも要素数に関係した処理で似ています。

count()は、特定の要素数を返してくれるメソッドに対して、Counterは元データ全体の要素とその数を紹介してくれます。


どちらもデータ処理には欠かせない機能なので、基礎はおさえておきましょう。



2.Counterの基本的な使い方


Counterの使い方、書き方をご紹介します。

①  ライブラリのimport

Counterは、モジュール:collections内にあるdictのサブクラスです。

import文の書き方としては、

import collections
my_counter = collections.Counter(my_array)

もしくは

from collections import Counter
my_counter = Counter(my_array)

という書き方をします。

②基本構文

実際にCounterを使う前後でデータがどのように変わるかを確認してみましょう。

from collections import Counter
my_array = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_counter = Counter(my_array)
print(type(my_counter))
print(my_counter)

結果

<class 'collections.Counter'>
Counter({'きゅうり': 3, 'レタス': 2, 'トマト': 1, 'コーン': 1, 'オニオン': 1})

配列内の要素とその数が紹介されていることが確認できますね。



3.さまざまなデータ型の要素を数える


Counter()で処理できるデータ型は、「配列型(list)」「辞書型(dict)」「文字型(str)」「タプル(tuple)」の4種類。


それぞれのデータ型をCounterで処理してみます。

①list

from collections import Counter
my_array = [90, 100, 92, 87, 100, 93, 91, 90, 90]
my_counter = Counter(my_array)
print(type(my_counter))
print(my_counter)

結果

<class 'collections.Counter'>
Counter({90: 3, 100: 2, 92: 1, 87: 1, 93: 1, 91: 1})

配列データの特徴を掴むときに役立ちそうです。

②dict

from collections import Counter
my_dict = {"okayama":1800000, "iwate":1210000, "tottori":550000, "okayama":1200000}
my_counter = Counter(my_dict)
print(type(my_counter))
print(my_counter)

結果

<class 'collections.Counter'>
Counter({'iwate': 1210000, 'okayama': 1200000, 'tottori': 550000})

辞書型データをCounterに入れると、キーは一つに集約されます。


上記の場合は、キー:okayamaが2つありますが、後方の値が割り当てられました。

また、キーの順番はabc順なことがわかります。

③str

from collections import Counter
my_str = "I Love Python"
my_counter = Counter(my_str)
print(type(my_counter))
print(my_counter)

結果

<class 'collections.Counter'>
Counter({' ': 2, 'o': 2, 'I': 1, 'L': 1, 'v': 1, 'e': 1, 'P': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1})

文字列をCounterで処理すると、どの文字が何回使われていたかを確認できます。

④tuple

from collections import Counter
my_tuple = (90, 100, 92, 87, 100, 93, 91, 90, 90)
my_counter = Counter(my_tuple)
print(type(my_counter))
print(my_counter)

結果

<class 'collections.Counter'>
Counter({90: 3, 100: 2, 92: 1, 87: 1, 93: 1, 91: 1})

タプルの場合は、配列と同じ結果になっていますね。



4.メソッドを使って処理する

Counterは、単にキーとその値を一覧表示するだけでなく、最もよく使われているキーを取得するなど、より応用的な使い方もできます。


Counterで使用できるメソッドを紹介します。

①keys()メソッドでキーを取得

Counterで取得したデータのキー一覧を確認する際は、keys()が便利です。

from collections import Counter
my_array = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_counter = Counter(my_array)
my_counter_keys = my_counter.keys()
print(type(my_counter_keys))
print(my_counter_keys)

結果

<class 'dict_keys'>
dict_keys(['きゅうり', 'トマト', 'レタス', 'コーン', 'オニオン'])

keys()メソッドを使うことで、データ型が「<class ‘collections.Counter’>」から「<class ‘dict_keys’>」に変わっていますね。

②values()メソッドで値を取得

Counterで取得したデータの値一覧を確認する際は、values()を使用できます。

from collections import Counter
my_array = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_counter = Counter(my_array)
my_counter_values = my_counter.values()
print(type(my_counter_values))
print(my_counter_values)

結果

<class 'dict_values'>
dict_values([3, 1, 2, 1, 1])

元データ内にキーが何個含まれているかを確認できました。

②  items()メソッドでキーと値のセットを取得

キーと値をセットで取得したい場合は、items()が便利です。

from collections import Counter
my_array = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_counter = Counter(my_array)
my_counter_items = my_counter.items()
print(type(my_counter_items))
print(my_counter_items)

print(list(my_counter_items)[0])

結果

<class 'dict_items'>
dict_items([('きゅうり', 3), ('トマト', 1), ('レタス', 2), ('コーン', 1), ('オニオン', 1)])
('きゅうり', 3)

元データ内で一番使われているキーとその値を、セットで取得しました。

④elements()で元の要素を持つイテレータを取得

配列やタプルをCounter処理した後に、元のデータに変換する方法としてelements()があります。

from collections import Counter
my_array = [90, 100, 92, 87, 100, 93, 91, 90, 90]
my_counter = Counter(my_array)
print(my_counter)
my_counter_elements = my_counter.elements()
print(type(my_counter_elements))
print( list(my_counter_elements) )

結果

Counter({90: 3, 100: 2, 92: 1, 87: 1, 93: 1, 91: 1})
<class 'itertools.chain'>
[90, 90, 90, 100, 100, 92, 87, 93, 91]

Counter後、元のデータに戻っていますが、順番は変わります。

基本的に、元データを変数で保持しておけば、elements()を使う必要はないかもしれませんね。

⑤most_common()でカウントが多い順に並べたリストを取得

配列内で一番よく使われているキーとその値を簡単に取得できる方法として、most_common()があります。

from collections import Counter
my_array = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_counter = Counter(my_array)
my_counter_items = my_counter.most_common(1)
print(type(my_counter_items))
print(my_counter_items)

結果

<class 'list'>
[('きゅうり', 3)]

most_common(1)内の数字を2や3に変更すると、1から2、1から3のデータを配列型で取得できます。

⑥subtract()で要素の引き算

複数のCounterオブジェクトの統計を求めることもできます。

差を求める場合は、subtractメソッドを使用します。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'トマト', 'きゅうり', 'きゅうり', 'きゅうり']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter_1.subtract(my_counter_2)
print(my_counter_1)

結果

Counter({'レタス': 2, 'コーン': 1, 'オニオン': 1, 'トマト': 0, 'きゅうり': -1})

配列1(my_array_1)と配列2(my_array_2)の要素数の差が確認できました。

⑦update()で要素の足し算

複数のCounterオブジェクトの和を求める場合は、updateメソッドを使いましょう。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'コーン', 'オニオン', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'たまご', 'きゅうり', 'きゅうり']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter_1.update(my_counter_2)
print(my_counter_1)

結果

Counter({'きゅうり': 6, 'レタス': 2, 'トマト': 1, 'コーン': 1, 'オニオン': 1, 'たまご': 1})

要素数の統計が確認できます。



5.演算子を使って処理する


複数のCounterオブジェクトの和や差は、updateメソッドやsubtractメソッド以外に+や-などの演算子でも処理できます。

① + で要素の足し算

2つのCounterオブジェクトを足してみます。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'トマト', 'ハム']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter = my_counter_1 + my_counter_2
print(my_counter)

結果

Counter({'きゅうり': 4, 'トマト': 2, 'レタス': 2, 'ハム': 1})

要素数が加算されていることが確認できました。

② – で要素の引き算

2つのCounterオブジェクトの差を求めてみます。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'トマト', 'レタス', 'きゅうり', 'きゅうり']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter = my_counter_1 - my_counter_2
print(my_counter)

結果

Counter({'レタス': 1})

要素数が削減されていますね。

subtract()メソッドとの違い

先程のCounterの引き算の処理を見てみると、元は存在したキー自体も消えていることがわかります。


(元)

my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'トマト', 'レタス', 'きゅうり', 'きゅうり']

(処理後)

Counter({'レタス': 1})

Counter演算の結果、要素数が0以下になる場合は、キーが消えます。


subtractの場合は、キーの値が0以下でもマイナス値で残りますので、演算子(-)とsubtractの違い、把握しておきましょう。

③ & で積集合を取得

Counter①にもCounter②にも両方共に含まれているキーとその値を求める場合は、積集合を意味する演算子&が使えます。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'ハム']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter = my_counter_1 & my_counter_2
print(my_counter)

結果

Counter({'きゅうり': 1})

ハムは、my_counter_1には含まれていませんので、演算結果には含まれないことが確認できますね。

④ | で和集合を取得

Counter①とCounter②のキーを合わせたいときは、和集合が便利です。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'ハム']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter = my_counter_1 | my_counter_2
print(my_counter)

結果

Counter({'きゅうり': 3, 'レタス': 2, 'トマト': 1, 'ハム': 1})

キーは合わさっても、その値は合わさらない点に注意しましょう。

③ == で等しいか判定

2つのCounterオブジェクトが、同じキーと値をもつか確認したいときは、==演算子を使用できます。

from collections import Counter
my_array_1 = ['きゅうり', 'トマト', 'レタス', 'レタス', 'きゅうり', 'きゅうり']
my_array_2 = ['きゅうり', 'ハム']
my_array_3 = ['ハム', 'きゅうり']
my_counter_1 = Counter(my_array_1)
my_counter_2 = Counter(my_array_2)
my_counter_3 = Counter(my_array_3)
my_counter_boolean_1 = my_counter_1 == my_counter_2
print(my_counter_boolean_1)

my_counter_boolean_2 = my_counter_2 == my_counter_3
print(my_counter_boolean_2)

結果

False
True

if文の条件分岐処理と併せて使うときに便利ですね。

⑥ <= でオブジェクトを内包しているか判定

2つのCounterオブジェクトを比較するときに、①のCounterオブジェクト内に②のCounterオブジェクトは、含まれるか確認したいときもあるでしょう。


そのときは内包演算子(<=)が便利です。


結果

Python version
3.10.4 (main, Mar 31 2022, 08:41:55) [GCC 7.5.0]
True

Counterオブジェクト:my_counter_1内に、Counterオブジェクト:my_counter_2は含まれますのでTrueが出力されています。


今回に限りPythonバージョンを出力しているのは、Counterオブジェクトの内包演算子(<=)は、Python3.10からサポートしている機能だからです。

Python3.9などではエラーになりますのでご注意ください。



この記事をシェア