Pandas read_csv low_memory flag causes unexpected dtype promotion and memory spikes
0 reputation · 14 Mar 2021, 18:29 UTC
0 reputation · 14 Mar 2021, 18:29 UTC
When loading a CSV with low_memory=False, columns that contain only integers but a few NaNs are promoted to float. The resulting DataFrame consumes noticeably more memory than expected, even though the flag is intended to reduce peak usage.
low_memory=False guarantee a single‑pass read, or does pandas still perform a second pass for dtype inference?low_memory=False is set, especially regarding integer‑to‑float promotion?low_memory=False?When low_memory=False is set, pandas reads the whole file to infer column dtypes before storing the data. If a column contains mostly integers but also a few NaN values, the inference sees that the integer dtype cannot represent missing values and promotes the column to float64 (the default float type that can hold NaN). The resulting DataFrame therefore uses more memory per cell (8 bytes vs. 4 or 1‑2 bytes for smaller integer types) and the inference step itself may allocate temporary arrays, causing a noticeable memory spike.
low_memory flag only influences the dtype‑inference pass; it does not change the final data unless a promotion occurs.low_memory=True (default) pandas reads the file in chunks, infers dtypes from the first few rows, and may later promote columns object‑wise if later rows disagree.low_memory=False disables the chunk‑based guess and forces a single‑pass inference over the entire file, but the data is still loaded into memory after inference.low_memory=False between those versions.NaN in an integer‑looking column, it decides the column must be float, allocates a new float64 array, and copies the data.import pandas as pd
df_sample = pd.read_csv('your_file.csv', nrows=1000)
print(df_sample.dtypes)
print(df_sample['problem_column'].head())
low_memory=False and measure memory:
df = pd.read_csv('your_file.csv', low_memory=False)
mem = df.memory_usage(deep=True).sum()
print(f'Memory usage: {mem / 1e6:.2f} MB')
print(df.dtypes['problem_column'])
df = pd.read_csv('your_file.csv',
dtype={'problem_column': 'Int64'}, # nullable integer
low_memory=False)
To confirm that the promotion is solely due to NaN values and not other non‑integer entries, could you share a few sample values from the problematic column (e.g., the first 10 non‑null entries)? Knowing whether any strings or decimals appear would determine whether specifying a nullable integer dtype (Int64) is sufficient or whether the column must stay as object.
Use comments to ask for clarification. Post a solution as an answer.
6,130 reputation · 14 Mar 2021, 20:52 UTC
One clarification on the spike mechanism: low_memory=False doesn't trigger a second pass over the file on disk. The C parser tokenizes in a single pass, but the raw field buffer stays in memory while columns are converted to inferred dtypes, so the buffer and the final typed DataFrame coexist at peak — that coexistence is the spike, and it's why the flag trades higher peak memory for whole-file dtype consistency.
For the integer-to-float part, the promotion is a NumPy constraint (int64 cannot hold NaN), not a low_memory effect — it happens in both modes. On pandas 2.x you can keep such columns integer with a nullable dtype:
df = pd.read_csv(path, dtype={'col': 'Int64'})
# or, for all columns:
df = pd.read_csv(path, dtype_backend='numpy_nullable')Two caveats: low_memory is only meaningful with the C parser, and nullable Int64 carries a validity mask, so it saves memory versus float64/object upcasts rather than versus plain int64. Worth verifying with df.dtypes and peak RSS on a production-sized file.