Pandas · Module 2 of 22 · 4 concepts · about 4 min

Creating DataFrames

Constructing frames deliberately, from the four shapes real data arrives in.

At work: Turning what an API, a form or a model hands back into a table you can analyse, test or send on.

From a dict of columns

What it is

pd.DataFrame({"a": [...], "b": [...]}) builds column by column. Each value becomes a Series, keys become column names.

Why it matters

This is the clearest constructor because it matches how a DataFrame is actually laid out, and it lets you set each column's dtype independently.

In the real world: Writing a quick test table of five orders to check that a discount calculation gives the right totals before running it on real data.

df = pd.DataFrame({
    "user_id": pd.Series([1, 2, 3], dtype="int64"),
    "spend": [9.99, 12.50, 0.0],
    "tier": pd.Series(["a", "b", "a"], dtype="category"),
})

From a list of records

What it is

A list of dicts, or a list of tuples with columns=, builds row by row. Missing keys across dicts become NaN rather than an error.

Why it matters

It is the shape API responses and JSON lines arrive in, so it is what you build from when the source is not already tabular.

In the real world: A payments API returns a list of transactions, one dict each. Turning that list into a table is the first step of any report on it.

rows = [{"id": 1, "score": 10}, {"id": 2}]   # second record has no score
pd.DataFrame(rows)
#    id  score
# 0   1   10.0
# 1   2    NaN

Appending to a DataFrame inside a loop to build it up. Each append copies the whole frame; collect the records in a plain Python list and construct once at the end.

From a NumPy array

What it is

pd.DataFrame(arr, columns=[...], index=[...]) wraps an existing 2-D array. Without columns you get integer column names.

Why it matters

It is the bridge back from modelling code, which hands you arrays of predictions or features with no labels attached.

In the real world: A model returns an array of predicted churn probabilities. Wrapping it in a table with customer IDs makes it something the retention team can use.

preds = model.predict_proba(X)            # shape (n, 2)
out = pd.DataFrame(preds, columns=["p_no", "p_yes"], index=X.index)

Forgetting to pass index= when the array's rows correspond to rows of an existing frame. You get a fresh RangeIndex, and the next operation aligns it against the original index and produces NaN everywhere.

Series construction, name and index

What it is

pd.Series(data, index=..., name=...) creates a single column. The name becomes the column header when the Series is put into a frame.

Why it matters

Naming a Series as you build it is what makes concat and assign produce readable columns instead of 0.

In the real world: Building a small lookup of exchange rates, one per currency code, to convert every order into dollars.

margin = pd.Series([1.5, 2.0], index=df.index, name="margin")
pd.concat([df, margin], axis=1).columns   # [..., 'margin'] not [..., 0]