先日下記の記事でCollectionsのCounterの基本的な使い方を紹介しました。
本記事では実際にCounterが現場でどのよう活用されているのかを紹介します。
ぜひ参考にしてください!
Counterの活用方法
要素の個数を数える
Counterオブジェクトを作成した場合だとそのまま出力してもトータルの要素数はパッとはわかりません。ですが割合などを出す場合に全体の要素数が必要になる場面は非常に多いと思います。
そんなトータルの要素数を簡単に取得する際は下記のように書きます。
count = Counter()
total = sum(count.values())
では実際にどのような場面で活用するのかをサンプルコードを作成しました。
今回は好きな食べ物のアンケート結果を集計する場面を想定しました。
from collections import Counter
# リストからCounterオブジェクトを生成
list_ = [
'Sushi', 'Curry', 'Steak', 'Ramen', 'Curry', 'Curry',
'Steak', 'Curry', 'Sushi', 'Sushi', 'Curry', 'Curry',
'Sushi', 'Ramen', 'Curry', 'Steak', 'Sushi', 'Curry',
]
count = Counter(list_)
print(count)
# Counter({'Curry': 8, 'Sushi': 5, 'Steak': 3, 'Ramen': 2})
total = sum(count.values())
print(total)
# 18
hoge_rate = count['Sushi'] / total
print(hoge_rate)
# 0.2777777777777778
アンケート結果からSushiに投票した人がどの全体のどれくらいを占めているのかを計算するために全体の投票数が必要になります。その際にsum(count.values())を使っています。
集計結果がリストで渡された場合であればcount[‘Sushi’] / len(list_)でも計算ができます。
ですが、sum(count.values())を使った方がいいです。使うべき主な理由は3つあります。
リスト以外のデータが渡されても同じコードを使ってトータル数を取得できる
リストの場合はlen()を使って合計数を取得できます。ですが、辞書型や引数を渡してCounterオブジェクトを生成した場合はリストのように簡単に要素数のトータルを取得することができません。
処理的には1行追加する程度で実装は可能ですが、その場合はどのようなデータがくるかに応じて実装を変える必要があります。
ですので汎用性の低いコードになる、もしくはデータの型を見てチェックして分岐させるなどの処理を書く必要があります。
ですが、Counterオブジェクトから取得するような実装をすることで、どのようなデータで集計データが渡されても同じコード使って処理が可能になります。
ですのでコードの汎用性が非常に高くなります。
複数の集計データからトータルを取得する場面でもシンプルに実装できる
データの集計の際に、元のデータが常に1つとは限りません。
複数のデータを合わせて集計をする必要がある場面も珍しくはないと思います。
もしCounterオブジェクト以外からデータを取得しようとすると、データの数だけ変数を用意して、その変数をさらに合計して計算をするなど、コード量がどうしても増えてしまいます。
ですが、CounterオブジェクトにするとCounterオブジェクトを結合してそこからデータを計算に必要な値を取得すればいいので、非常にシンプルな実装が可能になります。
データの取得元を統一することでバグを防ぐ
集計するデータをそのまま使うシンプルな実装であればそこまで意識する必要はありません。
ですが、集計したデータを少し加工してそこから何かを計算する場面もあると思います。
例えば、一定の数値より低い値は対象外として除外する、など。
そのような場合は渡された値と実際に計算に使うべき値が異なる場合があります。
その時に渡された値から計算用のデータを取得していると計算結果がズレてしまいます。
そのような事態を防ぐためにも集計や分析に使うデータは常にCounterオブジェクトから取得するように統一することで、バグの発生リスクを減らすことができます。
これらの3つの理由からもCounterオブジェクトから実際に計算に必要なデータを取得する方法は知っておくべきだと思います。
要素の重複をなくす
Counterを使う場面ではリスト型か辞書型でデータが渡されるような場面が多いと思います。
その渡された値を集計する際に要素が何種類あるのか集計したいこともあると思います。そのような場合には各要素を重複なく取得する必要があると思います。
重複なく要素を取得する方法にはリスト、辞書、Counterオブジェクトそれぞれに取得する方法があります。
リストや辞書からも取得は可能ですが、Counterを使う場面では基本Counterを経由して取得する方が望ましいと思います。
リストから重複なく要素を取得する方法
リストから取得する場合はSet型に変換して、リストに戻す方法を使います。
list_ = [
'Sushi', 'Curry', 'Steak', 'Ramen', 'Curry', 'Curry',
'Steak', 'Curry', 'Sushi', 'Sushi', 'Curry', 'Curry',
'Sushi', 'Ramen', 'Curry', 'Steak', 'Sushi', 'Curry',
]
list_3 = list(set(list_))
print(list_3)
# ['Steak', 'Sushi', 'Curry', 'Ramen']
この時注意すべきは、Set方は順番を保証しないということです。ですので順番を保つべき場面ではこの方法は使うのは避けた方がいいと思います。
順序を保証したい場合はdict.fromkeys()、sorted()を使います。
実際にコードを書くと下記のようになります。
list_ = [
'Sushi', 'Curry', 'Steak', 'Ramen', 'Curry', 'Curry',
'Steak', 'Curry', 'Sushi', 'Sushi', 'Curry', 'Curry',
'Sushi', 'Ramen', 'Curry', 'Steak', 'Sushi', 'Curry',
]
list_2 = list(set(list_))
print(list_2)
# ['Steak', 'Sushi', 'Curry', 'Ramen']
list_3 = list(dict.fromkeys(list_))
print(list_3)
# ['Sushi', 'Curry', 'Steak', 'Ramen']
list_4 = sorted(list_2, reverse=True)
print(list_4)
# ['Sushi', 'Steak', 'Ramen', 'Curry']
list_5 = sorted(list_3)
print(list_5)
# ['Curry', 'Ramen', 'Steak', 'Sushi']
まずSet型を使った場合ですが、順序が保証されないので出力結果も規則性のない順序になっています。
dict.fromkeys()を使った場合ですが、重複を削除する前のリストの先頭からの順番と同じになっています。
sorted()を使った場合は要素に対してアルファベット順に昇順、降順でソートされています。
以上からも順序を保証したい場合はdict.fromkeys()、sorted()を使う必要があることがわかると思います。
ただしdict.fromkeys()を使う場合には注意が1つあります。
それはPython3.7以上でないと順序を保証してくれいないことです。ですので使用する場合はその環境のPythonのバージョンをしっかりと確認した上で使用するように注意してください。
Python3.6以下の場合はsorted()を使ってください。
実際に実装で使う場合は、それぞれの特徴を知った上で実装することをお勧めします。
辞書からユニークな値を取得する方法
辞書の場合は要素の重複がそもそも許容されていないので、シンプルにキーを取得すればいいです。
辞書からキーを取得する方法は下記のようになります。
dict_ = {
'Curry': 8,
'Sushi': 5,
'Steak': 3,
'Ramen': 2
}
list_7 = list(dict_.keys())
print(list_7)
# ['Curry', 'Sushi', 'Steak', 'Ramen']
keys()を使った場合の返り値はdict_keysオブジェクトになります。ですのでリスト型に変換する必要があるのでその点は注意が必要です。
Counterオブジェクトから取得する方法
Counterオブジェクトから重複なく要素を取得する場合は非常にシンプルで、Counterオブジェクトをリスト型に変換するだけで可能です。
実際にコードを書いてみると下記のようになります。
from collections import Counter
list_ = [
'Sushi', 'Curry', 'Steak', 'Ramen', 'Curry', 'Curry',
'Steak', 'Curry', 'Sushi', 'Sushi', 'Curry', 'Curry',
'Sushi', 'Ramen', 'Curry', 'Steak', 'Sushi', 'Curry',
]
# init Counter obj from list.
count = Counter(list_)
print(count)
# Counter({'Curry': 8, 'Sushi': 5, 'Steak': 3, 'Ramen': 2})
list_2 = list(count)
print(list_2)
# ['Sushi', 'Curry', 'Steak', 'Ramen']
問題なく重複なしのデータが取得できていることがわかると思います。
辞書型のデータがdict.fromkeys()を使うことができるので、Counterオブジェクトに対してもdict.fromkeys()が使えるのではないかと思いたいところではありますが、Counterにはdict.fromkeys()は実装されていません。
そのため残念ではありますが使用できません。
ここは数少ないCounterと辞書型の違いです。
正負一方の値の要素のみを取得する
Counterは負の値も許容します。ですので場合によっては集計結果に負の値が含まれている可能性があります。正しく集計するためにも負の値を除外したい場合があると思います。
そのような場合は下記のように書きます。
from collections import Counter
dict_ = {
'Curry': -8,
'Sushi': -5,
'Steak': 3,
'Ramen': 2,
'Pizza': 0
}
# init Counter obj from dict.
count = Counter(dict_)
print(count)
# Counter({'Steak': 3, 'Ramen': 2, 'Pizza': 0, 'Sushi': -5, 'Curry': -8})
positive_val = +count
print(positive_val)
# Counter({'Steak': 3, 'Ramen': 2})
negative_val = -count
print(negative_val)
# Counter({'Curry': 8, 'Sushi': 5})
書き方は非常にシンプルで+もしくは–をつけるだけです。
+をつけた場合はプラスの値のみを、−をつけた場合はマイナスの値のみを取得したCounterオブジェクトが生成されます。
この時に注意すべきは−を使った時に生成されるCounterオブジェクトの要素の値は全て正の値になっているという点です。
また値が0の場合はどちらにも含まれません。
出現回数が少ない順番にする
Counterは要素の出現回数が多い順番でソートされたオブジェクトが生成されます。ですので要素を取り出そうとしても基本的には降順で取得されます。
ですが、場面によっては出現回数が少ない順番で取得したい場合もあると思います。
そのような場合は下記のように実装することで実現が可能です。
from collections import Counter
list_ = [
'Sushi', 'Curry', 'Steak', 'Ramen', 'Curry', 'Curry',
'Steak', 'Curry', 'Sushi', 'Sushi', 'Curry', 'Curry',
'Sushi', 'Ramen', 'Curry', 'Steak', 'Sushi', 'Curry',
]
# init Counter obj from list.
count = Counter(list_)
print(count)
# Counter({'Curry': 8, 'Sushi': 5, 'Steak': 3, 'Ramen': 2})
desc = count.most_common()
print(desc)
# [('Curry', 8), ('Sushi', 5), ('Steak', 3), ('Ramen', 2)]
asc = count.most_common()[::-1]
print(asc)
# [('Ramen', 2), ('Steak', 3), ('Sushi', 5), ('Curry', 8)]
いずれの場合もmost_common()を使用します。
most_common()をそのまま使用したら降順で要素を取得します。
昇順で取得したい場合はmost_common()で取得した値に対してスライスをすることで昇順で取得が可能です。
Counterの機能としては実装されていないのでこのようにするしかないです。
まとめ
今回はCounterに関して実際の現場でもよく使うものを紹介しました。
データ集計、分析などの需要が高い今の世の中においてCounterを使いこなすことができるだけでも、実装できるプログラムの幅が広がると思います。
もし、Counterに関して基礎を改めて確認したい場合は下記の記事を参考にしてください。
皆様の快適なPythonライフに役立ちますと幸いです。
コメント
[…] 【PythonのCollectionsを使いこなせ!】Counterの活用方法PythonのCollectionsのCounterのことは知っているが、実際にどのように活用すべきかわからないってお困りでしょうか?本記事では実際の現 […]
[…] […]