Pandas uniqueを使って重複しない値の一覧を取得

はい、承知いたしました。Pandasのunique()メソッドを使って重複しない値の一覧を取得する方法について、詳細な説明を含む約5000語の記事を作成します。記事の内容を直接表示します。


Pandasのunique()メソッド徹底解説:重複しない値を取得する完全ガイド

はじめに

データ分析を行う上で、データの重複は非常に一般的な問題です。例えば、顧客リストで同じ人物が複数回登録されていたり、商品リストで同じ商品が異なるコードで登録されていたりする場合があります。このような重複データは、集計値や統計量を歪め、分析結果の信頼性を低下させる可能性があります。また、データの種類やカテゴリ数を把握することも、データ理解の第一歩として非常に重要です。

Pandasは、Pythonでデータ分析を行う際に最も広く利用されているライブラリの一つであり、データ操作、クリーニング、集計、可視化など、データ分析のワークフローを強力にサポートします。Pandasには、データ内の重複を検出し、処理するための様々な便利な機能が備わっています。その中でも、特定の列(Series)に含まれる重複しない一意の値(ユニークな値)の一覧を取得するために、unique()メソッドは非常に頻繁に使用されます。

この記事では、Pandasのunique()メソッドに焦点を当て、その基本的な使い方から、詳細な動作、応用例、そして他の関連メソッドとの比較まで、徹底的に解説します。この記事を読むことで、あなたはunique()メソッドを効果的に活用し、データ分析における重複値の取り扱いをマスターできるようになるでしょう。

Pandasとは

本題に入る前に、Pandasについて簡単に触れておきましょう。Pandasは、構造化データ(テーブルデータ、時系列データなど)を扱うためのPythonライブラリです。主なデータ構造として、1次元のラベル付き配列であるSeriesと、2次元のラベル付きデータ構造であるDataFrameを提供します。Excelのスプレッドシートやリレーショナルデータベースのテーブルのようなデータを、Python上で非常に効率的に操作できます。データ分析の現場では、NumPyと並んで必須のツールとなっています。

  • Series: 1つの列を持つデータ構造です。各要素にはインデックス(ラベル)が付与されます。unique()メソッドは、このSeriesオブジェクトに対して使用します。
  • DataFrame: 複数の列を持つデータ構造です。各列はSeriesオブジェクトであり、行にはインデックス、列にはカラム名が付与されます。

データ分析の初期段階では、外部ファイル(CSV, Excel, データベースなど)からデータを読み込み、DataFrameとして扱います。そして、特定の列を選択してSeriesとして取り出し、その列に含まれる値の種類を調べたり、重複を取り除いたりといった処理を行います。unique()メソッドは、この「特定の列に含まれる値の種類を調べる」という用途で活躍します。

重複値とは何か?なぜ重要なのか?

データにおける重複値とは、データセット内で全く同じ値、あるいは同じ特徴を持つエントリが複数回出現することを指します。

例えば、以下のような簡単なデータがあるとします。

ID 氏名 都道府県
1 山田太郎 東京都
2 佐藤花子 大阪府
3 山田太郎 東京都
4 田中一郎 愛知県
5 佐藤花子 大阪府

このデータでは、「氏名」列において「山田太郎」と「佐藤花子」が重複して出現しています。「都道府県」列においても、「東京都」と「大阪府」が重複しています。

なぜ重複値の確認や処理が重要なのでしょうか?

  1. 統計値の歪み: 重複したデータが含まれていると、平均値、合計値、出現頻度などの統計量が正確でなくなります。例えば、上記の例で「東京都」の人数を数えると、重複を考慮しないと2人とカウントされてしまいますが、実際にはユニークな人物としては1人かもしれません(IDが異なるため、別人として扱われるかもしれませんが、もしIDも重複していたらより深刻な問題になります)。
  2. 分析結果の誤り: 重複によってデータの分布が偏ると、その後の分析結果(機械学習モデルの学習、傾向分析など)が誤った結論につながる可能性があります。
  3. データ理解の妨げ: あるカテゴリに含まれるユニークな種類がいくつあるのかを把握できないと、データの構造や特徴を正しく理解できません。例えば、アンケート調査で選択肢の回答の種類がいくつあったかを知りたい場合などです。
  4. 処理効率の低下: 重複が多いデータに対して処理を行うと、無駄な計算が増え、処理時間が長くなることがあります。

したがって、データ分析の過程では、データの重複状況を確認し、必要に応じて重複を削除したり、ユニークな値だけを取り出したりする作業が不可欠となります。unique()メソッドは、このうち「ユニークな値だけを取り出す」という目的に特化した非常に便利なツールです。

Pandas unique() メソッドの基本

それでは、いよいよPandasのunique()メソッドの使い方を見ていきましょう。

