13 Dummy Variables Part 2

There are several places in this file where you need to do things (e.g., write R code, write Latex). Some are labeled “YOUR CODE GOES HERE.” Some places tell you to “UN-COMMENT-OUT” code after you’ve done something else (un-comment-out means remove the hash tags from code in code chunks so that it becomes code instead of comments). Some places say “ReplaceWithCoefficientFromBetaModel”. Others are labeled “PLACEHOLDER”. Make sure to go through the file and find all the places you need to do something.

This chapter goes with LN4.2. In LN4.2 we examined two models of the relationship between education and wages. We examined two different ways we could define sets of dummy variables to represent people with a high school degree, a BA, a Master’s, or a PhD as their highest level of education. We saw how both models could be used to answer questions such as “what is the average wage for someone with a Master’s degree?” or “What is the expected difference in average wage for a person with a Master’s degree compared to a BA?” In this BP chapter you will explore these models empirically using micro-level data (i.e., data on individual people).

Specifically, the data we’ll use comes from the 2021 ACS 1-year Public Use Microdata Sample (PUMS) (the 2022 ACS was only released January 25, 2024 and the API doesn’t seem to have been fully updated yet, so 2021 is the most recently available data). This data has the responses of individuals that the Census Bureau uses to calculate the county- and state-level statistics they publish as part of the ACS (e.g., what you’re using for the CP). Formally to analyze this data properly we need to use survey weights to make calculations done using the individual responses representative of the population as a whole. We’re not going to worry about that. However, note that our calculations will be off somewhat from the true average wages for the groups we explore in this chapter.

We’ll obtain our data using the tidycensus package, as described here. If you don’t already have a Census API key, get one and use it, as described here. For our purposes, it’s fine to leave census_api_key("YOUR API KEY GOES HERE") in your code.

library(tidyverse)
library(tidycensus)
library(stargazer)
library(pander)
## This turns off scientific notation when there are fewer then 4 digits. Otherwise pander displays very small p values with way too many decimal places
options(scipen = 4)

13.1 Obtain and prepare data

To work with the PUMS data via tidycensus, you can pull the table of variables to find what variables are included. I’ve set this code chunk to not be evaluated (eval = FALSE) when you build because while doing this is helpful while you’re working with this data, it doesn’t belong in your HTML output

## To work with the PUMS data via tidycensus, you can pull the table of variables to find what variables are included
## I've set this code chunk to not be evaluated (eval = FALSE) when you build. You might want to do this on your own, but it doesn't belong in your HTML output
pums_vars_2021 <- pums_variables %>% 
  filter(year == 2021, survey == "acs1")

Let’s pull the following variables from the 2021 ACS-1 year Public Use Microdata (i.e., data on individual people) for the state of Wisconsin (we’re limiting our sample to Wisconsin so that it doesn’t take too long to load or work with).

Variable Description
PUMA Public Use Microdata Areas
WAGP WAGe of Person
AGEP AGE of Person
SCHL educational attainment of person (i.e., highest level obtained)
sex SEX of person

The results='hide' code chunk option keeps it from displaying status updates while it downloads (which is sometimes about 100 lines of output, so you definitely don’t want it in your HTML output).

## Download 2021 acs1 for Wisconsin.
wiPUMS <- get_pums(
  variables = c("PUMA","WAGP", "AGEP", "SCHL", "SEX"),
  state = "wi",
  survey = "acs1",
  year = 2021,
  recode = TRUE
)

Let’s rename WAGP as wage so that we remember what it is.

wiPUMS <- rename(wiPUMS, wage = WAGP)

Let’s see what levels of education exist in our data, and how many observations we have for each

wiPUMS %>% count(SCHL,SCHL_label) %>% pander()
SCHL SCHL_label n
01 No schooling completed 1555
02 Nursery school, preschool 654
03 Kindergarten 708
04 Grade 1 671
05 Grade 2 611
06 Grade 3 764
07 Grade 4 653
08 Grade 5 765
09 Grade 6 780
10 Grade 7 789
11 Grade 8 1377
12 Grade 9 966
13 Grade 10 1143
14 Grade 11 1329
15 12th grade - no diploma 859
16 Regular high school diploma 14503
17 GED or alternative credential 1618
18 Some college, but less than 1 year 3734
19 1 or more years of college credit, no degree 6587
20 Associate’s degree 5398
21 Bachelor’s degree 8796
22 Master’s degree 3209
23 Professional degree beyond a bachelor’s degree 672
24 Doctorate degree 483
bb N/A (less than 3 years old) 1657

