Load packages
library(openxlsx)
library(plyr)
library(tidyverse)
library(lubridate)
library(fs)
library(zoo)
To match the data availability from US Census, we will extract language enrollments data from the Modern Language Association database as follows:
# Load the data
MLA <- read.xlsx("https://www.mla.org/content/download/191323/file/Historical-language-enrollments-1958-2021.xlsx")
# Check column names
colnames(MLA)
# Create a data frame for total enrollments for each census
totl_lan <- MLA %>%
filter(TERM == "Fall") %>%
dplyr::rename(Year = SRVY_YEAR) %>%
dplyr::group_by(Year) %>%
dplyr::summarise(Totl_Lan = sum(ALL.LEVEL.TOTAL, na.rm = TRUE)) %>%
dplyr::select(Year, Totl_Lan)
totl_lan <- totl_lan[!(totl_lan$Year == 2020), ]
# Select needed languages and features
enrollments <- MLA %>%
filter(grepl("CHINESE|MANDARIN|JAPANESE|KOREAN|FILIPINO|HINDI|VIETNAMESE", LANGUAGE)) %>%
filter(TERM == "Fall") %>%
dplyr::rename(Year = SRVY_YEAR, Language = LANGUAGE) %>%
select(Year, Language, ALL.LEVEL.TOTAL)
summary(enrollments)
## Year Language ALL.LEVEL.TOTAL
## Min. :1958 Length:19312 Min. : 0.00
## 1st Qu.:1995 Class :character 1st Qu.: 14.00
## Median :2006 Mode :character Median : 32.00
## Mean :2003 Mean : 67.66
## 3rd Qu.:2016 3rd Qu.: 77.00
## Max. :2021 Max. :6620.00
## NA's :53
# Change data types
enrollments <- enrollments %>%
mutate(Language = as.factor(Language))
# Check all language course names
unique(enrollments$Language)
## [1] CHINESE JAPANESE
## [3] MANDARIN KOREAN
## [5] JAPANESE, CLASSICAL FILIPINO/PILIPINO/TAGALOG
## [7] HINDI VIETNAMESE
## [9] CHINESE, CLASSICAL HINDI-URDU
## [11] KOREAN, CLASSICAL CHINESE, PRE-MODERN
## 12 Levels: CHINESE CHINESE, CLASSICAL ... VIETNAMESE
# Combine related language courses
enrollments$Language <- recode(enrollments$Language, "CHINESE" = "Chinese", "CHINESE, CLASSICAL" = "Chinese",
"CHINESE, PRE-MODERN" = "Chinese", "MANDARIN" = "Chinese", "JAPANESE" = "Japanese",
"JAPANESE, CLASSICAL" = "Japanese", "KOREAN" = "Korean",
"KOREAN, CLASSICAL" = "Korean", "HINDI-URDU" = "Hindi",
"HINDI" = "Hindi", "VIETNAMESE" = "Vietnamese",
"FILIPINO/PILIPINO/TAGALOG" = "Filipino")
# Calculate enrollments for each year-term-language
enrollments <- enrollments %>%
dplyr::group_by(Year, Language) %>%
dplyr::summarise(Lang_Enrol = sum(ALL.LEVEL.TOTAL, na.rm = TRUE))
# Remove the enrollments of 2020 which was generated by a sample survey due to COVID-19
enrollments <- enrollments %>%
filter(Year != "2020")
# Remove 1958-1969 Korean enrollments and 1958-1979 Hindi/Vietnamese enrollments
enrollments <- enrollments %>%
filter(!(Year < 1970 & Language == "Korean")) %>%
filter(!(Year < 1980 & Language %in% c("Hindi", "Vietnamese")))
summary(enrollments)
## Year Language Lang_Enrol
## Min. :1958 Chinese :22 Min. : 1.0
## 1st Qu.:1974 Filipino :21 1st Qu.: 648.8
## Median :1990 Hindi :12 Median : 2162.0
## Mean :1990 Japanese :22 Mean :11653.2
## 3rd Qu.:2006 Korean :15 3rd Qu.:11403.5
## Max. :2021 Vietnamese:12 Max. :72379.0
There is only 21 Filipino data while it supposed to be 22. From MLA database, the enrollments of Filipino in 1961 is 0. We’ll add the data into the data frame.
enrollments[nrow(enrollments)+1, ] = list(1961, "Filipino", 0)
# Join the enrollments and the totl_lan table to calculate the percentage of each language course enrollments over the total language course enrollments that year
enrollments <- left_join(enrollments, totl_lan, by = "Year")
enrollments <- enrollments %>%
mutate(Lang_Enrol_Pct = round(Lang_Enrol / Totl_Lan, 4)) %>%
select(-Totl_Lan)
We will interpolate the language enrollments data for the years between two nearest census (MLA conducted 26 census from 1958 to 2021) using spline.
# Extend the data frame with the years needing interpolate
all_years <- seq(1958, 2021, by = 1)
ipt <- data.frame(Year = c(rep(all_years[!(all_years %in% enrollments[enrollments$Language == "Chinese",]$Year)], 3),
all_years[!(all_years %in% enrollments[enrollments$Language == "Korean",]$Year) & all_years > 1969],
rep(all_years[!(all_years %in% enrollments[enrollments$Language == "Vietnamese",]$Year) & all_years > 1979], 2)),
Language = c(rep("Chinese", 42), rep("Japanese", 42), rep("Filipino", 42),
rep("Korean", 37), rep("Vietnamese", 30), rep("Hindi", 30)),
Lang_Enrol = rep(NA, 223), Lang_Enrol_Pct = rep(NA, 223))
enrollments <- rbind(enrollments, ipt)
enrollments <- enrollments %>%
arrange(Language, Year)
# Interpolating the missing data
for (lan in unique(enrollments$Language)){
enrollments[enrollments$Language == lan, "Lang_Enrol"] = round(na.spline(enrollments[enrollments$Language == lan, "Lang_Enrol"]))
enrollments[enrollments$Language == lan, "Lang_Enrol_Pct"] = round(na.spline(enrollments[enrollments$Language == lan, "Lang_Enrol_Pct"]), 4)
}
enrollments$Language <- as.factor(enrollments$Language)
summary(enrollments)
## Year Language Lang_Enrol Lang_Enrol_Pct
## Min. :1958 Chinese :64 Min. : 0.0 Min. :0.00000
## 1st Qu.:1981 Filipino :64 1st Qu.: 727.8 1st Qu.:0.00070
## Median :1994 Hindi :42 Median : 2389.5 Median :0.00170
## Mean :1993 Japanese :64 Mean :13147.6 Mean :0.01004
## 3rd Qu.:2008 Korean :52 3rd Qu.:14369.5 3rd Qu.:0.01323
## Max. :2021 Vietnamese:42 Max. :72379.0 Max. :0.05550
ggplot(data = enrollments, aes(x = as.factor(Year), y = Lang_Enrol, group = Language, col = Language))+
geom_line()+
geom_point()+
theme_classic()+
theme(axis.text.x = element_text(angle = 90))+
labs(title = "Language course enrollment trend", x = "Year", y = "Enrollment")
# Load trade data 1985-2023 from US census
US_trade <- read.xlsx("https://www.census.gov/foreign-trade/balance/country.xlsx")
summary(US_trade)
us_total <- read.xlsx("https://www.census.gov/foreign-trade/statistics/historical/gands.xlsx", startRow = 4, cols = c(1, 6, 9))
colnames(us_total) <- c("Year", "Exp", "Imp")
us_total <- us_total %>%
mutate(US_Total = Exp + Imp) %>%
mutate(Year = year(as.Date(Year, "%Y"))) %>%
select(Year, US_Total)
us_total <- us_total[c(26:62), ]
summary(us_total)
## Year US_Total
## Min. :1985 Min. : 554003
## 1st Qu.:1994 1st Qu.:1171549
## Median :2003 Median :2016661
## Mean :2003 Mean :2372515
## 3rd Qu.:2012 3rd Qu.:3738771
## Max. :2021 Max. :4615280
# Select needed countries, features and years
trade <- as.data.frame(US_trade[US_trade$CTYNAME %in% c("China", "Korea, South", "Japan",
"India", "Vietnam", "Philippines"), ])
trade <- trade %>%
select(year, CTYNAME, IYR, EYR) %>%
filter(year <= 2021) %>%
mutate(Total_Trade = IYR + EYR) %>%
dplyr::rename(Language = CTYNAME, Year = year) %>%
mutate(Year = year(as.Date(Year, "%Y")), Language = as.factor(Language)) %>%
select(Year, Language, Total_Trade)
# Change country names to languages
trade$Language <- recode(trade$Language, "China" = "Chinese", "Korea, South" = "Korean",
"Japan" = "Japanese", "Vietnam" = "Vietnamese",
"India" = "Hindi", "Philippines" = "Filipino")
summary(trade)
## Year Language Total_Trade
## Min. :1985 Chinese :37 Min. : 4.6
## 1st Qu.:1995 Hindi :37 1st Qu.: 15944.5
## Median :2004 Japanese :37 Median : 48219.3
## Mean :2003 Korean :37 Mean :101202.3
## 3rd Qu.:2012 Filipino :37 3rd Qu.:136013.4
## Max. :2021 Vietnamese:30 Max. :658795.4
From the summary, we found that Vietnam only got 30 data point, let’s check which years are missing.
trade[trade$Language == "Vietnamese",]$Year
## [1] 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006
## [16] 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021
The Vietnam - US trade data starts from 1992, from US Census, all figures are in millions of U.S. dollars, meaning before that the volume was less than 1 million, which we will treat it as 0.
# Join the us_total table with the trade table, to calculate the trade_ratio
trade <- left_join(trade, us_total, by = "Year")
trade <- trade %>%
mutate(Trade_Pct = round(Total_Trade / US_Total, 4)) %>%
select(Year, Language, Total_Trade, Trade_Pct)
Load trade data 1958–1984, which are derived from Statistical Abstract of the United States.
# Combine trade data from 1958-2021
trade <- rbind(read_csv("Data/Trade 1958-1984.csv"), trade)
trade$Language <- as.factor(trade$Language)
summary(trade)
## Year Language Total_Trade Trade_Pct
## Min. :1958 Chinese :64 Min. : 0 Min. :0.00000
## 1st Qu.:1981 Filipino :64 1st Qu.: 3305 1st Qu.:0.00640
## Median :1994 Hindi :42 Median : 17709 Median :0.01575
## Mean :1993 Japanese :64 Mean : 68730 Mean :0.03786
## 3rd Qu.:2008 Korean :52 3rd Qu.: 78101 3rd Qu.:0.05158
## Max. :2021 Vietnamese:42 Max. :658795 Max. :0.18380
ggplot(data = trade, aes(x = as.factor(as.character(Year)), y = Trade_Pct, group = Language, col = Language))+
geom_point()+
geom_line()+
scale_y_continuous(labels = scales::percent)+
theme_classic()+
theme(axis.text.x = element_text(angle = 90))+
labs(title = "US-foreign trade trend", x = "Year", y = "Percentage")
# Load the data
university <- read.xlsx("https://nces.ed.gov/programs/digest/d23/tables/xls/tabn303.10.xlsx", startRow = 5)
university <- university[c(11:dim(university)[1]), c(1,2)]
university <- as.data.frame(university[university[,1] %in% all_years,])
colnames(university) <- c("Year", "Total_Stud")
There are no enrollment data for 1958, 1960 and 1962 in the NCES dataset. We found the figures from a US census report and impute the missing value.
# Impute missing values
university[nrow(university)+1, ] <- c(1958, 3242000)
university[nrow(university)+1, ] <- c(1960, 3570000)
university[nrow(university)+1, ] <- c(1962, 4208000)
# Change the data type
university <- mutate(university, Year = year(as.Date(Year, "%Y")))
summary(university)
## Year Total_Stud
## Min. :1958 Min. : 3242000
## 1st Qu.:1974 1st Qu.:10068328
## Median :1990 Median :13678598
## Mean :1990 Mean :13292322
## 3rd Qu.:2005 3rd Qu.:17554164
## Max. :2021 Max. :21019438
We downloaded the American Community Survey(ACS) S0201 table from US census. The available years are 2010-2019 and 2021.
# Load seven CSV files into a list
file_paths <- fs::dir_ls("Data/S0201")
ACS_file <- list()
for (i in seq_along(file_paths)){
ACS_file[[i]] <- read_csv(
file = file_paths[[i]], show_col_types = FALSE
)
}
# Clean the data
acs_df <- list()
Year <- c(seq(2010, 2019, by = 1), 2021)
for (i in 1:length(ACS_file)){
fil <- as.data.frame(ACS_file[i])
fil$`Label..Grouping.` <- str_trim(fil$`Label..Grouping.`)
fil <- fil[c(2, 97, 106, 165, 167, 168, 235, 272), ]
colnames(fil) <- c("feature", "United_States", "Chinese", "Japanese",
"Filipino", "Korean", "Hindi", "Vietnamese")
us_fil <- fil[, c(1,2)]
us_fil$United_States <- as.numeric(gsub("\\%", "",
gsub("\\,", "", us_fil$United_States)))
fil <- fil[, c(1, 3:8)]
rownames(fil) <- c(fil$feature)
fil <- fil[, c(2:7)]
fil <- t(fil)
fil <- as.data.frame(fil)
fil <- cbind(data.frame(Language = rownames(fil), Year = rep(Year[i], 6)), fil)
rownames(fil) <- NULL
cols_num <- 3:10
fil[, cols_num] <- lapply(fil[, cols_num], function(fil){as.numeric(gsub("\\,|%", "", fil))})
fil <- fil %>%
mutate(`Bachelor's degree or higher` = `Bachelor's degree or higher`*0.01,
`Language other than English` = `Language other than English`*0.01,
`Speak English less than \"very well\"`=`Speak English less than \"very well\"`*0.01,
`All families` = `All families`*0.01) %>%
mutate(HEdu_Pop = round(`Population 25 years and over` * `Bachelor's degree or higher`),
Lang_Pop = round(`Population 5 years and over` * `Language other than English`),
Eng_LesVw_Pop = round(`Speak English less than \"very well\"` * Lang_Pop)) %>%
dplyr::rename(HEdu_Pct = `Bachelor's degree or higher`, Pop = `Total population`,
Med_Inc = `Median household income (dollars)`, Pvt_Pct = `All families`) %>%
mutate(Pop_UsPct = round(Pop / us_fil[1,2], 4),
Lang_Pop_UsPct = round(Lang_Pop / us_fil[1,2], 4),
Eng_LesVw_Pop_UsPct = round(Eng_LesVw_Pop / us_fil[1,2], 4),
HEdu_UsPct = round(HEdu_Pop / round(us_fil[2,2] * us_fil[3,2] * 0.01), 4),
HEdu_Ratio = round(HEdu_Pct / (us_fil[3,2] * 0.01), 2),
Inc_Ratio = round(Med_Inc / us_fil[7,2], 2),
Pvt_Ratio = round(Pvt_Pct / (us_fil[8,2] * 0.01), 2),
) %>%
mutate(Language = as.factor(Language)) %>%
select(Year, Language, Pop, Pop_UsPct, Lang_Pop, Lang_Pop_UsPct, Eng_LesVw_Pop,
Eng_LesVw_Pop_UsPct, HEdu_Pop, HEdu_UsPct, HEdu_Pct, HEdu_Ratio,
Med_Inc, Inc_Ratio, Pvt_Pct, Pvt_Ratio)
acs_df[[i]] <- fil
}
# Merge all data frames into one ACS2010-2021
ACS10_21 <- acs_df[[1]]
for (i in 2:11){
ACS10_21 <- rbind(ACS10_21, acs_df[[i]])
}
summary(ACS10_21)
## Year Language Pop Pop_UsPct
## Min. :2010 Chinese :11 Min. : 742549 Min. :0.002200
## 1st Qu.:2012 Filipino :11 1st Qu.:1457178 1st Qu.:0.004525
## Median :2015 Hindi :11 Median :2204688 Median :0.006900
## Mean :2015 Japanese :11 Mean :2401309 Mean :0.007479
## 3rd Qu.:2018 Korean :11 3rd Qu.:3482517 3rd Qu.:0.011125
## Max. :2021 Vietnamese:11 Max. :4404678 Max. :0.013400
## Lang_Pop Lang_Pop_UsPct Eng_LesVw_Pop Eng_LesVw_Pop_UsPct
## Min. : 316515 Min. :0.001000 Min. : 72060 Min. :0.000200
## 1st Qu.:1056061 1st Qu.:0.003300 1st Qu.: 378358 1st Qu.:0.001200
## Median :1562282 Median :0.004900 Median : 461754 Median :0.001500
## Mean :1705465 Mean :0.005311 Mean : 581869 Mean :0.001814
## 3rd Qu.:2619424 3rd Qu.:0.008175 3rd Qu.: 720935 3rd Qu.:0.002275
## Max. :3348155 Max. :0.010200 Max. :1492502 Max. :0.004600
## HEdu_Pop HEdu_UsPct HEdu_Pct HEdu_Ratio
## Min. : 268719 Min. :0.004300 Min. :0.2520 Min. :0.880
## 1st Qu.: 352524 1st Qu.:0.005225 1st Qu.:0.4808 1st Qu.:1.562
## Median : 777614 Median :0.011850 Median :0.5260 Median :1.705
## Mean : 947692 Mean :0.014108 Mean :0.5204 Mean :1.685
## 3rd Qu.:1407387 3rd Qu.:0.022925 3rd Qu.:0.5697 3rd Qu.:1.817
## Max. :2336589 Max. :0.030100 Max. :0.7680 Max. :2.510
## Med_Inc Inc_Ratio Pvt_Pct Pvt_Ratio
## Min. : 50316 Min. :1.010 Min. :0.03500 Min. :0.3100
## 1st Qu.: 64758 1st Qu.:1.120 1st Qu.:0.04500 1st Qu.:0.4425
## Median : 76424 Median :1.300 Median :0.06450 Median :0.6750
## Mean : 78519 Mean :1.372 Mean :0.07518 Mean :0.7203
## 3rd Qu.: 88506 3rd Qu.:1.538 3rd Qu.:0.10475 3rd Qu.:0.9775
## Max. :141906 Max. :2.040 Max. :0.14000 Max. :1.1900
We derive US census 1950 – 2000 data from the US decennial census report. These data share the same features as the ACS data above.
# Load US census 1950-2000 data
US_census <- read_csv("Data/Census 1950-2000.csv")
## Rows: 28 Columns: 16
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Language
## dbl (15): Year, Pop, Pop_UsPct, Lang_Pop, Lang_Pop_UsPct, Eng_LesVw_Pop, Eng...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
summary(US_census)
## Year Language Pop Pop_UsPct
## Min. :1950 Length:28 Min. : 61636 Min. :0.000400
## 1st Qu.:1970 Class :character 1st Qu.: 318551 1st Qu.:0.001525
## Median :1980 Mode :character Median : 665439 Median :0.002850
## Mean :1980 Mean : 751007 Mean :0.003046
## 3rd Qu.:1990 3rd Qu.: 904890 3rd Qu.:0.003650
## Max. :2000 Max. :2432585 Max. :0.008600
##
## Lang_Pop Lang_Pop_UsPct Eng_LesVw_Pop Eng_LesVw_Pop_UsPct
## Min. : 53528 Min. :0.000300 Min. : 59740 Min. :0.000300
## 1st Qu.: 278014 1st Qu.:0.001125 1st Qu.: 138952 1st Qu.:0.000625
## Median : 423328 Median :0.001750 Median : 244145 Median :0.001050
## Mean : 579355 Mean :0.002277 Mean : 338464 Mean :0.001306
## 3rd Qu.: 789063 3rd Qu.:0.003025 3rd Qu.: 393395 3rd Qu.:0.001475
## Max. :2022145 Max. :0.007200 Max. :1166455 Max. :0.004100
## NA's :6 NA's :6 NA's :10 NA's :10
## HEdu_Pop HEdu_UsPct HEdu_Pct HEdu_Ratio
## Min. : 1370 Min. :0.000300 Min. :0.0350 Min. :0.560
## 1st Qu.: 27848 1st Qu.:0.002575 1st Qu.:0.1703 1st Qu.:1.427
## Median :124312 Median :0.004850 Median :0.3410 Median :1.800
## Mean :182506 Mean :0.005811 Mean :0.3008 Mean :1.824
## 3rd Qu.:266324 3rd Qu.:0.007875 3rd Qu.:0.4100 3rd Qu.:2.260
## Max. :798828 Max. :0.018000 Max. :0.6390 Max. :3.300
##
## Med_Inc Inc_Ratio Pvt_Pct Pvt_Ratio
## Min. : 1689 Min. :0.760 Min. :0.0630 Min. :0.5000
## 1st Qu.: 9282 1st Qu.:0.990 1st Qu.:0.0970 1st Qu.:0.7825
## Median :21262 Median :1.120 Median :0.1340 Median :0.8750
## Mean :25636 Mean :1.149 Mean :0.1495 Mean :1.0454
## 3rd Qu.:42164 3rd Qu.:1.323 3rd Qu.:0.1510 3rd Qu.:1.1250
## Max. :63669 Max. :1.510 Max. :0.3550 Max. :3.0600
##
US_census$Language <- as.factor(US_census$Language)
# Combine ACS10_21 and US_census data into one data frame
US_census <- rbind(US_census, ACS10_21)
However, there are still missing data for the years between the decennial census. As these are demographic data which usually change slowly and gradually, we will use cubic spline interpolation to estimate the missing data between two nearest decennial census.
# Extend the US_census data frame for interpolating data of the years between two decennial census
ipt <- data.frame(Year = c(rep(all_years[!(all_years %in% US_census$Year)],3),
all_years[all_years > 1969 & !(all_years %in% US_census$Year)],
rep(all_years[all_years > 1979 & !(all_years %in% US_census$Year)], 2)),
Language = c(rep("Chinese", 48), rep("Japanese", 48), rep("Filipino", 48),
rep("Korean", 37), rep("Hindi", 28), rep("Vietnamese", 28)))
for (i in 3:ncol(US_census)){
ipt[, i] = rep(NA, nrow(ipt))
}
colnames(ipt) <- colnames(US_census)
US_census <- rbind(US_census, ipt)
US_census <- US_census %>%
arrange(Language, Year)
# Interpolate missing data between two nearest decennial census using cubic spline interpolation
# The data related to Lang_Pop are only available since 1970; data related to Eng_LesVw are only available since 1980.
for (lan in unique(US_census$Language)){
US_census[US_census$Language == lan & US_census$Year > 1969, "Lang_Pop"] =
na.spline(US_census[US_census$Language == lan & US_census$Year > 1969, "Lang_Pop"])
US_census[US_census$Language == lan & US_census$Year > 1969, "Lang_Pop_UsPct"] =
na.spline(US_census[US_census$Language == lan & US_census$Year > 1969, "Lang_Pop_UsPct"])
US_census[US_census$Language == lan & US_census$Year > 1979, "Eng_LesVw_Pop"] =
na.spline(US_census[US_census$Language == lan & US_census$Year > 1979, "Eng_LesVw_Pop"])
US_census[US_census$Language == lan & US_census$Year > 1979, "Eng_LesVw_Pop_UsPct"] =
na.spline(US_census[US_census$Language == lan & US_census$Year > 1979, "Eng_LesVw_Pop_UsPct"])
}
# Interpolate the rest missing data
col_nam <- colnames(US_census)[c(3:4, 9:16)]
lang <- unique(US_census$Language)
for (nam in col_nam){
for (lan in lang){
US_census[US_census$Language == lan, nam] = na.spline(US_census[US_census$Language == lan, nam])
}
}
# Remove the unnecessary years
US_census <- US_census[US_census$Year %in% all_years, ]
# Round the figures to desired number of digits
for (nam in colnames(US_census)[3:16]){
if (grepl("UsPct", nam)){
US_census[, nam] = round(US_census[, nam], 4)
}
else if (grepl("_Pct", nam)){
US_census[, nam] = round(US_census[, nam], 3)
}
else if (grepl("Ratio", nam)){
US_census[, nam] = round(US_census[, nam], 2)
} else {
US_census[, nam] = round(US_census[, nam])
}
}
summary(US_census)
## Year Language Pop Pop_UsPct
## Min. :1958 Chinese :64 Min. : 74264 Min. :0.000400
## 1st Qu.:1981 Filipino :64 1st Qu.: 606186 1st Qu.:0.002500
## Median :1994 Hindi :42 Median : 895283 Median :0.003550
## Mean :1993 Japanese :64 Mean :1316853 Mean :0.004671
## 3rd Qu.:2008 Korean :52 3rd Qu.:1723252 3rd Qu.:0.006025
## Max. :2021 Vietnamese:42 Max. :4404678 Max. :0.013400
##
## Lang_Pop Lang_Pop_UsPct Eng_LesVw_Pop Eng_LesVw_Pop_UsPct
## Min. : 31614 Min. :0.00020 Min. : 44146 Min. :0.00020
## 1st Qu.: 343175 1st Qu.:0.00150 1st Qu.: 204825 1st Qu.:0.00080
## Median : 736797 Median :0.00290 Median : 377164 Median :0.00130
## Mean : 973738 Mean :0.00337 Mean : 448984 Mean :0.00156
## 3rd Qu.:1382340 3rd Qu.:0.00450 3rd Qu.: 543873 3rd Qu.:0.00202
## Max. :3348155 Max. :0.01020 Max. :1492502 Max. :0.00460
## NA's :36 NA's :36 NA's :76 NA's :76
## HEdu_Pop HEdu_UsPct HEdu_Pct HEdu_Ratio
## Min. : 4343 Min. :0.000600 Min. :0.0550 Min. :0.760
## 1st Qu.: 90257 1st Qu.:0.004600 1st Qu.:0.2567 1st Qu.:1.620
## Median : 258221 Median :0.006300 Median :0.3925 Median :1.800
## Mean : 423966 Mean :0.008906 Mean :0.3904 Mean :1.840
## 3rd Qu.: 552831 3rd Qu.:0.012025 3rd Qu.:0.5050 3rd Qu.:2.212
## Max. :2336589 Max. :0.030100 Max. :0.7680 Max. :3.300
##
## Med_Inc Inc_Ratio Pvt_Pct Pvt_Ratio
## Min. : 2817 Min. :0.760 Min. :0.0260 Min. :0.2500
## 1st Qu.: 19928 1st Qu.:1.067 1st Qu.:0.0660 1st Qu.:0.5700
## Median : 42842 Median :1.235 Median :0.0980 Median :0.8500
## Mean : 44165 Mean :1.243 Mean :0.1084 Mean :0.9129
## 3rd Qu.: 63466 3rd Qu.:1.383 3rd Qu.:0.1340 3rd Qu.:1.1200
## Max. :141906 Max. :2.040 Max. :0.3550 Max. :3.0600
##
# Load the data
foreign_stud <- read.xlsx("https://opendoorsdata.org/?download=https://opendoorsdata.org/wp-content/uploads/2023/11/Census_All-Places-of-Origin_OD23.xlsx", startRow = 3)
# Extract total international student data for each year
world_total <- as.data.frame(foreign_stud[264, c(4:35)])
# Change the wide table to a long table
world_total <- pivot_longer(world_total, everything(), names_to = "Year", values_to = "world_total")
# Edit the year value and only keep the start year (fall)
world_total$Year <- substr(world_total$Year, 1, 4)
# Change the data type
world_total <- world_total %>%
mutate(Year = year(as.Date(Year, "%Y")), world_total = as.numeric(world_total))
summary(world_total)
## Year world_total
## Min. :1954 Min. : 34232
## 1st Qu.:1993 1st Qu.: 436189
## Median :2006 Median : 584660
## Mean :2000 Mean : 620158
## 3rd Qu.:2013 3rd Qu.: 893063
## Max. :2021 Max. :1095299
# Extract data related to the countries of interest
# Select needed rows and columns
intl_stud <- as.data.frame(foreign_stud[foreign_stud$Place.of.Origin %in% c("China", "South Korea", "Japan",
"India", "Vietnam", "Philippines"), ])
intl_stud <- intl_stud[, -c(1, 3, 36:39)]
# change the wide table to a long table
intl_stud <- pivot_longer(intl_stud, !Place.of.Origin, names_to = "Year", values_to = "Intl_Stud")
# Edit the year value and only keep the start year (fall)
intl_stud$Year <- substr(intl_stud$Year, 1, 4)
summary(intl_stud)
## Place.of.Origin Year Intl_Stud
## Length:192 Length:192 Length:192
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
intl_stud$Intl_Stud <- gsub("\\-", "0", intl_stud$Intl_Stud)
intl_stud <- intl_stud %>%
dplyr::rename(Language = Place.of.Origin) %>%
mutate(Language = as.factor(Language), Year = year(as.Date(Year, "%Y")),
Intl_Stud = as.numeric(Intl_Stud)) %>%
select(Year, Language, Intl_Stud)
intl_stud$Language <- recode(intl_stud$Language, "China" = "Chinese", "South Korea" = "Korean",
"Japan" = "Japanese", "Vietnam" = "Vietnamese",
"India" = "Hindi", "Philippines" = "Filipino")
# Join the world_total data with the intl_stud data to calculate the percentage of each country's international student over the total international student in US
intl_stud <- left_join(intl_stud, world_total, by = "Year")
intl_stud <- intl_stud %>%
mutate(Intl_Stud_Pct = round(Intl_Stud / world_total, 3)) %>%
select(Year, Language, Intl_Stud, Intl_Stud_Pct)
summary(intl_stud)
## Year Language Intl_Stud Intl_Stud_Pct
## Min. :1954 Chinese :32 Min. : 0 Min. :0.00000
## 1st Qu.:1993 Hindi :32 1st Qu.: 3516 1st Qu.:0.01275
## Median :2006 Japanese :32 Median : 20340 Median :0.04300
## Mean :2000 Filipino :32 Mean : 51472 Mean :0.07036
## 3rd Qu.:2013 Korean :32 3rd Qu.: 62538 3rd Qu.:0.10425
## Max. :2021 Vietnamese:32 Max. :372532 Max. :0.34700
The historical data from Open Doors only provides figures every five years for the years before 2000. We found data for some years between 1960 and 1990 from the Open Doors annual reports available online and NCES Digest of Education Statistics.
# load international students data 1960-1990
stu60_98 <- read_csv("Data/Intl Students 1960-1998.csv")
## Rows: 67 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Language
## dbl (3): Year, Intl_Stud, Intl_Stud_Pct
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
intl_stud <- rbind(intl_stud, stu60_98)
intl_stud <- as.data.frame(intl_stud)
intl_stud <- arrange(intl_stud, Language, Year)
summary(intl_stud)
## Year Language Intl_Stud Intl_Stud_Pct
## Min. :1954 Chinese :44 Min. : 0 Min. :0.0000
## 1st Qu.:1989 Hindi :42 1st Qu.: 3500 1st Qu.:0.0110
## Median :2000 Japanese :44 Median : 20713 Median :0.0460
## Mean :1997 Filipino :44 Mean : 44208 Mean :0.0659
## 3rd Qu.:2011 Korean :43 3rd Qu.: 49428 3rd Qu.:0.0960
## Max. :2021 Vietnamese:42 Max. :372532 Max. :0.3470
## NA's :4 NA's :4
However, we cannot find data for all the years required for this project. Therefore, we will impute missing data using spline, similar to our approach with the census data.
# Extend the data frame for missing years of international students data
ipt <- data.frame(
Year = c(rep(all_years[!(all_years %in% intl_stud$Year)], 3),
all_years[all_years > 1969 & !(all_years %in% intl_stud$Year)],
rep(all_years[all_years > 1979 & !(all_years %in% intl_stud$Year)],2)),
Language = c(rep("Chinese", length(all_years[!(all_years %in% intl_stud$Year)])),
rep("Japanese", length(all_years[!(all_years %in% intl_stud$Year)])),
rep("Filipino", length(all_years[!(all_years %in% intl_stud$Year)])),
rep("Korean", length(all_years[all_years > 1969 & !(all_years %in% intl_stud$Year)])),
rep("Hindi", length(all_years[all_years>1979 & !(all_years %in% intl_stud$Year)])),
rep("Vietnamese", length(all_years[all_years>1979 & !(all_years %in% intl_stud$Year)]))))
ipt[, ncol(ipt)+1] <- rep(NA, nrow(ipt))
ipt[, ncol(ipt)+1] <- rep(NA, nrow(ipt))
colnames(ipt)[3:4] <- c("Intl_Stud", "Intl_Stud_Pct")
intl_stud <- rbind(intl_stud, ipt)
intl_stud <- arrange(intl_stud, Language, Year)
We will interpolate missing data as following: 1. The number of Chinese student increased dramatically after the year 1979, so we will use linear interpolation via na.approx for Chinese students before year 1980 to avoid negative values generating by na.spine. 2. The rest of the missing data are interpolate by na.spline.
intl_stud[intl_stud$Year < 1980 & intl_stud$Language == "Chinese", "Intl_Stud"] <-
round(na.approx(intl_stud[intl_stud$Year < 1980 & intl_stud$Language == "Chinese", "Intl_Stud"]))
intl_stud[intl_stud$Year < 1980 & intl_stud$Language == "Chinese", "Intl_Stud_Pct"] <-
round(na.approx(intl_stud[intl_stud$Year < 1980 & intl_stud$Language == "Chinese", "Intl_Stud_Pct"]), 3)
lang <- unique(intl_stud$Language)
for (lan in lang){
intl_stud[intl_stud$Language == lan, "Intl_Stud"] = round(na.spline(intl_stud[intl_stud$Language == lan, "Intl_Stud"]))
intl_stud[intl_stud$Language == lan, "Intl_Stud_Pct"] = round(na.spline(intl_stud[intl_stud$Language == lan, "Intl_Stud_Pct"]), 3)
}
# Select years and remove unwanted data
intl_stud <- intl_stud[intl_stud$Year %in% all_years, ]
intl_stud <- intl_stud[!(intl_stud$Language == "Korean" & intl_stud$Year < 1970), ]
intl_stud <- intl_stud[!(intl_stud$Language %in% c("Hindi", "Vietnamese") & intl_stud$Year < 1980), ]
summary(intl_stud)
## Year Language Intl_Stud Intl_Stud_Pct
## Min. :1958 Chinese :64 Min. : 0 Min. :0.00000
## 1st Qu.:1981 Hindi :42 1st Qu.: 3224 1st Qu.:0.01100
## Median :1994 Japanese :64 Median : 13474 Median :0.03550
## Mean :1993 Filipino :64 Mean : 35901 Mean :0.05625
## 3rd Qu.:2008 Korean :52 3rd Qu.: 42587 3rd Qu.:0.08125
## Max. :2021 Vietnamese:42 Max. :372532 Max. :0.34700
We derive the data of party government from History, Art & Archives
party_gov <- read_csv("Data/Party government.csv")
## Rows: 64 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): Presidency, Party_Gov
## dbl (1): Year
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
party_gov <- party_gov %>%
mutate(Presidency = as.factor(Presidency), Party_Gov = as.factor(Party_Gov))
summary(party_gov)
## Year Presidency Party_Gov
## Min. :1958 Democrat :29 Divided:41
## 1st Qu.:1974 Republican:35 Unified:23
## Median :1990
## Mean :1990
## 3rd Qu.:2005
## Max. :2021
all_data <- join_all(list(enrollments, US_census, intl_stud, trade), by = c("Year", "Language"), type = "left")
all_data <- join_all(list(all_data, university, party_gov), by = "Year", type = "left")
# Add a column "lan_UsPct" for the percentage of a language course enrollment over total US college enrollment
all_data <- all_data %>%
mutate(Lan_UsPct = round(Lang_Enrol/Total_Stud, 5))
summary(all_data)
## Year Language Lang_Enrol Lang_Enrol_Pct
## Min. :1958 Chinese :64 Min. : 0.0 Min. :0.00000
## 1st Qu.:1981 Filipino :64 1st Qu.: 727.8 1st Qu.:0.00070
## Median :1994 Hindi :42 Median : 2389.5 Median :0.00170
## Mean :1993 Japanese :64 Mean :13147.6 Mean :0.01004
## 3rd Qu.:2008 Korean :52 3rd Qu.:14369.5 3rd Qu.:0.01323
## Max. :2021 Vietnamese:42 Max. :72379.0 Max. :0.05550
##
## Pop Pop_UsPct Lang_Pop Lang_Pop_UsPct
## Min. : 74264 Min. :0.000400 Min. : 31614 Min. :0.00020
## 1st Qu.: 606186 1st Qu.:0.002500 1st Qu.: 343175 1st Qu.:0.00150
## Median : 895283 Median :0.003550 Median : 736797 Median :0.00290
## Mean :1316853 Mean :0.004671 Mean : 973738 Mean :0.00337
## 3rd Qu.:1723252 3rd Qu.:0.006025 3rd Qu.:1382340 3rd Qu.:0.00450
## Max. :4404678 Max. :0.013400 Max. :3348155 Max. :0.01020
## NA's :36 NA's :36
## Eng_LesVw_Pop Eng_LesVw_Pop_UsPct HEdu_Pop HEdu_UsPct
## Min. : 44146 Min. :0.00020 Min. : 4343 Min. :0.000600
## 1st Qu.: 204825 1st Qu.:0.00080 1st Qu.: 90257 1st Qu.:0.004600
## Median : 377164 Median :0.00130 Median : 258221 Median :0.006300
## Mean : 448984 Mean :0.00156 Mean : 423966 Mean :0.008906
## 3rd Qu.: 543873 3rd Qu.:0.00202 3rd Qu.: 552831 3rd Qu.:0.012025
## Max. :1492502 Max. :0.00460 Max. :2336589 Max. :0.030100
## NA's :76 NA's :76
## HEdu_Pct HEdu_Ratio Med_Inc Inc_Ratio
## Min. :0.0550 Min. :0.760 Min. : 2817 Min. :0.760
## 1st Qu.:0.2567 1st Qu.:1.620 1st Qu.: 19928 1st Qu.:1.067
## Median :0.3925 Median :1.800 Median : 42842 Median :1.235
## Mean :0.3904 Mean :1.840 Mean : 44165 Mean :1.243
## 3rd Qu.:0.5050 3rd Qu.:2.212 3rd Qu.: 63466 3rd Qu.:1.383
## Max. :0.7680 Max. :3.300 Max. :141906 Max. :2.040
##
## Pvt_Pct Pvt_Ratio Intl_Stud Intl_Stud_Pct
## Min. :0.0260 Min. :0.2500 Min. : 0 Min. :0.00000
## 1st Qu.:0.0660 1st Qu.:0.5700 1st Qu.: 3224 1st Qu.:0.01100
## Median :0.0980 Median :0.8500 Median : 13474 Median :0.03550
## Mean :0.1084 Mean :0.9129 Mean : 35901 Mean :0.05625
## 3rd Qu.:0.1340 3rd Qu.:1.1200 3rd Qu.: 42587 3rd Qu.:0.08125
## Max. :0.3550 Max. :3.0600 Max. :372532 Max. :0.34700
##
## Total_Trade Trade_Pct Total_Stud Presidency
## Min. : 0 Min. :0.00000 Min. : 3242000 Democrat :144
## 1st Qu.: 3305 1st Qu.:0.00640 1st Qu.:12205679 Republican:184
## Median : 17709 Median :0.01575 Median :14358953
## Mean : 68730 Mean :0.03786 Mean :14352656
## 3rd Qu.: 78101 3rd Qu.:0.05158 3rd Qu.:18658756
## Max. :658795 Max. :0.18380 Max. :21019438
##
## Party_Gov Lan_UsPct
## Divided:220 Min. :0.0000000
## Unified:108 1st Qu.:0.0000575
## Median :0.0001400
## Mean :0.0008159
## 3rd Qu.:0.0010300
## Max. :0.0037600
##
# Save the data frame as csv file
write_csv(all_data, "Data/all_data.csv")
write_csv(totl_lan, "Data/All_Lang_Enrol.csv")
write_csv(university, "Data/UsCollege_Enrol.csv")
write_csv(enrollments, "Data/Asian_Lang_Enrol.csv")
write_csv(trade, "Data/trade.csv")
write_csv(US_census, "Data/US_census.csv")