pandasデータ操作の実践テクニック集

ブラケット構文によるデータ選択

pandasでの列選択にはブラケットの使い方に注意が必要です。複数列を選択する際は二重ブラケットが必須となります。

# 複数列の選択(DataFrameを返す)
result = dataset[["col_a", "col_b", "col_c"]]

# 単一列の選択(Seriesを返す)
series_data = dataset["col_a"]

# 単一列をDataFrameとして取得
frame_data = dataset[["col_a"]]

データ型の変換手法

列のデータ型を変換するには複数のアプローチがあります。欠損値の扱いに注意が必要です。

# astypeを使用した変換
dataset.loc[:, "numeric_col"] = dataset["text_col"].astype("float")

# mapとlambdaを組み合わせた変換
dataset.loc[:, "numeric_col"] = dataset["text_col"].map(lambda v: float(v) if pd.notna(v) else 0)

# DataFrame全体の型変換(欠損値があると失敗する場合あり)
converted = pd.DataFrame(dataset, dtype=np.float64)

DataFrameの結合操作

横方向の結合

# 内部結合
merged = pd.merge(left=table_a, right=table_b, on="common_key")

# 外部結合
outer_merged = pd.merge(table_a, table_b, how="outer", left_on="id_a", right_on="id_b")

# クロス結合(デカルト積)
table_a["_merge_key"] = 1
table_b["_merge_key"] = 1
cross_result = pd.merge(table_a, table_b)

縦方向の結合

# 複数ファイルの読み込みと結合
import glob

file_list = glob.glob("./data/*.csv")
dataframes = [pd.read_csv(f) for f in file_list]
combined = pd.concat(dataframes, ignore_index=True)

行方向の反復処理

大規模データでのループ処理は避けるべきですが、必要な場合は以下の方法があります。

# zipを使用した複数列の同時走査
for val_x, val_y in zip(dataset["X"], dataset["Y"]):
    process(val_x, val_y)

# 辞書形式での行アクセス(メモリ効率は良くない)
for record in dataset.to_dict("records"):
    print(record["column_name"])

並べ替えと重複除去

# 単一列での降順ソート
sorted_df = dataset.sort_values(by="revenue", ascending=False)

# 複合条件でのソート
sorted_df = dataset.sort_values(by=["category", "priority"], ascending=[True, False])

# 重複除去とソートの組み合わせ
unique_sorted = dataset.drop_duplicates().sort_values(by=["date", "amount"])

インデックスの操作

# 時系列データのインデックス設定
dataset.set_index(pd.to_datetime(dataset["timestamp"]), inplace=True)

# インデックスのリセットと連番振り直し
dataset.reset_index(drop=True, inplace=True)

# 逆順への並べ替え
dataset = dataset.iloc[::-1].reset_index(drop=True)

列名の変更

# 辞書による個別変更
dataset.rename(columns={"old_name": "new_name"}, inplace=True)

# 関数による一括変更
dataset.rename(columns=lambda x: x.strip().lower(), inplace=True)

# 文字列操作による一括変更
dataset.columns = dataset.columns.str.replace("_", "-")

統計情報の取得

# 基本統計量
dataset.describe()

# データ型情報
dataset.dtypes
dataset.info()

# カテゴリ列の集計
dataset["status"].value_counts()
dataset["category"].unique()

# 相関・共分散
dataset.corr()  # 相関係数行列
dataset.cov()   # 共分散行列

データの抽出(loc/iloc)

# 行の選択(ラベルベース)
dataset.loc["2024-01-01"]           # 単一行
dataset.loc["2024-01-01":"2024-01-31"]  # 範囲指定

# 列の選択
dataset.loc[:, "column_name"]       # 単一列(Series)
dataset.loc[:, ["col_a", "col_b"]]  # 複数列(DataFrame)

# 条件付き選択
filtered = dataset.loc[
    (dataset["region"] == "東京") & 
    (dataset["amount"] > 10000), 
    :
]

条件に基づくデータクエリ

# 複合条件のフィルタリング
result = dataset.loc[
    (dataset["temperature"] <= 30) & 
    (dataset["humidity"] >= 40) &
    (dataset["weather"] == "晴れ"),
    :
]

# カスタム関数によるフィルタリング
def is_weekend(record):
    return pd.to_datetime(record["date"]).weekday() >= 5

weekend_data = dataset[dataset.apply(is_weekend, axis=1)]

新規列の追加

# 演算による列追加
dataset["difference"] = dataset["max_temp"] - dataset["min_temp"]

# applyによる複雑な計算
def categorize_temp(row):
    if row["max_temp"] > 35:
        return "猛暑"
    elif row["min_temp"] < 0:
        return "厳寒"
    return "普通"

dataset["temp_category"] = dataset.apply(categorize_temp, axis=1)

# assignによる複数列の一括追加
dataset = dataset.assign(
    fahrenheit_high=lambda x: x["max_temp"] * 9/5 + 32,
    fahrenheit_low=lambda x: x["min_temp"] * 9/5 + 32
)

行の追加