unique()メソッドは、PandasのSeriesオブジェクトに対して呼び出されます。DataFrame全体に対して直接unique()を使うことはできません。DataFrameの特定の列に含まれるユニークな値を取得したい場合は、まずその列をSeriesとして選択する必要があります。

基本的な使い方:

“`python
import pandas as pd

Seriesを作成

data = pd.Series([10, 20, 10, 30, 20, 40, 10])

unique()メソッドを呼び出す

unique_values = data.unique()

print(unique_values)
“`

このコードを実行すると、以下のような出力が得られます。

[10 20 30 40]

元のSeries data には 10 が3回、20 が2回出現していますが、unique()メソッドは重複を取り除き、ユニークな値である 10, 20, 30, 40 のみを返しています。

戻り値:

unique()メソッドの戻り値は、Pandas Seriesではなく、NumPyの配列 (ndarray) です。これは重要なポイントです。戻り値はユニークな値のリストであり、元のSeriesのインデックス情報は含まれません。

戻り値のデータ型は、元のSeriesのデータ型と同じになります。例えば、元のSeriesが整数型であれば、戻り値も整数型のNumPy配列になります。文字列型であれば、文字列型のNumPy配列になります。

コード例:様々なデータ型に対する unique()

数値データだけでなく、文字列やその他のデータ型に対してもunique()は同様に機能します。

“`python
import pandas as pd

文字列のSeries

cities = pd.Series([‘Tokyo’, ‘Osaka’, ‘Tokyo’, ‘Nagoya’, ‘Osaka’, ‘Fukuoka’])
unique_cities = cities.unique()
print(“ユニークな都市名:”, unique_cities)
print(“戻り値の型:”, type(unique_cities))
print(“戻り値の要素の型:”, unique_cities.dtype)

浮動小数点数のSeries

prices = pd.Series([100.5, 200.0, 100.5, 150.0, 200.0])
unique_prices = prices.unique()
print(“ユニークな価格:”, unique_prices)
print(“戻り値の型:”, type(unique_prices))
print(“戻り値の要素の型:”, unique_prices.dtype)

混合型のSeries (注意が必要)

PandasのSeriesは基本的には単一のデータ型を持つべきですが、

意図せず混合型になる場合もあります。

mixed_data = pd.Series([1, ‘apple’, 2, ‘banana’, 1, ‘apple’])
unique_mixed = mixed_data.unique()
print(“ユニークな混合データ:”, unique_mixed)
print(“戻り値の型:”, type(unique_mixed))

混合型の場合、戻り値の要素型はobjectになることが多い

print(“戻り値の要素の型:”, unique_mixed.dtype)

時系列データのSeries

dates = pd.Series(pd.to_datetime([‘2023-01-01’, ‘2023-01-05’, ‘2023-01-01’, ‘2023-01-10’]))
unique_dates = dates.unique()
print(“ユニークな日付:”, unique_dates)
print(“戻り値の型:”, type(unique_dates))
print(“戻り値の要素の型:”, unique_dates.dtype)
“`

出力例:

ユニークな都市名: ['Tokyo' 'Osaka' 'Nagoya' 'Fukuoka']
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: object
ユニークな価格: [100.5 200. 150. ]
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: float64
ユニークな混合データ: [1 'apple' 2 'banana']
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: object
ユニークな日付: ['2023-01-01T00:00:00.000000000' '2023-01-05T00:00:00.000000000'
'2023-01-10T00:00:00.000000000']
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: datetime64[ns]

ご覧の通り、様々なデータ型に対してunique()は期待通りにユニークな値のNumPy配列を返します。混合型のSeriesに対しては、戻り値の要素型がobjectになることに注意してください。これは、NumPy配列が単一のデータ型を保持しようとするため、異なる型の要素を格納する際に最も汎用的なobject型として扱うためです。

unique()メソッドの詳細な動作

unique()メソッドは、内部的に効率的なアルゴリズムを使用してユニークな値を抽出します。具体的な実装はPandasのバージョンや内部構造によって異なりますが、一般的にはハッシュテーブル(Pythonの辞書のようなもの)を使用して、既に出現した値を記録しながらSeriesを走査する方式が考えられます。これにより、データサイズに対して比較的効率的にユニーク値を特定できます。

要素の出現順序について:

unique()メソッドによって返されるNumPy配列は、原則として元のSeriesに出現した順序でユニークな値が並べられます。つまり、初めて出現したユニークな値から順にリストアップされます。

