東京を指定したのに一部の行が出てこない。同じ地域が別々に集計される。正しい列名なのにKeyErrorになる——原因は、CSVやExcelに紛れ込んだ見えない空白かもしれません。pandasでは、文字列が入った列にstr.strip()を使うと前後の空白を削除できます。
df["地域"] = df["地域"].str.strip()
左側だけならstr.lstrip()、右側だけならstr.rstrip()、列名の前後空白ならdf.columns.str.strip()を使います。この記事では、空白を見つけるところから、削除後に抽出・集計が正しくできることを確認するところまで、Google Colabで試せる例で解説します。
この記事でわかること
df["列名"].str.strip()で値の前後空白を削除する方法str.strip()・str.lstrip()・str.rstrip()の違いdf.columns.str.strip()で列名の空白を削除する方法- 半角スペース・全角スペース・タブ・改行・
NaNの注意点 str.strip()・str.replace()・replace()・rename()の使い分けunique()やvalue_counts()で修正前後を確認する方法
先に判断基準:値の両端なら
str.strip()、左端だけならstr.lstrip()、右端だけならstr.rstrip()、列名の両端ならdf.columns.str.strip()です。文字列の途中にある空白や、別の値への置換は目的が異なります。
Pandas DataFrame入門シリーズでの位置づけ
空白の除去は、データを読み込んで内容を確認したあと、抽出・集計・結合へ進む前に行う前処理です。
CSV・Excel読込 → 値と型の確認 → 前後空白の除去 → 表記ゆれの修正 → 抽出・集計・結合
Google ColabへのCSV読込はこちらの記事、Excelの読込はread_excel()の記事で詳しく解説しています。
見えない空白が抽出・集計を狂わせる
次の売上データには、地域名の前後に半角スペースや全角スペースが混ざっています。表では違いが分かるよう、半角スペースを␠、全角スペースを□として示します。実際のDataFrameでは、これらは見えにくい文字です。
| 行 | 実際の値 | 見える表記 | 売上 |
|---|---|---|---|
| 0 | 東京 |
東京 | 1200 |
| 1 | 東京 |
␠東京 | 1800 |
| 2 | 東京 |
東京␠ | 1500 |
| 3 | 東京 |
□東京□ | 900 |
| 4 | 大阪 |
大阪 | 1100 |
| 5 | NaN |
欠損値 | 700 |
まずは追加ファイル不要のサンプルデータを作り、空白を除去する前の状態を確認します。repr()を使うと、半角スペースは引用符の内側に見え、全角スペースも位置を確認しやすくなります。
import pandas as pd
import numpy as np
df = pd.DataFrame({
"地域": ["東京", " 東京", "東京 ", " 東京 ", "大阪", np.nan],
"商品": ["商品A", "商品B", "商品C", "商品D", "商品E", "商品F"],
"売上": [1200, 1800, 1500, 900, 1100, 700]
})
print("repr()で確認:")
print(df["地域"].map(repr).to_list())
print("\n空白除去前の件数:")
print(df["地域"].value_counts(dropna=False))
print("\n'東京'と完全一致する行:")
display(df[df["地域"] == "東京"] )
repr()で確認: ["'東京'", "' 東京'", "'東京 '", "'\\u3000東京\\u3000'", "'大阪'", 'nan'] 空白除去前の件数:
| count | |
|---|---|
| 東京 | 1 |
| 東京 | 1 |
| 東京 | 1 |
| 東京 | 1 |
| 大阪 | 1 |
| nan | 1 |
'東京'と完全一致する行:
| 地域 | 商品 | 売上 | |
|---|---|---|---|
| 0 | 東京 | 商品A | 1200 |
東京、東京、東京、 東京 は、pandasでは別の文字列です。そのため、空白除去前のvalue_counts()では別々に数えられ、== "東京"では空白のない1行しか抽出されません。
値の種類を詳しく調べる方法はunique()・nunique()の記事、件数の確認方法はvalue_counts()の記事も参考にしてください。
str.strip()で文字列の前後空白を削除する
pandasでは、DataFrameの1列分を**Series(シリーズ)**と呼びます。列に入っている文字列をまとめて処理するため、.strを付けてdf["地域"].str.strip()と書きます。
str.strip()は、前後の半角スペース・全角スペース・タブ・改行などを取り除きます。文字列の途中にある空白は残ります。画面では見えない特殊な文字が残る場合もあるため、実データでは処理前後を確認しましょう。
# 処理前の値を残し、比較できるようにする
df["地域_処理前"] = df["地域"]
# 結果を代入して、地域列を更新する
df["地域"] = df["地域"].str.strip()
comparison = df[["地域_処理前", "地域"]].copy()
comparison["処理前_repr"] = comparison["地域_処理前"].map(repr)
comparison["処理後_repr"] = comparison["地域"].map(repr)
display(comparison[["処理前_repr", "処理後_repr"]])
print("空白除去後の件数:")
print(df["地域"].value_counts(dropna=False))
print("\n空白除去後に'東京'と一致する行:")
display(df[df["地域"] == "東京"][["地域", "商品", "売上"]])
| 処理前_repr | 処理後_repr | |
|---|---|---|
| 0 | ‘東京’ | ‘東京’ |
| 1 | ‘ 東京’ | ‘東京’ |
| 2 | ‘東京 ‘ | ‘東京’ |
| 3 | ‘\u3000東京\u3000’ | ‘東京’ |
| 4 | ‘大阪’ | ‘大阪’ |
| 5 | nan | nan |
空白除去後の件数:
| count | |
|---|---|
| 東京 | 4 |
| 大阪 | 1 |
| nan | 1 |
空白除去後に'東京'と一致する行:
| 地域 | 商品 | 売上 | |
|---|---|---|---|
| 0 | 東京 | 商品A | 1200 |
| 1 | 東京 | 商品B | 1800 |
| 2 | 東京 | 商品C | 1500 |
| 3 | 東京 | 商品D | 900 |
処理後は、空白違いで分かれていた4つの値がすべて東京になります。その結果、value_counts()では東京が4件にまとまり、条件抽出でも4行を取得できます。空白除去によって別々だったカテゴリが統合されるため、集計件数が変わるのは正常です。
| 処理前 | 処理後 |
|---|---|
東京 |
東京 |
␠東京 |
東京 |
東京␠ |
東京 |
□東京□ |
東京 |
大阪 |
大阪 |
NaN |
NaN |
NaNは空文字列ではなく欠損値です。str.strip()を使っても欠損値のまま保たれます。欠損値を埋めたい場合は、空白除去とは分けてfillna()を検討します。
重要:
df["地域"].str.strip()を実行しただけでは、元の列は変わりません。更新するにはdf["地域"] = ...のように結果を代入します。
str.strip()・str.lstrip()・str.rstrip()の違い
同じ文字列を使うと、どちら側の空白が削除されるかを比較できます。
| 目的 | pandasでの書き方 | 結果のイメージ |
|---|---|---|
| 前後を削除 | s.str.strip() |
␠東京␠ → 東京 |
| 左側だけ削除 | s.str.lstrip() |
␠東京␠ → 東京␠ |
| 右側だけ削除 | s.str.rstrip() |
␠東京␠ → ␠東京 |
s = pd.Series([" 東京 ", " 大阪 ", "商品 A", "\t名古屋\n"])
result = pd.DataFrame({
"元の値": s.map(repr),
"strip": s.str.strip().map(repr),
"lstrip": s.str.lstrip().map(repr),
"rstrip": s.str.rstrip().map(repr)
})
display(result)
| 元の値 | strip | lstrip | rstrip | |
|---|---|---|---|---|
| 0 | ‘ 東京 ‘ | ‘東京’ | ‘東京 ‘ | ‘ 東京’ |
| 1 | ‘\u3000大阪\u3000’ | ‘大阪’ | ‘大阪\u3000’ | ‘\u3000大阪’ |
| 2 | ‘商品 A’ | ‘商品 A’ | ‘商品 A’ | ‘商品 A’ |
| 3 | ‘\t名古屋\n’ | ‘名古屋’ | ‘名古屋\n’ | ‘\t名古屋’ |
strip(chars)は指定した語句を丸ごと削除する機能ではない
strip("abc")は、abcという並びを探して削除する機能ではありません。文字列の両端から、a・b・cのどれかに当たる文字を1文字ずつ取り除きます。それ以外の文字に当たると処理が止まり、文字列の途中にある文字は削除されません。
次の例では、両端にあるH・e・l・o・!は削除されますが、途中のPythonに含まれるoは残ります。
sample = pd.Series(["Helloooo! Python!"])
print(sample.str.strip("Helo!").iloc[0])
# 結果は ' Python'。途中にあるPythonのoは削除されない
Python
補足:文字以外の値が混ざる列に注意する
1つの列に文字列と数値が混ざっている場合は注意が必要です。.str.strip()を使うと、数値やTrue・Falseは欠損値になります。元からあるNaNも欠損値のままです。実行前に、対象列へ文字列だけが入っているか確認しましょう。
mixed = pd.Series([" 東京 ", 100, True, np.nan], dtype="object")
mixed_result = pd.DataFrame({
"処理前": mixed.map(repr),
"処理後": mixed.str.strip().map(repr)
})
display(mixed_result)
| 処理前 | 処理後 | |
|---|---|---|
| 0 | ‘ 東京 ‘ | ‘東京’ |
| 1 | 100 | nan |
| 2 | True | nan |
| 3 | nan | nan |
商品 Aのような文字列途中の空白は、どのstrip系メソッドでも残ります。途中の空白を削除・置換したい場合はstr.replace()の役割です。
空白除去では引数を省略する使い方を基本にすると安全です。
列名の前後空白はdf.columns.str.strip()で削除する
CSVやExcelの見出しに空白が入っていると、df["売上"]と指定しても、実際の列名が" 売上"ならKeyErrorになります。列の値ではなく列名を整える場合は、df.columnsに対してstr.strip()を使います。
| 変更前の列名 | 変更後の列名 |
|---|---|
商品名 |
商品名 |
␠売上 |
売上 |
地域␠ |
地域 |
df_columns = pd.DataFrame(
[["商品A", 1200, "東京"], ["商品B", 1100, "大阪"]],
columns=["商品名", " 売上", "地域 "],
)
print("変更前:", [repr(col) for col in df_columns.columns])
df_columns.columns = df_columns.columns.str.strip()
print("変更後:", [repr(col) for col in df_columns.columns])
# 空白除去後は、空白なしの列名で選択できる
display(df_columns[["商品名", "売上", "地域"]])
変更前: ["'商品名'", "' 売上'", "'地域 '"] 変更後: ["'商品名'", "'売上'", "'地域'"]
| 商品名 | 売上 | 地域 | |
|---|---|---|---|
| 0 | 商品A | 1200 | 東京 |
| 1 | 商品B | 1100 | 大阪 |
df.columns.str.strip()は列名の前後空白を取る処理です。売上金額を売上へ変えるように、列名そのものを別名へ変更したい場合はrename()の記事を参照してください。
strip・replace・renameは目的で使い分ける
空白や表記ゆれを直す方法は似て見えますが、処理する場所と目的が違います。
| 目的 | 使う方法 | 主な対象 | 例 |
|---|---|---|---|
| 値の前後空白を削除 | s.str.strip() |
1列の各文字列 | ␠東京␠ → 東京 |
| 値の左端だけ削除 | s.str.lstrip() |
1列の各文字列 | ␠東京␠ → 東京␠ |
| 値の右端だけ削除 | s.str.rstrip() |
1列の各文字列 | ␠東京␠ → ␠東京 |
| 文字列の一部を置換 | s.str.replace() |
各文字列の一部分 | 商品 A → 商品A |
| 値全体を別の値へ置換 | s.replace() |
SeriesまたはDataFrameの値 | 東京都 → 東京 |
| 列名の前後空白を削除 | df.columns.str.strip() |
列名 | ␠売上 → 売上 |
| 列名を別名へ変更 | df.rename() |
列名・行ラベル | 売上金額 → 売上 |
Pythonの単一文字列textならtext.strip()、pandasの列df["地域"]ならdf["地域"].str.strip()です。値全体の置換や辞書を使った表記統一はpandas replace()の記事で詳しく扱っています。
よくあるミスと注意点
1. 結果を代入せず、元の列も変わったと思う
str.strip()は、空白を削除した新しい結果を返します。元の列は自動では変わらないため、df["地域"] = ...のように対象列へ戻します。
2. 文字列の途中にある空白まで消えると思う
strip()が対象にするのは両端です。商品 Aの中央の空白を変えたい場合はstr.replace()などを検討します。ただし、氏名や住所など、途中の空白に意味があるデータもあるため、一律に消してよいか確認が必要です。
3. NaNと空文字列を同じものとして扱う
NaNは欠損値、""は長さ0の文字列、" "は空白だけの文字列です。空白だけの文字列はstrip()後に""になりますが、自動でNaNにはなりません。
4. 混在列を安易にastype(str)へ変換する
数値や欠損値が混ざる列をastype(str)にすると、欠損値まで文字列の"nan"になる場合があります。まずdtypesで型を確認し、必要な列だけ処理しましょう。型変換はastype()の記事で詳しく解説しています。
5. 全角スペースを見落とす
日本語データには全角スペースが混ざることがあります。引数なしのstr.strip()では一般的な全角スペースも両端から除去できますが、repr()、unique()、value_counts()で処理前後を確認する習慣が大切です。
前処理の流れでstr.strip()を使うタイミング
実際の分析では、次の順番にすると空白による見落としを減らせます。
- CSVやExcelを読み込む
head()・dtypes・unique()・value_counts()で列と値を確認する- 文字列値は
Series.str.strip()、列名はdf.columns.str.strip()で前後空白を除去する unique()やvalue_counts()を再実行し、カテゴリが正しく統合されたか確認する- 必要なら
replace()で他の表記ゆれを統一する - 条件抽出、
groupby()、merge()、可視化へ進む
空白除去後の部分一致抽出はstr.contains()の記事、集計はgroupby×aggの記事、DataFrameの結合はmergeの記事へ進むと、分析の流れをつなげて学べます。
複数の文字列列をまとめて処理する方法や、正規表現による連続空白の統一、Unicode正規化、大規模データでの速度最適化もあります。ただし、最初は問題のある列を特定し、1列ずつ安全に確認する方法で十分です。
まとめ
- pandasで文字列の前後空白を削除する基本は
Series.str.strip() - 左側だけなら
str.lstrip()、右側だけならstr.rstrip() - 列名の前後空白は
df.columns.str.strip()で削除する strip()は文字列途中の空白を削除しないNaN、空文字列、空白だけの文字列は別の値として考える- 処理結果を代入し、
unique()やvalue_counts()で修正後を確認する - 空白除去は、データ読込・確認のあと、抽出・集計・結合の前に行う
まずは、見た目が同じ値が別々に集計されていないかを確認してみましょう。前後空白が原因なら、対象の文字列列にstr.strip()を使うことで、その後の抽出や集計を正しく進めやすくなります。
次に読みたい関連記事
- pandas unique()とnunique()の使い方:値の一覧と表記ゆれを確認する
- pandas value_counts()の使い方:修正前後の件数を比較する
- pandas replace()の使い方:値全体の置換や表記統一を行う
- pandas rename()の使い方:列名を別名へ変更する
- pandas str.contains()の使い方:文字列を含む行を抽出する
- Google ColabでCSVを読み込む方法:CSVを読み込んで確認する
- pandas read_excel()の使い方:Excelファイルを読み込む
- pandas astype()の使い方:object型・string型などの型変換を確認する
- Pandas groupby×aggの使い方:整えたデータをグループ別に集計する
str.strip()は半角スペースと全角スペースの両方を削除できますか?
引数を省略したstr.strip()は、文字列の前後にある一般的な半角スペース・全角スペース・タブ・改行などを削除します。ただし、画面では見えない特殊な文字が残る場合もあるため、repr()やunique()で結果を確認してください。
str.strip()とstr.replace()はどう使い分けますか?
前後の空白を取り除くならstr.strip()、文字列の途中を含む特定文字を置換するならstr.replace()です。値全体を別の値へ置換するSeries.replace()・DataFrame.replace()とも目的が異なります。
文字列の途中にある空白も削除できますか?
str.strip()では削除できません。途中の空白にはstr.replace()などを使えますが、氏名や住所の空白まで消してよいかを先に確認しましょう。
列名の前後空白をまとめて削除できますか?
はい。df.columns = df.columns.str.strip()で列名の前後空白をまとめて削除できます。列名を別の名前へ変更する場合はrename()を使います。
str.strip()を使ったのに元のDataFrameが変わらないのはなぜですか?
str.strip()は処理後の新しいSeriesを返すためです。df["地域"] = df["地域"].str.strip()のように対象列へ代入してください。
空白を削除したら別々だったカテゴリは同じ値として集計されますか?
はい。東京、東京、東京がすべて東京になれば、value_counts()やgroupby()では同じカテゴリとして集計されます。削除後に件数が意図どおりか必ず確認してください。
コメント