menuMultiple Regression

Chapter 13: Multiple Regression

Learning Objectives

  1. Explain why a single predictor can mislead, and how adding variables lets you compare observations while holding other variables constant.
  2. Build dummy variables in Excel, choose a baseline category, and avoid the dummy variable trap.
  3. Interpret the coefficients, R squared, adjusted R squared, overall F test and individual t tests of a multiple regression in business terms.
  4. Recognize omitted variable bias and multicollinearity, and check a model's residuals.
  5. Fit a multiple regression with LINEST, the Analysis ToolPak and R for Excel, explain why the baseline category changes the coefficients, and use the model to predict a business scenario.

13.1 What Drives a Busy Day?

Dana Whitfield is planning next year's staffing budget. Every store manager wants more hours on busy days, and Dana wants to know what actually makes a day busy. "Is it the promotions? Weekends? The weather? Is a drive-thru worth what it costs to build? I need to know how much each of these matters on its own, not all tangled together."

That last phrase is the problem. In Ridgeline's data, these things are tangled together. Bigger stores are mostly in the city, where it's also busier for reasons that have nothing to do with seats. Mountain stores are colder and quieter. Some stores have drive-thrus and some don't. If you compare transactions on hot and cold days, you're also comparing summer with winter and Mountain stores with Metro stores. A simple regression with one predictor, like the ones in Chapter 12. Correlation and Simple Regression, can't untangle that. This chapter's tool can.

Marcus has a related question, left over from Chapter 10. Comparing Many Groups: ANOVA. Metro customers are less satisfied than Mountain customers, but Metro customers also wait much longer (5.8 minutes on average, against 3.1 in the Mountain stores). "Is something wrong with our Metro stores, or is it just the lines?" The same tool answers that too.

13.2 Why One Predictor Isn't Enough

Multiple regression extends the regression line to several explanatory variables at once.

Multiple regression — A regression with two or more explanatory variables: ŷ = b0 + b1x1 + b2x2 + … + bkxk. Each coefficient b is chosen by least squares, just as in simple regression.

The big change is in what each coefficient means. In simple regression, the slope of seats tells you how transactions differ between stores with more and fewer seats, and nothing else. Any other difference between those stores rides along. In multiple regression, the coefficient of seats tells you how transactions differ between store-days with more and fewer seats that are alike on every other variable in the model.

Holding other variables constant — The meaning of a coefficient in multiple regression: the predicted change in y for a one-unit increase in that x when every other explanatory variable in the model stays the same. It's also described as "controlling for" or "adjusting for" the other variables.

Think of it as a fairer comparison. In Chapter 9. Comparing Two Groups, you compared promo days with other days at the same store, a paired design, so differences between stores couldn't muddy the comparison. Multiple regression does something similar with arithmetic instead of design. It can compare weekend days with weekdays at the same store, the same temperature and the same promo status, even if no two days in the data match exactly.

The flip side is the danger of leaving something out.

Omitted variable — A variable that affects the response and is related to one or more explanatory variables but isn't included in the model. Leaving it out biases the coefficients of the variables it's related to, because they absorb its effect. This is called omitted variable bias.

An omitted variable is the regression version of the confounding variable from Chapter 9. Comparing Two Groups. You'll see a clear case of it in Ridgeline's data shortly.

13.3 Prepare the Data in Excel

Dana's question uses the DailySales Table, where each row is one store on one day of 2025: 4,380 rows. The variables you need are:

Variable Where it lives Type
transactions DailySales numeric (the response)
promo_day DailySales Yes/No
weekend not there yet Yes/No, from day_of_week
high_temp_f DailySales numeric, °F
drive_thru Stores Yes/No
seats Stores numeric
region Stores Metro, Suburban or Mountain

Two jobs come first: bring the store information into each daily row, and turn every categorical variable into numbers that a regression can use.

13.3.1 Add a weekend column

Click the first empty cell to the right of revenue in the DailySales Table, type the header weekend, and enter:

=IF(OR([@day_of_week]="Sat",[@day_of_week]="Sun"),"Yes","No")

