What Makes a College Worth It?

Author

George Bothos

Cornell University campus

Image: Cornell University Arts Quad. Photograph by Tomwsulcer, Wikimedia Commons, CC0.

Introduction and Dataset Background

For this project, what I decided to do was explore how the different college characteristics actually relate to the future earnings of the students. I’m currently going through the college application process myself, and I thought it would be quite interesting to take a look at the factors beyond the basic ones, such as rankings and admission rates. I wanted to really examine how the different colleges compare in terms of the student outcomes.

The dataset that I used was the College Scorecard from the U.S. Department of Education. This dataset contained 690 colleges and 20 different variables. It included quantitative variables such as average SAT scores, tuition and faculty salaries, Pell Grant percentages, and median earnings 10 years after students enter college. In addition, it also had categorical variables, which were things like the institution type, geographical region, and state, along with the latitude and longitude coordinates.

The original dataset was filtered so that it would focus on currently operating public and private nonprofit institutions that primarily award bachelor’s degrees, have at least 1,500 undergraduate students, and report information about both SAT scores and earnings. I also went and took a look at the missing values, and what I found was that one college was missing its average faculty salary. My regression analysis ended up having 689 observations.

The goal I have is to investigate the different college characteristics that are associated with higher median earnings and then explore how these relationships are different across the institutions and geographical locations. I plan to use descriptive statistics, multiple linear regression, and visualizations so I can better understand these patterns.

Loading and Exploring the College Scorecard Dataset

# Loading the packages for data analysis and visualization
library(tidyverse)

# Importing the approved College Scorecard dataset
colleges <- readr::read_csv("college_scorecard_690.csv")

# Previewing the dataset and examining its structure
head(colleges)
# A tibble: 6 × 20
  UNITID INSTNM    CITY  STABBR CONTROL REGION LOCALE LATITUDE LONGITUDE PREDDEG
   <dbl> <chr>     <chr> <chr>    <dbl>  <dbl>  <dbl>    <dbl>     <dbl>   <dbl>
1 100654 Alabama … Norm… AL           1      5     12     34.8     -86.6       3
2 100663 Universi… Birm… AL           1      5     12     33.5     -86.8       3
3 100706 Universi… Hunt… AL           1      5     12     34.7     -86.6       3
4 100724 Alabama … Mont… AL           1      5     12     32.4     -86.3       3
5 100751 The Univ… Tusc… AL           1      5     12     33.2     -87.5       3
6 100830 Auburn U… Mont… AL           1      5     12     32.4     -86.2       3
# ℹ 10 more variables: CURROPER <dbl>, UGDS <dbl>, SAT_AVG <dbl>,
#   ADM_RATE <dbl>, PCTPELL <dbl>, AVGFACSAL <dbl>, TUITIONFEE_OUT <dbl>,
#   GRAD_DEBT_MDN <dbl>, C150_4 <dbl>, MD_EARN_WNE_P10 <dbl>
names(colleges)
 [1] "UNITID"          "INSTNM"          "CITY"            "STABBR"         
 [5] "CONTROL"         "REGION"          "LOCALE"          "LATITUDE"       
 [9] "LONGITUDE"       "PREDDEG"         "CURROPER"        "UGDS"           
