Skip to content

Pandas String Operations: Vectorized Text Cleaning

Updated on

テキスト列は検索・セグメンテーション・特徴量エンジニアリングの核となりますが、多くのパイプラインはいまだに行ループや None / NaN の混在に頼っており、壊れたフィルタや遅いコードの原因になっています。

  • 問題: 大文字小文字・空白・部分一致の乱れが、結合や分析の信頼性を損なう。
  • 不満点の具体化: 行ごとの Python ループは遅く、NoneNaN が混在すると比較が壊れやすい。
  • 解決策: 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

GoalMethodExample
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]")
  • string dtype(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> を尊重し、入力が string dtype の場合、結果は 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 を構築できます。