Live OLS · 350 days

Dummy Variables & the Base Case

Week 8 · OLS on ski-resort daily visits · target: visits
Built-in AI tutor. Not sure why the wrong encoding gives one slope, or what a base-case coefficient is reading against? Ask the helper on this page. It coaches toward the answer, it does not just hand it over.

A day of the week is a category, not a quantity. Drop one into a regression as a raw number (Monday = 1, Sunday = 7) and the model fits one straight slope through seven days that do not line up that way. The fix is to encode the day as a set of yes/no dummies and drop one of them as a base case. Below is a live OLS predicting daily visits to a Utah ski resort across two seasons. Flip between the two encodings and watch the same data go from one meaningless slope to a clean per-day reading. The goal is to feel the difference, not just be told it.

The model

Encode day_of_week and fit OLS for visits

Pick an encoding. The dataset and the target never change — only how the day of the week enters the regression.

Every coefficient is read against this day. Change it and the numbers re-reference: the predictions and R² do not move, but the day you drop becomes the one comparison the model no longer reports directly.
avg miss (visits)
fitted numbers
350
days (rows)
actual average visits per day model prediction

What the model fitted

termvaluewhat it reads as

Your read (optional)

The numbers behind it

The seven day averages in this data

These are the actual average visits per day across the two seasons. They are what every encoding is trying to reproduce. With a base case, the intercept is one of these averages and each coefficient is the gap between two of them.

dayday_numberavg visitsdays observed
Check the arithmetic. In the dummies encoding with Monday as the base, the Saturday coefficient should be roughly the Saturday average minus the Monday average. Read a coefficient straight off this table, then confirm it in the fit.
Takeaway

Why this matters past ski resorts

Many accounting predictors are categorical. Region, account type, weekday, product line: none of these are quantities, and they cannot go into a regression as raw numbers without claiming a fake order and fake equal spacing. Plenty of other drivers are genuinely continuous (amount, volume, balance, hours), and a real model usually mixes the two. The canonical course example regresses cost ~ Volume + quarter dummies, where Volume is continuous and the quarter dummies are categorical. The categorical predictors always follow the same rules:
  • Turn the category into a set of yes/no (0/1) columns, one per level. That is one-hot encoding. If the model keeps an intercept, drop one of those columns to act as the base case the others are measured against. If you drop the intercept instead, you can keep a column for every level (the cell-means form).
  • With a base case the intercept is the base level's own average and every coefficient is a contrast against that base.
  • Changing the base re-references the coefficients and never moves a prediction, but it does change which contrast each coefficient tests directly, and its standard error and p-value go with it.
  • Keeping all the dummies and the intercept is the dummy-variable trap: perfect collinearity, no unique fit.
Carry it to the tools. Both libraries can handle the base case for you, but you opt in. pandas.get_dummies only drops a level when you pass drop_first=True; it does not read anything from the column name, it just drops the first category. statsmodels' C(day_of_week) goes one step further: it builds the dummies and drops a reference level automatically. In Excel there is no helper, so you build the yes/no columns by hand and leave one out on purpose. The mechanic differs; the rule does not.
import pandas as pd
import statsmodels.formula.api as smf

# 1. pandas: you opt in to dropping the base with drop_first=True
dummies = pd.get_dummies(df["day_of_week"], drop_first=True)
# -> 6 columns; the first day (alphabetical) becomes the base case

# 2. statsmodels: C() builds the dummies and drops a reference level for you
model = smf.ols("visits ~ C(day_of_week)", data=df).fit()
print(model.params)   # intercept = base-day mean; each coef = that day vs base