文字列操作【Pandas .str アクセサ完全ガイド】
実際のデータに含まれるテキストは、決してクリーンではありません。余分な空白、大文字・小文字の不統一、特殊文字、混在したフォーマットなど、さまざまな問題があります。Pandas の .str アクセサは、文字列操作にベクトル化をもたらします。1行のコードでテキストの列全体をクリーニングでき、Python のループと比べて最大100倍も高速です。本セクションでは、最もよく使われる文字列メソッドを解説し、チェーン処理によるクリーニングパイプラインの構築方法を示します。
⚠️ 注意: 以下のコードを実行するには、ローカルの Python 環境が必要です。
1. 学習内容
- ❶ .str アクセサ
- ❷ 基本的なメソッド(strip/lower/upper/title/replace)
- ❸ 正規表現
- ❹ split / extract
- ❺ contains / startswith / match
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) ▶ テキストクリーニングのワークフロー
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})') で市外局番、プレフィックス、加入者番号をそれぞれ取得することもできます。正規表現はテキスト抽出の中核となるツールです。📖 まとめ
- .str アクセサはベクトル化された文字列操作を提供し、NaN を自動的に処理します
- strip/lstrip/rstrip は空白を除去し、lower/upper/title/capitalize は大文字・小文字を変換します
- replace は置換を実行し(正規表現に対応)、contains は条件でフィルタリングします(正規表現に対応)
- split は文字列を分割し(expand=True で個別の列を作成)、extract は正規表現のマッチを抽出します
- match は先頭からチェックし、contains は任意の場所をチェックし、startswith/endswith はプレーンなテキストのみ対応します
- メソッドをチェーンしてクリーニングパイプラインを構築できます:strip → replace → lower/title
- StringDtype は object に代わり、より効率的な文字列の保存を実現します
📝 練習問題
- 基礎(難易度 ⭐):余分な空白と大文字・小文字の不統一がある名前の Series を作成してください。.str.strip().str.title() でクリーニングし、クリーニング後の名前のうち 'A' で始まるものがいくつあるか数えてください。
- 応用(難易度 ⭐⭐):形式が混在した電話番号の Series を作成してください。replace + 正規表現で数字だけを抽出し、zfill で10文字に整形してください。
- 挑戦(難易度 ⭐⭐⭐):ユーザー登録データ(name/email/phone/zip のすべてに書式の問題があるもの)を模擬的に作成してください。5段階のチェーンクリーニングパイプライン(各段階で .str メソッドを1つ使用)を構築し、最終的なクリーンなテーブルを出力してください。