# concatによる結合
new_rows = pd.DataFrame([[5, 6], [7, 8]], columns=["A", "B"])
combined = pd.concat([original_df, new_rows], ignore_index=True)

欠損値の処理

# 欠損値の確認
dataset.isnull().sum()

# 欠損値を含む行の選択
valid_data = dataset[dataset["score"].notna()]

# 欠損値の削除
dataset.dropna(subset=["essential_col"], inplace=True)

# 欠損値の埋め方
dataset["score"].fillna(0, inplace=True)                    # 定数で埋める
dataset["name"].fillna(method="ffill", inplace=True)        # 前の値で埋める

時系列データの処理

# 文字列から日時型への変換
dataset["datetime"] = pd.to_datetime(dataset["date_string"])

# 特定フォーマットでの解析
dataset["parsed"] = pd.to_datetime(
    dataset["log_time"], 
    format="%d/%b/%Y:%H:%M:%S"
)

# 時系列インデックスの操作
dataset.loc["2024-01"]                    # 月単位の選択
dataset.loc["2024-01-01":"2024-01-07"]    # 範囲選択

# リサンプリング
hourly = dataset.resample("H")["value"].agg(["sum", "mean"])
daily = dataset.resample("D")["value"].sum()

# 日時コンポーネントの抽出
dataset["year_month"] = dataset["datetime"].dt.strftime("%Y%m")
dataset["date_only"] = dataset["datetime"].dt.date

SeriesからDataFrameへの変換

# reset_indexを使用
df_from_series = series_data.reset_index()

# 辞書からの構築
df_from_series = pd.DataFrame({
    "timestamp": series_data.index,
    "measurement": series_data.values
})

文字列処理

# 文字列メソッドの連鎖(strアクセサが必要)
dataset["cleaned"] = (
    dataset["raw_text"]
    .str.replace("[年月日]", "", regex=True)
    .str.strip()
)

# 部分文字列の抽出
dataset["prefix"] = dataset["code"].str[:3]

# 正規表現による抽出
dataset["extracted"] = dataset["text"].str.extract(r"(\d{4})")

GroupByによる集計

# 基本集計
grouped = dataset.groupby("category")["amount"].sum()

# 複合キーでの集計
grouped = dataset.groupby(["year", "month"]).agg({
    "revenue": "sum",
    "count": "mean",
    "std_dev": "std"
})

# インデックスを保持しない集計
grouped = dataset.groupby("region", as_index=False).agg({
    "sales": ["sum", "mean", "count"]
})

# グループごとのカスタム処理
def normalize_group(group_df):
    min_val = group_df["rating"].min()
    max_val = group_df["rating"].max()
    group_df["normalized"] = (group_df["rating"] - min_val) / (max_val - min_val)
    return group_df

normalized = dataset.groupby("user_id").apply(normalize_group)

多層インデックス(MultiIndex)

# 多層インデックスの作成
dataset.set_index(["company", "date"], inplace=True)

# 階層的なデータアクセス
dataset.loc["A社"]                          # 第1階層
dataset.loc[:, "2024-01-01"]              # 第2階層
dataset.loc[("A社", "2024-01-01"), :]     # 両階層

# unstackによるピボット
pivoted = dataset.unstack(level="date")

apply系メソッドの使い分け

メソッド適用対象関数の引数
mapSeries各要素の値
applySeries/DataFrameSeries全体(axis指定時)
applymapDataFrame各要素の値
# map: 値→値の変換
dataset["mapped"] = dataset["code"].map({"A": "優良", "B": "良"})

# apply: 行・列方向の処理
dataset["processed"] = dataset.apply(
    lambda row: process(row["col_a"], row["col_b"]), 
    axis=1
)

# applymap: 要素ごとの処理
dataset[numeric_cols] = dataset[numeric_cols].applymap(int)

データのピボット操作

# stack/unstack
stacked = dataset.stack()      # 列をインデックスに
unstacked = dataset.unstack()  # インデックスを列に

# pivotによる整形
pivoted = dataset.pivot(
    index="date",
    columns="category", 
    values="amount"
)

# pivot_tableによる集計付きピボット
summary = pd.pivot_table(
    dataset,
    values="sales",
    index="region",
    columns="product",
    aggfunc="sum",
    fill_value=0
)

Excelファイルの入出力

# 読み込み
df_from_excel = pd.read_excel("./data/input.xlsx", sheet_name="Sheet1")

# 複数シートの読み込み
all_sheets = pd.read_excel("./data/input.xlsx", sheet_name=None)
for sheet_name, sheet_df in all_sheets.items():
    process(sheet_df)

# 書き出し
dataset.to_excel("./output/result.xlsx", index=False, engine="openpyxl")

# 列順序の調整後に書き出し
cols = dataset.columns.tolist()
cols.insert(cols.index("id") + 1, cols.pop(cols.index("name")))
dataset[cols].to_excel("./output/reordered.xlsx", index=False)

タグ: Pandas DataFrame Series データ前処理 時系列分析

8月17日 06:38 投稿