エラーの内容
ValueError: could not convert string to float: '1,234'
数値文字列をfloatに変換しようとしていますが、値にカンマが含まれています。Pythonのfloat()が受け付けるのは'1234'や'1234.56'のような単純な文字列のみです。桁区切り文字が含まれていると、即座にエラーが発生します。
このエラーは、CSV、Excelファイル、データベースエクスポート、またはAPIから数値を読み込む際によく発生します。これらのソースでは数値が人間の読みやすさを考慮してフォーマットされており、'1,234'、'$1,234.56'、あるいはヨーロッパ式フォーマットを使うソースの場合は'1.234,56'といった形式になっています。
根本原因
'1,234'のカンマは小数点ではなく桁区切り文字です。ロケールによって数値のフォーマットは異なります:
- US/UK:
1,234.56— カンマ = 桁区切り、ピリオド = 小数点 - ヨーロッパ(DE/FRなど):
1.234,56— ピリオド = 桁区切り、カンマ = 小数点
Pythonのfloat()はデータがどちらの規則を使っているか判断できず、単純にエラーになります。スプレッドシート、ロケール対応のエクスポート、またはユーザー入力からデータが来る場合、これらの余分な文字が毎回変換を失敗させます。
手順ごとの修正方法
修正1: カンマを手動で除去する(最もシンプルな方法)
ソースがUS式フォーマット(桁区切りにカンマ、小数点にピリオド)を使っている場合は、単純に除去します:
value = '1,234'
result = float(value.replace(',', ''))
print(result) # 1234.0
通貨記号や余分な空白がある場合は、置換をチェーンします:
value = '$1,234.56'
result = float(value.replace(',', '').replace('$', '').strip())
print(result) # 1234.56
修正2: str.replace + pd.to_numericでpandasの列をクリーニングする
最もよくあるシナリオ:float64ではなくobject型として読み込まれたDataFrame列を1行でクリーニングします:
import pandas as pd
df = pd.DataFrame({'price': ['1,234', '5,678', '9,012']})
df['price'] = pd.to_numeric(df['price'].str.replace(',', '', regex=False))
print(df['price'])
# 0 1234.0
# 1 5678.0
# 2 9012.0
print(df.dtypes)
# price float64
修正3: CSV読み込み時にthousandsパラメータを指定する
さらに良い方法 — 読み込み時点で処理します。thousandsパラメータで区切り文字をpandasに事前に伝えることで、数値列が自動的にfloat64として読み込まれます:
import pandas as pd
df = pd.read_csv('data.csv', thousands=',')
print(df.dtypes) # 数値列はすでにfloat64になっている
カンマが小数点区切りとして使われるヨーロッパ式フォーマットのファイルは、両方のパラメータが必要です:
df = pd.read_csv('data.csv', decimal=',', thousands='.')
修正4: errors='coerce'で不正または混在したデータを処理する
本番データは雑然としています。1つの列に有効な数値、空文字列、'N/A'のようなラベルが混在することがあります。問題のある行でクラッシュさせる代わりに、errors='coerce'を使うと変換できる値は変換し、それ以外をNaNに変換します:
import pandas as pd
df = pd.DataFrame({'amount': ['1,234', 'N/A', '5,678', '', '9,012']})
df['amount'] = pd.to_numeric(
df['amount'].str.replace(',', '', regex=False),
errors='coerce'
)
print(df['amount'])
# 0 1234.0
# 1 NaN
# 2 5678.0
# 3 NaN
# 4 9012.0
修正5: localeモジュールでロケール対応の解析を行う
ソースのロケールが既知で一貫している場合、locale.atof()が区切り文字のルールを自動的に処理するため、手動の文字列操作は不要です:
import locale
# US形式: '1,234' → 1234.0
locale.setlocale(locale.LC_NUMERIC, 'en_US.UTF-8')
result = locale.atof('1,234')
print(result) # 1234.0
# ドイツ形式: '1.234,56' → 1234.56
locale.setlocale(locale.LC_NUMERIC, 'de_DE.UTF-8')
result = locale.atof('1.234,56')
print(result) # 1234.56
注意点: locale.setlocale()はプロセス全体の設定を変更し、スレッドセーフではありません。Webアプリや非同期コードでは、代わりに明示的なstr.replace()アプローチを使用してください。
修正の確認
単一の値に対するクイックチェック:
value = '1,234'
result = float(value.replace(',', ''))
assert result == 1234.0, f"予期しない値: {result}"
print("OK:", result) # OK: 1234.0
DataFrameの場合は、データ型が数値型に変換されたことを確認し、予期しないNaNの数を確認します:
print(df['price'].dtype) # float64
print(df['price'].isna().sum()) # 0 (または本当に欠損している行の想定数)
print(df['price'].describe()) # 最小値/最大値/平均値のサニティチェック
補足情報
- まず生データを確認する: クリーニングコードを書く前に
df['col'].head(20)またはdf['col'].unique()[:20]を実行してください。カンマ、スペース、通貨記号、全角ダッシュなど、どんな文字が含まれているかを正確に把握する必要があります。 - ヨーロッパ式小数点: データ内で
'1,5'が1.5を意味する場合はvalue.replace(',', '.')を使用します。ただし、同じ列に桁区切り文字がない場合に限ります。そうでないと'1.234,56'のような値を気づかないまま破損させてしまいます。 - Excelファイル:
.xlsxファイルの数値は通常、適切なfloat型として読み込まれます。そうでない場合も、同じstr.replace()+pd.to_numeric()のパイプラインがpd.read_excel()の出力に対して機能します。 - クリーニング後はNaNを処理する:
errors='coerce'を使用した場合は、それらの行を削除する(df.dropna(subset=['amount']))か、埋める(df['amount'].fillna(0))かをさらなる処理の前に決定してください。

