データ操作
田中VPoE「Pandasの基本がわかったところで、もう少し実践的な操作を学ぼう。実際の分析では、フィルタリングやグループ化を駆使してデータを掘り下げていくんだ。」
あなた「Excelのピボットテーブルみたいなことができるんですか?」
田中VPoE「まさにそうだ。しかもPandasなら、コードで再現性のある操作ができる。NetShop社の購買データを使って練習してみよう。」
フィルタリング
条件によるフィルタリング
import pandas as pd
# 単一条件
high_value = df[df['amount'] > 10000]
# 複数条件(AND)
target = df[(df['amount'] > 10000) & (df['category'] == '家電')]
# 複数条件(OR)
target = df[(df['category'] == '家電') | (df['category'] == '書籍')]
# NOT条件
non_cancelled = df[~df['status'].isin(['cancelled', 'returned'])]
# isinで複数値
electronics = df[df['category'].isin(['家電', 'PC', 'スマホ'])]
# 文字列条件
tokyo_customers = df[df['address'].str.contains('東京')]
queryメソッド
条件が複雑な場合、queryメソッドが読みやすくなります:
# queryメソッド
result = df.query('amount > 10000 and category == "家電"')
# 変数を使う
min_amount = 10000
result = df.query('amount > @min_amount')
ソート
# 単一カラムでソート
df_sorted = df.sort_values('amount', ascending=False)
# 複数カラムでソート
df_sorted = df.sort_values(['category', 'amount'], ascending=[True, False])
# インデックスでソート
df_sorted = df.sort_index()
グループ化と集計
groupby
SQLのGROUP BYに相当する、データ分析で最も重要な操作の1つです。
# カテゴリ別の集計
category_stats = df.groupby('category')['amount'].agg([
'count', 'sum', 'mean', 'median', 'std'
])
# 複数カラムでグループ化
monthly_category = df.groupby(['year_month', 'category'])['amount'].sum()
# 名前付き集計(推奨)
result = df.groupby('category').agg(
order_count=('order_id', 'count'),
total_revenue=('amount', 'sum'),
avg_amount=('amount', 'mean'),
unique_customers=('customer_id', 'nunique')
).reset_index()
集計関数一覧
| 関数 | 説明 | 用途 |
|---|---|---|
| count | 非欠損値の数 | 件数の集計 |
| sum | 合計 | 売上合計 |
| mean | 平均 | 平均客単価 |
| median | 中央値 | 外れ値に強い代表値 |
| std | 標準偏差 | ばらつきの把握 |
| min / max | 最小値 / 最大値 | 範囲の確認 |
| nunique | ユニーク数 | 顧客数、商品数 |
| first / last | 最初 / 最後の値 | 時系列の端点 |
ピボットテーブル
Excelのピボットテーブルと同じ機能です:
# ピボットテーブル
pivot = df.pivot_table(
values='amount',
index='category',
columns='month',
aggfunc='sum',
fill_value=0,
margins=True # 合計行・列を追加
)
クロス集計
# カテゴリと月のクロス集計(件数)
cross = pd.crosstab(df['category'], df['month'], margins=True)
ウィンドウ関数
時系列分析でよく使う移動計算:
# 7日移動平均
df['ma_7d'] = df['daily_sales'].rolling(window=7).mean()
# 累積合計
df['cumulative_sales'] = df['daily_sales'].cumsum()
# 前日比
df['daily_change'] = df['daily_sales'].pct_change()
# 前期の値
df['prev_month_sales'] = df['monthly_sales'].shift(1)
文字列操作
# 文字列メソッド(.str アクセサ)
df['name_upper'] = df['name'].str.upper()
df['first_name'] = df['name'].str.split(' ').str[0]
df['has_tokyo'] = df['address'].str.contains('東京')
df['email_domain'] = df['email'].str.split('@').str[1]
日付操作
# 日付型への変換
df['order_date'] = pd.to_datetime(df['order_date'])
# 日付要素の抽出
df['year'] = df['order_date'].dt.year
df['month'] = df['order_date'].dt.month
df['day_of_week'] = df['order_date'].dt.dayofweek # 0=月曜
df['year_month'] = df['order_date'].dt.to_period('M')
# 日付でフィルタ
recent = df[df['order_date'] >= '2025-01-01']
# 日付差分
df['days_since_order'] = (pd.Timestamp.now() - df['order_date']).dt.days
apply関数
組み込み関数で対応できない複雑な変換には、applyを使います:
# Seriesに対するapply
df['amount_category'] = df['amount'].apply(
lambda x: '高額' if x > 10000 else '中額' if x > 3000 else '少額'
)
# DataFrameの行に対するapply
def calc_discount_rate(row):
if row['customer_rank'] == 'Gold':
return row['amount'] * 0.1
elif row['customer_rank'] == 'Silver':
return row['amount'] * 0.05
return 0
df['discount'] = df.apply(calc_discount_rate, axis=1)
注意:applyは便利ですが、大量データではベクトル化された操作より遅くなります。可能な限りPandasの組み込みメソッドやNumPyを使いましょう。
まとめ
| 項目 | ポイント |
|---|---|
| フィルタリング | 条件式、isin、queryで柔軟にデータを抽出 |
| グループ化 | groupby + aggで集計。名前付き集計が推奨 |
| ピボットテーブル | pivot_tableで2次元の集計表を作成 |
| ウィンドウ関数 | rolling, cumsum, pct_changeで時系列分析 |
| 日付操作 | dtアクセサで年月日・曜日を抽出 |
チェックリスト
- 複数条件でデータをフィルタリングできる
- groupbyで名前付き集計ができる
- ピボットテーブルを作成できる
- 日付型のデータから年月・曜日を抽出できる
次のステップへ
データ操作の基本テクニックを学びました。次は、実データで必ず直面する欠損値の処理方法を学びましょう。
推定読了時間:30分