系統參數設定

getwd()
## [1] "C:/Users/sammy/Documents/sma/week4"
Sys.setlocale(category = "LC_ALL", locale = "zh_TW.UTF-8") # 避免中文亂碼
## Warning in Sys.setlocale(category = "LC_ALL", locale = "zh_TW.UTF-8"): 作業系統
## 回報無法實現設定語區為 "zh_TW.UTF-8" 的要求
## [1] ""

安裝需要的packages

packages = c("dplyr", "tidytext", "jiebaR", "gutenbergr", "stringr", "wordcloud2", "ggplot2", "tidyr", "scales", "curl")
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)

載入packages

require(dplyr)
## Loading required package: dplyr
## Warning: package 'dplyr' was built under R version 4.0.4
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
require(tidytext)
## Loading required package: tidytext
## Warning: package 'tidytext' was built under R version 4.0.4
require(jiebaR)
## Loading required package: jiebaR
## Warning: package 'jiebaR' was built under R version 4.0.4
## Loading required package: jiebaRD
## Warning: package 'jiebaRD' was built under R version 4.0.4
require(gutenbergr)
## Loading required package: gutenbergr
## Warning: package 'gutenbergr' was built under R version 4.0.4
require(stringr)
## Loading required package: stringr
require(wordcloud2)
## Loading required package: wordcloud2
## Warning: package 'wordcloud2' was built under R version 4.0.4
require(ggplot2)
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 4.0.4
require(tidyr)
## Loading required package: tidyr
## Warning: package 'tidyr' was built under R version 4.0.4
require(scales)
## Loading required package: scales
## Warning: package 'scales' was built under R version 4.0.4
require(curl)
## Loading required package: curl
## Warning: package 'curl' was built under R version 4.0.4
require(magrittr)
## Loading required package: magrittr
## Warning: package 'magrittr' was built under R version 4.0.4
## 
## Attaching package: 'magrittr'
## The following object is masked from 'package:tidyr':
## 
##     extract

西遊記 from gutenberg

# 下載 "西遊記" 書籍,並且將text欄位為空的行給清除,以及將重複的語句清除

jttw <- gutenberg_download(23962, mirror = "http://mirrors.xmission.com/gutenberg/")%>% filter(text!="") %>% distinct(gutenberg_id, text)

#西遊記每章節的開頭都會有“第X回”的詞 #使用正規表示式,將句子區分章節

jttw <- jttw %>% 
  mutate(chapter = cumsum(str_detect(jttw$text, regex("^第.*回|^ 第.*回"))))

#用head()檢查一下,已斷句成功

head(jttw,20)
## # A tibble: 20 x 3
##    gutenberg_id text                                                     chapter
##           <int> <chr>                                                      <int>
##  1        23962 第一回     靈根育孕源流出 心性修持大道生                      1
##  2        23962   詩曰:                                                     1
##  3        23962     混沌未分天地亂,茫茫渺渺無人見。                       1
##  4        23962     自從盤古破鴻濛,開闢從茲清濁辨。                       1
##  5        23962     覆載群生仰至仁,發明萬物皆成善。                       1
##  6        23962     欲知造化會元功,須看西遊釋厄傳。                       1
##  7        23962 蓋聞天地之數,有十二萬九千六百歲為一元。將一元分為十二會,乃子、丑、寅~       1
##  8        23962 、卯、辰、巳、午、未、申、酉、戌、亥之十二支也。每會該一萬八百歲。且就~       1
##  9        23962 一日而論:子時得陽氣,而丑則雞鳴﹔寅不通光,而卯則日出﹔辰時食後,而巳~       1
## 10        23962 則挨排﹔日午天中,而未則西蹉﹔申時晡,而日落酉,戌黃昏,而人定亥。譬於~       1
## 11        23962 大數,若到戌會之終,則天地昏曚而萬物否矣。再去五千四百歲,交亥會之初,~       1
## 12        23962 則當黑暗,而兩間人物俱無矣,故曰混沌。又五千四百歲,亥會將終,貞下起元~       1
## 13        23962 ,近子之會,而復逐漸開明。邵康節曰::「冬至子之半,天心無改移。一陽初~       1
## 14        23962 動處,萬物未生時。」到此,天始有根。再五千四百歲,正當子會,輕清上騰,~       1
## 15        23962 有日,有月,有星,有辰。日、月、星、辰,謂之四象。故曰,天開於子。又經~       1
## 16        23962 五千四百歲,子會將終,近丑之會,而逐漸堅實。《易》曰:「大哉乾元!至哉~       1
## 17        23962 坤元!萬物資生,乃順承天。」至此,地始凝結。再五千四百歲,正當丑會,重~       1
## 18        23962 濁下凝,有水,有火,有山,有石,有土。水、火、山、石、土,謂之五形。故~       1
## 19        23962 曰,地闢於丑。又經五千四百歲,丑會終而寅會之初,發生萬物。曆曰:「天氣~       1
## 20        23962 下降,地氣上升﹔天地交合,群物皆生。」至此,天清地爽,陰陽交合。再五千~       1

