この記事では、PythonのPandas(パンダス)ライブラリを使用して、DataFrame(データフレーム)から特定の列の値に基づいて行を抽出する方法を解説します。データ分析において、条件に合致するデータだけを取り出すフィルタリング操作は非常に重要です。
フィルタリング方法の比較
| 方法 | 用途 | 例 |
|---|---|---|
loc[] | ラベルベースの条件抽出 | df.loc[df['A'] == 'foo'] |
query() | SQL風のクエリ | df.query('A == "foo"') |
isin() | リスト内の値に一致 | df[df['A'].isin(['foo', 'bar'])] |
between() | 範囲内の値 | df[df['B'].between(1, 10)] |
str.contains() | 部分一致 | df[df['A'].str.contains('foo')] |
前提知識
- DataFrame: Pandasが提供する2次元のテーブル形式のデータ構造です。行と列で構成され、Excelのスプレッドシートのようなイメージで扱えます
- Pandas: Pythonでデータ分析を行うための主要なライブラリで、大規模なデータセットを効率的に操作できます
サンプルデータの準備
import pandas as pd
df = pd.DataFrame({
'name': ['田中', '鈴木', '佐藤', '高橋', '伊藤'],
'age': [25, 30, 28, 35, 22],
'department': ['営業', '開発', '営業', '開発', '人事'],
'salary': [350000, 450000, 380000, 520000, 300000]
})
print(df)
# name age department salary
# 0 田中 25 営業 350000
# 1 鈴木 30 開発 450000
# 2 佐藤 28 営業 380000
# 3 高橋 35 開発 520000
# 4 伊藤 22 人事 300000
基本的な行の抽出 - loc[]の使用
DataFrameの特定の列が特定の値に一致する行を抽出する最も基本的な方法は、loc[]を使うことです。
# 部署が「営業」の行を抽出
sales_dept = df.loc[df['department'] == '営業']
print(sales_dept)
# name age department salary
# 0 田中 25 営業 350000
# 2 佐藤 28 営業 380000
比較演算子の種類
# 等しい
df.loc[df['age'] == 30]
# 等しくない
df.loc[df['department'] != '営業']
# より大きい
df.loc[df['salary'] > 400000]
# 以上
df.loc[df['age'] >= 28]
# より小さい
df.loc[df['salary'] < 400000]
# 以下
df.loc[df['age'] <= 25]
複数の条件での行抽出
複数の条件を使う場合、&(AND)や|(OR)を使って条件を組み合わせます。
AND条件(すべての条件を満たす)
# 開発部門で、かつ給与が50万以上
high_earners = df.loc[(df['department'] == '開発') & (df['salary'] >= 500000)]
print(high_earners)
# name age department salary
# 3 高橋 35 開発 520000
OR条件(いずれかの条件を満たす)
# 営業部門、または人事部門
sales_or_hr = df.loc[(df['department'] == '営業') | (df['department'] == '人事')]
print(sales_or_hr)
# name age department salary
# 0 田中 25 営業 350000
# 2 佐藤 28 営業 380000
# 4 伊藤 22 人事 300000
条件の否定(NOT)
# 開発部門以外
not_dev = df.loc[~(df['department'] == '開発')]
# または
not_dev = df.loc[df['department'] != '開発']
isin()を使った複数の値のチェック
特定の列が指定したリストに含まれている行を抽出するには、isin()メソッドを使用します。
# 営業または開発部門
target_depts = ['営業', '開発']
filtered = df.loc[df['department'].isin(target_depts)]
print(filtered)
# name age department salary
# 0 田中 25 営業 350000
# 1 鈴木 30 開発 450000
# 2 佐藤 28 営業 380000
# 3 高橋 35 開発 520000
isin()の否定
# 営業・開発以外の部門
other_depts = df.loc[~df['department'].isin(['営業', '開発'])]
print(other_depts)
# name age department salary
# 4 伊藤 22 人事 300000
between()を使った範囲指定
数値や日付の範囲を指定するにはbetween()が便利です。
# 年齢が25〜30歳
age_range = df.loc[df['age'].between(25, 30)]
print(age_range)
# name age department salary
# 0 田中 25 営業 350000
# 1 鈴木 30 開発 450000
# 2 佐藤 28 営業 380000
# 給与が35万〜45万
salary_range = df.loc[df['salary'].between(350000, 450000)]
文字列のフィルタリング
文字列を含む列には、.strアクセサを使った様々なフィルタリングが可能です。
# サンプルデータ
df_str = pd.DataFrame({
'email': ['tanaka@example.com', 'suzuki@gmail.com', 'sato@example.jp', 'takahashi@company.co.jp'],
'name': ['田中太郎', '鈴木花子', '佐藤一郎', '高橋美咲']
})
部分一致(contains)
# メールアドレスに「example」を含む
example_emails = df_str.loc[df_str['email'].str.contains('example')]
print(example_emails)
前方一致(startswith)
# 名前が「田中」で始まる
tanaka = df_str.loc[df_str['name'].str.startswith('田中')]
後方一致(endswith)
# .comで終わるメールアドレス
com_emails = df_str.loc[df_str['email'].str.endswith('.com')]
正規表現を使用
# 日本のドメイン(.jp, .co.jp)
jp_emails = df_str.loc[df_str['email'].str.contains(r'\.jp$', regex=True)]
query()によるフィルタリング
query()メソッドを使うと、SQL風のクエリで条件を記述できます。
# 基本的な使い方
result = df.query('age >= 28')
# 複数条件
result = df.query('department == "開発" and salary > 400000')
# 変数を使用(@を付ける)
min_age = 25
max_salary = 400000
result = df.query('age >= @min_age and salary <= @max_salary')
query()のメリット
# loc[]を使った場合(長い)
df.loc[(df['department'] == '開発') & (df['age'] >= 28) & (df['salary'] > 400000)]
# query()を使った場合(読みやすい)
df.query('department == "開発" and age >= 28 and salary > 400000')
実践例
CSVデータのフィルタリング
import pandas as pd
# CSVファイルを読み込み
df = pd.read_csv('sales_data.csv')
# 2024年のデータのみ抽出
df['date'] = pd.to_datetime(df['date'])
df_2024 = df.loc[df['date'].dt.year == 2024]
# 売上が10万以上のデータ
high_sales = df.loc[df['amount'] >= 100000]
# 特定の商品カテゴリのみ
categories = ['電子機器', '家電']
filtered = df.loc[df['category'].isin(categories)]
複数条件の組み合わせ
def filter_employees(df, min_age=None, max_age=None,
departments=None, min_salary=None):
"""従業員データをフィルタリングする"""
result = df.copy()
if min_age is not None:
result = result.loc[result['age'] >= min_age]
if max_age is not None:
result = result.loc[result['age'] <= max_age]
if departments is not None:
result = result.loc[result['department'].isin(departments)]
if min_salary is not None:
result = result.loc[result['salary'] >= min_salary]
return result
# 使用例
filtered = filter_employees(
df,
min_age=25,
max_age=35,
departments=['営業', '開発'],
min_salary=350000
)
欠損値を考慮したフィルタリング
# 欠損値を含むデータ
df_with_na = pd.DataFrame({
'name': ['田中', '鈴木', '佐藤', None],
'score': [80, None, 90, 70]
})
# 欠損値でない行のみ抽出
valid_names = df_with_na.loc[df_with_na['name'].notna()]
# 欠損値を含む行を抽出
missing_scores = df_with_na.loc[df_with_na['score'].isna()]
# 複数列で欠損値がない行のみ
complete_rows = df_with_na.dropna()
パフォーマンスのヒント
大規模なDataFrameを扱う場合のパフォーマンス改善のヒントです。
# 推奨: ブールインデックスを直接使用
df[df['column'] == value] # 高速
# 避ける: 不要なコピーを作成
df.loc[df['column'] == value].copy() # 遅い(必要な場合のみ使用)
# 推奨: query()で複雑な条件を記述
df.query('col1 > 10 and col2 < 20') # 読みやすく、最適化される
# カテゴリ型への変換で高速化
df['department'] = df['department'].astype('category')
まとめ
| 用途 | 推奨メソッド |
|---|---|
| 単純な条件 | df[df['col'] == value] |
| 複数条件(読みやすさ重視) | df.query('条件') |
| リスト内の値に一致 | df[df['col'].isin(list)] |
| 範囲指定 | df[df['col'].between(min, max)] |
| 文字列の部分一致 | df[df['col'].str.contains('pattern')] |
Pandasのフィルタリング機能を使うと、特定の条件に基づいて簡単にDataFrameの行を抽出できます。データの構造や用途に応じて適切なメソッドを選択し、効率的なデータ分析を行いましょう。