上記の都市名の例を再掲します。
cities = pd.Series(['Tokyo', 'Osaka', 'Tokyo', 'Nagoya', 'Osaka', 'Fukuoka'])
ユニークな都市名は ['Tokyo', 'Osaka', 'Nagoya', 'Fukuoka'] となります。
‘Tokyo’ が最初に出現し、次に ‘Osaka’、そして ‘Nagoya’、最後に ‘Fukuoka’ が初めて出現します。この順序が保持されています。

しかし、この順序性は公式ドキュメントで保証されているわけではないという点に注意が必要です。ほとんどの場合は出現順になりますが、将来のバージョンで変更されたり、特定の条件下で順序が乱れる可能性もゼロではありません。もし、特定の順序(例えばアルファベット順や数値順)が必要な場合は、unique()で取得したNumPy配列を別途ソートする必要があります。

“`python
import pandas as pd
import numpy as np

cities = pd.Series([‘Tokyo’, ‘Osaka’, ‘Tokyo’, ‘Nagoya’, ‘Osaka’, ‘Fukuoka’])
unique_cities = cities.unique()

print(“出現順(原則):”, unique_cities)

アルファベット順にソートする場合

sorted_unique_cities = np.sort(unique_cities)
print(“アルファベット順:”, sorted_unique_cities)
“`

出力:

出現順(原則): ['Tokyo' 'Osaka' 'Nagoya' 'Fukuoka']
アルファベット順: ['Fukuoka' 'Nagoya' 'Osaka' 'Tokyo']

このように、順序が必要な場合は明示的にソートを行いましょう。

欠損値 (NaN) の扱い

データ分析では欠損値(Not a Number, NaN)の存在は避けて通れません。Pandasでは、数値データにはNumPyのnp.nanが、オブジェクト型データなどにはPythonのNoneが欠損値を表現するためによく使われます。

unique()メソッドは、欠損値(NaN)を一つのユニークな値として扱います。もしSeriesに複数のNaNが含まれていても、unique()の戻り値にはNaNが1つだけ含まれます。

“`python
import pandas as pd
import numpy as np

NaNを含むSeries

data_with_nan = pd.Series([1, 2, np.nan, 3, 1, np.nan, 4])
unique_data_with_nan = data_with_nan.unique()

print(“ユニークな値(NaNを含む):”, unique_data_with_nan)
print(“戻り値の型:”, type(unique_data_with_nan))
print(“戻り値の要素の型:”, unique_data_with_nan.dtype)

文字列のSeriesでNoneを含む場合

cities_with_none = pd.Series([‘Tokyo’, ‘Osaka’, None, ‘Tokyo’, ‘Nagoya’, None])
unique_cities_with_none = cities_with_none.unique()

print(“ユニークな都市名(Noneを含む):”, unique_cities_with_none)
print(“戻り値の型:”, type(unique_cities_with_none))
print(“戻り値の要素の型:”, unique_cities_with_none.dtype)
“`

出力:

ユニークな値(NaNを含む): [ 1. 2. nan 3. 4.]
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: float64
ユニークな都市名(Noneを含む): ['Tokyo' 'Osaka' None 'Nagoya']
戻り値の型: <class 'numpy.ndarray'>
戻り値の要素の型: object

数値型SeriesにNaNが含まれる場合、要素型はfloat64になることに注意してください。これは、np.nanが浮動小数点数として扱われるためです。オブジェクト型SeriesにNoneが含まれる場合も、Noneはユニークな値として扱われます。

欠損値をユニークな値として扱いたくない場合は、unique()を呼び出す前に欠損値を削除(dropna())するか、別の方法(後述のnunique(dropna=True)など)を使用する必要があります。

DataFrameでの unique() の使い方

前述の通り、unique()メソッドはSeriesオブジェクトに特化したメソッドです。DataFrame全体に対して直接unique()を呼び出すことはできません。

DataFrameの特定の列のユニークな値を取得したい場合は、まずその列をDataFrameから選択し、Seriesとして取り出す必要があります。

“`python
import pandas as pd
import numpy as np

サンプルDataFrameを作成

data = {‘ColumnA’: [1, 2, 1, 3, 2, 4, 1],
‘ColumnB’: [‘apple’, ‘banana’, ‘apple’, ‘orange’, ‘banana’, ‘grape’, ‘apple’],
‘ColumnC’: [True, False, True, False, False, True, True]}
df = pd.DataFrame(data)

print(“元のDataFrame:”)
print(df)

‘ColumnA’ 列のユニークな値を取得

unique_a = df[‘ColumnA’].unique()
print(“\n’ColumnA’ のユニークな値:”, unique_a)
print(“‘ColumnA’ のユニークな値の型:”, type(unique_a))

‘ColumnB’ 列のユニークな値を取得

unique_b = df[‘ColumnB’].unique()
print(“\n’ColumnB’ のユニークな値:”, unique_b)
print(“‘ColumnB’ のユニークな値の型:”, type(unique_b))

‘ColumnC’ 列のユニークな値を取得

unique_c = df[‘ColumnC’].unique()
print(“\n’ColumnC’ のユニークな値:”, unique_c)
print(“‘ColumnC’ のユニークな値の型:”, type(unique_c))
“`

