Sys.setlocale(category = "LC_ALL", locale = "zh_TW.UTF-8") # 避免中文亂碼
[1] "LC_CTYPE=zh_TW.UTF-8;LC_NUMERIC=C;LC_TIME=zh_TW.UTF-8;LC_COLLATE=zh_TW.UTF-8;LC_MONETARY=zh_TW.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=en_US.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C"

安裝需要的packages

packages = c("readr", "dplyr", "stringr", "jiebaR", "tidytext", "NLP", "readr", "tidyr", "ggplot2", "ggraph", "igraph", "scales", "reshape2", "widyr")
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)
require(readr)
require(dplyr)
require(stringr)
require(jiebaR)
require(tidytext)
require(NLP)
require(tidyr)
require(ggplot2)
require(ggraph)
require(igraph)
require(scales)
require(reshape2)
require(widyr)
mask<- read_csv("./nCoV_2019_mask_articleMetaData.csv") %>%
              mutate(sentence=gsub("[\n]{2,}", "。", sentence)) # 將兩個以上換行符號轉成句號
Parsed with column specification:
cols(
  artTitle = col_character(),
  artDate = col_date(format = ""),
  artTime = col_time(format = ""),
  artUrl = col_character(),
  artPoster = col_character(),
  artCat = col_character(),
  commentNum = col_double(),
  push = col_double(),
  boo = col_double(),
  sentence = col_character()
)
mask

ptt articles example:
https://www.ptt.cc/bbs/nCoV2019/M.1581147569.A.FF8.html https://www.ptt.cc/bbs/nCoV2019/M.1580804534.A.9C1.html

先做斷句

sample_data <- mask %>% head(4)
# 以全形或半形 驚歎號、問號、分號 以及 全形句號 爲依據進行斷句
sample_sentences <- strsplit(sample_data$sentence,"[。!;?!?;]")
# 回傳結果為list of vectors,每個vector的內容為每篇文章的斷句結果
sample_sentences
[[1]]
 [1] "這幾天上網查了一下"                                                                                                                                
 [2] "看新聞常提到「外科口罩」"                                                                                                                          
 [3] "可是我看這篇\nhttps://www.hcshb.gov.tw/home.jsp"                                                                                                   
 [4] "mserno=200802220002&serno=200802220015&menudata=HcshbMenu&contlink=hcshb/ap/news_view.jsp&dataserno=201407080003\n真正的外科口罩是第二等級醫療器材"
 [5] ""                                                                                                                                                  
 [6] "所以新聞說的是一般常見的第一等級醫用口罩"                                                                                                          
 [7] ""                                                                                                                                                  
 [8] "只是要查一下許可證字號"                                                                                                                            
 [9] ""                                                                                                                                                  
[10] "許可證字號可以到這查\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001Mobile.aspx\n可是我看第一等級的口罩"                                                
[11] "醫器類別名稱"                                                                                                                                      
[12] "有的是 I4040外科手術用衣物(外科手術衣及面罩)"                                                                                                    
[13] "有的是 I4040醫療用衣物"                                                                                                                            
[14] "有什麼差別嗎"                                                                                                                                      
[15] ""                                                                                                                                                  
[16] "我怕我說不清楚 附上網址\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001DMobile.aspx"                                                                    
[17] "Type=Lic&LicId=43001177\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001DMobile.aspx"                                                                    
[18] "Type=Lic&LicId=93006107\n可是我又看到這篇\nhttps://www.fda.gov.tw/Tc/lawContent.aspx"                                                              
[19] "cid=55&pn=20&id=740\n所以 i.4040醫療用衣物 是新的名稱嗎"                                                                                           
[20] ""                                                                                                                                                  
[21] "可是有的口罩有 CNS 國家標準"                                                                                                                       
[22] "是不是新的法規改了"                                                                                                                                
[23] " 要求不一樣了"                                                                                                                                     
[24] ""                                                                                                                                                  
[25] "我打了這麼多 會不會其實很多都理解錯誤"                                                                                                             
[26] ""                                                                                                                                                  
[27] "還是其實都沒查到重點"                                                                                                                              
[28] ""                                                                                                                                                  
[29] "臺灣產才是重點"                                                                                                                                    
[30] ""                                                                                                                                                  
[31] "我越查越糊塗"                                                                                                                                      
[32] "口罩到底怎麼挑"                                                                                                                                    
[33] "\n"                                                                                                                                                

[[2]]
[1] "在網路上有稍微查了一下,好像還是有快遞在運作" "請問台灣要寄送的話,可以找哪一家快遞呢"      
[3] " 有沒有網友有最近有打算寄送物品的"            ""                                            
[5] "(我是要寄送口罩、酒精棉片給我朋友)"          

[[3]]
[1] "昨天去家樂福買了口罩\n康匠「蹦米香」3D彈力口罩\nhttps://reurl.cc/XXzeoM\n看了包裝有許可證號跟醫療口罩\n但有個疑問 與一般新聞報導的外科口罩\n買的口罩與外科口罩不一樣\n請問該款也有防疫效果嗎 謝謝\n"

[[4]]
[1] "今天(20200126)下午我人在宜蘭家樂福,看逛賣場的人起碼90%以上\n都沒戴口罩"
[2] ""                                                                         
[3] "但家樂福大多數工作人員都有戴口罩,沒戴口罩的以把貨物上架的工作人\n員為主" 
[4] ""                                                                         
[5] "各位可以講講,你所看到的情況嗎"                                           
[6] ""                                                                         
[7] "PS:除了醫院外,目前在公共場所會戴口罩的人真的還是少數啊"                 
[8] "\n"                                                                       
# unlist會將list中所有的vector展開成一個一維的vector
sentences <- unlist(sample_sentences)
sentences
 [1] "這幾天上網查了一下"                                                                                                                                                                                 
 [2] "看新聞常提到「外科口罩」"                                                                                                                                                                           
 [3] "可是我看這篇\nhttps://www.hcshb.gov.tw/home.jsp"                                                                                                                                                    
 [4] "mserno=200802220002&serno=200802220015&menudata=HcshbMenu&contlink=hcshb/ap/news_view.jsp&dataserno=201407080003\n真正的外科口罩是第二等級醫療器材"                                                 
 [5] ""                                                                                                                                                                                                   
 [6] "所以新聞說的是一般常見的第一等級醫用口罩"                                                                                                                                                           
 [7] ""                                                                                                                                                                                                   
 [8] "只是要查一下許可證字號"                                                                                                                                                                             
 [9] ""                                                                                                                                                                                                   
