Introduction

In this paper, we used Dongfang zazhi 東方雜誌 (The Eastern Miscellany)—a major intellectual journal published by the Commercial Press from 1904 to 1948 — as our primary source to examine the debates surrounding students returning from Japan during the late Qing and Republican eras. Our analysis is based on the full-text, semantically enriched version of this journal, available through the Modern China Textual Database. Using this resource, we conducted a corpus-based analysis with the HistText R package developed by the ENP-China project for analyzing large collections of historical Chinese texts.

Corpus Building

Expanded search using embedding

Pre-trained embeddings are available at : https://sharedocs.huma-num.fr/#/2547/8997/6-HistText/Embeddings

library("fastTextR")

model <- fasttext()
model$load("~/embeddings/toastynews.bin")  # load embedding 
nearest_neighbors <- ft_nearest_neighbors(model,"留日", k = 30L) # find neighbors
nearest_neighbors <- names(nearest_neighbors)

# Add selected terms to the query (254 results)

liuri_exp <- search_documents('"留日" | "旅日" | "遊日"', corpus="dongfangzz") 

Concordance

Inspect keywords in context using concordance (built-in function included in HistText):

liuri_conc <- search_concordance('"留日" | "旅日" | "遊日"', corpus="dongfangzz") # 335 occurrences
liuri_conc

Filter relevant results

Select only documents that refer to students (107 documents remain):

# Create a vector with relevant expressions for filtering (study, youth)

xue <- c("學", "靑年")
xue_vec <- paste(xue, sep = "", collapse = "|")

# Search the vector within the target fields (After, Before, Title)

library(dplyr)

liuri_conc <- liuri_conc %>% mutate(xue = str_extract(After, xue_vec)) 
liuri_conc <- liuri_conc %>% mutate(xue2 = str_extract(Before, xue_vec))
liuri_conc <- liuri_conc %>% mutate(xue3 = str_extract(Title, xue_vec)) 
liuri_conc <- liuri_conc %>% mutate(match2 = paste0(xue, xue2, xue3))

# Eliminate non matches 

liuri_conc_filtered <- liuri_conc %>% filter(!match2 == "NANANA") # 136 occurrences 

# Export list of documents for manual checking 

write.csv(liuri_conc_filtered, "liuri_conc_to_check.csv")

# Re-import list of manually filtered documents (107)

library(readr)
liuri_filtered <- read_csv("~/data/liuri_filtered.csv")

Retrieve documents metadata and full text

liuri_meta <- get_search_fields_content(liuri_filtered, corpus="dongfangzz", 
                                        search_fields=c(list_search_fields("dongfangzz"),
                                                        list_filter_fields("dongfangzz")))

# Extract year of publication (first four digits of document Id)

liuri_meta <- liuri_meta %>%
  mutate(Year = str_extract(DocId, "\\d{4}")) %>%
  mutate(Year = as.numeric(Year))

liuri_meta <- liuri_meta %>% relocate(Year, .after = DocId)
liuri_meta

Article segmentation

Among our results, 34 documents consist of short, unrelated news pieces, where our subject of inquiry appears only as a limited segment within a broader text. We manually curated these documents, extracting only the relevant segments that focus on our object of study (i.e., Japan-returned students):

# Import properly segmented documents (34)

liuri_segmented <- read_delim("~/data/liuri_segmented.csv", 
                              delim = ";", escape_double = FALSE, trim_ws = TRUE)

liuri_segmented <- liuri_segmented %>%  dplyr:: rename(text_seg = text)
liuri_segmented
# Join with complete list of documents and remove duplicates

liuri_clean <- left_join(liuri_meta, liuri_segmented)
liuri_clean <- liuri_clean %>% unique()

# Replace missing text in the "text_seg" columns by the original text 

liuri_clean <- liuri_clean %>%
  mutate(text_seg = ifelse(is.na(text_seg), text, text_seg))

liuri_clean <- liuri_clean %>% relocate(text, .before = text_seg)

# Compute text length 

liuri_clean <- liuri_clean %>% mutate(length = nchar(text)) %>% mutate(length_seg = nchar(text_seg))
liuri_clean <- liuri_clean %>% relocate(category, .before = category_strd)
liuri_clean

Corpus Exploration

Distribution over time

library(ggplot2)
library(hrbrthemes)
library(viridis)

