skip to content
barorin&

pandasの行抽出・削除まとめ|複数条件フィルタ・正規表現・欠損値の処理

/ 10 min read

Table of Contents

はじめに

試算表や仕訳データを扱っていると、作業の大半は「必要な行だけ残す」ことに費やされます。特定の勘定科目だけ抜き出す、金額が閾値を超える行を探す、摘要に特定の文字が入っている行を除く、空欄の行を落とす。

この記事では、pandasで行を絞り込む操作をひととおりまとめました。**コードはすべて pandas 3.0 で実際に動かして確認しています。**古い書き方のまま使うと動かなくなっているものがあるので、そこも明示しました。

複数条件でフィルタする

条件を &(かつ)や |(または)でつなぎます。各条件を括弧で囲むのが必須です。Pythonの演算子の優先順位の都合で、括弧がないと比較より先に & が評価されてエラーになります。

df = df[
(df["勘定科目"].isin(["売掛金", "未収入金"]))
& (df["金額"] >= 100000)
& (df["摘要"].str.contains("振替"))
]

否定は ~ を使います(not は使えません)。

# 「振替」を含まない行
df[~df["摘要"].str.contains("振替", na=False)]

条件が増えて読みにくくなったら、変数に切り出すと見通しがよくなります。

is_target = df["勘定科目"].isin(["売掛金", "未収入金"])
is_large = df["金額"] >= 100000
df[is_target & is_large]

文字列を含む行を抽出する

df[df["摘要"].str.contains("振替")]

欠損値が混ざっている場合

摘要欄が空の行があると、str.contains() の結果は True / False ではなく欠損(NA)になります。古いpandasでは、これをそのまま絞り込みに使うと次のエラーが出ました。

ValueError: Cannot mask with non-boolean array containing NA / NaN values

pandas 3.0 では欠損は自動的に除外されるようになり、このエラーは出ません。ただしバージョンによって挙動が変わる部分なので、na を明示するのが安全です。

# 欠損行は「含まない」とみなす
df[df["摘要"].str.contains("振替", na=False)]
# 欠損行も残したいとき
df[df["摘要"].str.contains("振替", na=True)]

会計データの摘要欄は空欄が普通にあるので、na=False を癖にしておくと事故が減ります。

正規表現で抽出する

str.contains() はデフォルトで正規表現として解釈されます。より厳密にマッチさせたい場合は match / fullmatch を使い分けます。

# 数字を含む行(部分一致)
df[df["コード"].str.contains(r"\d", na=False)]
# 先頭からマッチする行
df[df["コード"].str.match(r"[A-Z]{2}", na=False)]
# 文字列全体が完全に一致する行
df[df["コード"].str.fullmatch(r"[A-Z]{2}\d{4}", na=False)]
メソッド マッチ範囲 用途
contains どこかに含まれる 摘要の部分検索
match 先頭から コードの接頭辞判定
fullmatch 文字列全体 コード体系の妥当性チェック

真偽値だけが欲しい場合は、そのまま列として受け取れます。

df["数字あり"] = df["コード"].str.contains(r"\d", na=False)

記号を含む文字列を検索するとき

.( は正規表現の特殊文字なので、そのまま検索すると意図しない結果になります。文字どおり検索したいときは regex=False を付けます。

df[df["摘要"].str.contains("(株)", regex=False, na=False)]

条件に合う行を削除する

いちばん素直なのは、残したい条件で絞り直す方法です。

# 「振替」を含む行を削除する = 含まない行を残す
df = df[~df["摘要"].str.contains("振替", na=False)]

インデックスを取得して drop() する方法もあります。複数条件を組み合わせるときは、こちらのほうが意図が読み取りやすい場合があります。

# 「tomato」を含み、かつ「orange」を含まない行を削除する
drop_index = df.index[
df["fruits"].str.contains("tomato", na=False)
& ~df["fruits"].str.contains("orange", na=False)
]
df = df.drop(drop_index)

複数条件でソートする

sort_values() に列のリストを渡し、ascending に列と同じ数の真偽値を渡します。

# 部門(昇順)、金額(降順)、日付(昇順)
df = df.sort_values(
["部門", "金額", "日付"],
ascending=[True, False, True],
)

並べ替えた後にインデックスを振り直したいときは ignore_index=True を付けます。

df = df.sort_values(["部門", "金額"], ascending=[True, False], ignore_index=True)

欠損値のある行・列を削除する

dropna()subset に対象列を指定します。

# 指定した列がすべて欠損の行を削除
df = df.dropna(subset=["借方金額", "貸方金額"], how="all")
# 指定した列のどれか1つでも欠損なら削除
df = df.dropna(subset=["借方金額", "貸方金額"], how="any")

how の違いは実務ではかなり重要です。

指定 削除される行
how="all" 対象列がすべて欠損の行
how="any" 対象列のどれか1つでも欠損の行(デフォルト)

借方・貸方のように「どちらか一方に入っていれば正しい」データでは how="all" を使わないと、正常な仕訳まで消えてしまいます。

列方向に削除したい場合は axis=1 を指定します。

# 指定した行がすべて欠損の列を削除する
df = df.dropna(subset=[0, 1, 2], axis=1, how="all")

欠損を他の列の値で埋める

A列の欠損をB列の値で埋めます。

df["ColA"] = df["ColA"].fillna(df["ColB"])

固定値や、列ごとに違う値で埋めることもできます。

# 金額列は0、摘要列は空文字で埋める
df = df.fillna({"金額": 0, "摘要": ""})

おわりに

行の絞り込みは基本的な操作ですが、欠損値が絡んだ瞬間に挙動が変わるのが厄介です。特に str.contains()nadropna()how は、指定を間違えても処理は通ってしまい、件数が合わなくなって初めて気づきます。

絞り込んだ後は len(df) で件数を、金額列は df["金額"].sum() で合計を確認する習慣をつけておくと、消えてはいけない行が落ちたことにその場で気づけます。

この記事を書いた人

barorinのプロフィール画像

barorin公認会計士

会計とITの二足のわらじで働く公認会計士です。Pythonを中心に、会計・監査の実務で実際に使っているコードや、Ubuntu・Raspberry Piの設定メモを書いています。