#確認總章節數

max(jttw$chapter)
## [1] 100

使用西遊記專有名詞字典

jieba_tokenizer <- worker(user="Journey.traditional.dict",stop_word="stop_words.txt")

設定斷詞

# 設定斷詞function
jttw_tokenizer <- function(t) {
  lapply(t, function(x) {
    tokens <- segment(x, jieba_tokenizer)
    return(tokens)
  })
}

進行斷詞

tokens <- jttw %>% unnest_tokens(word, text, token = jttw_tokenizer)

#完成斷詞
str(tokens)
## tibble [359,774 x 3] (S3: tbl_df/tbl/data.frame)
##  $ gutenberg_id: int [1:359774] 23962 23962 23962 23962 23962 23962 23962 23962 23962 23962 ...
##  $ chapter     : int [1:359774] 1 1 1 1 1 1 1 1 1 1 ...
##  $ word        : chr [1:359774] "第一回" "靈根育孕" "源流" "出" ...

計算詞彙的出現次數

tokens_count <- tokens %>% 
  filter(nchar(.$word)>1) %>% ##如果詞彙只有一個字則不列入計算
  group_by(word) %>% 
  summarise(sum = n()) %>% 
  filter(sum > 10) %>%
  arrange(desc(sum))

印出最常見的20個詞彙

head(tokens_count, 20)
## # A tibble: 20 x 2
##    word    sum
##    <chr> <int>
##  1 行者   3932
##  2 八戒   1615
##  3 師父   1543
##  4 三藏   1282
##  5 唐僧    963
##  6 大聖    877
##  7 沙僧    700
##  8 菩薩    691
##  9 和尚    629
## 10 妖精    612
## 11 長老    502
## 12 國王    438
## 13 徒弟    426
## 14 獃子    414
## 15 悟空    381
## 16 大王    355
## 17 老孫    320
## 18 小妖    283
## 19 兄弟    259
## 20 寶貝    253

文字雲

tokens_count %>% wordcloud2()

以語句數計算各章節長度

plot <- 
  bind_rows(
    jttw %>% 
      group_by(chapter) %>% 
      summarise(count = n(), type="sentences"),
    tokens %>% 
      group_by(chapter) %>% 
      summarise(count = n(), type="words")) %>% 
  group_by(type)%>%
  ggplot(aes(x = chapter, y=count, fill="type", color=factor(type))) +
  geom_line(size = 1) + 
  ggtitle("各章節的句子總數") + 
  xlab("章節") + 
  ylab("句子數量")
plot

計算四位主角出現的章回與頻率

# google 四位主角的別名,並存成變數

# 悟空 
wukong_alias = c("行者","孫悟空", "石猴","美猴王","猢猻","弼馬溫","妖仙","妖猴","猴頭","潑猴","齊天大聖","大聖","猴子","這廝","一老猿","上仙","尿精猴子","神猴","孫行者","心猿","金公","五百年前大鬧天宮的齊天大聖","大師兄","悟空","孫猴子","靈明石猴","孫長老","鬥戰勝佛")
# 唐僧
tangseng_alias = c("師父", "唐僧", "三藏", "唐三藏", "唐長老", "聖僧", "玄奘", "高僧", "唐御弟", "金蟬子", "唐玄奘")
# 沙悟淨
wujing_alias = c("沙僧", "悟淨", "沙和尚", "捲簾大將", "金身羅漢", "沙師弟", "老沙")
# 豬八戒
bajie_alias = c("八戒", "悟能", "豬剛", "老豬", "豬悟能", "豬八戒", "天蓬元帥", "豬剛鬣", "獃子")

孫悟空

# 計算悟空出現的頻率及比例
wukong_count = tokens %>% 
  filter(nchar(.$word) > 1) %>%                     # 保留非空白資料
  mutate(total = nrow(tokens)) %>%                # 資料總列數   
  group_by(word) %>% 
  filter(word %in% wukong_alias) %>%              # 保留悟空的等同詞
  summarise(count = n(), total = mean(total)) %>% # 計算群組內的數量
  arrange(desc(count))                            # 根據出現次數由大至小排列