We’re interested in examining how wages differ by educational attainment. We’d like to limit our data to people who are old enough to have completed high school (or equivalent) and college, and then be working. So, let’s limit our sample to people age 25 or older. Let’s also only examine people who have a wage.

wiPUMS <- wiPUMS %>% 
            filter(AGEP >= 25 & wage > 0)

We should look at what education levels (SCHL) remain after filtering

wiPUMS %>% count(SCHL,SCHL_label) %>% pander()
SCHL SCHL_label n
01 No schooling completed 158
02 Nursery school, preschool 4
03 Kindergarten 6
05 Grade 2 2
06 Grade 3 4
07 Grade 4 6
08 Grade 5 6
09 Grade 6 38
10 Grade 7 8
11 Grade 8 108
12 Grade 9 79
13 Grade 10 126
14 Grade 11 195
15 12th grade - no diploma 341
16 Regular high school diploma 6340
17 GED or alternative credential 823
18 Some college, but less than 1 year 1860
19 1 or more years of college credit, no degree 3273
20 Associate’s degree 3627
21 Bachelor’s degree 5882
22 Master’s degree 2130
23 Professional degree beyond a bachelor’s degree 455
24 Doctorate degree 328

After filtering, the value “bb” that indicates a person who is under 3 years old and thus can’t have any schooling is now gone. Now all of the levels of SCHL are numeric, which allows us to convert them from a character to a numeric data type. This will then allow us to use inequalities to define dummy variables (e.g., SCHL <16).

wiPUMS$SCHL <- as.numeric(wiPUMS$SCHL)

We’re comparing wages of people who have a high school degree (or equivalent) or higher, so we also need to drop everyone who has less than a high school degree. Because SCHL is now numeric, we can do this using an inequality (rather than listing all of the options separately).

wiPUMS <- wiPUMS %>% filter(SCHL >= 16)

In order to match with LN4.2, we also need to drop people with a professional degree. In practice we would probably want to examine them too, but for the sake of matching with what is in LN4.2 we’re going to drop them.

wiPUMS <- wiPUMS %>% filter(SCHL != 23)

Whenever you do something like filtering data, it’s a very good idea to look at the data and make sure it worked. In previous years of 380, students have wasted many hours trying to get models to work, only to finally go back and look at the data to find that they actually messed up an earlier step that seems easy. So even if it’s something easy you think you know how to do, look at the data. You can display summary measures as we’ll do here, but it’s also a good idea to click on it in the Environment tab and actually scroll through it quickly. Typically you don’t display the results in a paper, but for the purposes of the BP, I want to demonstrate what you might do (e.g., get a count by education levels). Here, we could just re-run this:

wiPUMS %>% count(SCHL,SCHL_label) %>% pander()
SCHL SCHL_label n
16 Regular high school diploma 6340
17 GED or alternative credential 823
18 Some college, but less than 1 year 1860
19 1 or more years of college credit, no degree 3273
20 Associate’s degree 3627
21 Bachelor’s degree 5882
22 Master’s degree 2130
24 Doctorate degree 328

Note that this variable is lists the highest level of education attained. That means it generally builds. We can assume someone who reached a 21 has a 16, and a person who reached a 22 has a 21 (and thus also a 16), and a person who reached 24 has a 22 (and thus also a 21 and 16).

13.2 Define dummy variables

In LN4.2 we had an “alpha model” and “beta model” which defined education in different ways.

Variable Alpha Model Definition Beta Model Definition
BA =1 if have B.A. degree,
=0 if don’t
=1 if highest degree is B.A. degree,
=0 otherwise
Masters =1 if have Master’s degree,
=0 if don’t
=1 if highest degree is Master’s,
=0 otherwise
PhD =1 if have PhD,
=0 if don’t
=1 if highest degree is PhD,
=0 otherwise

13.2.1 Alpha Model

In LN4.2, we defined the “Alpha Model” as

\[ wage = \alpha_0 + \alpha_1 BA + \alpha_2 Masters + \alpha_3 PhD + u \]

We need to create the dummy variables used for the “Alpha Model”. We’ll prefix the variables with “a” for “Alpha”. Later you’ll define “Beta Model” variables and prefix them with “b”.

In our data, some people have some college or an Associates Degree. While we might be interested in differences between people with some college and a only a high school degree, for the purposes of what we’re doing here (learning about dummy variables), let’s classify anyone without a BA as having high school as their highest degree (even if they have some college or an Associates Degree).

# Create dummy variables for "alpha model" (starting with a)
wiPUMS <- wiPUMS %>%
            mutate(aBA = ifelse(SCHL >= 21, 1, 0),
                   aMasters = ifelse(SCHL >= 22, 1, 0),
                   aPhD = ifelse(SCHL == 24, 1, 0)
            )