[13] "SAT_AVG"         "ADM_RATE"        "PCTPELL"         "AVGFACSAL"      
[17] "TUITIONFEE_OUT"  "GRAD_DEBT_MDN"   "C150_4"          "MD_EARN_WNE_P10"
dim(colleges)
[1] 690  20
str(colleges)
spc_tbl_ [690 × 20] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ UNITID         : num [1:690] 100654 100663 100706 100724 100751 ...
 $ INSTNM         : chr [1:690] "Alabama A & M University" "University of Alabama at Birmingham" "University of Alabama in Huntsville" "Alabama State University" ...
 $ CITY           : chr [1:690] "Normal" "Birmingham" "Huntsville" "Montgomery" ...
 $ STABBR         : chr [1:690] "AL" "AL" "AL" "AL" ...
 $ CONTROL        : num [1:690] 1 1 1 1 1 1 1 2 1 1 ...
 $ REGION         : num [1:690] 5 5 5 5 5 5 5 5 5 5 ...
 $ LOCALE         : num [1:690] 12 12 12 12 12 12 13 12 23 43 ...
 $ LATITUDE       : num [1:690] 34.8 33.5 34.7 32.4 33.2 ...
 $ LONGITUDE      : num [1:690] -86.6 -86.8 -86.6 -86.3 -87.5 ...
 $ PREDDEG        : num [1:690] 3 3 3 3 3 3 3 3 3 3 ...
 $ CURROPER       : num [1:690] 1 1 1 1 1 1 1 1 1 1 ...
 $ UGDS           : num [1:690] 6124 11635 6591 3477 33227 ...
 $ SAT_AVG        : num [1:690] 938 1258 1319 976 1285 ...
 $ ADM_RATE       : num [1:690] 0.58 0.882 0.686 0.976 0.766 ...
 $ PCTPELL        : num [1:690] 0.642 0.389 0.278 0.72 0.215 ...
 $ AVGFACSAL      : num [1:690] 8699 12612 10639 8153 11419 ...
 $ TUITIONFEE_OUT : num [1:690] 19318 23684 27148 19576 35342 ...
 $ GRAD_DEBT_MDN  : num [1:690] 27204 21250 20550 28197 21500 ...
 $ C150_4         : num [1:690] 0.277 0.661 0.625 0.307 0.737 ...
 $ MD_EARN_WNE_P10: num [1:690] 41266 59631 72098 40585 66081 ...
 - attr(*, "spec")=
  .. cols(
  ..   UNITID = col_double(),
  ..   INSTNM = col_character(),
  ..   CITY = col_character(),
  ..   STABBR = col_character(),
  ..   CONTROL = col_double(),
  ..   REGION = col_double(),
  ..   LOCALE = col_double(),
  ..   LATITUDE = col_double(),
  ..   LONGITUDE = col_double(),
  ..   PREDDEG = col_double(),
  ..   CURROPER = col_double(),
  ..   UGDS = col_double(),
  ..   SAT_AVG = col_double(),
  ..   ADM_RATE = col_double(),
  ..   PCTPELL = col_double(),
  ..   AVGFACSAL = col_double(),
  ..   TUITIONFEE_OUT = col_double(),
  ..   GRAD_DEBT_MDN = col_double(),
  ..   C150_4 = col_double(),
  ..   MD_EARN_WNE_P10 = col_double()
  .. )
 - attr(*, "problems")=<pointer: 0x00000180a94eefb0> 

Data Cleaning and Filtering

As I wrote above, the original College Scorecard dataset was filtered to include institutions that primarily award bachelor’s degrees, are currently operating, are public or private nonprofit, have at least 1,500 undergraduate students, and report both average SAT scores and median earnings. I also excluded institutions that were in outlying U.S. territories, and after I applied all of these criteria, the dataset ended up containing 690 colleges.

That is when I decided to then check the remaining variables for any missing values. I did find one institution that was missing its average faculty salary, so I excluded that observation from my regression analysis rather than trying to replace the missing value. This left me with 689 colleges available for my model that I was creating.

# Checking the original inclusion criteria in the approved dataset
eligible_colleges <- colleges %>%
  filter(PREDDEG == 3,
         CURROPER == 1,
         CONTROL %in% c(1, 2),
         REGION != 9,
         UGDS >= 1500,
         !is.na(SAT_AVG),
         !is.na(MD_EARN_WNE_P10))

# Confirming how many colleges satisfy these criteria
nrow(eligible_colleges)
[1] 690
# Filtering colleges to include complete regression variables
regression_colleges <- colleges %>%
  filter(!is.na(MD_EARN_WNE_P10),
         !is.na(SAT_AVG),
         !is.na(PCTPELL),
         !is.na(AVGFACSAL),
         !is.na(TUITIONFEE_OUT))

# Checking how many observations remain for regression
nrow(regression_colleges)
[1] 689