head(wukong_count, 28)
## # A tibble: 22 x 3
##    word     count  total
##    <chr>    <int>  <dbl>
##  1 行者      3932 359774
##  2 大聖       877 359774
##  3 悟空       381 359774
##  4 孫行者     220 359774
##  5 孫悟空     115 359774
##  6 齊天大聖    94 359774
##  7 猴子        78 359774
##  8 猢猻        53 359774
##  9 弼馬溫      47 359774
## 10 美猴王      41 359774
## # ... with 12 more rows
# 計算悟空在各章節中出現的頻率
wukong_plot = tokens %>% 
  filter(nchar(.$word) > 1) %>%
  filter(word %in% wukong_alias) %>%
  group_by(chapter) %>%  
  summarise(count = n()) %>%
  ggplot(aes(x = chapter, y=count)) +
  geom_col() + 
  ggtitle("各章節悟空出現總數") + 
  xlab("章節") + 
  ylab("悟空數量")
wukong_plot

由此圖可得知主角悟空在西遊記一書中並沒有每回都被提及,在第 9、10、11、13、29 回並沒有出現其詞彙,因此學生針對這幾回分析悟空沒有出現的原因:

(1)9、10、11: 第7回中可知如來佛祖收拾了悟空於五行山,而第 8 回則是整回在敘述如來佛祖與眾神在討論三藏取經之事。第 9 回至第 11 回則在描述唐太宗登朝宣布大赦天下,嚴禁毀僧謗佛的前因後果。之後眾人推舉陳玄奘主持水陸大會,為取經之路揭開了序幕。

(2)13: 唐僧騎馬西行時,途中被虎魔王部下生擒,最後太白金星搭救了唐僧。唐僧行至兩界山時,忽聽喊聲如雷:「我師父來也!」雖然此回的結尾孫悟空出現了,但由於小說僅描述了悟空大喊的話語,並沒有提及是誰大喊,因此此回也沒有有關於悟空的詞彙。

(3)29: 在第 28 回中,悟空因被唐僧驅逐而回到花果山。另一邊唐僧誤入妖穴被擒。八戒、沙僧與老妖黃袍怪開始了打鬥的情節。因此,在第 29 回中,整回都在講述唐僧、八戒、沙僧與老妖黃袍怪的故事,悟空並未被提及。

唐僧

# 計算唐僧出現的頻率及比例
tangseng_count = tokens %>% 
  filter(nchar(.$word) > 1) %>%                     # 保留非空白資料
  mutate(total = nrow(tokens)) %>%                # 資料總列數  
  group_by(word) %>% 
  filter(word %in% tangseng_alias) %>%            # 保留唐僧的等同詞
  summarise(count = n(), total = mean(total)) %>% # 計算群組內的數量
  arrange(desc(count))                            # 根據出現次數由大至小排列
head(tangseng_count,11)
## # A tibble: 10 x 3
##    word   count  total
##    <chr>  <int>  <dbl>
##  1 師父    1543 359774
##  2 三藏    1282 359774
##  3 唐僧     963 359774
##  4 聖僧     141 359774
##  5 玄奘      65 359774
##  6 唐三藏    54 359774
##  7 唐長老    18 359774
##  8 高僧      16 359774
##  9 唐御弟     8 359774
## 10 金蟬子     5 359774
# 計算唐僧在各章節中出現的頻率
tangseng_plot = tokens %>% 
  filter(nchar(.$word) > 1) %>%
  filter(word %in% tangseng_alias) %>%
  group_by(chapter) %>%  
  summarise(count = n()) %>%
  ggplot(aes(x = chapter, y=count)) +
  geom_col() + 
  ggtitle("各章節唐僧出現總數") + 
  xlab("章節") + 
  ylab("唐僧數量")
tangseng_plot

由此圖可得知主角唐僧在西遊記一書中並沒有每回都被提及,在第 3、4、6、11 回並沒有出現其詞彙,因此學生針對這幾回分析唐僧沒有出現的原因:

(1)3、4、6: 分析此處時,學生對於前 7 回部分回數出現與唐僧相關之詞感到奇怪,尤其第 2 回,因為唐僧第 8 回才開始出現在此書裡,因此學生查看了第 1、2 回的故事內容才發現問題所在,由於故事開始時悟空拜祖師為師父,因此前 7 回裡悟空所說的師父皆是指祖師而不是唐僧。

(2)11: 此處為太宗還魂過程,並未提及唐僧。

沙悟淨