出力:

“`
元のDataFrame:
ColumnA ColumnB ColumnC
0 1 apple True
1 2 banana False
2 1 apple True
3 3 orange False
4 2 banana False
5 4 grape True
6 1 apple True

‘ColumnA’ のユニークな値: [1 2 3 4]
‘ColumnA’ のユニークな値の型:

‘ColumnB’ のユニークな値: [‘apple’ ‘banana’ ‘orange’ ‘grape’]
‘ColumnB’ のユニークな値の型:

‘ColumnC’ のユニークな値: [ True False]
‘ColumnC’ のユニークな値の型:
“`

このように、DataFrameから単一の列を選択する際には、df['列名'] の形式を使用します。これによりSeriesオブジェクトが得られるため、続けて.unique()を呼び出すことができるのです。

複数の列のユニーク値を取得する場合:

もし複数の列についてそれぞれユニークな値を取得したい場合は、各列に対して個別にunique()を適用する必要があります。これは、列ごとにデータ型や値のセットが異なるためです。

“`python
import pandas as pd

data = {‘ColumnA’: [1, 2, 1, 3],
‘ColumnB’: [‘apple’, ‘banana’, ‘apple’, ‘orange’]}
df = pd.DataFrame(data)

for col in df.columns:
unique_vals = df[col].unique()
print(f”‘{col}’ 列のユニークな値: {unique_vals}”)
“`

出力:

'ColumnA' 列のユニークな値: [1 2 3]
'ColumnB' 列のユニークな値: ['apple' 'banana' 'orange']

このように、列名をループで回しながら各列に対してunique()を適用するのが一般的な方法です。

DataFrame全体のユニークな行を取得する場合:

unique()はSeriesのユニークな値を抽出するメソッドですが、DataFrame全体として「重複しない行」を取得したい場合もあるでしょう。このような目的には、unique()ではなくdrop_duplicates()メソッドを使用します。

drop_duplicates()は、重複する行を削除した新しいDataFrame(またはSeries)を返します。デフォルトでは、すべての列の組み合わせが一致する行を重複とみなします。特定の列の組み合わせで重複を判断することも可能です。

“`python
import pandas as pd

data = {‘ColumnA’: [1, 2, 1, 3, 2],
‘ColumnB’: [‘apple’, ‘banana’, ‘apple’, ‘orange’, ‘banana’]}
df = pd.DataFrame(data)

print(“元のDataFrame:”)
print(df)

DataFrame全体で重複する行を削除

df_unique_rows = df.drop_duplicates()
print(“\n重複行を削除したDataFrame:”)
print(df_unique_rows)

特定の列 (‘ColumnA’) に基づいて重複を判断・削除

ColumnAが重複している行のうち、最初に出現した行を残す

df_unique_col_a = df.drop_duplicates(subset=[‘ColumnA’])
print(“\n’ColumnA’ で重複を削除したDataFrame:”)
print(df_unique_col_a)
“`

出力:

“`
元のDataFrame:
ColumnA ColumnB
0 1 apple
1 2 banana
2 1 apple
3 3 orange
4 2 banana

重複行を削除したDataFrame:
ColumnA ColumnB
0 1 apple
1 2 banana
3 3 orange

‘ColumnA’ で重複を削除したDataFrame:
ColumnA ColumnB
0 1 apple
1 2 banana
3 3 orange
“`

drop_duplicates()は、unique()とは目的が異なります。unique()は値の「リスト」を返すのに対し、drop_duplicates()は重複を削除した「データ本体」を返すのです。DataFrameのユニークな行を取得したい場合はdrop_duplicates()を使う、と覚えておきましょう。

unique() メソッドの応用例

unique()メソッドは、データ分析の様々な場面で役立ちます。具体的な応用例をいくつか紹介します。

1. カテゴリカルデータの種類数確認:

あるカテゴリカル変数(例: 商品カテゴリ、顧客の地域、OSの種類など)に、どのような種類の値がどれだけ存在するかを確認することは、データ理解の第一歩です。unique()は、そのカテゴリのユニークな値のリストを提供します。ユニークな値の数を数える場合は、戻り値であるNumPy配列の.size属性やPythonのlen()関数を使用します。