liuri_clean %>% 
  ggplot( aes(x=Year)) +
  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 = "Japan-returned students in 東方雜誌",
       x = "Year",
       y = "Number of articles")

# Compare with the entire collection using the "stats_date()" function included in HistText

liuri_date <- liuri_year %>% rename(Date = Year)
liuri_stats <- stats_date(liuri_date,"dongfangzz",over_all=TRUE, to_plot = FALSE)
stats_date(liuri_date,"dongfangzz", over_all=TRUE, to_plot = TRUE)
stats_date(liuri_date,"dongfangzz",over_all=TRUE, to_plot = TRUE)
##  Found 9 records... Imported 9 records. Simplifying...

Distribution by category

liuri_clean %>% group_by(category) %>% count(sort = TRUE) %>% mutate(percent = n/107*100)
liuri_clean %>% group_by(category_strd) %>% count(sort = TRUE) %>% mutate(percent = n/107*100)
liuri_clean %>% group_by(category_sup) %>% count(sort = TRUE) %>% mutate(percent = n/107*100)

Save and export

Save and export the resulting corpus and statistics as dataframes:

write.csv(liuri_clean, "~/output/liuri_clean.csv")
write.csv(liuri_stats, "~/output/liuri_stats.csv")

Tokenization

Tokenization, the process of segmenting text into words, is a crucial preliminary step for text analysis, including word frequency counting, topic modeling, and sentiment analysis. However, Chinese word segmentation for early 20th-century texts presents unique challenges due to the language’s transition from classical wenyan—the literary language of the imperial administration and literati, where one character typically corresponds to one word—to a national (guoyu) and vernacular language (baihua) closer to modern Mandarin, characterized by more complex forms and diverse word combinations.

For this study, we employed the latest version (2023) of a tokenizer specifically designed for transitional Chinese, the linguistic stage of the period under investigation (Blouin et al., 2023).

# First remove artificial white spaces from the raw text 

liuri_to_token <- liuri_clean %>% 
  mutate(text2 = str_replace_all(text_seg, " ", ""))%>% 
  mutate(text2 = str_replace_all(text2, " ", "")) %>%  
  relocate(text2, .after = text_seg)

liuri_to_token <- liuri_to_token %>% mutate(length2 = nchar(text2))
liuri_to_token
# Apply the tokenizer

liuri_tokenized <- cws_on_df(liuri_to_token,
                             text_column = "text2",
                             id_column = "DocId",
                             model = "trftc_shunpao_23:zh:cws",
                             detailed_output = FALSE,
                             token_separator = " ",
                             verbose = TRUE)
liuri_tokenized


Important Note: When the text exceeds 300 characters, the tokenizer automatically splits the document into multiple lines. To reconstruct the article, we need to merge these text segments back into a continuous document.

liuri_token_merged <- liuri_tokenized %>%
  group_by(DocId) %>%
  summarise(text_token =paste(Text,collapse=''))
liuri_token_merged
# join with metadata

liuri_tokenized_meta <- left_join(liuri_token_merged, liuri_clean)
liuri_tokenized_meta <- liuri_tokenized_meta %>% relocate(text_token, .after = text_seg)
liuri_tokenized_meta <- liuri_tokenized_meta %>% mutate(lgth_tok_txt = nchar(text_token))
liuri_tokenized_meta
# save results

write.csv(liuri_tokenized_meta, "~/output/liuri_tokenized_meta.csv")

Word Frequencies

Examining word frequencies is a useful exploratory step for gaining a preliminary understanding of the vocabulary before conducting more advanced analyses, such as topic modeling or sentiment analysis.

library(tidytext)

liuri_tokens <- liuri_token_merged %>% 
  unnest_tokens(output = token, input = text_token) 

# remove noisy output 

liuri_tokens_clean <- liuri_tokens %>% 
  filter(stringr::str_detect(liuri_tokens$token, "[\\p{Han}]")) %>% 
  mutate(token = str_replace_all(token, "一", "")) %>% 
  mutate(token = str_replace_all(token, "[digits:]+", "")) %>% 
  mutate(token = str_replace_all(token, "〇", "")) %>% 
  mutate(token = str_replace_all(token, "◎", "")) %>% 
  mutate(token = str_replace_all(token, "⊙", "")) %>% 
  mutate(token = str_replace_all(token, "●", "")) %>% 
  mutate(token = str_replace_all(token, "▲", "")) %>% 
  mutate(token = str_replace_all(token, "△", "")) 