# 計算沙悟淨出現的頻率及比例
wujing_count = tokens %>% 
  filter(nchar(.$word) > 1) %>%                     # 保留非空白資料
  mutate(total = nrow(tokens)) %>%                # 資料總列數  
  group_by(word) %>% 
  filter(word %in% wujing_alias) %>%              # 保留沙悟淨的等同詞
  summarise(count = n(), total = mean(total)) %>% # 計算群組內的數量
  arrange(desc(count))                            # 根據出現次數由大至小排列
head(wujing_count,7)
## # A tibble: 6 x 3
##   word     count  total
##   <chr>    <int>  <dbl>
## 1 沙僧       700 359774
## 2 沙和尚      89 359774
## 3 悟淨        52 359774
## 4 捲簾大將    11 359774
## 5 金身羅漢     3 359774
## 6 老沙         2 359774
# 計算沙悟淨在各章節中出現的頻率
wujing_plot = tokens %>% 
  filter(nchar(.$word) > 1) %>%
  filter(word %in% wujing_alias) %>%
  group_by(chapter) %>%  
  summarise(count = n()) %>%
  ggplot(aes(x = chapter, y=count)) +
  geom_col() + 
  ggtitle("各章節沙悟淨出現總數") +
  xlab("章節") + 
  ylab("沙悟淨數量")
wujing_plot

由此圖可得知沙悟淨在本書第 22 回被唐僧收為徒弟,另外,在第 51 回並沒有出現其詞彙,因此學生針對第51回分析悟空沒有出現的原因:

(1)51:在第 50 回中,唐僧、沙悟淨及豬八戒因為不乖乖聽悟空的話躲在他畫的保護圈子裡,擅自離開而後立刻被妖怪抓走,悟空回來後發現三人已不見,因此 51 回即為悟空和綁架三人的妖怪大戰的特寫,並未提及沙悟淨。

豬八戒

# 計算豬八戒出現的頻率及比例
bajie_count = tokens %>% 
  filter(nchar(.$word) > 1) %>%                     # 保留非空白資料
  mutate(total = nrow(tokens)) %>%                # 資料總列數  
  group_by(word) %>% 
  filter(word %in% bajie_alias) %>%               # 保留豬八戒的等同詞
  summarise(count = n(), total = mean(total)) %>% # 計算群組內的數量
  arrange(desc(count))                            # 根據出現次數由大至小排列
head(bajie_count,9)
## # A tibble: 8 x 3
##   word     count  total
##   <chr>    <int>  <dbl>
## 1 八戒      1615 359774
## 2 獃子       414 359774
## 3 豬八戒     128 359774
## 4 老豬        84 359774
## 5 天蓬元帥    16 359774
## 6 豬悟能      13 359774
## 7 豬剛         5 359774
## 8 悟能         3 359774
# 計算豬八戒在各章節中出現的頻率
bajie_plot = tokens %>% 
  filter(nchar(.$word) > 1) %>%
  filter(word %in% bajie_alias) %>%
  group_by(chapter) %>%  
  summarise(count = n()) %>%
  ggplot(aes(x = chapter, y=count)) +
  geom_col() + 
  ggtitle("各章節豬八戒出現總數") + 
  xlab("章節") + 
  ylab("豬八戒數量")
bajie_plot

由此圖可得知豬八戒於第 18 回開始出現,51 回沒出現的原因同沙悟淨。

各主角出現章回綜合比較

wukong <- tokens %>%
  filter(word %in% wukong_alias) %>%
  group_by(chapter) %>% 
  summarise(count = n()) %>% 
  mutate(word = "悟空")

tangseng <- tokens %>%
  filter(word %in% tangseng_alias) %>%
  group_by(chapter) %>% 
  summarise(count = n()) %>% 
  mutate(word = "唐僧")

bajie <- tokens %>%
  filter(word %in% bajie_alias) %>%
  group_by(chapter) %>% 
  summarise(count = n()) %>% 
  mutate(word = "豬八戒")

wujing <- tokens %>%
  filter(word %in% wujing_alias) %>%
  group_by(chapter) %>% 
  summarise(count = n()) %>% 
  mutate(word = "沙悟淨")

bind_rows(wukong, tangseng, wujing, bajie) %>%
  ggplot(aes(x = chapter, y=count, fill=word)) +
  geom_col(show.legend = F) +
  facet_wrap(~word, ncol = 1) + 
  ggtitle("各主角比較") + 
  xlab("章節") + 
  ylab("出現次數")

綜觀來說,西遊記的四位主角在前 20 回可能會因為介紹各個人物登場而各自有沒被提到的時間,但在 20 回後各個角色在每回基本上都有被提及。此外,其中由各個圖表比較可得知悟空及唐僧被提及的次數相較於沙悟淨及豬八戒來的高出許多,由此可知悟空與唐僧在故事中的戲分較多也較為關鍵。