Now let’s estimate the regression shown as “Alpha Model” in LN4

alphaModel <- lm(wage ~ aBA + aMasters + aPhD, data = wiPUMS)
pander(summary(alphaModel))
  Estimate Std. Error t value Pr(>|t|)
(Intercept) 44840 400.7 111.9 0
aBA 22914 771.5 29.7 1.873e-190
aMasters 11608 1279 9.079 1.179e-19
aPhD 26332 2999 8.78 1.742e-18
Fitting linear model: wage ~ aBA + aMasters + aPhD
Observations Residual Std. Error \(R^2\) Adjusted \(R^2\)
24263 50562 0.07036 0.07025

13.2.2 Beta Model

In LN4.2, we defined the “Beta Model” (version 1…we won’t use the “version 2” at all) as:

\[ wage = \beta_0 + \beta_1 BA + \beta_2 Masters + \beta_3 PhD + u \]

We need to create the dummy variables for the “Beta Model” from LN4.2. Use the same categories as you did for the Alpha Model (so we’re considering anyone with some college or an associates degree as having high school as their highest education). Prefix these variables with a “b” for “Beta”.


YOUR CODE GOES HERE: add variables bBA, bMasters, and bPhD to wiPUMS

# Create dummy variables for "Beta Model" (prefix with b)
# YOUR CODE GOES HERE
wiPUMS <- wiPUMS %>%
            mutate(bBA = ifelse(SCHL == 21, 1, 0),
                   bMasters = ifelse(SCHL == 22, 1, 0),
                   bPhD = ifelse(SCHL == 24, 1, 0)
            )

Now let’s estimate the regression shown as “Beta Model” in LN4.2


UN-COMMENT-OUT the following code after you estimate the Beta Model

betaModel <- lm(wage ~ bBA + bMasters + bPhD, data = wiPUMS)
pander(summary(betaModel))
  Estimate Std. Error t value Pr(>|t|)
(Intercept) 44840 400.7 111.9 0
bBA 22914 771.5 29.7 1.873e-190
bMasters 34523 1167 29.59 4.048e-189
bPhD 60854 2820 21.58 2.747e-102
Fitting linear model: wage ~ bBA + bMasters + bPhD
Observations Residual Std. Error \(R^2\) Adjusted \(R^2\)
24263 50562 0.07036 0.07025

13.3 Compare the regressions side-by-side

In LN4.2 we talk about how different models can lead to equivalent results theoretically. Here we want to examine that theoretical equivalence numerically. Specifically, we want to write out various conditional expectations that should be equivalent theoretically and show that the estimated values are indeed equivalent.

Let’s start by displaying the two models on the same table. You have to be very careful doing this (so you don’t accidentally mis-label variables), but we can re-name the variable labels for each model so that they are the same (e.g., rename aBA and bBA to both be BA) and thus display on the same row of the stargazer table. We’ll make a copy of the model results that we use specifically for this purpose. Then we’ll rename the coefficients to have the same names. Then we’ll display them using stargazer.


UN-COMMENT-OUT the following code after you estimate the Beta Model

alphaModelStargazer <- alphaModel
betaModelStargazer <- betaModel
names(alphaModelStargazer$coefficients) <- c("(Intercept)", "BA", "Masters", "PhD")
names(betaModelStargazer$coefficients) <- c("(Intercept)", "BA", "Masters", "PhD")
stargazer(alphaModelStargazer, betaModelStargazer, 
           type = "html", 
           report=('vc*p'),
           notes = "<em>&#42;p&lt;0.1;&#42;&#42;p&lt;0.05;&#42;&#42;&#42;p&lt;0.01</em>", 
           notes.append = FALSE, 
           model.numbers = FALSE, 
           column.labels = c("Alpha","Beta"))
Dependent variable:
wage
Alpha Beta
BA 22,914.380*** 22,914.380***
p = 0.000 p = 0.000
Masters 11,608.400*** 34,522.790***
p = 0.000 p = 0.000
PhD 26,331.610*** 60,854.400***
p = 0.000 p = 0.000
Constant 44,840.080*** 44,840.080***
p = 0.000 p = 0.000
Observations 24,263 24,263
R2 0.070 0.070
Adjusted R2 0.070 0.070
Residual Std. Error (df = 24259) 50,562.000 50,562.000
F Statistic (df = 3; 24259) 612.030*** 612.030***
Note: *p<0.1;**p<0.05;***p<0.01

13.4 Compare the predictions of each model

