🎹

データ分析を劇的に改善!Pandasの`rank(method="dense")`と`factorize`で実現する「見やすい連番」作成術

に公開

はじめに

データ分析をしていると、「もっとこの識別子が見やすければ…」「カテゴリごとに連番を振って比較したい…」と感じることはありませんか? 私自身、Pandasのある機能を活用することで、分析作業が格段に効率化した経験があります。

それは、rank(method="dense")pd.factorize を使って、データに 「dense(密な)連番」 を付与するというテクニックです。

「denseな連番」とは、簡単に言うと1から始まる重複なし・抜けなしの綺麗な通し番号のことです。元のデータが飛び飛びの数値だったり、長い文字列だったりしても、これらの機能を使えば、分析しやすいシンプルな連番に変換できます。

今回は、この「denseな連番」を特定の列(ここでは例としてインデックスキーという名前の列を扱います)に対して作成し、さらにグループを示す情報(例:グループ名)と組み合わせることで、より便利な分析キーを作成する方法を2つご紹介します。

なぜ「denseな連番」が便利なのか?

連番を振りたい元の列(例えば商品コード、ユーザー識別子など)の値が、以下のような場合に「denseな連番」が役立ちます。

  • 値が飛び飛びの数値や長い文字列: 「ID: 105」と「ID: 203」、「商品コード: XYZ001」と「商品コード: ABC999」など、そのままでは比較や並び替えがしにくい。
  • グループごとの集計・可視化: カテゴリごとにデータを見たいとき、元の識別子が不規則だとグラフの軸が見づらかったり、集計結果の解釈が難しくなったりする。
  • 直感的な理解: 単純な「1, 2, 3...」という連番は、誰にとっても分かりやすい。

「denseな連番」は、これらの問題を解決し、分析の効率と分かりやすさを向上させます。

方法1: rank(method="dense") を使う(元の列が数値比較可能な場合)

連番を振りたい対象の列(コード例では インデックスキー 列)が数値や日付など、大小比較が意味を持つデータの場合に有効な方法です。rank()メソッドに method="dense" を指定すると、同じ値には同じランク(番号)を振りつつ、次の異なる値には連番(ランク+1)を振ってくれます。

コード例:

import pandas as pd

# 'インデックスキー' は連番を振りたい元の列、'グループ名' はカテゴリを示す列とする
data = {'インデックスキー': [10, 20, 10, 30, 20],
        'グループ名': ['チームA', 'チームA', 'チームB', 'チームA', 'チームB']}
df = pd.DataFrame(data)

print("--- 元データ ---")
print(df)

# 1. 'インデックスキー' 列に対して、dense rank を求める (1始まり)
#    小さい値から順に 1, 2, 3... と番号が付く
df["dense_index"] = df["インデックスキー"].rank(method="dense").astype(int)

# 2. 作成した dense_index と グループ名 を連結して新しいカラムを作成
#    文字列として連結するために .astype(str) を使用
df["連番とグループ名"] = df["dense_index"].astype(str) + "_" + df["グループ名"].astype(str)

# 結果を表示
print("\n--- rank(method=\"dense\") 適用後 ---")
print(df)

実行結果例:

--- 元データ ---
   インデックスキー   グループ名
0       10  チームA
1       20  チームA
2       10  チームB
3       30  チームA
4       20  チームB

--- rank(method="dense") 適用後 ---
   インデックスキー   グループ名  dense_index 連番とグループ名
0       10  チームA            1     1_チームA
1       20  チームA            2     2_チームA
2       10  チームB            1     1_チームB
3       30  チームA            3     3_チームA
4       20  チームB            2     2_チームB

この例では、元の インデックスキー 列の値 (10, 20, 30) に基づいて、dense_index 列に (1, 2, 3) という連番が振られています。そして、それを グループ名 と連結して、"1_チームA" のような分かりやすいキーを作成しています。(新しい列名は 連番とグループ名 に変更しました)

方法2: pd.factorize() を使う(元の列が文字列やカテゴリ値にも有効)

連番を振りたい対象の列(コード例では インデックスキー 列)が文字列の場合や、大小比較に意味がない(出現順などで連番を振りたい)場合に非常に便利なのが pd.factorize() です。これは、列に含まれるユニークな値に対して、最初に出現したものから順に 0, 1, 2... と整数を割り当てます。

コード例:

import pandas as pd

# 'インデックスキー' は連番を振りたい元の列、'グループ名' はカテゴリを示す列とする
data = {'インデックスキー': ["apple", "orange", "apple", "banana", "orange"],
        'グループ名': ['果物セットX', '果物セットX', '果物セットY', '果物セットX', '果物セットY']}
df = pd.DataFrame(data)

print("--- 元データ ---")
print(df)

# 1. 'インデックスキー' 列の固有の値に対して、出現順に dense な番号を割り当てる
#    pd.factorize は (整数配列, ユニーク値配列) のタプルを返すため [0] で整数配列を取得
#    0始まりなので +1 して 1始まりにする
df["dense_index"] = pd.factorize(df["インデックスキー"])[0] + 1

# 2. 作成した dense_index と グループ名 を連結して新しいカラムを作成
df["連番とグループ名"] = df["dense_index"].astype(str) + "_" + df["グループ名"].astype(str)

# 結果を表示
print("\n--- pd.factorize 適用後 ---")
print(df)

実行結果例:

--- 元データ ---
  インデックスキー     グループ名
0    apple  果物セットX
1   orange  果物セットX
2    apple  果物セットY
3   banana  果物セットX
4   orange  果物セットY

--- pd.factorize 適用後 ---
  インデックスキー     グループ名  dense_index 連番とグループ名
0    apple  果物セットX            1   1_果物セットX
1   orange  果物セットX            2   2_果物セットX
2    apple  果物セットY            1   1_果物セットY
3   banana  果物セットX            3   3_果物セットX
4   orange  果物セットY            2   2_果物セットY

こちらでは、インデックスキー 列の文字列 ("apple", "orange", "banana") に対して、出現順に dense_index として (1, 2, 3) が割り当てられています。文字列に対しても簡単に連番が振れ、それを グループ名 と組み合わせることで、"1_果物セットX" のような直感的なキーが作れます。

おわりに

  • 元の列の値の大小に基づいて連番を振りたい場合 → rank(method="dense")
  • 元の列が文字列やカテゴリ値で、出現順などに基づいて連番を振りたい場合 → pd.factorize()

どちらの方法でも、作成した dense_index(1から始まる連番)と、既存の「グループ名」(などのカテゴリを示す列)を astype(str) で文字列に変換し、+ "_" + のように連結すれば、"1_チームA", "2_果物セットX" のような非常に見やすい複合キーを作成できます。

この「denseな連番」と「複合キー」を活用することで、データのグループ化、集計、可視化などが格段にやりやすくなります。もし、扱いにくい識別子やIDを持つデータで分析に手間取っているなら、ぜひこの方法を試してみてください。きっと「データ分析が非常にしやすくなった!」と感じていただけると思います!

参考

https://pandas.pydata.org/docs/reference/api/pandas.Series.rank.html
https://pandas.pydata.org/docs/reference/api/pandas.factorize.html#pandas.factorize

Discussion