HOWTO · Pandas

Pandas のグループごとに一意の値をカウントする

Pandas ライブラリの df.groupby().nunique()、df.groupby().agg()、および df.groupby().unique()メソッドを使用して、DataFrame の列をグループ化して値をカウントする

このページの内容

DataFrame を分析していると、列全体ではなく各グループ内にいくつの異なる値が現れるかを知りたい場合がよくあります。たとえば、Countries の各エントリがどの code 値を使っているかを記録したデータセットがあり、国ごとの個数を1つずつ求めたいとします。このタスクには、pandas の groupby() 操作に実用的な3つの方法があります。

  1. df.groupby().nunique() は、各グループ内の異なる値の数を返します。
  2. df.groupby().agg() は、汎用の集計インターフェイスを通じて同じカウントを適用します。
  3. df.groupby().unique() は、各グループの異なる値そのものを一覧表示します。

以下の例はすべて、国とコードのペア9行から構成される同じ DataFrame を使用します。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

import pandas as pd

data = [
    [999, "Switzerland"],
    [113, "Switzerland"],
    [112, "Japan"],
    [112, "Switzerland"],
    [113, "Canada"],
    [114, "Japan"],
    [100, "Germany"],
    [114, "Japan"],
    [115, "Germany"],
]
df = pd.DataFrame(data, columns=["code", "Countries"])
print(df)
   code    Countries
0   999  Switzerland
1   113  Switzerland
2   112        Japan
3   112  Switzerland
4   113       Canada
5   114        Japan
6   100      Germany
7   114        Japan
8   115      Germany

GroupBy.nunique() で一意の値をカウントする

グループごとの異なる値の個数を数える最も直接的な方法は、nunique() です。groupby() の後に code 列を選択すると、グループ化オブジェクトがグループ化された Series になるため、nunique() は各国の行内にある異なるコードを数えます。

このセクションのコードと表示されるラベルや出力は英語のままです。

import pandas as pd

data = [
    [999, "Switzerland"],
    [113, "Switzerland"],
    [112, "Japan"],
    [112, "Switzerland"],
    [113, "Canada"],
    [114, "Japan"],
    [100, "Germany"],
    [114, "Japan"],
    [115, "Germany"],
]
df = pd.DataFrame(data, columns=["code", "Countries"])
result = df.groupby("Countries")["code"].nunique()
print(result)
Countries
Canada         1
Germany        2
Japan          2
Switzerland    3
Name: code, dtype: int64

結果は、グループ名をインデックス、個数を値として持つ Series です。スイスは3つの異なるコードを使い、ドイツと日本はそれぞれ2つ、カナダは1つを使っています。グループキーはインデックスになっているため、平坦な2列の DataFrame が必要な場合は reset_index() を呼び出すか、次のセクションの agg() 形式を使用してください。デフォルトでは、nunique() は欠損値(NaN)を数えません。欠損値も1つの異なる値として数える必要がある場合は、dropna=False を渡します。

GroupBy.agg() で一意の値をカウントする

agg() は、グループ化オブジェクトの汎用集計メソッドです。集計関数として nunique() を渡すと、DataFrame.aggregate() インターフェイスを通じて同じ個数を得られ、結果の形状をより細かく制御できます。

このセクションのコードと表示されるラベルや出力は英語のままです。

import pandas as pd

data = [
    [999, "Switzerland"],
    [113, "Switzerland"],
    [112, "Japan"],
    [112, "Switzerland"],
    [113, "Canada"],
    [114, "Japan"],
    [100, "Germany"],
    [114, "Japan"],
    [115, "Germany"],
]
df = pd.DataFrame(data, columns=["code", "Countries"])
result = df.groupby(by="Countries", as_index=False).agg({"code": pd.Series.nunique})
print(result)
     Countries  code
0       Canada     1
1      Germany     2
2        Japan     2
3  Switzerland     3

辞書 {"code": pd.Series.nunique} は、どの列を集計し、どの関数を適用するかを agg() に指示します。pd.Series.nunique は前のセクションで直接呼び出したのと同じ操作であり、名前を文字列として渡す agg({"code": "nunique"}) も同じように動作します。as_index=False 引数は、国名をインデックスへ移す代わりに通常の列として保持するため、result はインデックス付きの Series ではなく平坦な DataFrame になります。結果を DataFrame として取得したい場合や、一意の値の個数を1回の処理で計算する複数の集計の1つにしたい場合は、この形式を選びます。

GroupBy["code"].unique() で一意の値を一覧表示する

個数は「いくつあるか」に答えますが、各国がどのコードを使っているかを知る必要がある場合もあります。グループ化された Series に unique() を適用すると、異なる値そのものが返されます。

このセクションのコードと表示されるラベルや出力は英語のままです。

import pandas as pd

data = [
    [999, "Switzerland"],
    [113, "Switzerland"],
    [112, "Japan"],
    [112, "Switzerland"],
    [113, "Canada"],
    [114, "Japan"],
    [100, "Germany"],
    [114, "Japan"],
    [115, "Germany"],
]
df = pd.DataFrame(data, columns=["code", "Countries"])
result = df.groupby("Countries")["code"].unique()
print(result)
Countries
Canada                   [113]
Germany             [100, 115]
Japan               [112, 114]
Switzerland    [999, 113, 112]
Name: code, dtype: object

結果の各エントリは、その国で出現した順に並んだ、異なる code 値の配列です。スイスの配列は、前に数えた3つのコード 999、113、112 を確認できます。各要素が単一の数値ではなく配列であるため、この Series のdtypeは object です。注意すべき動作上の違いが1つあります。unique() は返される配列に NaN を含めますが、nunique() はデフォルトで欠損値をスキップします。

値のカウントと一覧表示を使い分ける

質問が定量的な場合、つまり「各国は何種類のコードを使っているか」を知りたい場合は nunique() を選びます。整然とした数値の個数が返されるため、並べ替え、フィルタリング、元の行への結合ができます。各行のグループ数を新しい列として付加するには、df.groupby("Countries")["code"].transform("nunique") を使用します。これはグループごとの個数をすべての行にブロードキャストします。

どの値であるかが重要な場合は unique() を選びます。たとえば、国同士で共有されているコードを監査したり、値のリストを後続の処理ステップへ渡したりする場合です。すでに unique() の結果を保持していてその長さが必要な場合は、result.apply(len) で nunique() が返すのと同じ個数を再現できます。

最後に、一意の値の個数が agg({"code": "nunique", "price": "sum"}) のようなより広い集計の一部にすぎない場合は agg() を使用します。これにより、グループごとの複数の統計量を1回の処理で計算できます。要約すると、個数が必要なら nunique()、値が必要なら unique()、DataFrame の結果が必要な場合や個数をほかの集計と組み合わせたい場合は agg() を使います。