Exploring Quantitative Variables and Missing Values

# Summarizing the main quantitative variables for our analysis
summary(colleges[, c("MD_EARN_WNE_P10", "SAT_AVG",
                     "PCTPELL", "AVGFACSAL", "TUITIONFEE_OUT")])
 MD_EARN_WNE_P10     SAT_AVG        PCTPELL         AVGFACSAL    
 Min.   : 37551   Min.   : 880   Min.   :0.0927   Min.   : 3147  
 1st Qu.: 56341   1st Qu.:1092   1st Qu.:0.2300   1st Qu.: 8565  
 Median : 64243   Median :1190   Median :0.3225   Median :10071  
 Mean   : 67866   Mean   :1210   Mean   :0.3361   Mean   :10626  
 3rd Qu.: 77120   3rd Qu.:1297   3rd Qu.:0.4085   3rd Qu.:12016  
 Max.   :152553   Max.   :1560   Max.   :0.8402   Max.   :25354  
                                                  NAs    :1      
 TUITIONFEE_OUT 
 Min.   : 6888  
 1st Qu.:21137  
 Median :34055  
 Mean   :37515  
 3rd Qu.:50931  
 Max.   :75162  
                
# Counting missing values in each column
colSums(is.na(colleges))
         UNITID          INSTNM            CITY          STABBR         CONTROL 
              0               0               0               0               0 
         REGION          LOCALE        LATITUDE       LONGITUDE         PREDDEG 
              0               0               0               0               0 
       CURROPER            UGDS         SAT_AVG        ADM_RATE         PCTPELL 
              0               0               0               0               0 
      AVGFACSAL  TUITIONFEE_OUT   GRAD_DEBT_MDN          C150_4 MD_EARN_WNE_P10 
              1               0               0               0               0 

Exploring Categorical Variables

# Counting colleges by institution type
table(colleges$CONTROL)

  1   2 
357 333 
# Counting colleges by geographical region
table(colleges$REGION)

  1   2   3   4   5   6   7   8 
 70 152 104  62 187  58  18  39 
# Checking how many states are represented
length(unique(colleges$STABBR))
[1] 49
# Checking the most frequently represented states
sort(table(colleges$STABBR), decreasing = TRUE)[1:10]

NY PA TX MA OH FL GA IL NC VA 
69 51 41 35 26 25 23 23 23 21 

Exploring the Distribution of College Earnings

# Creating a histogram of median earnings across colleges
ggplot(colleges, aes(x = MD_EARN_WNE_P10)) +
  geom_histogram(binwidth = 10000, fill = "steelblue",
                 color = "white") +
  theme_minimal() +
  labs(
    title = "Distribution of Median Earnings Across Colleges",
    x = "Median Earnings 10 Years After Entry ($)",
    y = "Number of Colleges",
    caption = "Source: U.S. Department of Education, College Scorecard"
  )

Comparing Earnings by College Type

# Creating readable labels for public and private nonprofit colleges
colleges <- colleges %>%
  mutate(college_type = factor(CONTROL,
                              levels = c(1, 2),
                              labels = c("Public", "Private Nonprofit")))

# Comparing earnings distributions across college types
ggplot(colleges, aes(x = college_type, y = MD_EARN_WNE_P10,
                     fill = college_type)) +
  geom_boxplot() +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
  labs(
    title = "Median Earnings by College Type",
    x = "College Type",
    y = "Median Earnings 10 Years After Entry ($)",
    fill = "College Type",
    caption = "Source: U.S. Department of Education, College Scorecard"
  )

Examining Correlations Between Quantitative Variables

# Selecting the quantitative variables for our regression analysis
regression_vars <- colleges %>%
  select(MD_EARN_WNE_P10, SAT_AVG, PCTPELL,
         AVGFACSAL, TUITIONFEE_OUT)

# Calculating correlations between the selected variables
cor(regression_vars, use = "pairwise.complete.obs")
                MD_EARN_WNE_P10    SAT_AVG    PCTPELL  AVGFACSAL TUITIONFEE_OUT
