【Python】Data Types徹底解説!collections.Counter.subtract()の使い方をマスターしよう


メソッドのしくみ

subtract() メソッドは、引数として渡されたカウンターオブジェクトの要素と、呼び出し元のカウンターオブジェクトの要素を比較します。一致する要素が見つかると、呼び出し元のカウンターオブジェクトのその要素のカウントが引数のカウンターオブジェクトのカウント分だけ減算されます。要素のカウントが 0 以下になると、その要素は呼び出し元のカウンターオブジェクトから削除されます。

メソッドの引数

subtract() メソッドは、1つの引数を取ります。

  • other: 差を取る対象となるカウンターオブジェクト。これは、Counter オブジェクト、辞書、またはマッピングを実装している任意のオブジェクトであることができます。

メソッドの戻り値

subtract() メソッドは、None を返します。

メソッドの使い方

subtract() メソッドは、以下のようないくつかの方法で使用できます。

  • 2つのカウンターオブジェクトの差を計算する
from collections import Counter

c1 = Counter('abcde')
c2 = Counter('abbc')

c1.subtract(c2)
print(c1)

このコードは、c1 から c2 の要素ごとの差を計算し、結果を c1 に格納します。出力は以下のようになります。

Counter({'e': 1, 'd': 1, 'c': 1})
  • ある集合から別の集合を引いて、その差集合を求める
from collections import Counter

vocab = Counter('abcdefghijklmnopqrstuvwxyz')
text = Counter('helloworld')

missing_letters = vocab.subtract(text)
print(missing_letters)

このコードは、vocab から text の要素ごとの差を計算し、結果を missing_letters に格納します。出力は以下のようになります。

Counter({'i': 1, 'j': 1, 'k': 1, 'm': 1, 'n': 1, 'o': 1, 'p': 1, 'q': 1, 'r': 1, 's': 1, 't': 1, 'u': 1, 'v': 1, 'w': 1, 'x': 1, 'y': 1, 'z': 1})

- 演算子との違い

subtract() メソッドと - 演算子は、一見同じように見えますが、異なる動作をします。

  • - 演算子は、新しいカウンターオブジェクトを作成し、その中に差の結果を格納します。
  • subtract() メソッドは、呼び出し元のカウンターオブジェクトを変更します。

つまり、subtract() メソッドを使用すると、元のカウンターオブジェクトが変更されますが、- 演算子を使用すると、元のカウンターオブジェクトは変更されません。

  • subtract() メソッドは、引数として渡されたカウンターオブジェクトを変更しません。引数のカウンターオブジェクトを変更する必要がある場合は、copy() メソッドを使用してコピーを作成してから、そのコピーに対して subtract() メソッドを呼び出す必要があります。
  • subtract() メソッドは、要素のカウントが 0 以下になっても要素を削除しません。要素のカウントが 0 以下の要素にアクセスするには、elements() メソッドを使用する必要があります。


from collections import Counter

c1 = Counter('abcde')
c2 = Counter('abbc')

c1.subtract(c2)
print(c1)
Counter({'e': 1, 'd': 1, 'c': 1})
from collections import Counter

vocab = Counter('abcdefghijklmnopqrstuvwxyz')
text = Counter('helloworld')

missing_letters = vocab.subtract(text)
print(missing_letters)
Counter({'i': 1, 'j': 1, 'k': 1, 'm': 1, 'n': 1, 'o': 1, 'p': 1, 'q': 1, 'r': 1, 's': 1, 't': 1, 'u': 1, 'v': 1, 'w': 1, 'x': 1, 'y': 1, 'z': 1})
from collections import Counter

c1 = Counter('abcde')
c2 = Counter('abbc')

c1.subtract(c2)

for elem, count in c1.elements():
    if count <= 0:
        print(elem, count)

このコードは、c1 から c2 の要素ごとの差を計算し、要素のカウントが 0 以下の要素をすべて出力します。出力は以下のようになります。

e -1
d -1
c -1
from collections import Counter

c1 = Counter('abcde')
c2 = Counter('abbc')

c1_copy = c1.copy()
c1_copy.subtract(c2)

print(c1)  # 元の c1 は変更されない
print(c1_copy)  # c1_copy にのみ差が反映される


手動でループ処理する

最も基本的な代替方法は、手動でループ処理を使用して差を計算することです。以下のコードは、c1 から c2 の要素ごとの差を計算し、新しい Counter オブジェクト result に格納する方法を示しています。

from collections import Counter

c1 = Counter('abcde')
c2 = Counter('abbc')

result = Counter()

for elem in c1:
    result[elem] = c1[elem] - c2.get(elem, 0)

print(result)

dict と operator モジュールを使用する

別の代替方法は、dictoperator モジュールを使用して差を計算することです。以下のコードは、c1 から c2 の要素ごとの差を計算し、新しい dict オブジェクト result に格納する方法を示しています。

from collections import Counter
import operator

c1 = Counter('abcde')
c2 = Counter('abbc')

result = Counter(operator.sub(c1, c2))

print(result)

この方法は、簡潔で読みやすいコードが書けます。ただし、operator.sub() 関数は、要素のカウントが 0 以下の要素を削除してしまうという点に注意が必要です。

第三者ライブラリを使用する

差を計算するためのサードパーティ製ライブラリもいくつかあります。例えば、more-itertools ライブラリには、subtract() 関数が提供されており、これを使用してカウンターオブジェクトの差を計算できます。

from collections import Counter
from more_itertools import subtract

c1 = Counter('abcde')
c2 = Counter('abbc')

result = subtract(c1, c2)

print(result)

この方法は、専用のライブラリをインストールする必要があるという点に注意が必要です。

最適な代替方法の選択

使用する代替方法は、状況によって異なります。単純な差の計算のみが必要な場合は、collections.Counter.subtract() メソッドが最良の選択肢です。より柔軟性が必要な場合は、手動でループ処理する方法が適しています。コードの簡潔さを優先する場合は、dictoperator モジュールを使用する方法が適しています。サードパーティ製ライブラリを使用する場合は、そのライブラリの機能とパフォーマンスを考慮する必要があります。