[10] "許可證字號可以到這查\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001Mobile.aspx\n可是我看第一等級的口罩"                                                                                                 
[11] "醫器類別名稱"                                                                                                                                                                                       
[12] "有的是 I4040外科手術用衣物(外科手術衣及面罩)"                                                                                                                                                     
[13] "有的是 I4040醫療用衣物"                                                                                                                                                                             
[14] "有什麼差別嗎"                                                                                                                                                                                       
[15] ""                                                                                                                                                                                                   
[16] "我怕我說不清楚 附上網址\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001DMobile.aspx"                                                                                                                     
[17] "Type=Lic&LicId=43001177\nhttps://info.fda.gov.tw/MLMS_MOBILE/H0001DMobile.aspx"                                                                                                                     
[18] "Type=Lic&LicId=93006107\n可是我又看到這篇\nhttps://www.fda.gov.tw/Tc/lawContent.aspx"                                                                                                               
[19] "cid=55&pn=20&id=740\n所以 i.4040醫療用衣物 是新的名稱嗎"                                                                                                                                            
[20] ""                                                                                                                                                                                                   
[21] "可是有的口罩有 CNS 國家標準"                                                                                                                                                                        
[22] "是不是新的法規改了"                                                                                                                                                                                 
[23] " 要求不一樣了"                                                                                                                                                                                      
[24] ""                                                                                                                                                                                                   
[25] "我打了這麼多 會不會其實很多都理解錯誤"                                                                                                                                                              
[26] ""                                                                                                                                                                                                   
[27] "還是其實都沒查到重點"                                                                                                                                                                               
[28] ""                                                                                                                                                                                                   
[29] "臺灣產才是重點"                                                                                                                                                                                     
[30] ""                                                                                                                                                                                                   
[31] "我越查越糊塗"                                                                                                                                                                                       
[32] "口罩到底怎麼挑"                                                                                                                                                                                     
[33] "\n"                                                                                                                                                                                                 
[34] "在網路上有稍微查了一下,好像還是有快遞在運作"                                                                                                                                                       
[35] "請問台灣要寄送的話,可以找哪一家快遞呢"                                                                                                                                                             
[36] " 有沒有網友有最近有打算寄送物品的"                                                                                                                                                                  
[37] ""                                                                                                                                                                                                   
[38] "(我是要寄送口罩、酒精棉片給我朋友)"                                                                                                                                                                 
[39] "昨天去家樂福買了口罩\n康匠「蹦米香」3D彈力口罩\nhttps://reurl.cc/XXzeoM\n看了包裝有許可證號跟醫療口罩\n但有個疑問 與一般新聞報導的外科口罩\n買的口罩與外科口罩不一樣\n請問該款也有防疫效果嗎 謝謝\n"
[40] "今天(20200126)下午我人在宜蘭家樂福,看逛賣場的人起碼90%以上\n都沒戴口罩"                                                                                                                          
[41] ""                                                                                                                                                                                                   
[42] "但家樂福大多數工作人員都有戴口罩,沒戴口罩的以把貨物上架的工作人\n員為主"                                                                                                                           
[43] ""                                                                                                                                                                                                   
[44] "各位可以講講,你所看到的情況嗎"                                                                                                                                                                     
[45] ""                                                                                                                                                                                                   
[46] "PS:除了醫院外,目前在公共場所會戴口罩的人真的還是少數啊"                                                                                                                                           
[47] "\n"                                                                                                                                                                                                 

但在unlist後我們就沒辦法判別,出現的每一句話是出自於哪篇文章了
我們希望在unlist的同時能夠保留句子屬於哪篇文章的資訊

rep function

# rep(x, times = 1, length.out = NA, each = 1)
# 當給定兩個vector長度相同時,rep function會自動對齊兩個vector的值。
# 前面的vector決定要重複的值
# 後面的vector則決定要重複的次數
# ex.
rep(c("Social", "Media"), c(2,5))
[1] "Social" "Social" "Media"  "Media"  "Media"  "Media"  "Media" 
# rep function會自動對齊兩個vector,
# "Social" 會重複2次,"Media"會重複5次
# 原本資料的artUrl
sample_data$artUrl
[1] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html"
[3] "https://www.ptt.cc/bbs/nCoV2019/M.1580015494.A.42A.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
# 回傳每篇文章的斷句後,包含了幾個句(vector的長度)
sapply(sample_sentences, length)
[1] 33  5  1  8
# 使用rep去配對原本資料的artUrl以及sapply的回傳(每篇文章包含了幾個句)
# 產生的長度會與 unlist(sample_sentences) 的長度一樣,
# 兩邊join起來就可以新增一個欄位代表每個句子來自哪篇文章
rep(sample_data$artUrl, sapply(sample_sentences, length))
 [1] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
 [3] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
 [5] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
 [7] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
 [9] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[11] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[13] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[15] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[17] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[19] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[21] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[23] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[25] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[27] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[29] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[31] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html"
[33] "https://www.ptt.cc/bbs/nCoV2019/M.1580010768.A.8DE.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html"
[35] "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html"
[37] "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580012814.A.D16.html"
[39] "https://www.ptt.cc/bbs/nCoV2019/M.1580015494.A.42A.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
[41] "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
[43] "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
[45] "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html" "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
[47] "https://www.ptt.cc/bbs/nCoV2019/M.1580032571.A.518.html"
data.frame(artUrl=rep(sample_data$artUrl, sapply(sample_sentences, length)),
           sentences = unlist(sample_sentences))

對全部的文章進行斷句,並儲存結果

# 以全形或半形 驚歎號、問號、分號 以及 全形句號 爲依據進行斷句
mask_sentences <- strsplit(mask$sentence,"[。!;?!?;]")
# 將每句句子,與他所屬的文章連結配對起來,整理成一個dataframe
mask_sentences <- data.frame(
                        artUrl = rep(mask$artUrl, sapply(mask_sentences, length)), 
                        sentence = unlist(mask_sentences)
                      ) %>%
                      filter(!str_detect(sentence, regex("^(\t|\n| )*$")))

mask_sentences$sentence <- as.character(mask_sentences$sentence)

mask_sentences

接着做斷詞

1.初始化斷詞器

# 使用默認參數初始化一個斷詞引擎
# 先不使用任何的字典和停用詞
jieba_tokenizer = worker()

chi_tokenizer <- function(t) {
  lapply(t, function(x) {
    if(nchar(x)>1){
      tokens <- segment(x, jieba_tokenizer)
      # 去掉字串長度爲1的詞彙
      tokens <- tokens[nchar(tokens)>1]
      return(tokens)
    }
  })
}

2.斷詞與整理斷詞結果

# 進行斷詞,並計算各詞彙在各文章中出現的次數
mask_words <- mask_sentences %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!str_detect(word, regex("[0-9a-zA-Z]"))) %>%
  count(artUrl, word, sort = TRUE)
mask_words
# 計算每篇文章包含的詞數
total_words <- mask_words %>% 
  group_by(artUrl) %>% 
  summarize(total = sum(n))
total_words
# 合併 mask_words(每個詞彙在每個文章中出現的次數)
# 與 total_words(每篇文章的詞數)
# 新增各個詞彙在所有詞彙中的總數欄位
mask_words <- left_join(mask_words, total_words)
Joining, by = "artUrl"
mask_words

計算 tf-idf

# 以每篇文章爲單位,計算每個詞彙在的tf-idf值
mask_words_tf_idf <- mask_words %>%
  bind_tf_idf(word, artUrl, n)