liuri_tokens_clean <- liuri_tokens_clean %>% drop_na(token)
liuri_tokens_clean
# join with metadata 

liuri_no_text <- liuri_clean %>% select(c(1:9))
liuri_tokens_meta <- left_join(liuri_tokens_clean, liuri_no_text)
liuri_tokens_meta <- liuri_tokens_meta %>% mutate(tok_length = nchar(token))
# Count tokens 

liuri_token_count <- liuri_tokens_meta %>% group_by(token, tok_length) %>% count() 
liuri_tokens_meta %>% group_by(token, tok_length) %>% count(sort = TRUE)  
# save results

write.csv(liuri_no_text, "~/output/liuri_no_text.csv")
write.csv(liuri_tokens_meta, "~/output/liuri_tokens_meta.csv")
write.csv(liuri_token_count, "~/output/liuri_token_count.csv")

Topic Modeling

In this research, we applied Structural Topic Modeling (STM), an LDA-based topic modeling method that incorporates metadata—such as publication date and article genre—allowing us to analyze how these factors influence the distribution of topics (Roberts et al. 2019).

Corpus preparation

library(stm)
library(stminsights)

# Select metadata

meta <- liuri_tokenized_meta %>% transmute(DocId, title, authors, category_sup, Year)  
meta <- as.data.frame(meta)

# Create stm corpus object 

corpus <- stm::textProcessor(liuri_tokenized_meta$text_token,
                             metadata = meta, 
                             stem = FALSE, 
                             wordLengths = c(2, Inf), 
                             customstopwords = c("中國", "日本", "可以", "學生", "我們"),
                             verbose = FALSE) 
# Determine threshold for removing words and documents based on frequency

stm::plotRemoved(corpus$documents, lower.thresh = c(0,10, by=5)) 

out <- stm::prepDocuments(corpus$documents, 
                          corpus$vocab, 
                          corpus$meta) 

# Removing 16487 of 22478 terms (16487 of 44936 tokens) due to frequency. 
# Removing 1 Documents with No Words.
# Your corpus now has 106 documents, 5991 terms and 28449 tokens.
# Inspect document removed and remove it from the initial dataset with metadata.

corpus$docs.removed
corpus.removed <- liuri_tokenized_meta[-c(97),  ]

# Inspect words removed 

wordsremoved <- as_tibble(out$words.removed) 
wordsremoved

Model building

# Build a 5-topic model 

mod.5 <- stm::stm(out$documents, 
                  out$vocab, K=5, 
                  prevalence =~ Year + category_sup, 
                  data=out$meta, verbose = FALSE)

# Incorporate year of publication and article category as co-variates

year5 <- stm::estimateEffect(1:5 ~ Year, mod.5, meta=out$meta)
cat5 <- stm::estimateEffect(1:5 ~ category_sup, mod.5, meta=out$meta)

# run_stminsights() # use stm interface for a preliminary exploration (optional)

Model exploration

Model summary

#  Topic proportions and words defining each topic

plot.STM(mod.5,"summary", n = 7)

Words per topic (beta)

library(tidytext)

td_beta5 <- tidytext::tidy(mod.5) 
options(repr.plot.width=7, repr.plot.height=8, repr.plot.res=100) 