The Table fills it down all 4,380 rows. Check it with =COUNTIF(DailySales[weekend],"Yes"), which should give 1,248 (104 weekend days at 12 stores).

13.3.2 Dummy variables

A regression needs numbers. Text like "Yes" and "Metro" has to be recoded.

Dummy variable — A column coded 1 when an observation is in a particular category and 0 when it isn't. A dummy lets a categorical variable enter a regression. Its coefficient is the predicted difference between that category and the baseline category, holding the other variables constant.

For a Yes/No variable you need one dummy: 1 for Yes, 0 for No. Add these columns to the DailySales Table, in this order, side by side:

Header Formula What it holds
promo01 =IF([@promo_day]="Yes",1,0) 1 on a promo day
weekend01 =IF([@weekend]="Yes",1,0) 1 on Saturday or Sunday
drive01 =IF(XLOOKUP([@store_id],Stores[store_id],Stores[drive_thru])="Yes",1,0) 1 if the store has a drive-thru
store_seats =XLOOKUP([@store_id],Stores[store_id],Stores[seats]) the store's seats
temp =[@high_temp_f] the day's high temperature
metro01 =IF(XLOOKUP([@store_id],Stores[store_id],Stores[region])="Metro",1,0) 1 for a Metro store
mountain01 =IF(XLOOKUP([@store_id],Stores[store_id],Stores[region])="Mountain",1,0) 1 for a Mountain store

XLOOKUP finds the row's store_id in the Stores Table and returns that store's drive-thru, seats or region. The temp column just copies high_temp_f. You'll see why it's there in a moment.

The DailySales Table scrolled right to show the new columns weekend, promo01, weekend01, drive01, store_seats, temp, metro01 and mountain01, all filled with 0s and 1s (or seats and temperatures). The formula bar shows =IF(XLOOKUP([@store_id],Stores[store_id],Stores[region])="Metro",1,0) for a selected metro01 cell.

Why the odd names? Later, R for Excel will merge DailySales with Stores, which already has columns named seats, drive_thru and region. Giving your helper columns different names (store_seats, drive01) keeps the two from colliding.

13.3.3 The baseline category and the dummy variable trap

Region has three categories, but you built only two dummies. That's deliberate.

Baseline category — The category of a categorical variable that gets no dummy. Its effect is built into the intercept, and every dummy's coefficient is a comparison with it. It's also called the reference category.

Here the baseline is Suburban. A Suburban store has metro01 = 0 and mountain01 = 0, so the model already knows where Suburban stores are. The metro01 coefficient will say how a Metro store differs from a comparable Suburban store, and mountain01 how a Mountain store does.

Why not add a third dummy, suburban01? Because every row would then have metro01 + mountain01 + suburban01 = 1. That's exactly the column of 1s that the intercept already uses. The model can't tell the intercept from the three dummies, and infinitely many sets of coefficients fit equally well. This is the dummy variable trap. Depending on the software, you get an error, a coefficient of NA, or a coefficient quietly set to 0. The rule: a categorical variable with k categories gets k − 1 dummies. Yes/No variables have two categories, so they get one dummy each.

Which category should be the baseline? Mathematically, any. The predictions are identical. Practically, choose the one that makes the comparisons you care about easiest to read. Suburban sits in the middle on most measures, so "Metro versus Suburban" and "Mountain versus Suburban" are natural.

13.4 How Multiple Regression Works

13.4.1 The idea

Multiple regression chooses the coefficients by least squares, exactly as in Chapter 12. Correlation and Simple Regression: it picks the set of b's that makes the sum of squared residuals as small as possible. With one x the fitted result is a line. With two it's a flat plane through a 3-D cloud of points. With seven you can't draw it, but the arithmetic is the same.

What makes "holding constant" work is that the coefficients are fitted together. If two predictors overlap, such as seats and region, least squares has to divide the credit between them. It gives each one only the part of the explanation that the other variables can't provide. That's why a coefficient can change, sometimes a lot, when you add or remove another variable.

13.4.2 Fit it with LINEST

