Pandas: 文字列操作【Pandas .str アクセサ完全ガイド】

最終更新:2026-08-26

実際のデータに含まれるテキストは、決してクリーンではありません。余分な空白、大文字・小文字の不統一、特殊文字、混在したフォーマットなど、さまざまな問題があります。Pandas の .str アクセサは、文字列操作にベクトル化をもたらします。1行のコードでテキストの列全体をクリーニングでき、Python のループと比べて最大100倍も高速です。本セクションでは、最もよく使われる文字列メソッドを解説し、チェーン処理によるクリーニングパイプラインの構築方法を示します。

⚠️ 注意: 以下のコードを実行するには、ローカルの Python 環境が必要です。

1. 学習内容



2. キャロルのユーザー入力クリーニング

(1) 問題:ユーザー入力は形式がバラバラ

キャロルが集めたユーザーデータは散々な状態です。

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['  alice  ', 'BOB', 'Charlie!', '  carol  ', 'DaVid123'],
    'email': ['alice@EXAMPLE.com', 'bob@company.ORG', 'charlie@uni.edu',
              'CAROL@example.COM', 'david@test.net'],
    'phone': ['555-0100', '(555) 0200', '555.0300', '+1-555-0400', '5550500']
})
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

(2) 解決策:.str によるチェーンクリーニング

▶ サンプル:チェーンによるテキストクリーニング(難易度 ⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['  alice  ', 'BOB', 'Charlie!', '  carol  ', 'DaVid123'],
    'email': ['alice@EXAMPLE.com', 'bob@company.ORG', 'charlie@uni.edu',
              'CAROL@example.COM', 'david@test.net']
})

# .str メソッドをチェーン:strip → replace → title
df['name_clean'] = (df['name']
    .str.strip()
    .str.replace(r'[^a-zA-Z ]', '', regex=True)
    .str.title()
)
print(df[['name', 'name_clean']])
#          name name_clean
# 0    alice        Alice
# 1         BOB        Bob
# 2   Charlie!    Charlie
# 3     carol        Carol
# 4   DaVid123       David

# メールアドレスのドメインを小文字化
df['email_clean'] = df['email'].str.lower()
print(df['email_clean'])
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。


3. .str アクセサ

(1) .str と Python の str の違い

機能 Python の str Pandas の .str
操作対象 単一の文字列 列全体(ベクトル化)
NaN の扱い エラーが発生 自動的にスキップ(NaN を返す)
チェーン処理 非対応 ✅ 連続呼び出しが可能
パフォーマンス ループ → 遅い ベクトル化 → 高速

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: 基本的な .str メソッド(難易度 ⭐)

PYTHON
import pandas as pd

s = pd.Series(['Hello World', 'pandas', 'DATA SCIENCE', None, 'python'])

# 大文字・小文字の変換
print(s.str.lower())    # hello world, pandas, data science, NaN, python
print(s.str.upper())    # HELLO WORLD, PANDAS, DATA SCIENCE, NaN, PYTHON
print(s.str.title())    # Hello World, Pandas, Data Science, NaN, Python
print(s.str.capitalize())  # Hello world, Pandas, Data science, NaN, Python

# 空白の除去
s2 = pd.Series(['  hello  ', '\tworld\n', '  pandas  '])
print(s2.str.strip())    # hello, world, pandas
print(s2.str.lstrip())   # hello  , world\n, pandas  
print(s2.str.rstrip())   #   hello, \tworld,   pandas

# 文字列の長さ
print(s.str.len())       # 11, 6, 12, NaN, 6
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。


4. replace / contains / match

(1) replace

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: replace による文字列の置換(難易度 ⭐⭐)

PYTHON
import pandas as pd

s = pd.Series(['price: $100', 'price: $200', 'cost: €50'])

# 単純な置換
print(s.str.replace('$', 'USD'))
# price: USD100, price: USD200, cost: €50

# 正規表現による置換
print(s.str.replace(r'[\$€]', '', regex=True))
# price: 100, price: 200, cost: 50

# 複数のパターンを置換
phones = pd.Series(['555-0100', '(555) 0200', '555.0300'])
clean = phones.str.replace(r'[^0-9]', '', regex=True)
print(clean)  # 5550100, 5550200, 5550300
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

(2) contains / startswith / endswith

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: 条件による文字列のフィルタリング(難易度 ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice Johnson', 'Bob Smith', 'Charlie Brown', 'Carol White'],
    'email': ['alice@example.com', 'bob@company.org', 'charlie@uni.edu', 'carol@test.net']
})

# 部分文字列を含むか
has_com = df[df['email'].str.contains('.com')]
print(has_com['name'])  # Alice Johnson

# startswith / endswith
starts_with_a = df[df['name'].str.startswith('A')]
print(starts_with_a)

edu_email = df[df['email'].str.endswith('.edu')]
print(edu_email['name'])  # Charlie Brown

# 正規表現による contains — org または edu ドメインを検索
org_or_edu = df[df['email'].str.contains(r'\.(org|edu)$', regex=True)]
print(org_or_edu[['name', 'email']])
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

