library(tidyverse)
library(RPostgreSQL)
## Warning: package 'RPostgreSQL' was built under R version 4.0.3
library(DBI)
This project is a group project with the following members:
Formal citation for data source used:
JobsPikr. (2019; December). 10000 Data Scientist Job Postings from the USA, Version 1. Retrieved 15 October 2020 from https://www.kaggle.com/jobspikr/data-scientist-job-postings-from-the-usa.
A Google Cloud Postgres database was created for this project to maximize reproducibility. The below code loads the data from the normalized database and converts it into data frames that can be used for further analysis.
This required (1) setting up a Postgres database; (2) creating a storage bucket; (3) uploading the raw data to the storage bucket; (4) migrating that raw data into the new database, which consists of two tables in the first normal form 1NF; and (5) opening up the firewall to allow public access. Per the rules for 1NF, each table cell should contain a single value and each record needs to be unique. Both of these conditions are satisfied and thus the data can be considered normalized.
# Configure database connection
db <- "postgres"
host_db <- "35.243.187.235"
db_port <- "5432"
# Using public account created with read only access
db_user <- "project3public"
db_password <- "cunymsds2020"
con <- dbConnect(RPostgres::Postgres(), dbname = db, host=host_db, port=db_port, user=db_user, password=db_password)
# Verify the connection to the database is working
dbListTables(con)
## [1] "jobs" "skills"
# Get job posting data
jobdata_raw <-
dbGetQuery(con, "SELECT job_title, job_description, category, company, city, state, job_board, post_date FROM jobs")
# Get skills data
skills_df <- dbGetQuery(con, "SELECT skill_name FROM skills")
# Disconnect from the database
dbDisconnect(con)
With the data now loaded into a data frame, it needs to be prepared for analysis.
Main rules of ‘tidyness’:
Following these rules we end up with a very wide data set in this instance.
# Start with a bit of exploratory data analysis
glimpse(jobdata_raw)
## Rows: 10,002
## Columns: 8
## $ job_title <chr> "Enterprise Data Scientist I", "Data Scientist", "D...
## $ job_description <chr> "Read what people are saying about working here. \n...
## $ category <chr> "Accounting/Finance", NA, NA, "Accounting/Finance",...
## $ company <chr> "Farmers Insurance Group", "Luxoft USA Inc", "Cinci...
## $ city <chr> "Woodland hills", "Middletown", "New york", "New yo...
## $ state <chr> "California", "New jersey", "New york", "New york",...
## $ job_board <chr> "indeed", "dice", "dice", "indeed", "monster", "ind...
## $ post_date <date> 2019-02-06, 2019-02-05, 2019-02-05, 2019-02-06, 20...
# Summary of job postings by site
jobdata_raw %>%
select(job_board) %>%
group_by(job_board) %>%
summarise(num_postings = n()) %>%
ggplot(aes(x = job_board, y = num_postings)) +
geom_bar(stat = 'identity')
## `summarise()` ungrouping output (override with `.groups` argument)
Prepare the data frame to capture the data needed.
# Copy the raw data into a new data frame to preserve the original
jobdata_tidy <- jobdata_raw
# Create wide data frame with new columns and blank based on each skill name
for (i in 1:nrow(skills_df)){
tempvar = skills_df$skill_name[i]
jobdata_tidy[[tempvar]] <- ""
}
Now that the data frame is in the intended format, with the key columns created, we will search through each job listing to identify whether each skill is listed, then add it to a count in the columns created.
The first for loop iterates through every row of the job postings data frame (jobdata_tidy), and then the nested for loop checks for each skill in the skills data frame (skills_df) to see if it is in the text of the description of the job posting. If it is there, the column is marked with a “yes” otherwise a “no”. Numbers were used rather than “yes” and “no” to facilitate analysis.
Note: It takes between 1 and 2 minutes to run
# Nested for loop to check each description field for each skill, then mark the relevant skill column accordingly
for (j in 1:nrow(jobdata_tidy)){
for (k in 1:nrow(skills_df)){
tempvar2 <- skills_df[k, 1]
tempregexstring <- tolower(paste0("\\s", tempvar2, "\\s|\\s",tempvar2,"[++][++]]\\s"))
ifelse((str_detect(tolower(jobdata_tidy$job_description[j]), tempregexstring, negate = FALSE)) == TRUE,
jobdata_tidy[j, tempvar2] <- "yes",
jobdata_tidy[j, tempvar2] <- "no")
}
}
With the data calculated and populated, a new data frame will be created in a longer format for easier analysis.
# Create long data frame with key data for easier analysis
skillsdata_long <- jobdata_tidy %>%
select(9:54) %>%
gather ("skill_name", "exists", 1:46) %>%
filter(exists == "yes")
[INSERT TEXT HERE TO DESCRIBE ANALYSIS]
skillssummary <- skillsdata_long %>%
group_by(skill_name) %>%
summarise(num_postings = n())
# Visualize in descending order the popularity of all skills identified
skillssummary %>%
ggplot(aes(x = reorder(skill_name, num_postings), y = num_postings)) +
geom_bar(stat = 'identity') +
coord_flip()
In conclusion, based on a consolidated list of 10,000 data science job postings from a variety of websites, the top 10 data science skills are:
top_n(arrange(skillssummary, desc(num_postings)), 10)
## Selecting by num_postings
## # A tibble: 10 x 2
## skill_name num_postings
## <chr> <int>
## 1 Python 3832
## 2 SQL 3370
## 3 R 2197
## 4 Spark 1139
## 5 Hadoop 1063
## 6 AWS 1034
## 7 Tableau 843
## 8 SAS 800
## 9 NoSQL 586
## 10 Azure 505