LINEST handles several x columns at once, as long as they're next to each other. That's why you built the dummies side by side and copied temperature into temp. A structured reference like DailySales[[promo01]:[temp]] means "every column from promo01 through temp."

Start with the five predictors without region. Call it Model A. On a new sheet, enter:

=LINEST(DailySales[transactions],DailySales[[promo01]:[temp]],TRUE,TRUE)

Then Model B, which adds the two region dummies:

=LINEST(DailySales[transactions],DailySales[[promo01]:[mountain01]],TRUE,TRUE)

LINEST returns coefficients in reverse order. For Model B, the first row of the spill is mountain01, metro01, temp, store_seats, drive01, weekend01, promo01, and finally the intercept in the last column. Type a row of labels above the result, right to left, before you read anything. The second row holds the matching standard errors. Rows 3 to 5 hold R², the standard error of the regression, F, its degrees of freedom and the sums of squares in the first two columns only; the rest of those rows show #N/A, which is normal.

A worksheet with =LINEST(DailySales[transactions],DailySales[[promo01]:[mountain01]],TRUE,TRUE) spilling into a 5-row by 8-column grid. A typed label row above it reads, left to right: mountain01, metro01, temp, store_seats, drive01, weekend01, promo01, intercept. The first row shows −83.46, 81.17, −1.58, 1.98, 48.82, 60.85, 32.82, 327.96. Rows 3–5 show values in the first two columns and #N/A in the others.

Rearranged into the usual order, here are both models side by side:

Predictor Model A coefficient Model B coefficient Model B 95% CI
Intercept 137.6 328.0
Promo day (Yes = 1) 36.9 32.8 [30.1, 35.5]
Weekend (Yes = 1) 60.9 60.8 [58.7, 63.0]
Drive-thru (Yes = 1) 44.1 48.8 [46.7, 50.9]
Seats (per seat) 6.24 1.98 [1.85, 2.11]
High temperature (per °F) −0.91 −1.58 [−1.64, −1.53]
Metro (vs. Suburban) 81.2 [78.3, 84.0]
Mountain (vs. Suburban) −83.5 [−86.1, −80.8]
R² 0.572 0.868
Adjusted R² 0.571 0.868
Standard error of the regression 58.1 32.3

Every p-value in both models is below 0.001.

13.4.3 Reading Model B

Each coefficient is a difference holding the other variables in the model constant:

13.4.4 A worked prediction

Dana asks for a concrete number: "What should Lakeside staff for on a Saturday promo day with a high of 70°F?" Lakeside is a Suburban store with a drive-thru and 44 seats. Put each value into Model B. Suburban is the baseline, so both region dummies are 0:

Term Coefficient Value for the scenario Product
Intercept 327.96 1 327.96
Promo day 32.82 1 32.82
Weekend 60.85 1 60.85
Drive-thru 48.82 1 48.82
Seats 1.9775 44 87.01
High temperature −1.5838 70 −110.87
Metro 81.17 0 0
Mountain −83.46 0 0
Predicted transactions 446.6

In Excel, type the coefficients in B2:B9 and the scenario values in C2:C9, and compute =SUMPRODUCT(B2:B9,C2:C9). You'll get the same prediction from R later, with an interval around it.

13.4.5 R squared and adjusted R squared

Model A explains 57% of the variation in daily transactions. Adding region raises that to 87%, and the typical prediction error (the standard error of the regression) falls from 58 to 32 transactions. Region matters a lot.

R² has a weakness in multiple regression: it never goes down when you add a variable, even a useless one. Add a column of random numbers and R² creeps up a little, purely by chance. Adjusted R² corrects for that.

Adjusted R squared — A version of R² that accounts for the number of explanatory variables. It rises when a new variable improves the model by more than chance would, and falls when it doesn't. Use it to compare models with different numbers of predictors.

With 4,380 rows and only seven predictors, the penalty is tiny, so R² and adjusted R² are almost equal. With small data sets, such as 104 staff, the gap is wider and matters more.

13.4.6 Omitted variable bias: why seats changed

Look at the seats coefficient. In Model A, each seat was worth 6.2 transactions a day. In Model B, it's worth 2.0. Nothing about the seats changed. What changed is that region joined the model.