(3) contains と match の違い

メソッド マッチ位置 正規表現
contains 文字列内の任意の場所 ✅
match 先頭から ✅
startswith 先頭から ❌(プレーンな文字列のみ)
endswith 末尾から ❌(プレーンな文字列のみ)


5. split / extract

(1) split

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: split による文字列の分割(難易度 ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice Johnson', 'Bob Smith', 'Charlie Brown'],
    'email': ['alice@example.com', 'bob@company.org', 'charlie@uni.edu']
})

# リストに分割
print(df['name'].str.split(' '))
# 0    [Alice, Johnson]
# 1    [Bob, Smith]
# 2    [Charlie, Brown]

# 個別の列に分割
name_split = df['name'].str.split(' ', expand=True)
print(name_split)
#         0        1
# 0   Alice  Johnson
# 1     Bob    Smith
# 2 Charlie    Brown

# 分割して最初の部分だけを取得
df['first_name'] = df['name'].str.split(' ').str[0]
print(df['first_name'])

# メールアドレスを分割してドメインを取得
df['domain'] = df['email'].str.split('@').str[1]
print(df['domain'])
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

(2) 正規表現を使った extract

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: extract によるパターンの抽出(難易度 ⭐⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'contact': [
        'Alice <alice@example.com>',
        'Bob <bob@company.org>',
        'Charlie (555-0100)',
        'Carol <carol@test.net>'
    ]
})

# 正規表現のグループでメールアドレスを抽出
df['email'] = df['contact'].str.extract(r'<(.+?)>')
print(df[['contact', 'email']])
#                   contact             email
# 0  Alice <alice@example.com>  alice@example.com
# 1      Bob <bob@company.org>    bob@company.org
# 2     Charlie (555-0100)                NaN
# 3    Carol <carol@test.net>    carol@test.net

# 複数のグループを抽出
df2 = pd.DataFrame({
    'email': ['alice@example.com', 'bob@company.org']
})
parts = df2['email'].str.extract(r'(.+?)@(.+)')
print(parts)
#        0             1
# 0  alice  example.com
# 1    bob   company.org

# すべてのマッチを抽出
colors = pd.Series(['red,blue,green', 'yellow,pink'])
print(colors.str.extractall(r'(\w+)'))
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。


6. その他のよく使うメソッド

(1) クイックリファレンス

メソッド 目的 例
len() 文字列の長さ s.str.len()
count() 部分文字列の出現回数 s.str.count('a')
find() 部分文字列の位置 s.str.find('bc')
isdigit() すべて数字か? s.str.isdigit()
isalpha() すべてアルファベットか? s.str.isalpha()
join() 区切り文字で結合 s.str.join(',')
pad() 固定幅にパディング s.str.pad(10, fillchar='0')
repeat() 文字列の繰り返し s.str.repeat(3)
slice() スライス s.str.slice(0, 5)

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: pad/slice/zfill(難易度 ⭐)

PYTHON
import pandas as pd

ids = pd.Series(['1', '23', '456', '7890'])

# 0でパディングして4桁にする
print(ids.str.zfill(4))
# 0    0001
# 1    0023
# 2    0456
# 3    7890

# 先頭2文字をスライス
codes = pd.Series(['US-001', 'UK-002', 'JP-003'])
print(codes.str.slice(0, 2))  # US, UK, JP
print(codes.str[:2])           # 同上、Python のスライス構文
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。


7. 総合例:ユーザーデータのテキストクリーニングパイプライン

(5) ▶ テキストクリーニングのワークフロー

100%
graph TB
    A[生テキスト] --> B[strip:空白を除去]
    B --> C[lower / upper:大文字・小文字を正規化]
    C --> D[replace:特殊文字を除去]
    D --> E[split / extract:情報を抽出]
    E --> F[title:出力を整える]
    F --> G[クリーンなテキスト]
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

▶ サンプル

TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

: エンドツーエンドのテキストクリーニング(難易度 ⭐⭐⭐)

PYTHON
import pandas as pd

# ============================================
# 総合例:ユーザーデータに対する
# テキストクリーニングパイプライン
# ============================================

# 1. 整形前のバラバラなユーザーデータ
df = pd.DataFrame({
    'raw_name': ['  ALICE johnson  ', 'bob   SMITH', '  CHARLIE! brown  ', 'CAROL white123'],
    'raw_email': ['ALICE@Example.COM', 'bob@company.ORG', 'charlie@uni.EDU', 'CAROL@TEST.NET'],
    'raw_phone': ['555-0100', '(555) 0200', '555.0300 ext 42', '+1-555-0400'],
    'raw_zip': ['0123', '12345', '  90210  ', '10001-2345']
})

# 2. 名前のクリーニング:strip → 特殊文字を除去 → タイトルケース
df['name'] = (df['raw_name']
    .str.strip()
    .str.replace(r'[^a-zA-Z\s]', '', regex=True)
    .str.replace(r'\s+', ' ', regex=True)  # 連続する空白を1つにまとめる
    .str.title()
)

