Python ValueError: could not convert string to float '1,234' を修正する — ロケール数値フォーマット

beginner🐍 Python2026-06-20| Python 3.x、pandas 1.x / 2.x、任意のOS(Windows、Linux、macOS)

Error Message

ValueError: could not convert string to float: '1,234'
#python#float#string#pandas#データ処理#ロケール

エラーの内容

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))かをさらなる処理の前に決定してください。

Related Error Notes