Ridgeline's Metro stores are its biggest (45 seats on average, against 34.5 in Suburban and 28.5 in Mountain stores) and its busiest, for reasons beyond seats: downtown foot traffic, commuters, a university. In Model A, region was an omitted variable. Seats was the only variable that could stand in for "this is a big-city store," so it took credit for Metro's extra business. Once region is in the model, seats keeps only the part of the effect that belongs to seats.

Temperature moved too, from −0.91 to −1.58, in the other direction. The Mountain stores are both colder (an average high of about 48°F, against about 60–62°F elsewhere) and quieter. In Model A, a cold day partly meant "probably a Mountain store," which made cold weather look worse for business than it really is, and hid part of the true heat effect. Holding region constant uncovered it.

This is omitted variable bias at work, and it's the main reason multiple regression exists. For Dana, the practical lesson is sharp: Model A would have told her that adding 10 seats to a store brings about 62 more transactions a day. Model B says about 20. Using Model A to justify a renovation would badly overstate the payoff.

Adding variables can't remove every confounder. Model B holds constant the variables in it. Anything you didn't measure, such as parking, competition nearby or a manager's skill, can still bias the coefficients. Multiple regression on observational data shows associations adjusted for what you included. It doesn't prove cause.

13.4.7 The overall F test and the individual t tests

A multiple regression output has two kinds of test, and they ask different questions.

The two can disagree. A model can have a highly significant F while several individual variables are not significant: those variables add nothing once the others are known. You'll see exactly that in the satisfaction model below.

13.4.8 Multicollinearity

When explanatory variables are strongly related to each other, the model struggles to separate their effects.

Multicollinearity — Strong correlation among the explanatory variables of a regression. It makes the coefficients unstable and inflates their standard errors, so individual variables can look insignificant even when, together, they predict well.

The usual check is the variance inflation factor (VIF). For each explanatory variable, regress it on all the other explanatory variables and find that regression's R². Then VIF = 1 / (1 − R²). A VIF of 1 means the variable is unrelated to the others. A common rule of thumb treats values above 5, and certainly above 10, as a warning.

In Model B, the VIFs are all below 3: seats 1.91, temperature 1.15, drive-thru 1.22, and the region dummies under 3. Seats and region overlap, which is why adding region changed the seats coefficient, but not so much that the model can't tell them apart. Multicollinearity isn't a problem here.

13.4.9 Checking the residuals

The assumptions are the same as for simple regression: linearity, independence, constant spread and roughly normal residuals. Check them the same way, with residual plots. In multiple regression, plot the residuals against the predicted values, and against each numeric predictor.

For Model B, the plot against predicted values is an even band. The plot against temperature isn't quite. The residuals average about +8 on days with highs in the 50s and low 60s and about −12 on days in the 80s. The line for temperature is a little too simple. Transactions hold up through mild weather and then fall faster once it's hot, a curve that a single straight-line slope can't follow.

A scatterplot of Model B residuals (vertical axis, about −100 to +100) against high_temp_f (horizontal axis, 10 to 103°F), with a horizontal line at zero. The cloud bows slightly: residuals sit a little above zero in the 50s and 60s and below zero above about 75°F.

A residual plot like this tells you how to improve the model. One common fix is a helper column such as =MAX(0,[@high_temp_f]-65), "degrees above 65°F," which lets the model have one slope in mild weather and a steeper one in the heat. Try it as an extension.

Independence needs thought, too. The 4,380 rows are 12 stores observed every day, and a store's busy days tend to cluster. Store-level variables like seats, drive-thru and region vary across only 12 stores, not 4,380 independent observations. So their standard errors and confidence intervals are probably narrower than they should be. The direction and rough size of these effects are believable. The precision is overstated.

13.5 Describe the Data to AI

Open R for Excel. In 1. Data, choose Add a table… twice, once for DailySales and once for Stores. Type this data description into the 2. Ask your AI box:

