Chapter 1 · Data Structures and Methods of Series
Section 1.1 · Chapter 1 · Learning Statistics with Python
Data Structures and Methods of Series
Prof. Xuhu Wan
ISOM, HKUST Business School · 2026 Edition
A Python list holds numbers but cannot summarise them. A Series can — and it carries a label for every value. You will predict what the index does to arithmetic, and discover why ddof changes your standard deviation.
alist = [300, 100, 0, 200, 1000] is five monthly sales figures. Predict what happens when you ask the list for its mean.
What does alist.mean() do?
320.0[300, 100, 0, 200, 1000]AttributeError — lists have no meanNoneSame five numbers, but now sales.mean() returns 320.0. The index is a RangeIndex(start=0, stop=5) — pandas invented positional labels because you gave none.
A Series accepts a list, a dict, or a NumPy array. Predict which constructor sets the index for you.
What does pd.Series({"Jan": 300, "Feb": 100}).index print?
Index(['Jan', 'Feb'], dtype='object')
Dict keys become labels. The NumPy array gives float64, the integer list int64 — the dtype is inferred from the values, and every element of a Series shares it.
sales is now labelled Jan … May. There are two doors into it.
Which pair returns the January and May figures?
sales.loc[0] and sales.loc[4]sales.loc["Jan"] and sales.iloc[-1]sales.iloc["Jan"] and sales.iloc["May"]sales[0] and sales["May"] — both raiseComparing a Series to a number gives a Series of booleans of the same length — a mask. Predict its dtype and what indexing with it returns.
sales.median() is 200. Which labels survive sales[sales > sales.median()]?
['Jan', 'May']
bool mask, {'Jan': 300, 'May': 1000} survive. Combine masks with & and | (each side in parentheses) — never with Python’s and/or, which cannot handle five answers at once.
scoresheet holds your subject scores. Build low = the scores strictly below the mean (72.5). Expected: only Stats 20.
The notebook’s Kaggle sales-analysis file: historical sales for products a retailer must decide to keep or drop. PriceReg is the list price, SoldFlag = 1 if sold in the past six months.
df["col"] (or df.SoldFlag) hands back a Series named after the column, with the frame’s RangeIndex — 44.99, 24.81, 46.0 ….
(ReleaseNumber + 2) / 100 applies to every element at once (broadcasting). No loop.
The first ReleaseNumber is 15. What is the first element of (ReleaseNumber + 2) / 100?
0.17
[0.17, 0.09, 0.02] and revenues [674.85, 173.67, 0.0]. Try alist + 2 on a plain list: TypeError. Broadcasting is a Series (NumPy) privilege.
pd.Series([1,2,3,4]) + pd.Series([100,200,300]) gives …
ValueError: lengths differNaN4[101.0, 202.0, 303.0, nan] then [101.0, 202.0, 303.0, 4.0]. The dtype silently became float64 — NaN is a float. The method form .add(...).div(...) chains: each method works on the outcome of the previous one.
appl.csv runs 2015–2024 (2 516 days); microsoft.csv runs 2014-12-31 … 2018-02-05 (780 days). Predict how many days of aapl + msft are not NaN.
(aapl + msft).notna().sum() is …
The rule you uncovered: arithmetic aligns by label, not position, then operates. 2 517 labels in the union, 779 matched. A hand-written loop would need a lookup dict, a membership test and a policy for misses — one + folds in all three.
>, <, == return masks; .gt, .lt, .eq, .between are their method twins. Combine with &, |, ~ or np.logical_and.
All three spellings give {'Jan': 300, 'April': 200}. ~s1 flips the mask: {'Feb': 100, 'March': 0}. .between is inclusive by default, so it counts 3.
Central tendency, dispersion, shape — every summary statistic is one method call. But one keyword changes the answer, and you should predict which way.
\[s=\sqrt{\frac{\sum(x_i-\bar x)^2}{n-1}} \qquad \sigma=\sqrt{\frac{\sum(x_i-\mu)^2}{n}}\]
For the same data, how does price.std() compare with price.std(ddof=0)?
ddof only affects .var()ddof must always be givenMean 109.85, median 89.95, mode 0.0 — a right tail up to 2 800 and a spike of free items. The two standard deviations (85.9252 vs 85.9230) differ only in the 4th digit because \(n = 20\,000\); with \(n = 5\) they differ by 12 %.
True counts as 1. If 15 146 of 20 000 prices exceed 50, what does round(price.gt(50).mean(), 3) print?
0.757
The trick you discovered: the mean of a boolean mask is a proportion. It is how you will compute default rates, hit rates and p-values all term. idxmax returns the label of the maximum — row 15511.
agg takes names, lists, and your own functionsSkewness 3.78 and kurtosis 70.1 — a normal distribution has 0 and 0. agg with a list returns a Series of statistics; with a function it returns one number. Three spellings — price.mean(), price.agg("mean"), price.agg(mymean) — same answer. Each method returns a scalar, so ratios come free: a Sharpe ratio is mean / std.
For a discrete or text Series the summary is value_counts() — the frequency table.
2010 leads with 1 796 products, then 2008, 2009, 2007. 61 distinct years — including a 0, which is a missing value in disguise. normalize=True turns counts into shares (9.0 %).
A Series has one dtype. Changing it is explicit — and the most important change, next slide, is text → dates.
After p32 = price.astype(“float32”), which statement is true?
price is now float32 toop32 uses half the memory and price is unchangedp32 holds exactly the same decimals as priceastype raises because prices have decimalshistory.index holds "10/09/2022" …. Before conversion, what is type(history.index[0]).__name__?
str
str → Timestamp. Only after pd.to_datetime can you ask for .year, compare against a date, or slice by month. A date stored as text sorts alphabetically — "10/09" before "2/01". For a Series of dates, the .dt accessor (Sunday, Monday) is the door to those properties.
Select the months of sales with units at least 100 and below 500 into between. Expected: Jan 300, Feb 100, April 200.
NaN, and NaN forces float64..loc uses labels, .iloc positions; a comparison returns a boolean mask you index with.mask.mean() is a proportion; std() divides by \(n-1\) unless you say ddof=0..agg accepts names, lists and your own functions; astype and pd.to_datetime return new Series.Next: §1.2 — if-else without loops, missing values, clipping, ranking and binning.
Prof. Xuhu Wan · HKUST ISOM · Learning Statistics with Python