mask_words_tf_idf
# 選出每篇文章,tf-idf最大的十個詞
mask_words_tf_idf %>% 
  group_by(artUrl) %>%
  top_n(10) %>%
  arrange(desc(artUrl))
Selecting by tf_idf
# 選每篇文章,tf-idf最大的十個詞,
# 並查看每個詞被選中的次數
mask_words_tf_idf %>% 
  group_by(artUrl) %>%
  top_n(10) %>%
  arrange(desc(artUrl)) %>%
  ungroup() %>%
  count(word, sort=TRUE)
Selecting by tf_idf

因爲我們是以每篇文章爲一個document單位(總共有721個document)
因此我們就不畫課本第三章中,比較各document中tf-idf較高的詞彙比較圖

jiebar and ngrams

# 使用結巴斷詞,並搭配NLP packages中的 ngrams function
# e.g.
tokens <- segment("外出請戴口罩", jieba_tokenizer)
tokens
[1] "外出" "請戴" "口罩"
bigram <- ngrams(tokens, 2)
bigram
[[1]]
[1] "外出" "請戴"

[[2]]
[1] "請戴" "口罩"
# Combine each bigrams into a single string, with the " " as the seperater.
bigram <- lapply(bigram, paste, collapse = " ")
unlist(bigram)
[1] "外出 請戴" "請戴 口罩"

bigram function

# remove stopwords
jieba_tokenizer = worker()

# unnest_tokens 使用的bigram分詞函數
# Input: a character vector
# Output: a list of character vectors of the same length
jieba_bigram <- function(t) {
  lapply(t, function(x) {
    if(nchar(x)>1){
      tokens <- segment(x, jieba_tokenizer)
      bigram<- ngrams(tokens, 2)
      bigram <- lapply(bigram, paste, collapse = " ")
      unlist(bigram)
    }
  })
}

jieba_bigram(c("外出請戴口罩", "保持社交距離"))
[[1]]
[1] "外出 請戴" "請戴 口罩"

[[2]]
[1] "保持 社交" "社交 距離"
# 執行bigram分詞
mask_bigram <- mask %>%
  unnest_tokens(bigram, sentence, token = jieba_bigram)
mask_bigram
# 清楚包含英文或數字的bigram組合
# 計算每個組合出現的次數
mask_bigram %>%
  filter(!str_detect(bigram, regex("[0-9a-zA-Z]"))) %>%
  count(bigram, sort = TRUE)
# unnest_tokens 使用的ngram分詞函數
# Input: a character vector
# Output: a list of character vectors of the same length
jieba_trigram <- function(t) {
  lapply(t, function(x) {
    if(nchar(x)>1){
      tokens <- segment(x, jieba_tokenizer)
      ngram<- ngrams(unlist(tokens), 3)
      ngram <- lapply(ngram, paste, collapse = " ")
      unlist(ngram)
    }
  })
}

jieba_trigram(c("外出請戴口罩", "保持社交距離"))
[[1]]
[1] "外出 請戴 口罩"

[[2]]
[1] "保持 社交 距離"
# 執行ngram分詞
mask_trigram <- mask %>%
  unnest_tokens(ngrams, sentence, token = jieba_trigram)
mask_trigram %>%
  filter(!str_detect(ngrams, regex("[0-9a-zA-Z]"))) %>%
  count(ngrams, sort = TRUE)

上方的結果可以發現有很多包含停止詞的trigram組合,所以我們接著將stopwords清除再看看又什麼新組合

Remove stop words

載入stop words字典

# load stop words
stop_words <- scan(file = "./dict/stop_words.txt", what=character(),sep='\n', 
                   encoding='utf-8',fileEncoding='utf-8')
Read 1220 items
# remove the stop words in bigram
mask_bigram %>%
  filter(!str_detect(bigram, regex("[0-9a-zA-Z]"))) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% 
  filter(!(word1 %in% stop_words), !(word2 %in% stop_words)) %>%
  count(word1, word2, sort = TRUE) %>%
  unite_("bigram", c("word1","word2"), sep=" ")
# remove the stop words in trigram
mask_trigram %>%
  filter(!str_detect(ngrams, regex("[0-9a-zA-Z]"))) %>%
  separate(ngrams, c("word1", "word2", "word3"), sep = " ") %>% 
  filter(!(word1 %in% stop_words), !(word2 %in% stop_words), !(word3 %in% stop_words)) %>%
  count(word1, word2, word3, sort = TRUE) %>%
  unite_("ngrams", c("word1", "word2", "word3"), sep=" ")

從上面的bigram和trigram的結果中,我們可以整理出一個更好的斷詞字典。
我們將詞彙整理好存在dict文件夾中的 mask_lexicon.txt 中

載入自建字典

# load mask_lexicon
mask_lexicon <- scan(file = "./dict/mask_lexicon.txt", what=character(),sep='\n', 
                   encoding='utf-8',fileEncoding='utf-8')
Read 30 items
# 自建疫情相關字典
mask_lexicon
 [1] "武漢肺炎"             "陳時中"               "中央流行疫情指揮中心" "熔噴不織布"           "醫療級口罩"          
 [6] "新冠肺炎"             "特約藥局"             "蔡英文"               "疾病管制署"           "過濾率"              
[11] "趴趴走"               "自然人憑證"           "臉書"                 "大眾運輸工具"         "鑽石公主號"          
[16] "確診病例"             "嚴重特殊傳染性肺炎"   "瘋搶"                 "新台幣"               "中央疫情指揮中心"    
[21] "健保卡"               "陳其邁"               "台大公衛"             "衛生福利部"           "新南向國家"          
[26] "身分證字號"           "來源網址"             "自由時報"             "原文連結"             "完整標題"            

bigram

jieba_tokenizer = worker()

# 使用疫情相關字典重新斷詞
new_user_word(jieba_tokenizer, c(mask_lexicon))
[1] TRUE
chi_tokenizer <- function(t) {
  lapply(t, function(x) {
    if(nchar(x)>1){
      tokens <- segment(x, jieba_tokenizer)
      tokens <- tokens[!tokens %in% stop_words]
      # 去掉字串長度爲1的詞彙
      tokens <- tokens[nchar(tokens)>1]
      return(tokens)
    }
  })
}

Word Correlation

# 剛才的斷詞結果沒有使用新增的辭典,
# 因此我們重新進行斷詞,再計算各詞彙在各文章中出現的次數
mask_words <- mask_sentences %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!str_detect(word, regex("[0-9a-zA-Z]"))) %>%
  count(artUrl, word, sort = TRUE)
mask_words
# 計算兩個詞彙同時出現的總次數
word_pairs <- mask_words %>%
  pairwise_count(word, artUrl, sort = TRUE)

word_pairs
# 計算兩個詞彙間的相關性
word_cors <- mask_words %>%
  group_by(word) %>%
  filter(n() >= 20) %>%
  pairwise_cor(word, artUrl, sort = TRUE)

word_cors
# 與陳時中相關性高的詞彙
word_cors %>%
  filter(item1 == "陳時中") %>% 
  head(10)