Each row of DailySales is one store on one day of 2025. transactions is the number of customer transactions that day. promo_day is Yes or No. high_temp_f is the day's high temperature in °F. day_of_week is Mon to Sun. Each row of Stores is one store; join it to DailySales by store_id to get region (Metro, Suburban or Mountain), seats, and drive_thru (Yes or No). Ignore my 0/1 helper columns in DailySales.
Question: How much does each of these change daily transactions, holding the others constant: promo day, weekend (Saturday or Sunday), drive-thru, seats, high temperature, and region? Then predict transactions at the Lakeside store (Suburban, drive-thru, 44 seats) on a Saturday promo day with a high of 70°F.
I'm in an intro business statistics course and have learned simple and multiple regression, dummy variables and baseline categories, R squared and adjusted R squared, and prediction intervals.
Please fit a multiple regression with Suburban as the baseline region, and give a 95% prediction interval for the Lakeside scenario. Use base R only. Send the key results to Excel with excel_out(), and explain how to read them.

Click Copy prompt and paste the prompt into your AI assistant.

Needs: a reply (sent with the full R for Excel prompt) whose base-R script merges DailySales with Stores by store_id, creates a weekend variable from day_of_week, sets Suburban as the baseline with relevel(factor(region), ref = 'Suburban'), fits lm(transactions ~ promo_day + weekend + drive_thru + seats + high_temp_f + region), sends the model to Excel with excel_out(), and uses predict(..., interval = 'prediction') for the Lakeside scenario. The explanation should interpret coefficients as differences holding other variables constant. If the reply forgets the baseline or uses a package such as car, the text after this block uses that as the teaching example.

13.6 Read the Script

A script for this usually looks like the one below.

# Join store details (region, seats, drive_thru) onto each daily row
sales <- merge(DailySales, Stores, by = "store_id")

# Weekend = Saturday or Sunday
sales$weekend <- ifelse(sales$day_of_week %in% c("Sat", "Sun"), "Yes", "No")

# Make Suburban the baseline region (R would pick Metro, alphabetically first)
sales$region <- relevel(factor(sales$region), ref = "Suburban")

# Model A (no region) and Model B (with region)
model_a <- lm(transactions ~ promo_day + weekend + drive_thru + seats + high_temp_f, data = sales)
model_b <- lm(transactions ~ promo_day + weekend + drive_thru + seats + high_temp_f + region, data = sales)
excel_out(model_a, "Model A: transactions without region")
excel_out(model_b, "Model B: transactions with region")

# Predict the Lakeside scenario with a 95% prediction interval
lakeside <- data.frame(promo_day = "Yes", weekend = "Yes", drive_thru = "Yes",
                       seats = 44, high_temp_f = 70, region = "Suburban")
excel_out(predict(model_b, newdata = lakeside, interval = "prediction"), "Lakeside, Saturday promo day, 70F")

# Residuals against temperature
plot(sales$high_temp_f, resid(model_b), xlab = "High temperature (F)", ylab = "Residual")
abline(h = 0)

What each part does:

13.6.1 Why the AI's coefficients may look different

Suppose the AI's script doesn't include the relevel line. Then Metro is the baseline, and the output shows regionMountain and regionSuburban instead of regionMetro and regionMountain. The coefficients look completely different: Mountain −164.6 and Suburban −81.2, with an intercept of 409.1.

Nothing is wrong. Those numbers compare each region with Metro instead of Suburban. Mountain versus Metro is −83.5 − 81.2 = −164.6, and Suburban versus Metro is the mirror of Metro versus Suburban, −81.2. The other coefficients, R², the predictions and the prediction intervals are identical. Changing the baseline only changes which comparisons the output prints. Always check which category is missing from the coefficient list. That's the baseline, and every dummy is compared with it.

Watch for packages. If you ask an AI for VIFs, it will often write library(car) and vif(model). The car package isn't part of base R, and R for Excel would have to download it. Ask for "base R only," or skip it: the VIF formula, 1 / (1 − R²), takes one lm per variable.

13.7 Run It in R for Excel

Paste the reply into 3. Run R code and click Run. If you get an error, click Copy a prompt to fix it and send that to the AI. A common one is a name collision: if DailySales already has a column named seats or region, merge renames the pair seats.x and seats.y, and the model can't find seats. That's why your helper columns have different names.