“`python
import pandas as pd

サンプルデータ: 商品リスト

products_data = {‘ProductID’: [101, 102, 103, 104, 105, 106, 107],
‘ProductName’: [‘Apple’, ‘Banana’, ‘Cherry’, ‘Date’, ‘Elderberry’, ‘Fig’, ‘Grape’],
‘Category’: [‘Fruit’, ‘Fruit’, ‘Fruit’, ‘Fruit’, ‘Berry’, ‘Fruit’, ‘Fruit’]}
df_products = pd.DataFrame(products_data)

‘Category’ 列のユニークな値を取得

unique_categories = df_products[‘Category’].unique()
print(“ユニークな商品カテゴリ:”, unique_categories)

ユニークな商品カテゴリの数を取得

num_unique_categories = len(unique_categories)
print(“ユニークな商品カテゴリの数:”, num_unique_categories)

あるいは、戻り値のsize属性を使う

num_unique_categories_size = unique_categories.size
print(“ユニークな商品カテゴリの数 (.size):”, num_unique_categories_size)

さらに、NaNもユニークとしてカウントしたい場合は、NaNを含むカテゴリデータを用意

products_data_nan = {‘ProductID’: [101, 102, 108],
‘ProductName’: [‘Apple’, ‘Banana’, ‘Kiwi’],
‘Category’: [‘Fruit’, ‘Fruit’, pd.NA]} # PandasのNAはobjectとして扱われやすい
df_products_nan = pd.DataFrame(products_data_nan)

unique_categories_nan = df_products_nan[‘Category’].unique()
print(“\nユニークな商品カテゴリ (NaNを含む):”, unique_categories_nan)
print(“ユニークな商品カテゴリの数 (NaNを含む):”, len(unique_categories_nan))
“`

出力:

“`
ユニークな商品カテゴリ: [‘Fruit’ ‘Berry’]
ユニークな商品カテゴリの数: 2
ユニークな商品カテゴリの数 (.size): 2

ユニークな商品カテゴリ (NaNを含む): [‘Fruit’ ]
ユニークな商品カテゴリの数 (NaNを含む): 2
“`

unique()len()(または.size)を組み合わせることで、カテゴリの種類数を確認できます。ただし、ユニークな値の「数」だけを知りたい場合は、後述のnunique()メソッドの方がより直接的で効率的です。

2. データ分布の確認:

数値データにおいて、どのような値が存在するかを確認したい場合にもunique()は役立ちます。特に、離散的な数値データの場合に有効です。連続的な数値データの場合は、ユニーク値が膨大になる可能性があるため、ヒストグラムなどで分布を確認する方が一般的です。

“`python
import pandas as pd

サンプルデータ: 年齢リスト

ages = pd.Series([25, 30, 25, 35, 30, 25, 40, 35])

ユニークな年齢を取得

unique_ages = ages.unique()
print(“ユニークな年齢:”, unique_ages)

ユニークな年齢の数を取得

num_unique_ages = len(unique_ages)
print(“ユニークな年齢の数:”, num_unique_ages)
“`

出力:

ユニークな年齢: [25 30 35 40]
ユニークな年齢の数: 4

この例では、年齢として 25, 30, 35, 40 という4種類の値が存在することがわかります。

3. 異常値の検出:

期待される値の範囲やリストから外れたユニークな値を見つけることで、データ入力エラーや異常値を検出できます。例えば、性別が「男性」「女性」のいずれかであるべき列に、「不明」や「その他」といった予期しない値が含まれていないかを確認できます。

“`python
import pandas as pd

サンプルデータ: 性別リスト (エラーを含む)

genders = pd.Series([‘Male’, ‘Female’, ‘Male’, ‘Female’, ‘Other’, ‘Male’, ‘Femlae’]) # Typo!

ユニークな性別を取得

unique_genders = genders.unique()
print(“ユニークな性別:”, unique_genders)
“`

出力:

ユニークな性別: ['Male' 'Female' 'Other' 'Femlae']

この出力から、「Other」やタイポによる「Femlae」といった異常な(または意図しない)値が含まれていることをすぐに確認できます。このようにunique()はデータクリーニングの初期段階で非常に有用です。

4. データクリーニング:

表記の揺れを確認する際にもunique()は役立ちます。例えば、都市名や会社名などで「株式会社〇〇」「(株)〇〇」といった表記の違いがないかを確認できます。

“`python
import pandas as pd

サンプルデータ: 会社名リスト (表記揺れを含む)

companies = pd.Series([‘株式会社A’, ‘B Inc.’, ‘株式会社A’, ‘C Co.’, ‘B Inc.’, ‘(株)A’])

ユニークな会社名を取得

unique_companies = companies.unique()
print(“ユニークな会社名:”, unique_companies)
“`

出力:

ユニークな会社名: ['株式会社A' 'B Inc.' 'C Co.' '(株)A']

「株式会社A」と「(株)A」が別のユニークな値として認識されていることがわかります。データ分析の前に、これらの表記を統一するクリーニング作業が必要であることがこれで明らかになります。

