Abstract
This document provides the code for exploring how and by whom the American-returned students were discussed in the Chinese periodical Dongfang zazhi 東方雜誌 (Eastern Miscellany) from 1905 to 1948.
This document is part of a series dedicated to a corpus-based analysis of articles on Japan- and U.S.-returned students in Dongfang zazhi 東方雜誌 (The Eastern Miscellany). Building on a previous script aimed at constructing and exploring a corpus focused on American-returned students, this document provides code for extracting and analyzing biographical information about the authors of these articles.
We searched for biographical information on the authors in Chinese Wikipedia and the Who’s Who publications provided by the Institute of Modern History (IMH), Academia Sinica, Taipei.
# Create a function for multiple queries
multiple_search <- function(queries, corpus) {
results <- histtext::search_documents_ex(queries[1], corpus) %>%
mutate(Q=queries[1])
for(q in queries){
new_result <- histtext::search_documents_ex(q, corpus) %>%
mutate(Q=q)
results <- dplyr::bind_rows(results, new_result)
}
distinct(results)
}
# Create the list of names to query
liumei_authors_list <- liumei_authors %>% drop_na(Author) %>% mutate(Queries=str_glue('"{Author}"'))
library(histtext)
liumei_authors_wiki <- multiple_search(liumei_authors_list$Queries, "wikibio-zh") # 957 results
liumei_authors_wiki <- liumei_authors_wiki %>% group_by(Q)
# Eliminate irrelevant results
liumei_list_names <- liumei_authors_list$Author
liumei_list_names <- paste(liumei_list_names, sep = "", collapse = "|")
liumei_authors_wiki <- liumei_authors_wiki %>% mutate(title_match = str_extract(Title, liumei_list_names))
liumei_authors_wiki <- liumei_authors_wiki %>% group_by(title_match) %>% add_tally()
liumei_authors_wiki_match <- liumei_authors_wiki %>% filter(!is.na(title_match)) # 23 matches
# 23 bios remain
liumei_authors_wiki_match
# Extract the full text of biographies
liumei_authors_wiki_ft <- histtext::get_documents(liumei_authors_wiki_match, "wikibio-zh")
liumei_authors_wiki_ft
library(histtext)
liumei_authors_imh <- multiple_search(liumei_authors_list$Queries, "imh-zh") # 202 results
liumei_authors_imh <- liumei_authors_imh %>% mutate(title_match = str_extract(Title, liumei_list_names))
liumei_authors_imh <- liumei_authors_imh %>% group_by(title_match) %>% add_tally()
# Eliminate irrelevant results
liumei_authors_imh_match <- liumei_authors_imh %>% filter(!is.na(title_match)) # 108 results remain
liumei_authors_imh_match <- liumei_authors_imh_match %>% distinct(DocId, Title) %>%
mutate(Title_clean = str_replace(Title, "【", "")) %>%
mutate(Title_clean = str_replace(Title_clean, "】", "")) %>%
mutate(Title_clean = str_extract(Title_clean, "^.{3}")) %>% relocate(title_match, .after = Title_clean)
liumei_authors_imh_match <- liumei_authors_imh_match %>% filter(!DocId == "imh-72-629")
liumei_authors_imh_match <- liumei_authors_imh_match %>% mutate(title_length = nchar(Title)) %>% mutate(title_length2 = nchar(Title_clean)) %>%
mutate(query_length = nchar(title_match)) %>% mutate(dif = (query_length-title_length)) %>% mutate(dif2 = (query_length-title_length2))
liumei_authors_imh_filtered <- liumei_authors_imh_match %>% filter(!dif2 == "-1") # 77 bios remain
liumei_authors_imh_unique <- liumei_authors_imh_filtered %>%
select(DocId, title_match) %>% rename(Name = title_match) %>%
group_by(Name) %>% count() # 28 unique authors remain, from 1 to 7 biographies each
liumei_authors_imh_filtered <- liumei_authors_imh_filtered %>% select(DocId, title_match) %>% rename(Name = title_match)
liumei_authors_imh_filtered <- liumei_authors_imh_filtered %>%
group_by(Name) %>% add_tally()
# 77 biographies referring to 28 unique names remain, from 1 to 7 bios per author
liumei_authors_imh_filtered
# Retrieve full text
liumei_authors_imh_ft <- histtext::get_documents(liumei_authors_imh_filtered, "imh-zh")
liumei_authors_imh_ft
## Bind the two datasets
liumei_authors_bio <- bind_rows(liumei_authors_imh_ft, liumei_authors_wiki_ft)
# Retrieve year of birth
liumei_authors_bio$year <- regmatches(liumei_authors_bio$Text, gregexpr("\\d{4}", liumei_authors_bio$Text))
liumei_authors_bio$birth <- regmatches(liumei_authors_bio$year, regexpr("[[:digit:]]+", liumei_authors_bio$year))
liumei_authors_bio$year <- NULL
liumei_authors_bio_filtered <- liumei_authors_bio %>% filter(birth < 1948) %>% filter(birth > 1800) # 46 bios remain
liumei_authors_bio_filtered
# Retrieve native provinces based on list
library(readr)
provinces <- read_delim("~/indexes/provinces.csv",
delim = ";", escape_double = FALSE, trim_ws = TRUE)
provinces <- provinces %>% na.omit()
# create vector
prov_list <- provinces$Province
prov_list <- paste(prov_list, sep = "", collapse = "|")
liumei_authors_bio_filtered <- liumei_authors_bio_filtered %>% mutate(birthplace = str_extract(Text, prov_list))
# Retrieve academic disciplines based on typology
# Load typology
library(readr)
disciplines <- read_delim("~/indexes/disciplines.csv",
delim = ";", escape_double = FALSE, trim_ws = TRUE)
# create vector
disc_list <- disciplines$Level2__ZhT_MCBD
disc_vec <- paste(disc_list, sep = "", collapse = "|")
liumei_authors_bio_filtered <- liumei_authors_bio_filtered %>%
mutate(discipline = str_extract_all(Text, disc_vec)) %>%
mutate(discipline = as.character(discipline))
# identify those who studied or traveled to foreign countries
liumei_authors_bio_filtered <- liumei_authors_bio_filtered %>%
mutate(edu_country = str_extract_all(Text, "留美|留日|留法|留德|留英")) %>%
mutate(edu_country = as.character(edu_country)) %>%
mutate(country = str_extract_all(Text, "美國|日本|法國|德國|英國")) %>%
mutate(country = as.character(country))
liumei_authors_bio_filtered
In the following section, we extract named entities from biographies using models specifically developed by the ENP-China team for modern Chinese historical texts, integrated into the HistText package.
liumei_authors_imh_ner <- ner_on_corpus(liumei_authors_imh_ft, corpus = "imh-zh")
liumei_authors_wiki_ner <- ner_on_corpus(liumei_authors_wiki_ft, corpus = "wikibio-zh")
liumei_authors_ner <- bind_rows(liumei_authors_imh_ner, liumei_authors_wiki_ner) # bind the two lists
liumei_authors_ner
In the following section, we focus on organizations to define
occupational profiles, but we could also examine other types of
entities, such as events in which they were involved or creative works
they produced during their careers.
# Filter organizations
liumei_authors_org <- liumei_authors_ner %>% filter(Type == "ORG")
# Preliminary cleaning
liumei_authors_org <- liumei_authors_org %>%
mutate(text_clean = str_replace(Text, "《", "")) %>%
mutate(text_clean = str_replace(text_clean, ">", "")) %>%
relocate(text_clean, .after = Text) %>%
mutate(text_clean, text_clean=str_replace(text_clean,"(.*","")) %>%
mutate(text_clean, text_clean=str_replace(text_clean,"\\(.*","")) %>%
mutate(text_clean, text_clean=str_replace(text_clean,"\\(.*",""))%>%
mutate(text_clean = str_replace(text_clean, " ", "")) %>%
mutate(text_clean = str_replace(text_clean, " ", ""))%>%
mutate(length = nchar(text_clean)) %>%
relocate(length, .after = text_clean)
liumei_authors_org <- liumei_authors_org %>% filter(length > 1) # 1370
# Extract suffixes and prefixes to help classify and locate institutions
liumei_authors_org <- liumei_authors_org %>%
mutate(class = str_sub(text_clean,-2,-1)) %>%
relocate(class, .after = text_clean)
liumei_authors_org <- liumei_authors_org %>%
mutate(pre = str_sub(text_clean, 1, 2)) %>%
relocate(pre, .before = text_clean)
# Remove residual noise (position verbs, etc)
liumei_authors_org <- liumei_authors_org %>%
mutate(text_clean = str_replace(text_clean, "任", ""))%>%
mutate(text_clean = str_replace(text_clean, "入", ""))%>%
mutate(text_clean = str_replace(text_clean, "久", ""))%>%
mutate(text_clean = str_replace(text_clean, "于", ""))%>%
mutate(text_clean = str_replace(text_clean, "於", ""))%>%
mutate(text_clean = str_replace(text_clean, "從", "")) %>%
mutate(length = nchar(text_clean)) %>%
filter(length > 1) %>% #
mutate(pre = str_sub(text_clean, 1, 2))
liumei_authors_org <- liumei_authors_org %>%
filter(stringr::str_detect(liumei_authors_org$text_clean, "[\\p{Han}]"))
# 1368 organizations remain
# Most important institutions (across biographies)
liumei_authors_org %>% group_by(text_clean) %>%
count(sort = TRUE)
# Most important institutions (based on unique occurrences in biographies)
liumei_authors_org %>% distinct(DocId, text_clean) %>%
group_by(text_clean) %>% count(sort = TRUE)
# Most important types of institutions
liumei_authors_org %>% distinct(DocId, text_clean, class) %>%
group_by(class) %>% count(sort = TRUE)
Finally, we export the list of authors with extracted
information for manual verification to remove redundant or irrelevant
results. We also export the list of organizational affiliations to
standardize and categorize the authors and their occupations.
write.csv(liumei_authors_org, "~/output/liumei_authors_org.csv")
write.csv(liumei_authors_bio_filtered, "~/output/liumei_authors_bio_filtered.csv")
# Reimport curated list of authors and their attributes
library(readr)
liumei_authors_to_analyze <- read_delim("~/liumei_authors_to_analyze.csv",
delim = ";", escape_double = FALSE, trim_ws = TRUE)
# Replace empty fields with NAs
liumei_authors_to_analyze <-liumei_authors_to_analyze %>%
mutate(across(everything(), ~ifelse(.=="", NA, as.character(.))))
liumei_authors_to_analyze
liumei_authors_to_analyze %>% group_by(Nationality) %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by(Generation) %>% count()
library(hrbrthemes)
library(viridis)
liumei_authors_to_analyze %>% drop_na(BirthYear) %>%
mutate(BirthYear = as.numeric(BirthYear))%>%
ggplot( aes(x=BirthYear)) +
geom_histogram( binwidth=1, fill="#69b3a2", color="#e9ecef", alpha=0.9) +
ggtitle("Bin size = 1") +
theme_ipsum() +
theme(
plot.title = element_text(size=15)
) +
labs(title = "Authors' Year of Birth (留美)",
x = "Year",
y = "Frequency")
### Native Place
liumei_authors_to_analyze %>% group_by(Birthplace) %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by('Country of Education') %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by('Education Country') %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by(Occupation) %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by(Occupation2) %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by(Specialization) %>% count(sort = TRUE)
liumei_authors_to_analyze %>% group_by(Party) %>% count(sort = TRUE)
Note: DL = Democratic League. GMD = Guomindang.
TMH = Tongmenghui.
In the final section, we employed Multiple Correspondence Analysis (MCA)—a statistical method used to identify correlations between categorical attributes and to cluster individuals with similar profiles. This analysis aims to examine how biographical factors—generation, native place, education, and occupation—correlate with the topics authors engaged with and the overall sentiment (positive or negative) they expressed toward Japanese- and American-returned students.
liumei_mca <- liumei_authors_to_analyze %>%
filter(Nationality == "Chinese") %>%
select(Name, Generation, 'Education Country', Occupation, Sentiment, Topic)
liumei_mca <- liumei_mca %>% drop_na(Generation) # 34 remain
liumei_mca <- column_to_rownames(liumei_mca, "Name")
liumei_mca
library(FactoMineR)
res.MCA<-MCA(liumei_mca,graph=FALSE)
# Plot 2 first dimensions
plot.MCA(res.MCA, choix='var',title="Category Plot (留美)",col.var=c(1,2,3,4,5))
plot.MCA(res.MCA,col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot (留美)", cex=0.8,cex.main=0.8,cex.axis=0.8, autoLab = "yes",label =c('ind','var'))
plot.MCA(res.MCA,col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot: Variables (留美)", invisible= 'ind', cex=0.8,cex.main=0.8,cex.axis=0.8, autoLab = "yes",label =c('var'))
plot.MCA(res.MCA,col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot: Individuals (留美)", invisible= 'var', cex=0.8,cex.main=0.8,cex.axis=0.8, autoLab = "yes",label =c('ind'))
# Add 3rd dimension
plot.MCA(res.MCA, choix='var',title="Category Plot (留美), Dimensions 2/3",axes=c(2,3),col.var=c(1,2,3,4,5))
plot.MCA(res.MCA,axes=c(2,3),col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot (留美), Dimensions 2/3", autoLab = "yes", cex=0.8,cex.main=0.8,cex.axis=0.8,label =c('ind','var'))
plot.MCA(res.MCA,axes=c(2,3),col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot: Variables (留美), Dimensions 2/3", invisible= 'ind', autoLab = "yes", cex=0.8,cex.main=0.8,cex.axis=0.8,label =c('var'))
plot.MCA(res.MCA,axes=c(2,3),col.var=c(1,1,2,2,2,2,2,3,3,3,3,3,4,4,5,5,5,5,5),title="Biplot: Variables (留美), Dimensions 2/3", invisible= 'var', autoLab = "yes", cex=0.8,cex.main=0.8,cex.axis=0.8,label =c('ind'))
# Statistics
dimdesc(res.MCA)
## $`Dim 1`
##
## Link between the variable and the categorical variable (1-way anova)
## =============================================
## R2 p.value
## Generation 0.5918376 1.058704e-07
## Topic 0.7002421 2.887926e-07
## Education.Country 0.3492185 1.283056e-03
## Occupation 0.4318505 1.998733e-03
## Sentiment 0.1607814 1.876354e-02
##
## Link between variable and the categories of the categorical variables
## ================================================================
## Estimate p.value
## Generation=Pre1895 0.5151144 1.058704e-07
## Topic=T5. Regulation/Organization 0.7945787 1.077276e-04
## Education.Country=China-NA 0.3654297 2.801661e-03
## Occupation=scholar-official 0.6716172 4.936421e-03
## Sentiment=Negative 0.2804236 1.876354e-02
## Occupation=other -0.7172533 2.366305e-02
## Sentiment=Positive -0.2804236 1.876354e-02
## Topic=T3. Society/Culture -0.5955265 1.022774e-03
## Education.Country=USA -0.5217103 2.842826e-04
## Generation=Post1895 -0.5151144 1.058704e-07
##
## $`Dim 2`
##
## Link between the variable and the categorical variable (1-way anova)
## =============================================
## R2 p.value
## Occupation 0.7055525 2.243954e-07
## Topic 0.5483233 8.848269e-05
## Education.Country 0.3821583 5.735841e-04
##
## Link between variable and the categories of the categorical variables
## ================================================================
## Estimate p.value
## Topic=T3. Society/Culture 0.7500199 8.015290e-05
## Education.Country=China-NA 0.6251503 5.156057e-03
## Occupation=other 0.2446606 2.648915e-02
## Occupation=official-bureaucrat 0.5239642 2.858156e-02
## Occupation=professional writer 0.2345137 2.922574e-02
## Topic=T1. East/West -0.4518646 9.763668e-03
## Education.Country=Japan/Europe -0.6994855 3.301671e-03
## Occupation=scholar -0.8407680 1.963564e-08
##
## $`Dim 3`
##
## Link between the variable and the categorical variable (1-way anova)
## =============================================
## R2 p.value
## Occupation 0.7521306 1.958818e-08
## Sentiment 0.4537824 1.262209e-05
##
## Link between variable and the categories of the categorical variables
## ================================================================
## Estimate p.value
## Sentiment=Negative 0.3901873 1.262209e-05
## Occupation=official-bureaucrat 1.0598608 1.055228e-03
## Occupation=scholar-official 0.4332867 4.969405e-03
## Topic=T1. East/West 0.4841439 9.125685e-03
## Occupation=professional writer -0.9707434 6.605967e-04
## Sentiment=Positive -0.3901873 1.262209e-05
Finally, hierarchical clustering is applied to categorize authors based on shared characteristics:
res.MCA<-MCA(liumei_mca,ncp=14,graph=FALSE)
res.HCPC<-HCPC(res.MCA,nb.clust=7,consol=FALSE,graph=FALSE)
# Plots
plot.HCPC(res.HCPC,choice='tree',title='Tree Map (留美)')
plot.HCPC(res.HCPC,choice='map',draw.tree=FALSE,title='Factor Map (留美)')
plot.HCPC(res.HCPC,choice='3D.map',ind.names=FALSE,centers.plot=FALSE,angle=60,title='3D Tree Map (留美)')
# Statistics
summary(res.HCPC)
## Length Class Mode
## data.clust 6 data.frame list
## desc.var 3 catdes list
## desc.axes 3 catdes list
## desc.ind 2 -none- list
## call 7 -none- list
For a comparative perspective, please consult the corresponding scripts on Japan-returned students.