td_beta5 %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  mutate(topic = paste0("Topic ", topic),
         term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(term, beta, fill = as.factor(topic))) +
  geom_col( show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free_y") +
  coord_flip() +
  scale_x_reordered() +
  labs(x = NULL, y = expression(beta),
       title = "Highest word probabilities for each topic",
       subtitle = "Topics related to 留日 in 東方雜誌 (1905-1948)")

Topic proportions

# Extract topic proportions by document

topicprop5 <-make.dt(mod.5, meta)
topicprop5

Topic proportions over time

# Compute topic proportions by year

topic5prop <- topicprop5%>% select(c(2:6))

topic_proportion_per_year5 <- aggregate(topic5prop, by = list(Year = topicprop5$Year), mean)

library(reshape)
dfviz5y <- melt(topic_proportion_per_year5, id.vars = "Year")

# Change color palette

library(RColorBrewer)

color_palette5 <- brewer.pal(5, "Set3") 
# Plot topic proportions over year

ggplot(dfviz5y, aes(x=Year, y=value, fill=variable)) + 
  geom_bar(stat = "identity") + ylab("proportion") + 
  scale_fill_manual(values = color_palette5, name = "Topic") + 
  theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  labs(title="Topics related to 留日 in 東方雜誌", 
       subtitle = "Topic proportion over time (5-topic)")

Topical Clusters

This section aims to assign a synthetic variable to each document, representing its topical profile. This variable will be used in the final part of the article, which explores the correlation between document topics, tonal sentiment, and the authors’ biographical profiles. To generate these synthetic variables, we applied Principal Component Analysis (PCA) and Hierarchical Clustering based on article topic proportions.

# Principal Component Analysis (PCA) on Topic Proportions 

pca5 <- topicprop5 %>% select(DocId, Topic1, Topic2, Topic3, Topic4, Topic5)
pca5 <- pca5 %>% column_to_rownames("DocId")

library(FactoMineR)

res.PCA<-PCA(pca5,graph=FALSE)
plot.PCA(res.PCA,choix='var',title="PCA Graph of Variables (topic proportions)")

plot.PCA(res.PCA,title="PCA Graph of Individuals (documents)")

# Hierarchical Clustering to cluster documents with similar topical profiles 

res.HCPC<-HCPC(res.PCA,nb.clust=5,consol=FALSE,graph=FALSE)
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 on Factor Map')

# Extract clusters 
pca_clusters <- as.data.frame(res.HCPC$data.clust)
pca_clusters <- rownames_to_column(pca_clusters, "DocId") 
pca_clusters <- pca_clusters %>% dplyr:: rename(topicluster = clust)

# Label clusters 

pca_clusters <- pca_clusters %>% mutate (topiclabel = fct_recode(topicluster, "T3. Regulations/Petitions" = "1", 
                                                                  "T2. Education/Political Reforms" = "2", 
                                                                  "T4. Diplomacy/Cultural Exchanges" = "3", 
                                                                  "T1. Sino-Japanese Tensions" = "4", 
                                                                  "T5. Science/Society" = "5"))
# join with authors (see dedicated script for how to retrieve the authors)

liuri_pca_id <- pca_clusters %>% select(DocId, topiclabel)

liuri_pca_meta <- left_join(liuri_pca_id, liuri_clean)
liuri_pca_meta <- liuri_pca_meta %>% select(-c(text, text_seg))
liuri_authors_topic <- liuri_pca_meta %>% select(DocId, authors, topiclabel)


liuri_authors_topic <- liuri_authors_topic %>% select(DocId, authors, topiclabel) %>%
  mutate(Name = str_replace_all(authors, "\\[", "")) %>%
  mutate(Name = str_replace_all(Name, "\\]", ""))%>%
  mutate(Name = str_replace_all(Name, "\\'", ""))

liuri_authors_topic <- liuri_authors_topic %>%
  separate_rows(Name, sep = ",\\s*")

liuri_authors_topic_unique <- liuri_authors_topic %>% select(authors, Name, topiclabel) %>% drop_na(Name) %>% unique()

liuri_authors_joined2 <- left_join(liuri_author_joined_clean, liuri_authors_topic_unique)
liuri_authors_joined2

Sentiment Analysis

Load Dictionaries

# NRC lexicon 

nrc_lexic <- read.delim("~/lexicon/Chinese-Traditional-NRC-EmoLex.txt")

nrc_lexic <- nrc_lexic %>% dplyr::rename(token = Chinese.Traditional.Word) %>% 
  select(token, negative, positive, English.Word) %>% 
  rename(nrc_negative = negative, nrc_positive = positive)
nrc_lexic
# Chinese (Taiwan) dictionaries

tw_lexic <- read.csv("~/lexicon/opinion_word_utf8.csv", header=FALSE)

# Rename variables

tw_lexic <- tw_lexic %>% rename(token = V1, copeopi = V2, tw_positive = V3, tw_neutral = V4, 
                                tw_negative = V5, tw_nonsent = V6, tw_nonword = V7)
tw_lexic

Join dictionaries with our corpus

liuri_senti_nrc <- liuri_tokens_meta %>% 
  select(DocId, Year, title, category_sup, authors, token, tok_length) %>%   
  inner_join(nrc_lexic) 

liuri_senti_nrc <- liuri_senti_nrc %>% unique()

liuri_senti_tw <- liuri_tokens_meta %>% 
  select(DocId, Year, title, category_sup, authors, token, tok_length) %>%   
  inner_join(tw_lexic)

liuri_senti_tw <- liuri_senti_tw %>% unique()

liuri_senti <- full_join(liuri_senti_nrc, liuri_senti_tw)
liuri_senti <- liuri_senti %>% relocate(English.Word, .after = token)
liuri_senti <- liuri_senti %>% filter(tok_length >1)
liuri_senti

Most negative/postive terms

# Compute sentiment scores for each token 

liuri_senti_token <- liuri_senti %>% select(token, English.Word, tok_length, nrc_negative, nrc_positive, 
                                            copeopi, tw_negative, tw_positive, tw_neutral, tw_nonsent, tw_nonword) %>% 
  mutate(nrc_ratio = (nrc_positive-nrc_negative), tw_ratio = (tw_positive - tw_negative)) %>% 
  group_by(token) %>% add_tally() 

liuri_senti_token <- liuri_senti_token %>% unique()
# Most negative tokens 

liuri_senti_token %>% select(token, English.Word, nrc_ratio) %>% arrange((nrc_ratio)) # based on NRC
liuri_senti_token %>% select(token, English.Word, tw_ratio) %>% arrange((tw_ratio)) # based on ANTUSD 
liuri_senti_token %>% select(token, English.Word, copeopi) %>% arrange((copeopi)) # based on CopeOpi
# Most positive articles 

liuri_senti_token %>% select(token, English.Word, nrc_ratio) %>% arrange(desc(nrc_ratio)) # based on NRC
liuri_senti_token  %>% select(token, English.Word, tw_ratio) %>% arrange(desc(tw_ratio)) # based on ANTUSD 
liuri_senti_token %>% select(token, English.Word, copeopi) %>% arrange(desc(copeopi)) # based on CopeOpi
# Plot as wordcloud

library(wordcloud2)

nrc_positive <- liuri_senti_token %>%
  filter(nrc_positive > 0) %>%  # Keep only positive sentiment tokens
  select(token, n) 
wordcloud2(nrc_positive, size = 4, backgroundColor = "grey", color = "random-light")
library(reshape2)

liuri_senti_token %>% drop_na(nrc_ratio) %>%
  mutate(sentiment = ifelse(nrc_ratio > 0, "positive", "negative")) %>%
  count(token, sentiment, sort = TRUE) %>%
  acast(token ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c("steelblue", "red"),
                   max.words = 500, scale = c(2, 2))

Most negative/positive articles

# Compute mean sentiment value per document

liuri_senti_docs <- liuri_senti %>% group_by(DocId, Year, title, category_sup, authors) %>%
  summarise(across(nrc_negative:tw_nonword, ~ mean(.x, na.rm = TRUE))) %>% 
  mutate(nrc_ratio = (nrc_positive-nrc_negative)) %>% 
  mutate(tw_ratio = (tw_positive-tw_negative))

liuri_senti_docs <- liuri_senti_docs %>% relocate(nrc_ratio, .after = nrc_positive) %>% 
  relocate(tw_ratio, .after = tw_positive)
# Most negative articles 

liuri_senti_docs %>% select(DocId, Year, category_sup, title, nrc_ratio) %>% arrange((nrc_ratio)) # based on NRC
liuri_senti_docs %>% select(DocId, Year, category_sup, title, tw_ratio) %>% arrange((tw_ratio)) # based on ANTUSD 
liuri_senti_docs %>% select(DocId, Year, category_sup, title, copeopi) %>% arrange((copeopi)) # based on CopeOpi
# Most positive articles 

liuri_senti_docs %>% select(DocId, Year, category_sup, title, nrc_ratio) %>% arrange(desc(nrc_ratio)) # based on NRC
liuri_senti_docs  %>% select(DocId, Year, category_sup, title, tw_ratio) %>% arrange(desc(tw_ratio)) # based on ANTUSD 
liuri_senti_docs %>% select(DocId, Year, category_sup, title, copeopi) %>% arrange(desc(copeopi)) # based on CopeOpi

Sentiment value by category

# Compute sentiment value by category

liuri_senti_category <- liuri_senti_docs %>% 
  group_by(category_sup) %>%
  summarise(across(c("nrc_negative", "nrc_positive", "copeopi", "tw_negative", "tw_positive"), ~ mean(.x, na.rm = TRUE)))  %>% 
  mutate(tw_ratio = (tw_positive-tw_negative)) %>% 
  mutate(nrc_ratio = (nrc_positive-nrc_negative)) 

liuri_senti_category <- liuri_senti_category %>% relocate(nrc_ratio, .after = nrc_positive) %>% 
  relocate(tw_ratio, .after = tw_positive)
liuri_senti_category

Sentiment over time

# Compute sentiment value over time 

liuri_senti_year<- liuri_senti_docs %>%
  group_by(Year) %>%
  summarise(across(c("nrc_negative", "nrc_positive", "copeopi", "tw_negative", "tw_positive"), ~ mean(.x, na.rm = TRUE)))  %>% 
  mutate(tw_ratio = (tw_positive-tw_negative)) %>% 
  mutate(nrc_ratio = (nrc_positive-nrc_negative)) %>% 
  relocate(nrc_ratio, .after = nrc_positive) %>% 
  relocate(tw_ratio, .after = tw_positive)
p1 <- liuri_senti_year %>% 
  ggplot() + 
  geom_col(aes(x = Year, y = nrc_ratio)) + 
  labs(title = "Sentiment analysis of 留日 in 東方雜誌", 
       subtitle = "Sentiment value over time", 
       x = "Year", 
       y = "Mean value",
       caption = "Based on NRC Lexicon (Canada)")

p2 <- liuri_senti_year %>% 
  ggplot() + 
  geom_col(aes(x = Year, y = tw_ratio)) + 
  labs(title = "Sentiment analysis of 留日 in 東方雜誌", 
       subtitle = "Sentiment value over time", 
       x = "Year", 
       y = "Mean value",
       caption = "Based on ANTUSD Lexicon (Taiwan)")

p3 <- liuri_senti_year %>% 
  ggplot() + 
  geom_col(aes(x = Year, y = copeopi)) + 
  labs(title = "Sentiment analysis of 留日 in 東方雜誌", 
       subtitle = "Sentiment value over time", 
       x = "Year", 
       y = "Mean value",
       caption = "Based on CopeOpi Lexicon (Taiwan)")


library(plotly)
ggplotly(p1)
ggplotly(p2)
ggplotly(p3)

Sentiment and authors

# Compute sentiment value by author 

liuri_senti_authors <- liuri_senti_docs %>%
  group_by(authors) %>%
  summarise(across(c("nrc_negative", "nrc_positive", "copeopi", "tw_negative", "tw_positive"), ~ mean(.x, na.rm = TRUE)))  %>% 
  mutate(tw_ratio = (tw_positive-tw_negative)) %>% 
  mutate(nrc_ratio = (nrc_positive-nrc_negative)) %>% 
  relocate(nrc_ratio, .after = nrc_positive) %>% 
  relocate(tw_ratio, .after = tw_positive)
# Most negative authors 

liuri_senti_authors %>% select(authors, nrc_ratio) %>% arrange((nrc_ratio)) # based on NRC
liuri_senti_authors %>% select(authors, tw_ratio) %>% arrange((tw_ratio)) # based on ANTUSD 
liuri_senti_authors %>% select(authors, copeopi) %>% arrange((copeopi)) # based on CopeOpi
# Most positive authors 

liuri_senti_authors %>% select(authors, nrc_ratio) %>% arrange(desc(nrc_ratio)) # based on NRC
liuri_senti_authors  %>% select(authors, tw_ratio) %>% arrange(desc(tw_ratio)) # based on ANTUSD 
liuri_senti_authors %>% select(authors, copeopi) %>% arrange(desc(copeopi)) # based on CopeOpi
# Join sentiment values with topic proportions

liuri_topic_senti_docs <- left_join(pca_clusters, liuri_senti_docs)
# Export sentiment values

write.csv(liuri_senti_token, "~/output/liuri_senti_token.csv")
write.csv(liuri_senti_docs, "~/output/liuri_senti_docs.csv")
write.csv(liuri_senti_year, "~/output/liuri_senti_year.csv")
write.csv(liuri_senti_authors, "~/output/liuri_senti_authors.csv")
write.csv(liuri_senti_category, "~/output/liuri_senti_category.csv")
write.csv(liuri_topic_senti_docs, "~/output/liuri_topic_senti_docs.csv")

Going Further

For the next steps, see our script on GitHub for extracting and analyzing biographical data related to the authors. Scripts for building and exploring a corpus centered on liuMei using the same method are also available on GitHub.