Q&A library Python

How do I read a CSV where numbers use commas as separators?

Python Intermediate localeparsingread-csv

Short answer

Pass thousands="," for grouped digits, and decimal="," with the right sep for European files. Without them, numbers arrive as text and every later calculation silently fails or gives the wrong answer.

The short version

Pass thousands="," when numbers are written like 1,200, and decimal="," (with a matching sep) for European files where the comma is the decimal point. Without these, the values are read as text, not numbers, and arithmetic on them fails or silently misbehaves.

Grouped thousands

import pandas as pd
import io

data = 'v\n"1,200"\n'

pd.read_csv(io.StringIO(data))["v"].tolist()                  # ['1,200'] , a string!
pd.read_csv(io.StringIO(data), thousands=",")["v"].tolist()   # [1200] , a number

Without thousands, pandas cannot parse 1,200 as a number, so it leaves it as text. The column's dtype becomes object, and any sum or mean on it either raises or concatenates strings.

European decimals

In much of Europe the comma is the decimal point and the semicolon separates fields.

data = "v;w\n1,5;2\n"

pd.read_csv(io.StringIO(data), sep=";", decimal=",")["v"].tolist()   # [1.5]

You need both arguments: sep=";" so the fields split correctly, and decimal="," so 1,5 is read as one and a half rather than as text.

Why this matters so much

The failure is silent. A numeric column read as text does not raise on import. It raises much later, or worse, does something wrong: sum on a text column concatenates the strings, sort_values orders them alphabetically (so "9" comes after "10"), and comparisons give nonsense.

By the time you notice, the cause is many steps away from the symptom.

Always check the dtypes after reading

df.dtypes

This one line catches the whole family of problems. A column you expect to be numeric showing as object means it did not parse, and you should find out why before doing anything else.

Other locale problems

Currency symbols, percent signs and spaces as thousands separators all block parsing in the same way and none is handled automatically. Strip them first, then convert.

df["amount"] = pd.to_numeric(
    df["amount"].str.replace("£", "", regex=False)
                .str.replace(",", "", regex=False)
)

Use errors="coerce" on the conversion if some values may still fail, so you can count and inspect the failures rather than crashing on the first one.

Fix it at read time where you can

Doing it with read_csv arguments is better than cleaning afterwards, because it happens once, at the boundary, and the frame is correctly typed from the very first line of your analysis. Cleaning afterwards means every step before the fix was operating on text.

Encoding often travels with this

Files using European conventions are also often not UTF-8. If you get a UnicodeDecodeError, try encoding="latin-1" or encoding="cp1252", which are common for Windows exports from that region.

Enjoying these?

Go from piecing together answers to real fluency.

Our courses take you from looking things up to doing the work with confidence, step by step on real data.

Browse the courses