# 分別尋找與 "中國", "韓國", "義大利", "美國" 這四個國家相關性最高的 10 個詞彙
word_cors %>%
  filter(item1 %in% c("中國", "韓國", "義大利", "美國")) %>%
  group_by(item1) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, correlation)) %>%
  ggplot(aes(item2, correlation)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  coord_flip()+ 
  theme(text = element_text(family = "Heiti TC Light")) #加入中文字型設定,避免中文字顯示錯誤。
Selecting by correlation

# 顯示相關性大於0.4的組合
set.seed(2020)

word_cors %>%
  filter(correlation > 0.4) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) +
  geom_node_point(color = "lightblue", size = 3) +
  geom_node_text(aes(label = name), repel = TRUE, family = "Heiti TC Light") + #加入中文字型設定,避免中文字顯示錯誤。
  theme_void()

# 顯示相關性大於0.5的組合
set.seed(2020)

word_cors %>%
  filter(correlation > 0.5) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) +
  geom_node_point(color = "lightblue", size = 3) +
  geom_node_text(aes(label = name), repel = TRUE, family = "Heiti TC Light") +
  theme_void()

# 設定幾個詞做爲seed words
seed_words <- c("完整", "作者")
# 設定threshold爲0.5
threshold <- 0.5
# 跟seed words相關性高於threshold的詞彙會被加入移除列表中
remove_words <- word_cors %>%
                filter((item1 %in% seed_words|item2 %in% seed_words), correlation>threshold) %>%
                .$item1 %>%
                unique()
remove_words
[1] "作者" "標題" "完整" "連結"
# 清除存在這些詞彙的組合
word_cors_new <- word_cors %>%
                filter(!(item1 %in% remove_words|item2 %in% remove_words))

word_cors_new %>%
  filter(correlation > 0.4) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) + 
  geom_node_point(color = "lightblue", size = 3) +
  geom_node_text(aes(label = name), repel = TRUE, family = "Heiti TC Light") +
  theme_void()