In LN4.2 we claimed that the Alpha Model and Beta Model were theoretically equivalent. Here we are demonstrating that they are also empirically equivalent. The first thing you notice is that the \(R^2\) and adjusted \(R^2\) are identical. We haven’t talked about the other statistics (Residual Std. Error and F Statistic), but they’re identical too. That’s what we would expect if the models are indeed equivalent. Recall the definition of \(R^2\). You’d expect equivalent models to explain the same fraction of the variation in wages.

Now let’s check if real-world situations that are supposed to be equivalent in theory are indeed equivalent numerically.

First, let’s store our coefficients in variables so they’re easier to work with:

## Alpha Model
a0 <- coef(alphaModel)["(Intercept)"]
a1 <- coef(alphaModel)["aBA"]
a2 <- coef(alphaModel)["aMasters"]
a3 <- coef(alphaModel)["aPhD"]

## Beta Model
## FILL THESE IN WITH COEFFICIENTS FROM YOUR BETA MODEL
b0 <- coef(betaModel)["(Intercept)"]
b1 <- coef(betaModel)["bBA"]
b2 <- coef(betaModel)["bMasters"]
b3 <- coef(betaModel)["bPhD"]

## I'll use this to let you know where you need to fill in answers
PLACEHOLDER <- "PLACEHOLDER"

We’ll check a variety of situations explored by these models. For each situation, we’ll write out the following 6 steps (if the steps aren’t clear, also look at the examples below):

  1. First, we’ll write out the situation in English (e.g., “Expected/average wage for a person whose highest degree is high school”)

  2. Next, we’ll write out the conditional expectation corresponding to that situation (e.g., \(E(wage|HS)\)) on the left hand side of the equation. To te right of the | in the E(...|...), write it out as a short description of the situation in the world (e.g., HS) instead of writing out all the variable values (e.g., BA=0, Masters=0, PhD=0).

  3. Then, on the right hand side of the equation, we’ll write out the conditional expectation in terms of model parameters using parameters from the Alpha Model (e.g., \(\alpha_0\))…

  4. …and in terms of model parameters using parameters from the Beta Model (e.g., \(\beta_0\)).

  5. Last, for the third and fourth rows on the right hand side of the equation, we’ll calculate the value estimated using the Alpha Model (e.g., 44840.08)…

  6. …and the value estimated using the Beta Model (e.g., 44840.08).

Note that we’re using format(...,nsmall=2) instead of round(...,2) to display the coefficients to two decimal places.

I’ve filled in a few parts for you below to show you what you’re supposed to do. Anyplace you see “PLACEHOLDER” you need to fill in whatever should go there. (Note that I filled in the coefficient b0 for you, but until you do the parts above, it will display as ReplaceWithCoefficientFromBetaModel.)

Use LaTex for parameters (e.g., \(\alpha_0\), \(\beta_0\)). For values, each value should be displayed to 2 decimal places using format(...,nsmall=2). Note that you can add together multiple coefficients inside the format function (e.g. format(a0 + a1,nsmall=2)). This structure is already set up for you below (i.e., you just need to replace everywhere that says PLACEHOLDER).

When you start filling it in, make sure to build frequently so you don’t mess it up and then are unable to figure out what isn’t working. For the first few, I’d build each time you make a change (remember to set it in _bookdown.yml to only build this chapter while you’re working on it, and then just build the entire book when you’re finished before committing/pushing to GitHub).

For each scenrio, your parameters in rows one and two on the right hand side should match the slides, and the values in rows three and four should be identical. If the values aren’t identical, you did something wrong.

Here are the scenarios:

Expected/average wage for a person whose highest degree is high school (HS)

\[ \begin{aligned} E(wage|HS) &= \alpha_0 \\&= \beta_0 \\&= 44840.08 \\&= 44840.08 \end{aligned} \]

Expected/average wage for a person whose highest degree is BA

\[ \begin{aligned} E(wage|BA) &= \alpha_0 + \alpha_1 \\&= \beta_0 + \beta_1 \\&= 67754.47 \\&= 67754.47 \end{aligned} \]

Expected/average wage for a person whose highest degree is Master’s

\[ \begin{aligned} E(wage|Masters) &= \alpha_0 + \alpha_1 + \alpha_2 \\&= \beta_0 + \beta_2 \\&= 79362.87 \\&= 79362.87 \end{aligned} \]

Expected/average wage for a person whose highest degree is PhD

