How to Suppress Scientific Notation in pandas and NumPy

In NumPy, call np.set_printoptions(suppress=True); in pandas, set pd.set_option("display.float_format", "{:.2f}".format) (or use pd.option_context for a single block). Both only change how numbers are displayed; the stored values are untouched, and files or reports need their own formatting via to_csv(float_format=...), Styler.format or f-strings.
Why scientific notation appears
Python, NumPy and pandas each pick a representation that is short and unambiguous. Plain Python's repr() switches to exponent form when the decimal exponent is below -4 or at least 16:
print(1e16, 0.0001, 0.00001, 123456789012345678.0)
1e+16 0.0001 1e-05 1.2345678901234568e+17
NumPy formats a whole array uniformly. By default it uses scientific notation for every element when the smallest absolute value is below 1e-4 or the ratio between the largest and smallest exceeds 1e3, as described in the set_printoptions reference. One tiny value is enough to change the look of the whole array:
import numpy as np
a = np.array([0.00001234, 1.5, 123456.0])
print(a)
print(np.array([0.5, 1.5, 2.5]))
[1.23400e-05 1.50000e+00 1.23456e+05]
[0.5 1.5 2.5]
pandas formats column by column. A column switches to scientific notation when its values would not fit nicely at the current display.precision (6 by default): very large magnitudes (around 1e9 and above with default settings) or values too small to show at that precision.
import pandas as pd
df = pd.DataFrame({
"item": ["bolt", "server", "license"],
"unit_cost": [0.000045, 12500.0, 1.5],
"revenue": [1.2e9, 345.67, 98765432.1],
})
print(df)
item unit_cost revenue
0 bolt 0.000045 1.200000e+09
1 server 12500.000000 3.456700e+02
2 license 1.500000 9.876543e+07
df.describe() is a frequent offender because statistics such as count, std and max land in the same column with very different magnitudes.
NumPy: set_printoptions(suppress=True)
suppress=True forces fixed-point notation; precision sets the number of digits after the decimal point (default 8):
a = np.array([0.00001234, 1.5, 123456.0])
np.set_printoptions(suppress=True)
print(a)
np.set_printoptions(suppress=True, precision=3)
print(a)
np.set_printoptions(suppress=True, precision=8, floatmode="fixed")
print(a)
[ 0.00001234 1.5 123456. ]
[ 0. 1.5 123456. ]
[ 0.00001234 1.50000000 123456.00000000]
Notice the second line: with precision=3, 0.00001234 prints as 0.. Suppression rounds tiny values to zero in the output, which can be misleading in scientific data. floatmode="fixed" always prints exactly precision digits, which is easier to scan in columns.
Two limits worth knowing: values of 1e16 and above still print in exponent form even with suppress=True ([1.e+20 1.e+00]), and set_printoptions is global for the whole process. Calling it with no arguments does not reset anything; pass the defaults back explicitly (precision=8, suppress=False, floatmode="maxprec") or, better, use the context manager so the change is scoped:
with np.printoptions(suppress=True, precision=6):
print(a)
print(a) # back to the previous settings
[ 0.000012 1.5 123456. ]
[1.23400e-05 1.50000e+00 1.23456e+05]
Formatting a single NumPy value or array as text
For one number, np.format_float_positional returns the shortest positional string that round-trips, without exponent:
print(np.format_float_positional(1.234e-5))
print(np.format_float_positional(1e16))
print(np.format_float_positional(np.float32(0.1)))
print(np.array2string(a, formatter={"float_kind": lambda x: f"{x:.4f}"}))
0.00001234
10000000000000000.
0.1
[0.0000 1.5000 123456.0000]
When writing arrays to text files, np.savetxt takes a fmt argument: np.savetxt("out.txt", a, fmt="%.8f") writes 0.00001234, 1.50000000, 123456.00000000. Its default "%.18e" is scientific.
pandas: display.float_format
pandas' options system accepts a callable that turns each float into a string. A bound str.format method is the usual idiom:
pd.set_option("display.float_format", "{:.6f}".format)
print(df)
pd.set_option("display.float_format", "{:,.2f}".format)
print(df)
pd.reset_option("display.float_format")
item unit_cost revenue
0 bolt 0.000045 1200000000.000000
1 server 12500.000000 345.670000
2 license 1.500000 98765432.100000
item unit_cost revenue
0 bolt 0.00 1,200,000,000.00
1 server 12,500.00 345.67
2 license 1.50 98,765,432.10
The format applies to every float column, so pick one that suits all of them; above, two decimals hides the bolt's unit cost entirely. Raising display.precision (for example pd.set_option("display.precision", 10)) is a gentler alternative: small values get enough digits to be shown in fixed notation, but large values may still switch to exponents.
Scoped changes with option_context
In notebooks and libraries, avoid changing global state. pd.option_context applies options only inside a with block:
with pd.option_context("display.float_format", "{:.4f}".format):
print(df)
print(df)
item unit_cost revenue
0 bolt 0.0000 1200000000.0000
1 server 12500.0000 345.6700
2 license 1.5000 98765432.1000
item unit_cost revenue
0 bolt 0.000045 1.200000e+09
1 server 12500.000000 3.456700e+02
2 license 1.500000 9.876543e+07
Per-column formatting for reports: Styler.format
When different columns need different formats (and you are producing HTML, a notebook view or Excel output), use the Styler. It requires jinja2 and leaves the DataFrame unchanged:
styled = df.style.format({"unit_cost": "{:.6f}", "revenue": "{:,.2f}"})
html = styled.to_html() # or display(styled) in Jupyter, styled.to_excel(...)
For plain-text output with per-column formats, convert the columns to strings explicitly; just remember the result is no longer numeric:
print(df["revenue"].map("{:,.2f}".format).tolist())
['1,200,000,000.00', '345.67', '98,765,432.10']
Writing files: to_csv(float_format=...)
Display options have no effect on files. to_csv writes the shortest round-tripping repr, which includes exponents for small numbers:
df.to_csv("report.csv", index=False)
df.to_csv("report.csv", index=False, float_format="%.6f")
item,unit_cost,revenue
bolt,4.5e-05,1200000000.0
server,12500.0,345.67
license,1.5,98765432.1
item,unit_cost,revenue
bolt,0.000045,1200000000.000000
server,12500.000000,345.670000
license,1.500000,98765432.100000
float_format takes a %-style string or, in recent pandas versions, a callable. Note that 4.5e-05 is perfectly valid CSV and any parser reads it back correctly; only format the output if a human or a picky downstream tool needs it.
Plain Python: f-strings and format()
For individual values, the format specification mini-language gives full control. The f type is always fixed-point:
x = 0.000012345
big = 1.5e17
print(f"{x:f}") # fixed point, 6 decimals by default
print(f"{x:.10f}")
print(f"{big:,.0f}")
print(f"{x:.10f}".rstrip("0").rstrip("."))
0.000012
0.0000123450
150,000,000,000,000,000
0.000012345
Avoid the g type if the goal is to avoid exponents: f"{x:g}" still gives 1.2345e-05.
Precision caveats
Printing more digits in fixed notation exposes the fact that floats are binary approximations:
print(f"{0.1:.20f}")
print(f"{1e23:f}")
print(f"{np.float32(0.1):.10f}")
0.10000000000000000555
99999999999999991611392.000000
0.1000000015
A float64 carries about 15–17 significant decimal digits; a float32 about 7. Digits beyond that are noise, not data. Scientific notation is often the more honest display for very large or very small measurements. If you need exact decimal values (money, accounting exports), store them as decimal.Decimal or as integer cents rather than formatting floats harder.
Common pitfalls
round()does not remove exponents.df.round(2)changes the values, but the column can still display as1.200000e+09. Rounding and formatting are separate steps.- Converting to strings too early.
df["x"].map("{:.2f}".format)produces text; sorting and arithmetic stop working. Format at the very end. - Suppressed tiny values look like zero. With
suppress=Trueand a low precision,1e-12prints as0.; check before concluding a value is zero. - Integers shown as floats in scientific notation usually mean the column became
float64because of missing values. Use the nullableInt64dtype (df["id"].astype("Int64")) to keep integer display.
Summary
Scientific notation is a display choice, not a property of the data. Use np.set_printoptions(suppress=True, precision=...) or the np.printoptions context manager for NumPy arrays, pd.set_option("display.float_format", ...) or pd.option_context for pandas output, Styler.format for per-column report formatting, to_csv(float_format=...) and np.savetxt(fmt=...) for files, and f-strings with the f type for individual values. Keep in mind that extra digits beyond a float's precision carry no information.
DataKurtosis in Python
Compute kurtosis in Python with scipy.stats.kurtosis, pandas Series.kurt and NumPy. Excess vs Pearson, the bias flag, worked examples and how to interpret it.
DataUnicodeDecodeError in read_csv
Why pd.read_csv raises UnicodeDecodeError: 'utf-8' codec can't decode byte, and how to fix it with the right encoding, BOM handling and UTF-8 conversion.
PythonCould not convert string to float
Why float() raises "could not convert string to float": empty strings, commas, currency symbols, N/A values, and how to fix them in Python and pandas.