Project 2 Tidy Data

Introduction

When taking in the data, we have to change the encoding style to be able to read it like any other csv. In addition, the dataset we are working with has several blank pieces of data that need to be removed. When performing an analysis we should break it up by demographics and look at subsection of those demographics. In this instance, we are looking at Age, Education, and Income. Within those categories we are given age ranges, education levels, and yearly income.

library(tidyverse)

raw <- read_csv(
  "https://raw.githubusercontent.com/Keenan-Roe/DATA-607/main/Project%202/employ2.csv",
  col_names = FALSE,
  col_types = cols(.default = col_character()),
  locale = locale(encoding = "windows-1252"),
  skip_empty_rows = FALSE
)

#from the start of data in the col to the end of data in the column 
start <- which(raw$X1 == "Total")[1]
end   <- which(str_detect(raw$X1, "^\\*\\*"))[1] - 1

#trim out all empty data and rename rows 
body <- raw[start:end, 1:10]
names(body) <- c("label", "Total", "emp_total", "emp_Gov", "emp_private", "emp_nonprofit", "emp_self", "emp_not_reported", "unemp", "unemp_not_reported")

employment_column <- c("Employed", "Total", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "No", "No")

type_column <- c("All Serveyed", "All", "Total", "Government", "Private", "NonProfit", "Self_Employed", "Employed_Unknown", "Unemployed", "Unemployed_Unknown") 

# create 3 datasets that are identicaly shaped for easy transforms 
age <- rbind(
  employment_column,
  type_column,
  body |> slice(3:6)
)

education <- rbind( 
  employment_column, 
  type_column, 
  body |> slice(21:23))

income <- rbind( 
  employment_column, 
  type_column,
  body |> slice(39:43))

age_long <- as.data.frame(t(age)) |> 
  remove_rownames() |> 
  rename_with(~ age$label) |> 
  slice(-1) 

education_long <- as.data.frame(t(education)) |> 
  remove_rownames() |>
  rename_with(~ education$label) |>
  slice(-1) 


income_long <- as.data.frame(t(income)) |> 
  remove_rownames() |>
  rename_with(~ income$label) |> 
  slice(-1)

Wide to long

Now that we have the 3 demographics we will be looking at in the exact same formatting we can create a function that will be able to transform each of them in to a clean longer style dataset giving us the opportunity to use pivot longer and put them back together to analyse each category together. We will also need to convert the text numbers into real numbers to perform some basic analysis.

read_one <- function(path, dimension) {
  read_csv(path, col_types = cols(.default = "c")) |>
    select(-1) |>                                  
    rename(employed = Employed, category = `All Serveyed`) |>
    pivot_longer(-c(employed, category),
                 names_to = "group", values_to = "n") |>
    mutate(n = parse_number(n),                    
           group = factor(group, levels = unique(group)),
           dimension = dimension)
}

Clean Data Aggregation

Using our function, we will create different datasets and output some basic summary statistics. This does not help us glean much information so we will move on to analysis and plot creation to get an idea of the trends we can identify in the data.

age       <- read_one("age_long.csv",       "Age")
education <- read_one("education_long.csv", "Education")
income    <- read_one("income_long.csv",    "Income")

all_data <- bind_rows(age, education, income) |>
  mutate(group = fct_inorder(as.character(group)))

glimpse(all_data)
Rows: 108
Columns: 5
$ employed  <chr> "Total", "Total", "Total", "Total", "Yes", "Yes", "Yes", "Ye…
$ category  <chr> "All", "All", "All", "All", "Total", "Total", "Total", "Tota…
$ group     <fct> "18 - 34", "35 - 49", "50 - 64", "65 and above", "18 - 34", …
$ n         <dbl> 74040588, 64578630, 60081266, 62834010, 51357981, 51286243, …
$ dimension <chr> "Age", "Age", "Age", "Age", "Age", "Age", "Age", "Age", "Age…
summary(all_data$n)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
   110129   1898868   6598528  17887954  27179994 105894822 

Analysis

Now that we have the data in a clean data format, we can start looking at it proportionally. With our previously created status of employment indicators we can break this up into yes and no indicators to create a proportion of employment between each category. We can then present the results in different charts showcasing the proportionality.

emp_rate <- all_data |>
  filter((employed == "Yes" & category == "Total") |
           (employed == "Total" & category == "All")) |>
  select(dimension, group, category, n) |>
  pivot_wider(names_from = category, values_from = n) |>
  rename(employed = Total, surveyed = All) |>
  mutate(emp_rate = employed / surveyed)