Send each output card to a New sheet, and click Insert into Excel under the plot.

R for Excel's Results area after the Model B script runs: output cards titled "Model A: transactions without region," "Model B: transactions with region" and "Lakeside, Saturday promo day, 70F," each with New sheet and At selected cell buttons, plus a residual plot against temperature with an Insert into Excel button.

Each model sheet is laid out like the Analysis ToolPak: Regression Statistics, ANOVA and a coefficients table. On the Model B sheet, R Square is 0.8678, Adjusted R Square 0.8676, Standard Error 32.3, Observations 4,380, F 4,099, and the coefficients match the table above, labeled with R's names (promo_dayYes, weekendYes, drive_thruYes, seats, high_temp_f, regionMetro, regionMountain).

The Lakeside card shows a fit of 446.6, with a 95% prediction interval from 383 to 510 transactions.

13.8 Interpret and Verify

13.8.1 The ToolPak with several X columns

  1. Choose Data > Data Analysis > Regression.
  2. Input Y Range: the transactions column with its header, $F$1:$F$4381 on the DailySales sheet.
  3. Input X Range: all seven helper columns at once, from the promo01 header to the bottom of mountain01 (for example $J$1:$P$4381). The ToolPak, like LINEST, needs the X columns side by side, and it accepts up to 16 of them.
  4. Check Labels and Residuals, choose New Worksheet Ply, and click OK.

Compare the ToolPak sheet with R's Model B sheet. They use different labels (promo01 in one, promo_dayYes in the other) but every number should match: R Square, the ANOVA table, each coefficient, standard error, t Stat, P-value and 95% limit. If one model has Metro as its baseline and the other Suburban, the region rows and the intercept won't match, for the reason explained above. Fix the baseline before you go looking for errors.

Then check the Lakeside fit against your SUMPRODUCT (446.6), and give it a reality check. Lakeside had seven Saturday promo days in 2025, with highs between 49°F and 81°F. Its transactions on those days ranged from 432 to 507, comfortably inside the prediction interval.

13.8.2 Write the conclusion

What drives a busy day, each holding the others constant. A weekend day brings about 61 more transactions than a weekday, a promo day about 33 more, and each degree of heat about 1.6 fewer. A drive-thru is worth about 49 transactions a day, and each seat about 2. Region is the biggest single factor: a Metro store does about 81 more transactions a day than a comparable Suburban store, and a Mountain store about 83 fewer. Together these explain 87% of the day-to-day variation in transactions. For staffing, plan Lakeside's Saturday promo days at about 450 transactions, and expect almost any such day to fall between roughly 380 and 510. Two cautions: these are associations in 2025 data, not controlled experiments, and the effects of store features like seats and drive-thrus rest on only 12 stores, so they're less precise than the output suggests.

13.9 The Same Tool, Three More Questions

Multiple regression can revisit questions from earlier chapters and sharpen their answers. Each of these uses one lm line in the same kind of script. The models are labeled C, D and E.

13.9.1 Is it Metro, or is it the lines?

In Chapter 10. Comparing Many Groups: ANOVA, average satisfaction differed by region: Metro 7.01, Suburban 7.83, Mountain 8.36. A regression of satisfaction on region alone (Model C0, Suburban baseline) restates that: Metro −0.83 and Mountain +0.52, both significant. Now add wait time, plus loyalty membership and order type (Model C):

# Region is in Stores, so join it onto each survey response
svy <- merge(Survey, Stores, by = "store_id")
svy$region <- relevel(factor(svy$region), ref = "Suburban")

model_c <- lm(satisfaction ~ wait_minutes + region + loyalty_member + order_type, data = svy)
excel_out(model_c, "Model C: satisfaction")
Predictor Model C0 (region only) Model C (with wait) p-value in Model C
Wait (per minute) −0.33 < 0.001
Metro (vs. Suburban) −0.83 −0.30 0.008
Mountain (vs. Suburban) +0.52 +0.14 0.206
Loyalty member (Yes) −0.06 0.529
Drive-thru order (vs. Counter) +0.06 0.618
Mobile order (vs. Counter) −0.05 0.685
R² 0.168 0.409