unique() と他の関連メソッドとの比較

Pandasには、ユニークな値に関連するいくつかのメソッドがあります。それぞれの目的とunique()との違いを理解することは、適切なメソッドを選択するために重要です。

主な関連メソッドは以下の通りです。

  1. value_counts()
  2. nunique()
  3. drop_duplicates()

これらのメソッドとunique()を比較してみましょう。

1. unique() vs value_counts()

  • unique():

    • 目的: Series内のユニークな値の「リスト」(NumPy配列)を取得する。
    • 戻り値: NumPy配列。要素はユニークな値そのもの。
    • 欠損値 (NaN): NaNを1つのユニークな値として含める。
    • 頻度: 値の出現頻度は含まれない。
  • value_counts():

    • 目的: Series内のユニークな値と、それぞれの「出現頻度」を取得する。
    • 戻り値: Series。インデックスがユニークな値、値が出現頻度。出現頻度が多い順にソートされる(デフォルト)。
    • 欠損値 (NaN): デフォルトではNaNをカウントに含めない (dropna=True)。dropna=FalseとするとNaNもカウントに含めることができる。
    • 頻度: 各ユニーク値の出現頻度が含まれる。

使い分け:

  • 単にユニークな値の種類を知りたい、またはユニークな値のリスト(配列)が必要な場合は、unique() を使用します。
  • 各ユニークな値がデータ中にどれだけ出現するか(頻度)を知りたい場合は、value_counts() を使用します。

コード例:

“`python
import pandas as pd
import numpy as np

data = pd.Series([‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘A’, np.nan, ‘C’, np.nan])

print(“— unique() の結果 —“)
print(data.unique())

print(“\n— value_counts() の結果 (dropna=True) —“)

デフォルトは dropna=True

print(data.value_counts())

print(“\n— value_counts() の結果 (dropna=False) —“)
print(data.value_counts(dropna=False))
“`

出力:

“`
— unique() の結果 —
[‘A’ ‘B’ ‘C’ nan]

— value_counts() の結果 (dropna=True) —
A 3
B 2
C 2
Name: count, dtype: int64

— value_counts() の結果 (dropna=False) —
A 3
B 2
C 2
NaN 2
Name: count, dtype: int64
“`

この例から、unique()はユニークな値である[‘A’, ‘B’, ‘C’, nan]をリストとして返すのに対し、value_counts()はそれぞれの頻度情報を含むSeriesを返すことがわかります。value_counts()ではdropna引数でNaNの扱いを制御できますが、unique()にはそのような引数はなく、常にNaNをユニーク値として含みます。

2. unique() vs nunique()

  • unique():

    • 目的: Series内のユニークな値の「リスト」(NumPy配列)を取得する。
    • 戻り値: NumPy配列。要素はユニークな値そのもの。
    • 欠損値 (NaN): NaNを1つのユニークな値として含める。
  • nunique():

    • 目的: Series内のユニークな値の「数」を取得する。
    • 戻り値: 整数 (int)。
    • 欠損値 (NaN): デフォルトではNaNをカウントに含めない (dropna=True)。dropna=FalseとするとNaNもカウントに含めることができる。
    • 頻度: 関係なし。

使い分け:

  • ユニークな値の「リスト」が必要な場合は、unique() を使用します。
  • ユニークな値の「数」だけを知りたい場合は、nunique() を使用します。nunique()の方が、リストを取得するオーバーヘッドがない分、数だけを知りたい場合には効率的です。

コード例:

“`python
import pandas as pd
import numpy as np

data = pd.Series([‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘A’, np.nan, ‘C’, np.nan])

print(“— unique() の結果 (リスト) —“)
unique_list = data.unique()
print(unique_list)
print(“リストの長さ:”, len(unique_list)) # unique().size でも同じ

print(“\n— nunique() の結果 (数) —“)

デフォルトは dropna=True

print(“ユニークな値の数 (NaN除く):”, data.nunique())

dropna=False に設定

print(“ユニークな値の数 (NaN含む):”, data.nunique(dropna=False))
“`

出力:

“`
— unique() の結果 (リスト) —
[‘A’ ‘B’ ‘C’ nan]
リストの長さ: 4

— nunique() の結果 (数) —
ユニークな値の数 (NaN除く): 3

ユニークな値の数 (NaN含む): 4
“`

nunique()は戻り値が単なる整数なので、ユニーク値のリスト自体が不要で、その数だけを知りたい場合に最適です。また、nunique()dropna引数を持つのに対し、unique()にはそれがありません。これは、nunique()が数を数える際に欠損値を無視するかどうかを指定できるのに対し、unique()は欠損値を「値そのもの」としてリストに含めるかどうかが目的であるためです。