MD_EARN_WNE_P10       1.0000000  0.7919288 -0.5554074  0.7718899      0.7281464
SAT_AVG               0.7919288  1.0000000 -0.6998781  0.6894231      0.7871921
PCTPELL              -0.5554074 -0.6998781  1.0000000 -0.3811864     -0.5667173
AVGFACSAL             0.7718899  0.6894231 -0.3811864  1.0000000      0.5399948
TUITIONFEE_OUT        0.7281464  0.7871921 -0.5667173  0.5399948      1.0000000

Multiple Linear Regression Analysis

# Building a multiple linear regression model to predict median earnings
earnings_model <- lm(MD_EARN_WNE_P10 ~ SAT_AVG + PCTPELL +
                       AVGFACSAL + TUITIONFEE_OUT,
                     data = regression_colleges)

# Displaying the regression coefficients and model statistics
summary(earnings_model)

Call:
lm(formula = MD_EARN_WNE_P10 ~ SAT_AVG + PCTPELL + AVGFACSAL + 
    TUITIONFEE_OUT, data = regression_colleges)

Residuals:
   Min     1Q Median     3Q    Max 
-31505  -4794   -995   4059  35384 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     6.724e+03  5.163e+03   1.303  0.19318    
SAT_AVG         2.295e+01  4.666e+00   4.918  1.1e-06 ***
PCTPELL        -1.050e+04  3.379e+03  -3.107  0.00197 ** 
AVGFACSAL       2.581e+00  1.524e-01  16.935  < 2e-16 ***
TUITIONFEE_OUT  2.524e-01  2.776e-02   9.092  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 8317 on 684 degrees of freedom
Multiple R-squared:  0.7578,    Adjusted R-squared:  0.7564 
F-statistic:   535 on 4 and 684 DF,  p-value: < 2.2e-16

Regression Results and Interpretation

I used multiple linear regression to take a look at and examine how SAT score, Pell Grant percentages, average faculty salaries, and out-of-state tuition really relate to the median earnings 10 years after the student enters the college. I chose and selected these variables because they each showed meaningful correlations with earnings, and they represented different characteristics of the colleges.

The regression model was:

Predicted Earnings = 6,724 + 22.95(SAT_AVG) − 10,500(PCTPELL) + 2.581(AVGFACSAL) + 0.2524(TUITIONFEE_OUT)

The adjusted R-squared value came out to be 0.7564. What this means is that the model explained approximately 75.6% of the variation in the median earnings across the different colleges that I included in this regression. All four of the independent variables had p-values that were below 0.05, and what this suggests statistically is that there are significant associations with earnings after we account for the other variables.

The average faculty salary is measured monthly, and so the model suggested that if there was an additional $1,000 in monthly faculty salary, it would be associated with approximately $2,581 higher median earnings (while holding the other variables constant).

We saw that the average SAT scores, faculty salaries, and tuition all had positive relationships with earnings, while the Pell Grant percentages, on the other hand, had a negative relationship with it. Something I also noticed is that the SAT scores and tuition were strongly correlated with one another, which means some of the predictors may overlap in the information they provide.

As I mentioned earlier, there was one college that was excluded from the regression because it was missing the average faculty salary. That left us with 689 observations. Although these results did show us evidence of relationships between institutional characteristics and earnings, we cannot say that they establish causation.

Interactive Visualization: SAT Scores and College Earnings

# Loading Plotly to create an interactive visualization
library(plotly)

# Creating a scatterplot comparing SAT scores and median earnings
sat_plot <- ggplot(colleges, aes(
  x = SAT_AVG,
  y = MD_EARN_WNE_P10,
  color = college_type,
  text = INSTNM
)) +
  geom_point(alpha = 0.7, size = 2) +
  scale_color_brewer(palette = "Set1") +
  theme_minimal() +
  labs(
    title = "Average SAT Scores and College Earnings",
    subtitle = "Comparing public and private nonprofit colleges",
    x = "Average SAT Score",
    y = "Median Earnings 10 Years After Entry ($)",
    color = "College Type",
    caption = "Source: U.S. Department of Education, College Scorecard"
  )