# 3. メールアドレスのクリーニング:小文字化
df['email'] = df['raw_email'].str.lower()

# 4. 電話番号のクリーニング:数字だけを残す
df['phone_digits'] = df['raw_phone'].str.replace(r'[^0-9]', '', regex=True)
df['phone_formatted'] = df['phone_digits'].str.slice(0, 3) + '-' + df['phone_digits'].str.slice(3, 7)

# 5. 郵便番号:5桁のコードを抽出
df['zip5'] = df['raw_zip'].str.strip().str.extract(r'(\d{5})')

# 6. メールアドレスのドメイン
df['domain'] = df['email'].str.split('@').str[1]

# 7. クリーニング結果を表示
print("=== Cleaned Data ===")
print(df[['name', 'email', 'phone_formatted', 'zip5', 'domain']])
TEXT 📖 参照専用
> **出力:** ローカルの Python 環境(pandas 2.x)で実行してください。Piston サーバーには pandas がプリインストールされていません。ローカルにインストール(`pip install pandas`)してから試してください。実際の値は pandas のバージョンによって若干異なる場合があります。

❓ よくある質問

Q .str と Python の str の違いは何ですか?
A Python の str メソッドは単一の文字列を操作しますが、.str アクセサは列全体を操作します(ベクトル化)。.str アクセサは NaN の値を自動的にスキップし(NaN を返す)ますが、Python の str は NaN に対してエラーを発生させます。.str アクセサはメソッドチェーン(s.str.strip().str.lower())に対応していますが、Python の str は対応していません。パフォーマンスの面では、.str はループより10〜100倍高速です。
Q NaN の値はどのように扱われますか?
A .str メソッドは NaN を自動的にスキップします。入力が NaN の場合、出力も NaN になります。欠損値を手動でチェックする必要はありません。これは、.str が apply + Python の str を使うより便利な理由の1つです。なお、contains/match は NaN の値に対して NaN ではなく False を返すため、フィルタリングの際は注意してください。
Q extract は何を返しますか?
A extract はデータフレームを返します。正規表現のグループごとに1列となります。グループが1つなら1列のデータフレーム、複数のグループなら複数の列になります。extractall はすべてのマッチを返します(MultiIndex 付き)。注意:マッチしない行は NaN を返します。Series が必要な場合は、.str.extract(...)[0] を使ってください。
Q contains と match の違いは何ですか?
A contains は文字列内の任意の場所にマッチします(例:'abc' が 'b' を contains → True)。match は文字列の先頭からのみマッチします(例:'abc' が 'b' に match → False)。startswith/endswith はプレーンな文字列の比較です(正規表現には非対応)。正規表現が必要なときは contains/match を、単純なテキストチェックのときは startswith/endswith を使ってください。
Q StringDtype の利点は何ですか?
A デフォルトでは、文字列の列は object 型(Python オブジェクト)を使用し、各値が個別の str インスタンスになります。StringDtype(df['col'].astype('string'))は Pandas 専用の文字列型で、欠損値に pd.NA を使用し、メモリ効率が高く、より一貫した .str の動作を提供します。Pandas 2.x では、文字列データには object より StringDtype が推奨されています。
Q str.replace と df.replace の違いは何ですか?
A str.replace は文字列の列のみを操作し、正規表現に対応し(regex=True)、要素ごとに置換します。df.replace はデータフレーム全体を操作し、任意の型の値を置換でき、正規表現にも対応します。文字列の置換には str.replace(意味が明確)を、列をまたぐ置換には df.replace を使うのがよいでしょう。
Q 電話番号から数字を抽出するにはどうすればよいですか?
A s.str.replace(r'[^0-9]', '', regex=True) を使うと、数字以外の文字をすべて除去できます。あるいは extract を使い、s.str.extract(r'(\d{3})[-.)\s]*(\d{3})[-.)\s]*(\d{4})') で市外局番、プレフィックス、加入者番号をそれぞれ取得することもできます。正規表現はテキスト抽出の中核となるツールです。

📖 まとめ


📝 練習問題

  1. 基礎(難易度 ⭐):余分な空白と大文字・小文字の不統一がある名前の Series を作成してください。.str.strip().str.title() でクリーニングし、クリーニング後の名前のうち 'A' で始まるものがいくつあるか数えてください。
  2. 応用(難易度 ⭐⭐):形式が混在した電話番号の Series を作成してください。replace + 正規表現で数字だけを抽出し、zfill で10文字に整形してください。
  3. 挑戦(難易度 ⭐⭐⭐):ユーザー登録データ(name/email/phone/zip のすべてに書式の問題があるもの)を模擬的に作成してください。5段階のチェーンクリーニングパイプライン(各段階で .str メソッドを1つ使用)を構築し、最終的なクリーンなテーブルを出力してください。

← 前へ:データの再整形とピボット · 次へ:日付と時刻 →

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%