print(emp_rate, n = Inf)
# A tibble: 12 × 5
   dimension group                            surveyed employed emp_rate
   <chr>     <fct>                               <dbl>    <dbl>    <dbl>
 1 Age       18 - 34                          74040588 51357981    0.694
 2 Age       35 - 49                          64578630 51286243    0.794
 3 Age       50 - 64                          60081266 40451416    0.673
 4 Age       65 and above                     62834010 11246569    0.179
 5 Education High school graduate or less    105894822 51858168    0.490
 6 Education Some college/associate's degree  58760346 34454796    0.586
 7 Education Bachelor's degree or higher      96879326 68029245    0.702
 8 Income    Less than $35,000                38889164 14015531    0.360
 9 Income    $35,000 to $74,999               59640818 33007013    0.553
10 Income    $75,000 to $99,999               31787579 20943449    0.659
11 Income    $100,000 to $149,999             38944777 26875838    0.690
12 Income    $150,000 and over                51445375 40819427    0.793
sector_share <- all_data |>
  filter(employed == "Yes", category != "Total") |>
  group_by(dimension, group) |>
  mutate(share = n / sum(n)) |>
  ungroup()

sector_share |>
  select(dimension, group, category, share) |>
  mutate(share = round(share, 3)) |>
  pivot_wider(names_from = category, values_from = share) |>
  print(n = Inf)
# A tibble: 12 × 7
   dimension group   Government Private NonProfit Self_Employed Employed_Unknown
   <chr>     <fct>        <dbl>   <dbl>     <dbl>         <dbl>            <dbl>
 1 Age       18 - 34      0.123   0.655     0.106         0.092            0.023
 2 Age       35 - 49      0.144   0.599     0.105         0.137            0.015
 3 Age       50 - 64      0.168   0.561     0.088         0.161            0.022
 4 Age       65 and…      0.08    0.471     0.115         0.289            0.045
 5 Education High s…      0.096   0.614     0.053         0.201            0.037
 6 Education Some c…      0.134   0.664     0.08          0.108            0.014
 7 Education Bachel…      0.174   0.553     0.15          0.109            0.014
 8 Income    Less t…      0.062   0.476     0.129         0.257            0.077
 9 Income    $35,00…      0.15    0.629     0.086         0.126            0.008
10 Income    $75,00…      0.14    0.574     0.069         0.211            0.005
11 Income    $100,0…      0.156   0.565     0.12          0.138            0.022
12 Income    $150,0…      0.153   0.619     0.115         0.103            0.009
ggplot(emp_rate, aes(group, emp_rate)) +
  geom_col(fill = "steelblue") +
  geom_text(aes(label = scales::percent(emp_rate, accuracy = 1)), vjust = -0.3) +
  labs(title = "Employment rate by group", x = NULL, y = "Share employed") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

ggplot(sector_share, aes(group, share, fill = category)) +
  geom_col() +
  facet_wrap(~dimension, scales = "free_x") +
  scale_y_continuous(labels = scales::percent) +
  labs(title = "Type of employment among the employed",
      x = NULL, y = "Share of employed", fill = NULL) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

Some of the trends represented in the data are somewhat surprising. The total employment rate is given to be 0.5901409 which does not represent the actual unemployment rate. The unemployment rate only covers the porportion of hte population that is eligible to work and is not able to or choose not to work while this will take into account all individuals that do not work regardless of status. We can see that even within retirement age, we still have 18% employment rate while the peak employment rate is seen between 35 and 49. Moving onto education, we see that employment rates increase steadily with the level of education achieved. Finally, the employment rate for those with income increases as income becomes higher as one would expect.

When looking at the types of employment we see a slight uniformity across the board with a few noticeable exceptions. Self-employment gets much more popular in later years of life peaking past 25% in the 65 and above category while many at that age choose to retire from government position. We can see that non-profit work is far more common among those with higher education degrees as well while self-employment is slightly more common among those that chose to stop at a high school degree or less.

Conclusion

It is very difficult to spot clear trends in large datasets like this when breaking down the groups in such broad terms. Much more specific descriptions would be needed to determine habits of groups and what environmental pressures lead them to make these decisions. It is easy to say that government workers retired at 65 for their pension, but that still leaves us wondering what pressures those who chose to stay were experiencing. This data should be in line with even a layman’s understanding of employment trends. The next important step would be statistics that help us identify motivations in the demographics themselves.