# Making the graph interactive with college names on hover
ggplotly(sat_plot, tooltip = c("text", "x", "y", "colour"))

Source: U.S. Department of Education, College Scorecard.

This interactive scatterplot compares average SAT scores and median earnings across public and private nonprofit colleges. Hovering over a point displays the institution’s name and corresponding values.

Geographical Visualization of College Earnings

# Loading the package used for U.S. state boundaries
library(maps)

# Preparing the map boundaries and college locations
us_states <- map_data("state")

map_colleges <- colleges %>%
  filter(!STABBR %in% c("AK", "HI"))

# Creating a map showing college locations and earnings
ggplot() +
  geom_polygon(data = us_states,
               aes(x = long, y = lat, group = group),
               fill = "gray95", color = "gray75") +
  geom_point(data = map_colleges,
             aes(x = LONGITUDE, y = LATITUDE,
                 color = MD_EARN_WNE_P10),
             size = 2, alpha = 0.8) +
  scale_color_viridis_c(labels = scales::label_dollar()) +
  coord_quickmap() +
  annotate("label", x = -112, y = 26,
           label = "684 colleges shown\nAlaska and Hawaii excluded",
           size = 3, fill = "white", alpha = 0.9) +
  theme_minimal() +
  labs(
    title = "College Locations and Median Earnings Across the U.S.",
    subtitle = "Colleges colored by median earnings 10 years after entry",
    x = "Longitude",
    y = "Latitude",
    color = "Median Earnings",
    caption = "Source: U.S. Department of Education, College Scorecard"
  )

Visualization Results and Interpretation

The interactive scatterplot that I created is comparing the average SAT scores and the median earnings 10 years after students enter the college. Each point on it represents a different college, and the colors distinguish between the public and the private nonprofit institutions. When you hover over the points, you are able to identify the individual colleges and examine the reported values.

A pattern that really stood out to me was the positive relationship that existed between the average SAT scores and earnings. Colleges that had higher average SAT scores were generally reporting higher median earnings. Something else I noticed is that private nonprofit colleges did have a wider range of earnings, which also did include several institutions with particularly high values.

The geographical map is showing where the colleges in my dataset are actually located when looking across the contiguous United States. Each different point that you see is also representing a college, and its color is indicating median earnings. Something I noticed here is that colleges with higher reported earnings appear in several parts of the country, particularly, though, around the Northeast and some major metropolitan areas. However, the map is showing that earnings can really vary considerably, even when looking within the same geographical region.

One limitation of my analysis is that the earnings data reflect students who entered college around 2011-2013, with their earnings then being measured 10 years after, in 2022-2023. Some of my other college characteristics actually come from more recent years, so the years from which each data characteristic is being taken vary. The regression I made also is not really accounting for every factor that could influence earnings, such as the students’ majors, career choices, or local cost of living. If I were to expand this project, I would most likely explore how these additional factors I mentioned, as well as some other ones, might affect the results that I got.

Overall, the visualizations that I created definitely do help demonstrate that institutional characteristics and geographical location are associated with students’ future earnings. They do not establish, however, that attending a particular college causes higher earnings.

References

Data Source

U.S. Department of Education. College Scorecard: Most Recent Institution-Level Data. https://collegescorecard.ed.gov/data/

Image Source

Tomwsulcer. Cornell University campus quadrangle. Wikimedia Commons, CC0 1.0. https://commons.wikimedia.org/wiki/File:Campus_quadrangle_and_grass_and_trees_at_Cornell_University.jpg

Coding and Documentation Resources

Posit. ggplot2 documentation. https://ggplot2.tidyverse.org/

Plotly. Interactive visualizations in R. https://plotly.com/r/

AI Assistance

I used ChatGPT and Claude for help troubleshooting errors, including understanding why they happened and as a guide to better understand how to fix them, and for reviewing my analysis to make sure everything made sense. I wanted another look at my work so I could be more confident in it.