Chapter 2 · DataFrames
Section 2.1 · Chapter 2 · Learning Statistics with Python
DataFrames
Prof. Xuhu Wan
ISOM, HKUST Business School · 2026 Edition
A DataFrame is a dict of Series sharing one index — two axes, labelled both ways. Every method you learnt in Chapter 1 now needs one more decision, which axis?, and every arithmetic operation silently aligns on labels before it computes. In this section you predict the shape of each result before pandas shows it.
PJM is the grid operator for the eastern United States; each column is a utility’s hourly load in megawatts. We load five utilities with parse_dates=True and index_col="Datetime".
After loading, what does raw.loc[“2017-07-04 15:00”, “AEP”] return?
df.axes is a list of two Index objects. Commit before you run.
What does df.axes[1] return?
DatetimeIndex of 8 760 timestampsIndex(['AEP', 'COMED', 'DAYTON', 'DEOK', 'DOM'])5df.sum(axis=0) and df.sum(axis=1) are both legal. Predict the length of each.
How many numbers does df.sum(axis=1) return?
Read axis=1 as “collapse the columns”. The row-wise sum is the combined load of the five utilities in that hour. The default is always column-wise; getting this orientation right is most of what makes DataFrame code correct.
Row-wise statistics use the same axis=1 switch. quantile accepts it too.
Compute the interquartile range of each row (across the five utilities) into iqr_row. Hint: df.quantile(q, axis=1) gives one quantile per row.
sort_values(by="AEP", ascending=False) puts the peak-load hours first.
After df.sort_values(by=“AEP”, ascending=False), what is the DatetimeIndex?
sort_values also takes axis=1: then by= names a row label and the columns are reordered.
Reorder the columns of df so that the values in the first row are ascending, storing the result in by_first. Hint: df.sort_values(by=df.index[0], axis=1).
New columns are assignments. drop returns a new frame — predict what happens to the original.
After df2 = df.drop([“Month”, “Season”], axis=1), does df still contain Month?
drop returns a copy; df is unchangeddrop always edits in placeSeason column survivesinplace=TrueThree ways to change content without touching the row axis.
assign is chainable and never edits in place; df["new"] = … is faster (roughly 15× on the full file) but mutates. replace takes a scalar, a list, or a dict of old → new.
df1 is rows 0–2 × columns 0–2. df2 is rows 2–3 × columns 0–3. Their labels overlap in exactly one row.
What is the shape of df1 + df2, and how many cells are non-NaN?
Stacking df1 on df2 with pd.concat keeps every row — including the one whose timestamp both frames share.
df1 covers 00:00–02:00 and df2 covers 02:00–03:00. What does this print?
1
.index.duplicated() is a boolean mask; keep="last" marks every copy except the last as a duplicate. In §2.2 you will meet a real duplicated timestamp that no concat created.
agg takes a list of function names, or a dict mapping columns to their own lists. Predict the shape of the dict version.
What does num.agg({“AEP”: [“sum”,“count”], “DEOK”: [“mean”,“count”]}) return?
The notebook’s example: build \(Y = 10 + 0.8X + \text{noise}\) with a function that receives one row at a time. Predict the column means.
X is np.arange(100). What is the first number of newdf.apply(np.mean).round(2)?
49.5
apply(..., axis=1) is how you build a feature from several other features. It is a Python loop under the hood — fine for thousands of rows, slow for millions, where the vectorised 10 + 0.8*newdf.X + newdf.Noise wins by 10–100×. §2.4 returns to this.
Compute the range (max − min) of each row of num twice: rng_agg via .agg(["max","min"], axis=1) and a subtraction, and rng_apply via .apply with a lambda. The two must agree.
read_csv(parse_dates=True, index_col=…) arrives date-typed: a string like "2017-07-04 15:00" or "2017" indexes straight in.axis=0 collapses rows (one result per column, the default); axis=1 collapses columns (one result per row). sum, quantile, agg, apply, drop, sort_index all obey it.sort_values carries the index with the rows; sort_index() restores time order; sort_values(by=<row>, axis=1) reorders columns.drop, rename, assign, replace return new frames — nothing changes unless you re-assign or say inplace=True.df1 + df2 was 4 × 4 with only 3 real cells. pd.concat keeps duplicated labels; ~index.duplicated(keep="last") removes them.Next: §2.2 — the real file has holes and a duplicated hour. Audit first, then decide.
Prof. Xuhu Wan · HKUST ISOM · Learning Statistics with Python