Read it carefully, because it's one of the most useful results in the book.

The business message for Marcus and Rosa is clear: fix the waits, not the regions. Most of what looked like a Metro satisfaction problem is a Metro line problem, which is something Ridgeline can act on with staffing, equipment or mobile ordering. A smaller Metro gap of about 0.3 points remains, worth investigating separately.

13.9.2 Does training still matter, holding tenure and role constant?

In Chapter 9. Comparing Two Groups, trained staff had customer ratings 0.37 points higher than untrained staff. But trained staff also had more tenure (26.5 months versus 14.1) and were more often managers and shift leads. Is it training, or experience? Model D answers that, using the Staff Table:

# Baseline: completed_training = No, role = Barista (both first alphabetically)
model_d <- lm(customer_rating ~ completed_training + tenure_months + role, data = Staff)
excel_out(model_d, "Model D: staff rating")
Predictor Model D0 (training only) Model D p-value in Model D
Completed training (Yes) +0.37 +0.34 < 0.001
Tenure (per month) +0.0053 0.019
Manager (vs. Barista) −0.25 0.069
Shift Lead (vs. Barista) +0.03 0.747
R² 0.325 0.390
Adjusted R² 0.319 0.365

Holding tenure and role constant, trained staff are rated about 0.34 points higher (95% CI 0.23 to 0.45). That's barely smaller than the 0.37 from the simple comparison. Tenure matters a little too: about 0.06 points per year. But tenure and role don't explain away the training gap. Here the gap between R² and adjusted R² is bigger than in the sales model, because there are only 104 staff.

This resolves the confounding question from Chapter 9. Comparing Two Groups as far as observational data can. Tenure and role weren't the explanation. Something else could still be, such as more motivated employees choosing to take the training. Only a randomized rollout, like the coupon test, could settle that completely.

13.9.3 A t-test is a regression

Run Model E on the coupon experiment:

# One Yes/No-style predictor: group is Control (baseline) or Coupon
model_e <- lm(spend_next_30 ~ group, data = CouponTest)
excel_out(model_e, "Model E: coupon spend")

The intercept is 21.94, the Control group's mean spend. The groupCoupon coefficient is 3.81, the difference between the two group means, with t = 4.14 and p < 0.001. Those are exactly the numbers from the equal-variance two-sample t-test (t = 4.14), and the Welch test in Chapter 9. Comparing Two Groups gave almost the same (t = 4.16). A regression with one dummy is a two-sample t-test. A regression with dummies for several groups is an ANOVA. Regression is the general tool that the earlier tests are special cases of, and its advantage is that you can keep adding variables to hold constant.

13.10 Summary

Multiple regression fits one equation with several explanatory variables, chosen by least squares, and each coefficient is a difference holding the other variables constant. Categorical variables enter as dummy variables: k − 1 dummies for k categories, with the missing category as the baseline that every dummy is compared with. In Excel, build dummies with IF and XLOOKUP, place the X columns side by side, and use LINEST (which returns coefficients in reverse order) or the ToolPak's Regression tool. In R, lm makes the dummies itself but chooses the baseline alphabetically, so use relevel to pick it, and expect different-looking coefficients when the baseline differs.

For Ridgeline's daily transactions, a weekend adds about 61, a promo day about 33, a drive-thru about 49, and each degree of heat costs about 1.6, with Metro stores about 81 above comparable Suburban stores and Mountain stores about 83 below. Adding region raised R² from 0.57 to 0.87 and cut the seats effect from 6.2 to 2.0 per seat, a clear case of omitted variable bias. VIFs below 3 showed multicollinearity wasn't a problem, and a residual plot against temperature showed where the model could improve. The same tool showed that most of Metro's satisfaction gap runs through its longer waits, that training is still worth about 0.34 rating points holding tenure and role constant, and that a regression with one dummy reproduces a two-sample t-test. Multiple regression adjusts for what you measure. It can't adjust for what you didn't.