LS0tCnRpdGxlOiAi5YiG5p6QbkNvVjIwMTnos4fmlpnpm4bkuK3lj6PnvankuYvoqZ7lvZnpl5zkv4IiCmF1dGhvcjogIumZs+eQqOe/lCIKZGF0ZTogIjIwMjAvMDQvMTEiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCmBgYHtyfQpTeXMuc2V0bG9jYWxlKGNhdGVnb3J5ID0gIkxDX0FMTCIsIGxvY2FsZSA9ICJ6aF9UVy5VVEYtOCIpICMg6YG/5YWN5Lit5paH5LqC56K8CmBgYAoKIyMg5a6J6KOd6ZyA6KaB55qEcGFja2FnZXMKYGBge3J9CnBhY2thZ2VzID0gYygicmVhZHIiLCAiZHBseXIiLCAic3RyaW5nciIsICJqaWViYVIiLCAidGlkeXRleHQiLCAiTkxQIiwgInJlYWRyIiwgInRpZHlyIiwgImdncGxvdDIiLCAiZ2dyYXBoIiwgImlncmFwaCIsICJzY2FsZXMiLCAicmVzaGFwZTIiLCAid2lkeXIiKQpleGlzdGluZyA9IGFzLmNoYXJhY3RlcihpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pCmZvcihwa2cgaW4gcGFja2FnZXNbIShwYWNrYWdlcyAlaW4lIGV4aXN0aW5nKV0pIGluc3RhbGwucGFja2FnZXMocGtnKQpgYGAKCmBgYHtyfQpyZXF1aXJlKHJlYWRyKQpyZXF1aXJlKGRwbHlyKQpyZXF1aXJlKHN0cmluZ3IpCnJlcXVpcmUoamllYmFSKQpyZXF1aXJlKHRpZHl0ZXh0KQpyZXF1aXJlKE5MUCkKcmVxdWlyZSh0aWR5cikKcmVxdWlyZShnZ3Bsb3QyKQpyZXF1aXJlKGdncmFwaCkKcmVxdWlyZShpZ3JhcGgpCnJlcXVpcmUoc2NhbGVzKQpyZXF1aXJlKHJlc2hhcGUyKQpyZXF1aXJlKHdpZHlyKQpgYGAKCmBgYHtyfQptYXNrPC0gcmVhZF9jc3YoIi4vbkNvVl8yMDE5X21hc2tfYXJ0aWNsZU1ldGFEYXRhLmNzdiIpICU+JQogICAgICAgICAgICAgIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJbXG5dezIsfSIsICLjgIIiLCBzZW50ZW5jZSkpICMg5bCH5YWp5YCL5Lul5LiK5o+b6KGM56ym6Jmf6L2J5oiQ5Y+l6JmfCm1hc2sKYGBgCj4gcHR0IGFydGljbGVzIGV4YW1wbGU6IDxicj4KIGh0dHBzOi8vd3d3LnB0dC5jYy9iYnMvbkNvVjIwMTkvTS4xNTgxMTQ3NTY5LkEuRkY4Lmh0bWwKIGh0dHBzOi8vd3d3LnB0dC5jYy9iYnMvbkNvVjIwMTkvTS4xNTgwODA0NTM0LkEuOUMxLmh0bWwKIAojIyDlhYjlgZrmlrflj6UKYGBge3J9CnNhbXBsZV9kYXRhIDwtIG1hc2sgJT4lIGhlYWQoNCkKIyDku6XlhajlvaLmiJbljYrlvaIg6ama5q2O6Jmf44CB5ZWP6Jmf44CB5YiG6JmfIOS7peWPiiDlhajlvaLlj6XomZ8g54iy5L6d5pOa6YCy6KGM5pa35Y+lCnNhbXBsZV9zZW50ZW5jZXMgPC0gc3Ryc3BsaXQoc2FtcGxlX2RhdGEkc2VudGVuY2UsIlvjgILvvIHvvJvvvJ8hPztdIikKIyDlm57lgrPntZDmnpzngrpsaXN0IG9mIHZlY3RvcnPvvIzmr4/lgIt2ZWN0b3LnmoTlhaflrrnngrrmr4/nr4fmlofnq6DnmoTmlrflj6XntZDmnpwKc2FtcGxlX3NlbnRlbmNlcwpgYGAKCmBgYHtyfQojIHVubGlzdOacg+Wwh2xpc3TkuK3miYDmnInnmoR2ZWN0b3LlsZXplovmiJDkuIDlgIvkuIDntq3nmoR2ZWN0b3IKc2VudGVuY2VzIDwtIHVubGlzdChzYW1wbGVfc2VudGVuY2VzKQpzZW50ZW5jZXMKYGBgCj4g5L2G5ZyodW5saXN05b6M5oiR5YCR5bCx5rKS6L6m5rOV5Yik5Yil77yM5Ye654++55qE5q+P5LiA5Y+l6Kmx5piv5Ye66Ieq5pa85ZOq56+H5paH56ug5LqGPGJyPgogIOaIkeWAkeW4jOacm+WcqHVubGlzdOeahOWQjOaZguiDveWkoOS/neeVmeWPpeWtkOWxrOaWvOWTquevh+aWh+eroOeahOizh+ioigoKIyMjIHJlcCBmdW5jdGlvbgpgYGB7cn0KIyByZXAoeCwgdGltZXMgPSAxLCBsZW5ndGgub3V0ID0gTkEsIGVhY2ggPSAxKQojIOeVtue1puWumuWFqeWAi3ZlY3RvcumVt+W6puebuOWQjOaZgu+8jHJlcCBmdW5jdGlvbuacg+iHquWLleWwjem9iuWFqeWAi3ZlY3RvcueahOWAvOOAggojIOWJjemdoueahHZlY3RvcuaxuuWumuimgemHjeikh+eahOWAvAojIOW+jOmdoueahHZlY3RvcuWJh+axuuWumuimgemHjeikh+eahOasoeaVuAojIGV4LgpyZXAoYygiU29jaWFsIiwgIk1lZGlhIiksIGMoMiw1KSkKIyByZXAgZnVuY3Rpb27mnIPoh6rli5XlsI3pvYrlhanlgIt2ZWN0b3LvvIwKIyAiU29jaWFsIiDmnIPph43opIcy5qyh77yMIk1lZGlhIuacg+mHjeikhzXmrKEKYGBgCgpgYGB7cn0KIyDljp/mnKzos4fmlpnnmoRhcnRVcmwKc2FtcGxlX2RhdGEkYXJ0VXJsCmBgYAoKYGBge3J9CiMg5Zue5YKz5q+P56+H5paH56ug55qE5pa35Y+l5b6M77yM5YyF5ZCr5LqG5bm+5YCL5Y+l77yIdmVjdG9y55qE6ZW35bqm77yJCnNhcHBseShzYW1wbGVfc2VudGVuY2VzLCBsZW5ndGgpCmBgYAoKYGBge3J9CiMg5L2/55SocmVw5Y676YWN5bCN5Y6f5pys6LOH5paZ55qEYXJ0VXJs5Lul5Y+Kc2FwcGx555qE5Zue5YKz77yI5q+P56+H5paH56ug5YyF5ZCr5LqG5bm+5YCL5Y+l77yJCiMg55Si55Sf55qE6ZW35bqm5pyD6IiHIHVubGlzdChzYW1wbGVfc2VudGVuY2VzKSDnmoTplbfluqbkuIDmqKPvvIwKIyDlhanpgopqb2lu6LW35L6G5bCx5Y+v5Lul5paw5aKe5LiA5YCL5qyE5L2N5Luj6KGo5q+P5YCL5Y+l5a2Q5L6G6Ieq5ZOq56+H5paH56ugCnJlcChzYW1wbGVfZGF0YSRhcnRVcmwsIHNhcHBseShzYW1wbGVfc2VudGVuY2VzLCBsZW5ndGgpKQpgYGAKCmBgYHtyfQpkYXRhLmZyYW1lKGFydFVybD1yZXAoc2FtcGxlX2RhdGEkYXJ0VXJsLCBzYXBwbHkoc2FtcGxlX3NlbnRlbmNlcywgbGVuZ3RoKSksCiAgICAgICAgICAgc2VudGVuY2VzID0gdW5saXN0KHNhbXBsZV9zZW50ZW5jZXMpKQpgYGAKCiMjIOWwjeWFqOmDqOeahOaWh+eroOmAsuihjOaWt+WPpe+8jOS4puWEsuWtmOe1kOaenApgYGB7cn0KIyDku6XlhajlvaLmiJbljYrlvaIg6ama5q2O6Jmf44CB5ZWP6Jmf44CB5YiG6JmfIOS7peWPiiDlhajlvaLlj6XomZ8g54iy5L6d5pOa6YCy6KGM5pa35Y+lCm1hc2tfc2VudGVuY2VzIDwtIHN0cnNwbGl0KG1hc2skc2VudGVuY2UsIlvjgILvvIHvvJvvvJ8hPztdIikKYGBgCgpgYGB7cn0KIyDlsIfmr4/lj6Xlj6XlrZDvvIzoiIfku5bmiYDlsaznmoTmlofnq6DpgKPntZDphY3lsI3otbfkvobvvIzmlbTnkIbmiJDkuIDlgItkYXRhZnJhbWUKbWFza19zZW50ZW5jZXMgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgICAgICAgICAgICAgYXJ0VXJsID0gcmVwKG1hc2skYXJ0VXJsLCBzYXBwbHkobWFza19zZW50ZW5jZXMsIGxlbmd0aCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2VudGVuY2UgPSB1bmxpc3QobWFza19zZW50ZW5jZXMpCiAgICAgICAgICAgICAgICAgICAgICApICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFzdHJfZGV0ZWN0KHNlbnRlbmNlLCByZWdleCgiXihcdHxcbnwgKSokIikpKQoKbWFza19zZW50ZW5jZXMkc2VudGVuY2UgPC0gYXMuY2hhcmFjdGVyKG1hc2tfc2VudGVuY2VzJHNlbnRlbmNlKQoKbWFza19zZW50ZW5jZXMKYGBgCgoKIyMg5o6l552A5YGa5pa36KmeCiMjIyAxLuWIneWni+WMluaWt+ipnuWZqApgYGB7cn0KIyDkvb/nlKjpu5joqo3lj4PmlbjliJ3lp4vljJbkuIDlgIvmlrfoqZ7lvJXmk44KIyDlhYjkuI3kvb/nlKjku7vkvZXnmoTlrZflhbjlkozlgZznlKjoqZ4KamllYmFfdG9rZW5pemVyID0gd29ya2VyKCkKCmNoaV90b2tlbml6ZXIgPC0gZnVuY3Rpb24odCkgewogIGxhcHBseSh0LCBmdW5jdGlvbih4KSB7CiAgICBpZihuY2hhcih4KT4xKXsKICAgICAgdG9rZW5zIDwtIHNlZ21lbnQoeCwgamllYmFfdG9rZW5pemVyKQogICAgICAjIOWOu+aOieWtl+S4sumVt+W6pueIsjHnmoToqZ7lvZkKICAgICAgdG9rZW5zIDwtIHRva2Vuc1tuY2hhcih0b2tlbnMpPjFdCiAgICAgIHJldHVybih0b2tlbnMpCiAgICB9CiAgfSkKfQpgYGAKCiMjIyAyLuaWt+ipnuiIh+aVtOeQhuaWt+ipnue1kOaenApgYGB7cn0KIyDpgLLooYzmlrfoqZ7vvIzkuKboqIjnrpflkIToqZ7lvZnlnKjlkITmlofnq6DkuK3lh7rnj77nmoTmrKHmlbgKbWFza193b3JkcyA8LSBtYXNrX3NlbnRlbmNlcyAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlLCB0b2tlbj1jaGlfdG9rZW5pemVyKSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIlswLTlhLXpBLVpdIikpKSAlPiUKICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQptYXNrX3dvcmRzCmBgYAoKYGBge3J9CiMg6KiI566X5q+P56+H5paH56ug5YyF5ZCr55qE6Kme5pW4CnRvdGFsX3dvcmRzIDwtIG1hc2tfd29yZHMgJT4lIAogIGdyb3VwX2J5KGFydFVybCkgJT4lIAogIHN1bW1hcml6ZSh0b3RhbCA9IHN1bShuKSkKdG90YWxfd29yZHMKYGBgCgpgYGB7cn0KIyDlkIjkvbUgbWFza193b3Jkc++8iOavj+WAi+ipnuW9meWcqOavj+WAi+aWh+eroOS4reWHuuePvueahOasoeaVuO+8iQojIOiIhyB0b3RhbF93b3Jkc++8iOavj+evh+aWh+eroOeahOipnuaVuO+8iQojIOaWsOWinuWQhOWAi+ipnuW9meWcqOaJgOacieipnuW9meS4reeahOe4veaVuOashOS9jQptYXNrX3dvcmRzIDwtIGxlZnRfam9pbihtYXNrX3dvcmRzLCB0b3RhbF93b3JkcykKbWFza193b3JkcwpgYGAKCiMjIOioiOeulyB0Zi1pZGYKYGBge3J9CiMg5Lul5q+P56+H5paH56ug54iy5Zau5L2N77yM6KiI566X5q+P5YCL6Kme5b2Z5Zyo55qEdGYtaWRm5YC8Cm1hc2tfd29yZHNfdGZfaWRmIDwtIG1hc2tfd29yZHMgJT4lCiAgYmluZF90Zl9pZGYod29yZCwgYXJ0VXJsLCBuKQptYXNrX3dvcmRzX3RmX2lkZgpgYGAKCmBgYHtyfQojIOmBuOWHuuavj+evh+aWh+eroO+8jHRmLWlkZuacgOWkp+eahOWNgeWAi+ipngptYXNrX3dvcmRzX3RmX2lkZiAlPiUgCiAgZ3JvdXBfYnkoYXJ0VXJsKSAlPiUKICB0b3BfbigxMCkgJT4lCiAgYXJyYW5nZShkZXNjKGFydFVybCkpCmBgYAoKYGBge3J9CiMg6YG45q+P56+H5paH56ug77yMdGYtaWRm5pyA5aSn55qE5Y2B5YCL6Kme77yMCiMg5Lim5p+l55yL5q+P5YCL6Kme6KKr6YG45Lit55qE5qyh5pW4Cm1hc2tfd29yZHNfdGZfaWRmICU+JSAKICBncm91cF9ieShhcnRVcmwpICU+JQogIHRvcF9uKDEwKSAlPiUKICBhcnJhbmdlKGRlc2MoYXJ0VXJsKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGNvdW50KHdvcmQsIHNvcnQ9VFJVRSkKYGBgCgo+IOWboOeIsuaIkeWAkeaYr+S7peavj+evh+aWh+eroOeIsuS4gOWAi2RvY3VtZW505Zau5L2N77yI57i95YWx5pyJNzIx5YCLZG9jdW1lbnTvvIk8YnI+CiAg5Zug5q2k5oiR5YCR5bCx5LiN55Wr6Kqy5pys56ys5LiJ56ug5Lit77yM5q+U6LyD5ZCEZG9jdW1lbnTkuK10Zi1pZGbovIPpq5jnmoToqZ7lvZnmr5TovIPlnJYKCiMjIGppZWJhciBhbmQgbmdyYW1zCmBgYHtyfQojIOS9v+eUqOe1kOW3tOaWt+ipnu+8jOS4puaQremFjU5MUCBwYWNrYWdlc+S4reeahCBuZ3JhbXMgZnVuY3Rpb24KIyBlLmcuCnRva2VucyA8LSBzZWdtZW50KCLlpJblh7roq4vmiLTlj6PnvakiLCBqaWViYV90b2tlbml6ZXIpCnRva2VucwoKYmlncmFtIDwtIG5ncmFtcyh0b2tlbnMsIDIpCmJpZ3JhbQpgYGAKYGBge3J9CiMgQ29tYmluZSBlYWNoIGJpZ3JhbXMgaW50byBhIHNpbmdsZSBzdHJpbmcsIHdpdGggdGhlICIgIiBhcyB0aGUgc2VwZXJhdGVyLgpiaWdyYW0gPC0gbGFwcGx5KGJpZ3JhbSwgcGFzdGUsIGNvbGxhcHNlID0gIiAiKQp1bmxpc3QoYmlncmFtKQpgYGAKCiMjIyBiaWdyYW0gZnVuY3Rpb24KYGBge3J9CiMgcmVtb3ZlIHN0b3B3b3JkcwpqaWViYV90b2tlbml6ZXIgPSB3b3JrZXIoKQoKIyB1bm5lc3RfdG9rZW5zIOS9v+eUqOeahGJpZ3JhbeWIhuipnuWHveaVuAojIElucHV0OiBhIGNoYXJhY3RlciB2ZWN0b3IKIyBPdXRwdXQ6IGEgbGlzdCBvZiBjaGFyYWN0ZXIgdmVjdG9ycyBvZiB0aGUgc2FtZSBsZW5ndGgKamllYmFfYmlncmFtIDwtIGZ1bmN0aW9uKHQpIHsKICBsYXBwbHkodCwgZnVuY3Rpb24oeCkgewogICAgaWYobmNoYXIoeCk+MSl7CiAgICAgIHRva2VucyA8LSBzZWdtZW50KHgsIGppZWJhX3Rva2VuaXplcikKICAgICAgYmlncmFtPC0gbmdyYW1zKHRva2VucywgMikKICAgICAgYmlncmFtIDwtIGxhcHBseShiaWdyYW0sIHBhc3RlLCBjb2xsYXBzZSA9ICIgIikKICAgICAgdW5saXN0KGJpZ3JhbSkKICAgIH0KICB9KQp9CgpqaWViYV9iaWdyYW0oYygi5aSW5Ye66KuL5oi05Y+j572pIiwgIuS/neaMgeekvuS6pOi3nemboiIpKQpgYGAKCmBgYHtyfQojIOWft+ihjGJpZ3JhbeWIhuipngptYXNrX2JpZ3JhbSA8LSBtYXNrICU+JQogIHVubmVzdF90b2tlbnMoYmlncmFtLCBzZW50ZW5jZSwgdG9rZW4gPSBqaWViYV9iaWdyYW0pCm1hc2tfYmlncmFtCmBgYAoKYGBge3J9CiMg5riF5qWa5YyF5ZCr6Iux5paH5oiW5pW45a2X55qEYmlncmFt57WE5ZCICiMg6KiI566X5q+P5YCL57WE5ZCI5Ye654++55qE5qyh5pW4Cm1hc2tfYmlncmFtICU+JQogIGZpbHRlcighc3RyX2RldGVjdChiaWdyYW0sIHJlZ2V4KCJbMC05YS16QS1aXSIpKSkgJT4lCiAgY291bnQoYmlncmFtLCBzb3J0ID0gVFJVRSkKYGBgCgpgYGB7cn0KIyB1bm5lc3RfdG9rZW5zIOS9v+eUqOeahG5ncmFt5YiG6Kme5Ye95pW4CiMgSW5wdXQ6IGEgY2hhcmFjdGVyIHZlY3RvcgojIE91dHB1dDogYSBsaXN0IG9mIGNoYXJhY3RlciB2ZWN0b3JzIG9mIHRoZSBzYW1lIGxlbmd0aApqaWViYV90cmlncmFtIDwtIGZ1bmN0aW9uKHQpIHsKICBsYXBwbHkodCwgZnVuY3Rpb24oeCkgewogICAgaWYobmNoYXIoeCk+MSl7CiAgICAgIHRva2VucyA8LSBzZWdtZW50KHgsIGppZWJhX3Rva2VuaXplcikKICAgICAgbmdyYW08LSBuZ3JhbXModW5saXN0KHRva2VucyksIDMpCiAgICAgIG5ncmFtIDwtIGxhcHBseShuZ3JhbSwgcGFzdGUsIGNvbGxhcHNlID0gIiAiKQogICAgICB1bmxpc3QobmdyYW0pCiAgICB9CiAgfSkKfQoKamllYmFfdHJpZ3JhbShjKCLlpJblh7roq4vmiLTlj6PnvakiLCAi5L+d5oyB56S+5Lqk6Led6ZuiIikpCmBgYAoKYGBge3J9CiMg5Z+36KGMbmdyYW3liIboqZ4KbWFza190cmlncmFtIDwtIG1hc2sgJT4lCiAgdW5uZXN0X3Rva2VucyhuZ3JhbXMsIHNlbnRlbmNlLCB0b2tlbiA9IGppZWJhX3RyaWdyYW0pCm1hc2tfdHJpZ3JhbSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3QobmdyYW1zLCByZWdleCgiWzAtOWEtekEtWl0iKSkpICU+JQogIGNvdW50KG5ncmFtcywgc29ydCA9IFRSVUUpCmBgYAo+IOS4iuaWueeahOe1kOaenOWPr+S7peeZvOePvuacieW+iOWkmuWMheWQq+WBnOatouipnueahHRyaWdyYW3ntYTlkIjvvIzmiYDku6XmiJHlgJHmjqXokZflsIdzdG9wd29yZHPmuIXpmaTlho3nnIvnnIvlj4jku4DpurzmlrDntYTlkIgKCiMjIFJlbW92ZSBzdG9wIHdvcmRzCiMjIyDovInlhaVzdG9wIHdvcmRz5a2X5YW4CmBgYHtyfQojIGxvYWQgc3RvcCB3b3JkcwpzdG9wX3dvcmRzIDwtIHNjYW4oZmlsZSA9ICIuL2RpY3Qvc3RvcF93b3Jkcy50eHQiLCB3aGF0PWNoYXJhY3RlcigpLHNlcD0nXG4nLCAKICAgICAgICAgICAgICAgICAgIGVuY29kaW5nPSd1dGYtOCcsZmlsZUVuY29kaW5nPSd1dGYtOCcpCmBgYAoKCmBgYHtyfQojIHJlbW92ZSB0aGUgc3RvcCB3b3JkcyBpbiBiaWdyYW0KbWFza19iaWdyYW0gJT4lCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KGJpZ3JhbSwgcmVnZXgoIlswLTlhLXpBLVpdIikpKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lIAogIGZpbHRlcighKHdvcmQxICVpbiUgc3RvcF93b3JkcyksICEod29yZDIgJWluJSBzdG9wX3dvcmRzKSkgJT4lCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgdW5pdGVfKCJiaWdyYW0iLCBjKCJ3b3JkMSIsIndvcmQyIiksIHNlcD0iICIpCmBgYAoKYGBge3J9CiMgcmVtb3ZlIHRoZSBzdG9wIHdvcmRzIGluIHRyaWdyYW0KbWFza190cmlncmFtICU+JQogIGZpbHRlcighc3RyX2RldGVjdChuZ3JhbXMsIHJlZ2V4KCJbMC05YS16QS1aXSIpKSkgJT4lCiAgc2VwYXJhdGUobmdyYW1zLCBjKCJ3b3JkMSIsICJ3b3JkMiIsICJ3b3JkMyIpLCBzZXAgPSAiICIpICU+JSAKICBmaWx0ZXIoISh3b3JkMSAlaW4lIHN0b3Bfd29yZHMpLCAhKHdvcmQyICVpbiUgc3RvcF93b3JkcyksICEod29yZDMgJWluJSBzdG9wX3dvcmRzKSkgJT4lCiAgY291bnQod29yZDEsIHdvcmQyLCB3b3JkMywgc29ydCA9IFRSVUUpICU+JQogIHVuaXRlXygibmdyYW1zIiwgYygid29yZDEiLCAid29yZDIiLCAid29yZDMiKSwgc2VwPSIgIikKYGBgCj4g5b6e5LiK6Z2i55qEYmlncmFt5ZKMdHJpZ3JhbeeahOe1kOaenOS4re+8jOaIkeWAkeWPr+S7peaVtOeQhuWHuuS4gOWAi+abtOWlveeahOaWt+ipnuWtl+WFuOOAgjxicj4KICDmiJHlgJHlsIfoqZ7lvZnmlbTnkIblpb3lrZjlnKhkaWN05paH5Lu25aS+5Lit55qEIG1hc2tfbGV4aWNvbi50eHQg5LitCgoKCiMjIyDovInlhaXoh6rlu7rlrZflhbggCmBgYHtyfQojIGxvYWQgbWFza19sZXhpY29uCm1hc2tfbGV4aWNvbiA8LSBzY2FuKGZpbGUgPSAiLi9kaWN0L21hc2tfbGV4aWNvbi50eHQiLCB3aGF0PWNoYXJhY3RlcigpLHNlcD0nXG4nLCAKICAgICAgICAgICAgICAgICAgIGVuY29kaW5nPSd1dGYtOCcsZmlsZUVuY29kaW5nPSd1dGYtOCcpCmBgYAoKYGBge3J9CiMg6Ieq5bu655ar5oOF55u46Zec5a2X5YW4Cm1hc2tfbGV4aWNvbgpgYGAKCiMjIGJpZ3JhbQpgYGB7cn0KamllYmFfdG9rZW5pemVyID0gd29ya2VyKCkKCiMg5L2/55So55ar5oOF55u46Zec5a2X5YW46YeN5paw5pa36KmeCm5ld191c2VyX3dvcmQoamllYmFfdG9rZW5pemVyLCBjKG1hc2tfbGV4aWNvbikpCgpjaGlfdG9rZW5pemVyIDwtIGZ1bmN0aW9uKHQpIHsKICBsYXBwbHkodCwgZnVuY3Rpb24oeCkgewogICAgaWYobmNoYXIoeCk+MSl7CiAgICAgIHRva2VucyA8LSBzZWdtZW50KHgsIGppZWJhX3Rva2VuaXplcikKICAgICAgdG9rZW5zIDwtIHRva2Vuc1shdG9rZW5zICVpbiUgc3RvcF93b3Jkc10KICAgICAgIyDljrvmjonlrZfkuLLplbfluqbniLIx55qE6Kme5b2ZCiAgICAgIHRva2VucyA8LSB0b2tlbnNbbmNoYXIodG9rZW5zKT4xXQogICAgICByZXR1cm4odG9rZW5zKQogICAgfQogIH0pCn0KYGBgCgoKIyMgV29yZCBDb3JyZWxhdGlvbgpgYGB7cn0KIyDliZvmiY3nmoTmlrfoqZ7ntZDmnpzmspLmnInkvb/nlKjmlrDlop7nmoTovq3lhbjvvIwKIyDlm6DmraTmiJHlgJHph43mlrDpgLLooYzmlrfoqZ7vvIzlho3oqIjnrpflkIToqZ7lvZnlnKjlkITmlofnq6DkuK3lh7rnj77nmoTmrKHmlbgKbWFza193b3JkcyA8LSBtYXNrX3NlbnRlbmNlcyAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlLCB0b2tlbj1jaGlfdG9rZW5pemVyKSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIlswLTlhLXpBLVpdIikpKSAlPiUKICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQptYXNrX3dvcmRzCmBgYAoKYGBge3J9CiMg6KiI566X5YWp5YCL6Kme5b2Z5ZCM5pmC5Ye654++55qE57i95qyh5pW4CndvcmRfcGFpcnMgPC0gbWFza193b3JkcyAlPiUKICBwYWlyd2lzZV9jb3VudCh3b3JkLCBhcnRVcmwsIHNvcnQgPSBUUlVFKQoKd29yZF9wYWlycwpgYGAKCmBgYHtyfQojIOioiOeul+WFqeWAi+ipnuW9memWk+eahOebuOmXnOaApwp3b3JkX2NvcnMgPC0gbWFza193b3JkcyAlPiUKICBncm91cF9ieSh3b3JkKSAlPiUKICBmaWx0ZXIobigpID49IDIwKSAlPiUKICBwYWlyd2lzZV9jb3Iod29yZCwgYXJ0VXJsLCBzb3J0ID0gVFJVRSkKCndvcmRfY29ycwpgYGAKCmBgYHtyfQojIOiIh+mZs+aZguS4reebuOmXnOaAp+mrmOeahOipnuW9mQp3b3JkX2NvcnMgJT4lCiAgZmlsdGVyKGl0ZW0xID09ICLpmbPmmYLkuK0iKSAlPiUgCiAgaGVhZCgxMCkKYGBgCgpgYGB7cn0KIyDliIbliKXlsIvmib7oiIcgIuS4reWciyIsICLpn5PlnIsiLCAi576p5aSn5YipIiwgIue+juWciyIg6YCZ5Zub5YCL5ZyL5a6255u46Zec5oCn5pyA6auY55qEIDEwIOWAi+ipnuW9mQp3b3JkX2NvcnMgJT4lCiAgZmlsdGVyKGl0ZW0xICVpbiUgYygi5Lit5ZyLIiwgIumfk+WciyIsICLnvqnlpKfliKkiLCAi576O5ZyLIikpICU+JQogIGdyb3VwX2J5KGl0ZW0xKSAlPiUKICB0b3BfbigxMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShpdGVtMiA9IHJlb3JkZXIoaXRlbTIsIGNvcnJlbGF0aW9uKSkgJT4lCiAgZ2dwbG90KGFlcyhpdGVtMiwgY29ycmVsYXRpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBmYWNldF93cmFwKH4gaXRlbTEsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKSsgCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiKSkgI+WKoOWFpeS4reaWh+Wtl+Wei+ioreWumu+8jOmBv+WFjeS4reaWh+Wtl+mhr+ekuumMr+iqpOOAggpgYGAKCmBgYHtyfQojIOmhr+ekuuebuOmXnOaAp+Wkp+aWvDAuNOeahOe1hOWQiApzZXQuc2VlZCgyMDIwKQoKd29yZF9jb3JzICU+JQogIGZpbHRlcihjb3JyZWxhdGlvbiA+IDAuNCkgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvcnJlbGF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gMykgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbCA9IFRSVUUsIGZhbWlseSA9ICJIZWl0aSBUQyBMaWdodCIpICsgI+WKoOWFpeS4reaWh+Wtl+Wei+ioreWumu+8jOmBv+WFjeS4reaWh+Wtl+mhr+ekuumMr+iqpOOAggogIHRoZW1lX3ZvaWQoKQpgYGAKCmBgYHtyfQojIOmhr+ekuuebuOmXnOaAp+Wkp+aWvDAuNeeahOe1hOWQiApzZXQuc2VlZCgyMDIwKQoKd29yZF9jb3JzICU+JQogIGZpbHRlcihjb3JyZWxhdGlvbiA+IDAuNSkgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvcnJlbGF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gMykgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbCA9IFRSVUUsIGZhbWlseSA9ICJIZWl0aSBUQyBMaWdodCIpICsKICB0aGVtZV92b2lkKCkKYGBgCgpgYGB7cn0KIyDoqK3lrprlub7lgIvoqZ7lgZrniLJzZWVkIHdvcmRzCnNlZWRfd29yZHMgPC0gYygi5a6M5pW0IiwgIuS9nOiAhSIpCiMg6Kit5a6adGhyZXNob2xk54iyMC41CnRocmVzaG9sZCA8LSAwLjUKIyDot59zZWVkIHdvcmRz55u46Zec5oCn6auY5pa8dGhyZXNob2xk55qE6Kme5b2Z5pyD6KKr5Yqg5YWl56e76Zmk5YiX6KGo5LitCnJlbW92ZV93b3JkcyA8LSB3b3JkX2NvcnMgJT4lCiAgICAgICAgICAgICAgICBmaWx0ZXIoKGl0ZW0xICVpbiUgc2VlZF93b3Jkc3xpdGVtMiAlaW4lIHNlZWRfd29yZHMpLCBjb3JyZWxhdGlvbj50aHJlc2hvbGQpICU+JQogICAgICAgICAgICAgICAgLiRpdGVtMSAlPiUKICAgICAgICAgICAgICAgIHVuaXF1ZSgpCnJlbW92ZV93b3JkcwpgYGAKCmBgYHtyfQojIOa4hemZpOWtmOWcqOmAmeS6m+ipnuW9meeahOe1hOWQiAp3b3JkX2NvcnNfbmV3IDwtIHdvcmRfY29ycyAlPiUKICAgICAgICAgICAgICAgIGZpbHRlcighKGl0ZW0xICVpbiUgcmVtb3ZlX3dvcmRzfGl0ZW0yICVpbiUgcmVtb3ZlX3dvcmRzKSkKCndvcmRfY29yc19uZXcgJT4lCiAgZmlsdGVyKGNvcnJlbGF0aW9uID4gMC40KSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gY29ycmVsYXRpb24pLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gMykgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbCA9IFRSVUUsIGZhbWlseSA9ICJIZWl0aSBUQyBMaWdodCIpICsKICB0aGVtZV92b2lkKCkKYGBgCg==