Code for producing the SoJSM Employment Outlook.

# ============================================================
# 1. Install and load required packages
# ============================================================

if (!require("tidyverse"))
  install.packages("tidyverse")
if (!require("plotly"))
  install.packages("plotly")

library(tidyverse)
library(plotly)
library(scales)

options(scipen = 999)


# ============================================================
# 2. Read BLS employment projections data and filter to
#    bachelor’s-degree entry jobs with little/no experience
# ============================================================

# Data source:
# https://data.bls.gov/projections/occupationProj

AllData <- read.csv("Employment Projections.csv")

BAjobs <- AllData %>%
  filter(
    Typical.Entry.Level.Education == "Bachelor's degree" &
      Work.Experience.in.a.Related.Occupation %in% c("None", "Less than 5 years")
  )


# ============================================================
# 3. Clean and standardize wage, openings, and SOC code fields
# ============================================================

BAjobs$Pay <- as.numeric(gsub(",", "", BAjobs$Median.Annual.Wage.2023))
BAjobs$Openings <- BAjobs$Occupational.Openings..2023.2033.Annual.Average * 1000
BAjobs$Occupation.Code <- gsub('"',"",BAjobs$Occupation.Code)
BAjobs$Occupation.Code <- gsub('=',"",BAjobs$Occupation.Code)


# ============================================================
# 4. Compute overall medians for comparison lines in charts
# ============================================================

MedianPay <- median(BAjobs$Pay)
MedianOpenings <- median(BAjobs$Openings)


# ============================================================
# 5. Filter to media-related occupations using SOC codes
#    (SoJSM job list)
#    SOC reference: https://www.bls.gov/oes/current/oes_stru.htm
# ============================================================

Mediajobs <- BAjobs %>% 
  filter(Occupation.Code %in% c(
    "11-2032",
    "11-2033",
    "11-2011",
    "27-3041",
    "27-3042",
    "27-3043",
    "27-3031",
    "27-3023",
    "27-1024",
    "15-1254",
    "15-1255",
    "27-2012",
    "27-3011",
    "27-4032",
    "27-4031"
  ))


# ============================================================
# 6. Shorten occupation titles for cleaner chart labels
# ============================================================

Mediajobs$Job <- str_split_i(Mediajobs$Occupation.Title,"    *", 1)


# ============================================================
# 7. Prepare data for pay comparison chart (jobs vs median)
# ============================================================

Job <- "Median"
Pay <- MedianPay
PayLine <- data.frame(Job,Pay)

PayChart <- Mediajobs %>% 
  select(Job,Pay) %>% 
  rbind(PayLine) %>% 
  arrange(desc(Pay))


# ============================================================
# 8. Prepare data for openings comparison chart (jobs vs median)
# ============================================================

Job <- "Median"
Openings <- MedianOpenings
OpeningsLine <- data.frame(Job,Openings)

OpeningsChart <- Mediajobs %>% 
  select(Job,Openings) %>% 
  rbind(OpeningsLine) %>% 
  arrange(desc(Openings))


# ============================================================
# 9. Remove intermediate objects to clean workspace
# ============================================================

rm(AllData,
   BAjobs,
   Mediajobs,
   OpeningsLine,
   PayLine,
   Job,
   MedianOpenings,
   MedianPay,
   Openings,
   Pay)


# ============================================================
# 10. Create horizontal bar chart of median pay by job
# ============================================================

PayChart <- PayChart %>% 
  mutate(Dollars = dollar(Pay))

PayFig <- PayChart %>% 
  mutate(Job = forcats::fct_reorder(Job, Pay)) %>% 
  ggplot(aes( x = Pay, y = Job)) +
  geom_col(aes(fill = Job != "Median")) +
  scale_fill_manual(values = c("#FF7F3E","#384B70"), guide = "none")+
  geom_text(aes(label = Dollars),
            color = "white",
            hjust = 1, nudge_x = -.1) +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.title.y = element_blank(),
    panel.background = element_blank())

PayFig


# ============================================================
# 11. Create horizontal bar chart of annual openings (in thousands)
# ============================================================

OpeningsChart <- OpeningsChart %>% 
  mutate(Openings = Openings / 1000)

OpeningsFig <- OpeningsChart %>% 
  mutate(Job = forcats::fct_reorder(Job, Openings)) %>% 
  ggplot(aes( x = Openings, y = Job,)) +
  geom_col(aes(fill = Job != "Median")) +
  scale_fill_manual(values = c("#FF7F3E","#384B70"), guide = "none")+
  geom_text(aes(label = Openings),
            color = "white",
            hjust = 1, nudge_x = -.1) +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.title.y = element_blank(),
    panel.background = element_blank())

OpeningsFig