pandas from_dict() — build a DataFrame straight from a dictionary

Your dict is already a table — from_dict() just needs to know which way it points.

The dict you got from that API response is one orient= away from being a DataFrame — most people rebuild it the slow way.

What it does

pd.DataFrame.from_dict() turns a dictionary into a DataFrame. With the default orient='columns', each key becomes a column and the values (lists, arrays, or Series) become that column's data — all columns must be the same length. With orient='index', the outer keys become row labels and inner dicts become rows; pass columns=[...] to name them at construction time. In pandas 1.4+ there's orient='tight', which packs index, columns, and data into one dict so you can round-trip a frame (MultiIndex included) losslessly and stash it as JSON.

Why it matters

Half of real-world pandas work starts from a dict: a parsed API response, an aggregated result, hand-typed test fixtures, a config-driven report. The plain constructor pd.DataFrame(d) only handles one of these shapes cleanly, so people reshuffle with .T or pd.DataFrame(list(d.values())) and get mangled dtypes. from_dict() names the direction explicitly and validates it — wrong orientation fails fast instead of silently transposing mixed types into object columns. It's also the only constructor that reads pandas' own tight format, which makes it the missing to_dict() counterpart for serializing frames.

Examples

A colleague mails you dict-shaped sales data. Build the report table directly — keys become columns:
>>> sales = pd.DataFrame.from_dict({
...     "city": ["Vienna", "Graz", "Linz", "Salzburg"],
...     "units": [120, 85, 60, 95],
...     "revenue": [4800, 3060, 1980, 3800],
... })
>>> print(sales)
       city  units  revenue
0    Vienna    120     4800
1      Graz     85     3060
2      Linz     60     1980
3  Salzburg     95     3800
>>> type(sales["units"])
<class 'pandas.Series'>

orient='columns' is the default: values must be equal-length sequences, and dtype is inferred per column — units stays int64, not one object column like a naive transpose would give you.

Now the dict is row-shaped: weekday → {city → units}. Flip it with orient='index' and name the columns up front:
>>> week = pd.DataFrame.from_dict(
...     {"Mon": {"Vienna": 31, "Graz": 12},
...      "Tue": {"Vienna": 28, "Graz": 15}},
...     orient="index", columns=["Vienna", "Graz"],
... )
>>> print(week)
     Vienna  Graz
Mon      31    12
Tue      28    15

orient='index' + columns= is the one-shot version of 'build then rename' — inner keys become the index, and the columns list replaces the default integer labels immediately.

A report script returns nested-column names as tuples. Rebuild it and prove the tight round-trip is lossless:
>>> raw = {"stat": ["mean", "max"],
...        ("revenue", "EUR"): [2955.0, 4800.0],
...        ("units", "pcs"): [90, 120]}
>>> df = pd.DataFrame.from_dict(raw)
>>> df.columns.tolist()
['stat', ('revenue', 'EUR'), ('units', 'pcs')]
>>> tight = df.to_dict(orient="tight")
>>> print(tight["columns"])
['stat', ('revenue', 'EUR'), ('units', 'pcs')]
>>> pd.DataFrame.from_dict(tight, orient="tight").equals(df)
True

orient='tight' (pandas 1.4+) is the only lossless dict serialization for MultiIndex frames — index, columns, and data travel together, so equals() comes back True. Regular to_dict('list') flattens the tuples.

Sensor readings with a target dtype for the whole frame:
>>> sensors = pd.DataFrame.from_dict(
...     {"temp_c": [21.5, 19.8], "hum_pct": [48, 52]}, dtype="float32"
... )
>>> print(sensors.dtypes)
temp_c    float32
hum_pct   float32
dtype: object

dtype= applies to the entire frame at construction — handy when downstream code expects float32 before any arithmetic happens.

Flags

FlagMeaning
data (first argument)A dict; with orient='columns' values must be equal-length sequences, with orient='index' values are inner dicts/sequences keyed by column.
orient='columns' (default)Keys become column names, values become column data. The shape you want when the dict is column-major.
orient='index'Outer keys become row labels, inner keys become column names; pairs with columns= to set labels in the same call.
orient='tight'Expects a dict with keys index, columns, data — pandas' lossless serialization format, round-trips MultiIndex exactly (pandas 1.4+).
columns=[...]Set column labels at construction (only with orient='index'/'tight'); avoids a separate rename step.
dtype=...Cast the whole frame to one dtype on the way in, e.g. dtype='float32' for compact sensor data.

2012-02-13 · pandas 0.7.0

from_dict appeared as a classmethod on DataFrame in the 0.7.0 release (Wes McKinney's pre-1.0 API build-out, the same year pandas got its first tag on GitHub).

2022-01-22 · pandas 1.4.0

orient='tight' was added to from_dict and to_dict together (issue 4889) — a decade after the method itself, closing the serialization loop for frames with MultiIndex.

Why orientation must be declared, not inferred

from_dict() constructs through the internal _from_arrays-style path: with orient='columns' it extracts values from each sequence and builds one column per key, inferring dtype per column; with orient='index' it walks outer keys as rows. Because dict keys are unique, from_dict() never needs the duplicate-label handling the main constructor does. The tight format stores index, columns, and data as three separate entries — mirroring how pandas' internal block manager thinks about a frame — which is exactly why it can rebuild a MultiIndex losslessly while the other two orientations cannot.

Fun facts

Pros

Cons

Takeaways