3. unique() vs drop_duplicates()

  • unique():

    • 目的: Series内のユニークな値の「リスト」(NumPy配列)を取得する。
    • 戻り値: NumPy配列。要素はユニークな値そのもの。
    • データの形式: 元のSeriesと同じ値を持つNumPy配列。SeriesのインデックスやDataFrameの構造は失われる。
  • drop_duplicates():

    • 目的: DataFrameまたはSeriesから重複した「行」や「値」を「削除」した新しいオブジェクトを取得する。
    • 戻り値: 重複を削除した新しいDataFrameまたはSeries。元の構造(インデックス、カラム名など)は維持される。
    • データの形式: DataFrameまたはSeries。

使い分け:

  • 単にユニークな値のリストが必要な場合は、unique() を使用します。
  • データ本体から重複を削除し、後続の分析のために重複のないデータセットを作成したい場合は、drop_duplicates() を使用します。

コード例(Seriesに対して drop_duplicates() を適用した場合):

“`python
import pandas as pd
import numpy as np

data = pd.Series([‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘A’, np.nan, ‘C’, np.nan])

print(“— unique() の結果 —“)
print(data.unique())

print(“\n— drop_duplicates() の結果 —“)

Seriesに対して drop_duplicates() を適用

print(data.drop_duplicates())
“`

出力:

“`
— unique() の結果 —
[‘A’ ‘B’ ‘C’ nan]

— drop_duplicates() の結果 —
0 A
1 B
3 C
6 NaN
dtype: object
“`

Seriesに対するdrop_duplicates()は、重複する値を削除したSeriesを返します。元のSeriesのインデックスが保持されている点に注目してください。NaNもユニークな値として扱われ、最初のNaNの行が残されます。

DataFrameに対するdrop_duplicates()については、すでに上記の「DataFrameでの unique() の使い方」セクションで説明しました。DataFrameの重複行を削除したい場合は、drop_duplicates()を使うのが適切です。

パフォーマンスに関する考察

Pandasのunique()メソッドは、大規模なデータセットに対しても比較的効率的に動作するように設計されています。内部的にはハッシュベースのアプローチなどが利用されており、データサイズに比例した計算時間(O(n)に近い線形時間)でユニーク値を抽出できることが多いです。

Python標準ライブラリのsetを使ってユニークな値を取得することも可能ですが、Pandas Seriesを一度Pythonリストに変換してからsetを使うなどの手順が必要になり、Seriesの最適化された内部構造を活用できないため、Pandas Seriesに対してはSeries.unique()メソッドを使用する方が通常は推奨されます。

“`python
import pandas as pd
import numpy as np
import time

大規模なSeriesを作成

size = 1_000_000
large_series = pd.Series(np.random.randint(0, 10000, size)) # 0から9999の整数をsize個ランダムに生成

unique()メソッドのパフォーマンス測定

start_time = time.time()
unique_vals_pandas = large_series.unique()
end_time = time.time()
print(f”Pandas unique() 実行時間: {end_time – start_time:.4f}秒”)
print(f”ユニークな値の数: {len(unique_vals_pandas)}”)

Python標準のset()を使った場合のパフォーマンス測定 (参考)

Seriesをリストに変換してからsetを使う

start_time = time.time()
unique_vals_set = set(large_series.tolist()) # tolist()は大きなリストを作成するためメモリ効率が悪い可能性
end_time = time.time()
print(f”\nPython set() (tolist経由) 実行時間: {end_time – start_time:.4f}秒”)
print(f”ユニークな値の数: {len(unique_vals_set)}”)

あるいは、イテレータとしてsetに渡す (より効率的かも)

start_time = time.time()
unique_vals_set_iter = set(large_series)
end_time = time.time()
print(f”\nPython set() (イテレータ経由) 実行時間: {end_time – start_time:.4f}秒”)
print(f”ユニークな値の数: {len(unique_vals_set_iter)}”)
“`

注意: 上記のパフォーマンス比較はあくまで参考であり、データの内容、データ型、マシンのスペック、PandasやNumPyのバージョンなど、様々な要因によって結果は変動します。しかし、一般的にはPandasの最適化されたメソッドの方が、Pandasオブジェクトを扱う上では有利であることが多いです。特に、データがNumPy配列として効率的に格納されている場合、unique()はその構造を直接利用できます。

メモリ使用量については、unique()はユニークな値のみを格納したNumPy配列を新しく作成します。元のSeriesに比べてユニークな値の数が非常に少ない場合はメモリ効率が良いですが、ユニークな値が多数(元のSeriesの要素数に近い数)存在する場合は、元のSeriesと同程度のメモリが必要になります。非常に大規模なデータセット(メモリに乗り切らないほど)を扱う場合は、Daskのような並列・分散処理ライブラリの利用も検討が必要になるかもしれません。

