Pandas String Operations: Vectorized Text Cleaning
Updated on
テキスト列は検索・セグメンテーション・特徴量エンジニアリングの核となりますが、多くのパイプラインはいまだに行ループや None / NaN の混在に頼っており、壊れたフィルタや遅いコードの原因になっています。
- 問題: 大文字小文字・空白・部分一致の乱れが、結合や分析の信頼性を損なう。
- 不満点の具体化: 行ごとの Python ループは遅く、
NoneとNaNが混在すると比較が壊れやすい。 - 解決策: pandas の string dtype とベクトル化された
.strメソッドを使い、正規表現の挙動を明示的に制御しつつ、安全な split / explode を組み合わせることで、高速で予測可能なテキスト処理を行う。
Want an AI agent that understands your pandas text cleaning and notebook context?
RunCell is a JupyterLab AI agent that can read your code, analyze DataFrames, understand notebook context, debug errors, and even generate & execute code for you. It works directly inside JupyterLab—no switching windows or copy-pasting.
👉 Try RunCell: runcell.dev (opens in a new tab)
Quick Reference
| Goal | Method | Example |
|---|---|---|
| Normalize casing/whitespace | .str.casefold(), .str.strip() | s.str.strip().str.casefold() |
| Literal replace (fast) | .str.replace(..., regex=False) | s.str.replace("-", " ", regex=False) |
| Regex extract / find | .str.extract(), .str.contains() | s.str.extract(r"(\d{4})") |
| Split to columns | .str.split(expand=True) | s.str.split("/", expand=True, n=2) |
| Split to rows | .str.split().explode() | df.assign(tag=df["tags"].str.split(",")).explode("tag") |
| Length/counts | .str.len(), .str.count() | s.str.count(r"\d") |
Start with a Proper String Dtype
import pandas as pd
df["title"] = df["title"].astype("string[python]")stringdtype(Arrow が入っている場合は"string[pyarrow]")を使うと、欠損値が<NA>として扱われ、.strメソッドや比較との相性が良くなります。- 可能な限り Python のオブジェクト型との混在は避けましょう。
object列はテキスト処理において遅く、一貫性も劣ります。
Core Cleaning Patterns
df["clean_name"] = (
df["name"]
.str.strip()
.str.replace(r"\s+", " ", regex=True)
.str.title()
)- 単純な文字列置換では
regex=Falseを指定し、意図しない正規表現解釈を避けつつ高速化します。 .str.normalize("NFKC")は、ユーザー入力などで Unicode の表記揺れを揃えるのに役立ちます。
ノイズ除去と大文字小文字の統一
df["search_key"] = df["product"].str.casefold().str.replace("-", " ", regex=False)casefold は国際化されたテキスト比較において、lower より強力です。
Filtering and Extracting
# Exact prefix/suffix checks
promo_rows = df[df["code"].str.startswith("PROMO", na=False)]
# Regex contains with NA-safe handling
has_year = df["description"].str.contains(r"\b20\d{2}\b", regex=True, na=False)
# Capture groups to columns
df[["year", "country"]] = df["id"].str.extract(r"(?P<year>\d{4})-(?P<country>[A-Z]{2})")- フィルタリング時に
na=Falseを指定すると、欠損値を「マッチしない」として扱えます。 extractで名前付きグループを使うと、後から列名を変更しなくても、意味の分かりやすい列が得られます。
Split, Expand, and Explode
# Split into multiple columns
df[["city", "state"]] = df["location"].str.split(",", n=1, expand=True)
# Split a list-like column into rows
tags = df.assign(tag=df["tags"].str.split("|"))
tags = tags.explode("tag").assign(tag=lambda t: t["tag"].str.strip())expand=Trueを使うと、分割した結果を列としてきれいに揃えられます。各セルに複数トークンが入っており、それを行として展開したいときはexplodeを優先します。- 結合や集計の前に、分割した要素には (
str.strip,str.casefold) などで正規化をかけておきましょう。
Counting and Measuring Text
df["word_count"] = df["abstract"].str.split().str.len()
df["digit_count"] = df["code"].str.count(r"\d").str.len()は<NA>を尊重し、入力がstringdtype の場合、結果はInt64になります。- 数値系パイプラインで通常の整数が必要なら、
fillna(0)と組み合わせて使います。
Performance and Safety Tips
apply(lambda ...)よりもベクトル化された.strメソッドを優先しましょう。高速かつ NA に対して安全です。- 単なる文字列置換であれば
regex=Falseを指定し、正規表現のオーバーヘッドを避けます。 - 大きなテキスト列では、
pyarrowがインストールされていればstring[pyarrow]を検討すると、メモリ使用量の削減や一般的な操作の高速化が期待できます。 - 中間キー(strip + casefold など)は一度正規化してから、join や重複排除に再利用し、毎回計算し直さないようにしましょう。
ベクトル化された文字列操作を使うことで、テキストクレンジングを簡潔かつ高性能に保てます。pandas-data-cleaning と組み合わせればより広範な品質チェックが行え、正規化済みキーを pandas-merge-join に渡すことで、信頼性の高い join を構築できます。