\[ \begin{aligned} E(wage|Masters) &= \alpha_0 + \alpha_1 + \alpha_2 + \alpha_3 \\&= \beta_0 + \beta_3 \\&= 105694.48 \\&= 105694.48 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a BA compared to someone whose highest degree is high school (HS)

\[ \begin{aligned} E(wage|BA) - E(wage|HS) &= \alpha_0 + \alpha_1 - (\alpha_0) = \ alpha_1 \\&= \beta_0 + \beta_1 - (\beta_0) = \beta_1 \\&= 22914.38 \\&= 22914.38 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a Master’s compared to someone whose highest degree is high school (HS)

\[ \begin{aligned} E(wage|Masters) - E(wage|HS) &= \alpha_0 + \alpha_1 + \alpha_2 - (\alpha_0) = \alpha_1 + \alpha_2 \\&= \beta_0 + \beta_2 - (\beta_0) = \beta_2 \\&= 34522.79 \\&= 34522.79 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a PhD compared to someone whose highest degree is high school (HS)

\[ \begin{aligned} E(wage|PhD) - E(wage|HS) &= \alpha_0 + \alpha_1 + \alpha_2 + \alpha_3 - (\alpha_0) = \alpha_1 + \alpha_2 + \alpha_3 \\&= \beta_0 + \beta_3 - (\beta_0) = \beta_3 \\&= 60854.40 \\&= 60854.40 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a Master’s compared to someone whose highest degree is a BA

\[ \begin{aligned} E(wage|Masters) - E(wage|BA) &= \alpha_0 + \alpha_1 + \alpha_2 + - (\alpha_0 + \alpha_1) = \alpha_2 \\&= \beta_0 + \beta_2 - (\beta_0 + \beta_1) = \beta_2 - \beta_1 \\&= 11608.40 \\&= 11608.40 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a PhD compared to someone whose highest degree is a BA

\[ \begin{aligned} E(wage|PhD) - E(wage|BA) &= \alpha_0 + \alpha_1 + \alpha_2 + \alpha_3 - (\alpha_0 + \alpha_1) = \alpha_2 + \alpha_3 \\&= \beta_0 = \beta_3 - (\beta_0 + \beta_1) = \beta_3 - \beta_1 \\&= 37940.02 \\&= 37940.02 \end{aligned} \]

How much higher do you expect the average wage to be for someone whose highest degree is a PhD compared to someone whose highest degree is a Master’s

\[ \begin{aligned} E(wage|PhD) - E(wage|Masters) &= \alpha_0 + \alpha_1 + \alpha_2 + \alpha_3 - (\alpha_0 + \alpha_1 + \alpha_2) = \alpha_3 \\&= \beta_0 = \beta_3 - (\beta_0 + \beta_2) = \beta_3 - \beta_2 \\&= 26331.61 \\&= 26331.61 \end{aligned} \]

13.5 Other things you should think about (and possibly do)

Doing the things below (and doing them correctly) is required to get a check plus. If you don’t do them, you can still get a check.

Remember that a check plus is a 100 and a check is a 93, so the difference is small. Only do the following if you actually have time (e.g., the priority is doing well on the group projects). But if you have time, do them. It will help you better understand.

13.5.1 Group averages

Linear regression when all explanatory variables are dummy variables produces the same estimates of average values for different groups as if you simply took the average of the \(y\) value for each group. Try confirming this using group_by() and summarize() to get average wage for each education level (make sure not to save wiPUMS as grouped it still works as expected below), and then compare them with what we found above in the regressions (i.e., make sure they are the same).

Why then do we use regression? One reason is because we easily get standard errors, confidence intervals, and p-values for hypothesis tests of the group averages and differences between groups. Another reason is because we can also control for other variables (such as age…see below).

13.5.2 Causal estimates?

Make sure you can explain why we should not interpret our results as causal. For example, above we estimated that the average wage is 22914.38 higher for someone whose highest degree is a BA compared to someone whose highest degree is high school (HS). Why can we not claim that getting a BA causes this increase in wage? In other words, why is \(\hat{\alpha}_1\) (or \(\hat{\beta}_1\)) not an unbiased estimate of the true effect of getting a BA on wages?

13.5.3 What about age?

How do the results change if you control for age (i.e., add age as a variable)? You can use either the alpha or beta model and display the results side-by-side (i.e., the original model and the new model including age) using stargazer.

13.5.4 What about sex?

How might you add sex to the model?

In the data, male is coded as sex=1 and female is coded as sex=2 (see the PUMS data dictionary). Create a female dummy variable (i.e., female=1 indicates female, female=0 indicates male because that’s the only other option in the data).

Estimate two models (explained below) and display the results side-by-side using stargazer, along with the original model for comparison (either alpha or beta).

For the first model, simply add female as a variable.

For the second model, allow how education affects wages to be different for males versus females.

What does each model allow you to say about differences in wages by sex? In your answer, include specific examples using your coefficients.