実践的なヒントと注意点

unique()メソッドを効果的に利用するための実践的なヒントと、使用上の注意点をいくつかまとめます。

  1. 戻り値はNumPy配列: unique()が返すのはPandas SeriesではなくNumPy配列です。もし、後続の処理でPandas Seriesの機能(インデックスを使った操作、Pandasメソッドの適用など)が必要な場合は、明示的にpd.Series()で変換する必要があります。

    “`python
    import pandas as pd

    data = pd.Series([1, 2, 1, 3])
    unique_array = data.unique() # NumPy配列

    NumPy配列にはSeriesのメソッドは使えない

    unique_array.value_counts() # エラーになる

    Seriesに変換

    unique_series = pd.Series(unique_array)
    print(unique_series)

    これでSeriesメソッドが使えるようになる

    print(unique_series.value_counts()) # これならOK

    “`

  2. 戻り値の順序は保証されない: 原則出現順ですが、保証されるわけではありません。特定の順序が必要な場合は、np.sort()やリストのsort()メソッドなどで明示的にソートしてください。

  3. データ型の一貫性を確認: unique()を使う列のデータ型が意図した通りになっているかを確認しましょう。特に、数値データが文字列として読み込まれていたりすると、重複が正しく検出されない場合があります。例えば、’10’と10は異なるユニーク値として扱われます。df['列名'].dtypeでデータ型を確認し、必要であればastype()で変換してからunique()を使うと良いでしょう。

    “`python
    import pandas as pd

    data = pd.Series([10, ’10’, 20, 10])
    print(“元のデータ型:”, data.dtype) # object になっている可能性
    print(“ユニークな値 (変換前):”, data.unique())

    数値型に変換を試みる (変換できない値があるとエラーになるかNaNになる)

    errors=’coerce’で変換できない値をNaNにする

    data_numeric = pd.to_numeric(data, errors=’coerce’)
    print(“変換後のデータ型:”, data_numeric.dtype)
    print(“ユニークな値 (変換後):”, data_numeric.unique())
    “`

  4. 大規模データでのメモリ使用量: 非常に多くのユニーク値が含まれる大規模なSeriesに対してunique()を呼び出すと、一時的に大きなメモリを消費する可能性があります。システムメモリの限界に注意が必要です。

  5. 欠損値の扱い: unique()はNaNやNoneをユニークな値として含めます。欠損値を除外したい場合は、事前にdropna()でSeriesから欠損値を削除するか、nunique(dropna=True)などの別のメソッドを検討してください。

    “`python
    import pandas as pd
    import numpy as np

    data = pd.Series([1, 2, np.nan, 3, 1, np.nan])

    print(“ユニークな値 (NaN含む):”, data.unique())

    NaNを除外してからunique()を呼び出す

    data_no_nan = data.dropna()
    print(“ユニークな値 (NaN除く):”, data_no_nan.unique())
    “`

まとめ

この記事では、Pandasライブラリのunique()メソッドについて、その基本的な使い方から詳細な挙動、応用例、そして他の関連メソッドとの比較まで、幅広く解説しました。

unique()メソッドは、Pandas Seriesオブジェクトに対して適用され、データに含まれる重複しない一意の値のリストをNumPy配列として返します。データ分析におけるカテゴリの種類把握、異常値の発見、データクリーニングの前処理など、様々な場面で非常に役立つ基本的なツールです。

主要なポイントをまとめます。

  • unique()はSeriesメソッドであり、DataFrameの列に対して df['列名'].unique() のように使用します。
  • 戻り値はユニークな値のNumPy配列です。
  • 欠損値(NaNやNone)は、1つのユニークな値として含まれます。
  • 原則として、戻り値の要素の順序は元のSeriesに出現した順ですが、これは保証されません。
  • ユニークな値のリストだけでなく、その出現頻度も知りたい場合は value_counts() を使用します。
  • ユニークな値の「数」だけを知りたい場合は、より効率的な nunique() を使用します (nunique()は欠損値の扱いを制御できます)。
  • DataFrame全体で重複する「行」を削除したい場合は drop_duplicates() を使用します。

これらの知識を身につけることで、Pandasを使ったデータ探索やデータクリーニングの作業がより効率的かつ正確に行えるようになるでしょう。データ分析の現場では、まずデータの全体像を掴むために、各列のユニークな値の種類や数を把握することがよくあります。unique()nunique()は、まさにそのような探索的データ分析(EDA)の強力な味方となります。

ぜひ、あなたのデータ分析ワークフローでunique()メソッドを積極的に活用してください。


(記事終)

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール