Writing data out

pandas · Practical · 10 min

Results only matter once they reach a file, a colleague or another system in a usable, correctly typed form.

Every reader has a matching writer.

`df.to_csv(path, index=False)` is universal. index=False is important: otherwise the row index becomes an unnamed first column that reappears as Unnamed: 0 when you read it back. Other options: sep=";", float_format="%.2f", date_format="%Y-%m-%d", na_rep="", columns=[...] to choose and order columns. Compression is inferred from the extension: to_csv("out.csv.gz") writes gzip. CSV loses all type information, so dates, categories and leading zeros need care on the way back in.

`df.to_parquet(path)` (needs pyarrow) keeps dtypes, compresses well and reads back fast. It's the best choice between Python steps, for data lakes, and for anything big. partition_cols=["year"] writes a directory partitioned by a column, which query engines and read_parquet(filters=...) can skip through.

`df.to_excel(path, sheet_name="Summary", index=False)` (needs openpyxl) is for humans. To write several sheets into one workbook, use a writer:

with pd.ExcelWriter("report.xlsx") as xw:
    summary.to_excel(xw, sheet_name="Summary", index=False)
    detail.to_excel(xw, sheet_name="Detail", index=False)

`df.to_json(orient="records", lines=True)` gives one JSON object per line, a common format for APIs and log pipelines; date_format="iso" makes dates readable.

Also available: to_sql for databases, to_feather for fast local interchange, to_markdown and to_html for reports, and to_dict(orient="records") to hand plain Python structures to other code.

Write to a temporary name and rename when the output feeds other jobs, so readers never see a half-written file.

Example

import pandas as pd
from pathlib import Path

out = Path("out"); out.mkdir(exist_ok=True)
df = pd.DataFrame({
    "zip": ["02134", "10001"],
    "day": pd.to_datetime(["2024-05-01", "2024-05-02"]),
    "revenue": [1234.567, 98.1],
})

df.to_csv(out / "rev.csv.gz", index=False, float_format="%.2f", date_format="%Y-%m-%d")
back = pd.read_csv(out / "rev.csv.gz")
print(back.dtypes.astype(str).to_dict())   # zip -> int64, day -> text: CSV lost types

df.to_parquet(out / "rev.parquet", index=False)
print(pd.read_parquet(out / "rev.parquet").dtypes.astype(str).to_dict())   # types kept

with pd.ExcelWriter(out / "report.xlsx") as xw:
    df.to_excel(xw, sheet_name="Detail", index=False)
    df[["revenue"]].sum().to_frame("total").to_excel(xw, sheet_name="Summary")

print(df.to_json(orient="records", lines=True, date_format="iso").splitlines()[0])
# {"zip":"02134","day":"2024-05-01T00:00:00.000","revenue":1234.567}

Remember

  • Pass index=False to to_csv unless the index holds real data
  • Parquet keeps dtypes and is the default choice between pipeline steps
  • Use pd.ExcelWriter to put several sheets in one workbook
  • Compression is inferred from file extensions like .gz

Watch out for

  • Round-tripping through CSV and losing leading zeros, dates and categories without noticing.

Check yourself

  1. A colleague's CSV, written from pandas, has a first column called 'Unnamed: 0' when read. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: pandas.pydata.org/docs/user_guide/io.html