Word Cloud là gì?

Word Cloud (còn gọi là Tag Cloud hoặc Text Cloud) là một phương pháp trực quan hóa dữ liệu văn bản phổ biến, giúp hiển thị tần suất xuất hiện của các từ trong một tập hợp văn bản. Về cơ bản, nó là một “đám mây” các từ, trong đó kích thước của mỗi từ tỷ lệ thuận với tần suất xuất hiện của nó trong văn bản gốc. Những từ xuất hiện thường xuyên hơn sẽ được hiển thị lớn hơn, nổi bật hơn, trong khi những từ ít phổ biến hơn sẽ nhỏ hơn.

File dữ liệu sử dụng:Xu ly y kien KS tinh gon. xlsx.

Đây là file tách các cột ý kiến khác, yêu cầu khác, nêu ý kiến, kiến nghị… Gồm 56.591 dòng tương ứng số đối tượng khảo sát Để chọn đúng đường dẫn file trong R, dùng lệnh file.choose()

Gói R cần sử dụng để tạo Word Cloud: quanteda

Chỉ cài một lần, các lần sau gọi ra bằng lệnh library() Lệnh cài như sau: install.packages(c(“readxl”, “tidyverse”, “table1”, “compareGroups”, “ggplot2”, “gridExtra”, “tidyverse”, “GGally”, “ggfortify”, “DescTools”, “relaimpo”, “carData”, “rms”, “caret”, “BMA”, “pROC”, “epiDisplay”), dependencies = T)

install.packages(c(“readxl”, “dplyr”, “quanteda”, “quanteda.textstats”, “quanteda.textplots”, “wordcloud”, “RColorBrewer”))

install.packages(“topicmodels”, “wordcloud2)

library(readxl)
library(dplyr)
## 
## 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
library(quanteda)
## Package version: 4.3.0
## Unicode version: 15.1
## ICU version: 74.1
## Parallel computing: 6 of 6 threads used.
## See https://quanteda.io for tutorials and examples.
library(quanteda.textstats)
library(quanteda.textplots)
library(wordcloud)
## Loading required package: RColorBrewer
library(RColorBrewer)
library(ggplot2)
library("topicmodels")
data <- read_excel("F:\\My Drive\\Training\\R Training\\Xu ly y kien KS tinh gon.xlsx") ## Nạp file dữ liệu vào R

Làm sạch dữ liệu

Các ý kiến có một lượng lớn các câu trả lời kiểu: Không ý kiến, không,không có… Một số câu rỗng, bỏ trống Lưu ý R phân biệt chữ hoa, chữ thường,cầm chuyển toàn bộ về chữ thường Sử dụng gói dplyr để làm sạch

remove_vals <- c("không", "không có", "không có ý kiến", "ko", "no", "none", "na", "NA", "Không", "Không có", "Không có ý kiến", "Ko", "Không ý kiến", "không ý kiến") ## Tạo chuỗi văn bản cần lọc
data_clean <- data %>% ## sử dụng dplyr
  filter(!is.na(kiennghi), ## Lọc giá trị rỗng
         !tolower(kiennghi) %in% remove_vals, ## Lọc các phản hồi không ý kiến
         nchar(kiennghi) > 5) ## Chỉ giũ những phản hồi có từ 5 ký tự trở lên

Dùng gói quantenda chuyển văn bản thành các token

Tokenization là quá trình chia nhỏ văn bản thành các đơn vị có ý nghĩa nhỏ hơn, thường là các từ, nhưng cũng có thể là câu, ký tự hoặc các đơn vị khác tùy thuộc vào cài đặt. Mỗi đơn vị này được gọi là một token. Xử lý từ kép –> Nguyên tắc là ghép các từ xuất hiện với tần suất nhiều

tokens_base <- tokens(data_clean$kiennghi, remove_punct = TRUE, remove_symbols = TRUE) ### tạo tokens ở cột kiến ngh
collocs <- textstat_collocations(tokens_base, size = 2, min_count = 5) ### Xác định các từ xuất hiện từ 05 lần trở lên
tokens_compounded <- tokens_compound(tokens_base, pattern = collocs) ### Tạo tokens mới với các cụm từ đã tìm được
tokens_final <- tokens_tolower(tokens_compounded) ### Chuyển về chữ thường

Tạo dfm

dfm: document - features matrix. Giải thích kiểu huyền bí như sau: Trong DFM, mỗi hàng sẽ đại diện cho một tài liệu (ý kiến của một người), và mỗi cột sẽ đại diện cho một đặc trưng (feature), thường là một từ hoặc một cụm từ đã được ghép nối. Xem các kết quả trên nền tảng dfm: các từ xuất hiện phổ biến nhất

dfm_final <- dfm(tokens_final)
topfeatures(dfm_final, 30)  ### Xem 30 từ xuất hiện phổ biến nhất
##               và            không           đồng_ý              nên 
##              228              192              191              189 
##              cần        quy_trình              thì không_có_đề_xuất 
##              161              145              144              142 
##               để               về             được          thủ_tục 
##              141              132              129              125 
##               vì              khi               là             hoặc 
##              122              120              115              114 
##       tăng_lương             phải    không_đề_xuất            trong 
##              112              112              111              110 
##        đối_tượng              cho               có          đề_xuất 
##              110              108              108              103 
##               từ                1            nhiều             giảm 
##              101               99               96               95 
##              mới           hỗ_trợ 
##               95               92
dfm_ngrams <- dfm_keep(dfm_final, pattern = "*_*", valuetype = "glob") ### Chỉ chọn các từ kép 
topfeatures(dfm_ngrams, 30) ### Xem 30 từ kép phổ biến nhất
##           đồng_ý        quy_trình không_có_đề_xuất          thủ_tục 
##              191              145              142              125 
##       tăng_lương    không_đề_xuất        đối_tượng          đề_xuất 
##              112              111              110              103 
##           hỗ_trợ       thống_nhất          đảm_bảo          phù_hợp 
##               92               92               85               78 
##        thực_hiện         không_có         thay_đổi          sắp_xếp 
##               74               73               73               72 
##           cụ_thể           cán_bộ         hiệu_quả        thời_gian 
##               68               67               65               65 
##       giải_quyết         xây_dựng           ủng_hộ          ưu_tiên 
##               64               63               62               62 
##         đánh_giá         tinh_gọn          quản_lý           hợp_lý 
##               60               60               57               57 
##  thiết_kế_cụ_thể         lãnh_đạo 
##               56               56

Vẽ Word Cloud

lệnh textplot_wordcloud

set.seed(123) ### Cố định chọn
textplot_wordcloud(dfm_ngrams, ### dùng dfm theo từ kép đã tạo ở trên
                   max_words = 100,min_size = 0.5, max_size = 3, ### xuất hiện tối đa 100 từ với các kích thước min/max
                   color = RColorBrewer::brewer.pal(8, "Dark2"),
                   random_order = FALSE) ###  Các từ sẽ được sắp xếp theo thứ tự tần suất giảm dần, bắt đầu từ trung tâm của word cloud

File sử dụng dữ liệu là file văn bản:

Ví dụ: muốn biết những keyword chính trong báo cáo, nghị quyết gì gì đó Lưu file văn bản dạng .txt (lưu ý khi lưu file dạng text trong Word, chọn text encoding là UTF-8) Sử dụng nạp file văn bản: - Bỏ các từ dừng (stopwords) - Các bước sau tương tự như trên

text_data <- readChar("F:\\My Drive\\Training\\R Training\\294-QD_TW.txt", file.info("F:\\My Drive\\Training\\R Training\\294-QD_TW.txt")$size, useBytes = TRUE)
# text_data <- iconv(text_data, from = "latin1", to = "UTF-8") # sử dụng khi có ký tự lạ
my_stopwords_vi <- c("và", "là", "có", "những", "các", "một", "trong", "với", 
                     "khi", "được", "đã", "nếu", "thì", "hoặc", "về", "từ", 
                     "sẽ", "cho", "đến", "theo", "này", "nên", "của", "rằng", "đó", 
                     "cũng", "như", "vẫn", "nào", "đang", "không", "mà", "phải")
tokens_text <- tokens(text_data, remove_punct = TRUE, remove_symbols = TRUE) %>%
  tokens_remove(my_stopwords_vi)
# tokens_text <- tokens(text_data, remove_punct = TRUE, remove_symbols = TRUE) %>%
  # tokens_remove(stopwords("en")) # nếu tiếng Anh thì stopwords đã có sẵn
collocs1 <- textstat_collocations(tokens_text, size = 2, min_count = 3)
tokens_compounded_text <- tokens_compound(tokens_text, pattern = collocs1)
dfm_text <- dfm(tokens_tolower(tokens_compounded_text))
textplot_wordcloud(dfm_text,
max_words = 100,
color = brewer.pal(8, "Dark2"),
random_order = FALSE)
## Warning in wordcloud(x, min_size, max_size, min_count, max_words, color, :
## vấn_đề_liên_quan could not be fit on page. It will not be plotted.

## Tách nhóm Word Cloud

docvars(tokens_final, "nhom") <- data_clean$Khối  ## Gán nhóm
dfm_grouped <- dfm(tokens_final) %>%
  dfm_group(groups = docvars(tokens_final, "nhom")) ## Gộp nhóm trong dfm mới
textplot_wordcloud(dfm_grouped, comparison = TRUE,
                   max_words = 100,min_size = 0.5, max_size = 3, labelsize=1,
                   color = RColorBrewer::brewer.pal(8, "Dark2"),
                   random_order = FALSE)

## Dùng keyness

textstat_keyness(dfm(tokens_final), target = docvars(tokens_final, "nhom") == "Phường/Xã") ##tính từ khóa đặc trưng của nhóm "Phường/Xã" so với các nhóm còn lại bằng test thống kê log-likelihood ratio (G²), được dùng trong phân tích định tính.

Bóc tách theo chủ đề bằng cách chọn từ khóa thủ công

classify_topic <- function(text) {
  text <- tolower(text)
  if (grepl("tổ chức|cơ cấu|đầu mối|phòng ban|chồng chéo", text)) {
    return("Tổ chức bộ máy")
  } else if (grepl("biên chế|nhân sự|tinh giản|cắt giảm|dôi dư", text)) {
    return("Nhân sự, biên chế")
  } else if (grepl("lương|phụ cấp|thu nhập|ngân sách|chế độ", text)) {
    return("Tài chính, lương thưởng")
  } else if (grepl("tuyên truyền|thuyết phục|truyền thông|giải thích", text)) {
    return("Công tác tuyên truyền")
  } else if (grepl("đào tạo|bồi dưỡng|nâng cao|kỹ năng", text)) {
    return("Đào tạo, nâng cao năng lực")
  } else if (grepl("văn bản|quy định|thể chế|nghị định|hướng dẫn", text)) {
    return("Thể chế, pháp luật")
  } else if (grepl("công nghệ|số hóa|ứng dụng|phần mềm|ai|trí tuệ nhân tạo", text)) {
    return("Ứng dụng công nghệ")
  } else {
    return("Khác")
  }
}

data_clean$chude <- sapply(data_clean$ykien, classify_topic) ## gán kết quả với phân nhóm ở trên
unique(data_clean$chude)
## [1] "Tài chính, lương thưởng"    "Khác"                      
## [3] "Ứng dụng công nghệ"         "Thể chế, pháp luật"        
## [5] "Nhân sự, biên chế"          "Tổ chức bộ máy"            
## [7] "Đào tạo, nâng cao năng lực" "Công tác tuyên truyền"
table(data_clean$chude)
## 
##      Công tác tuyên truyền Đào tạo, nâng cao năng lực 
##                          7                        158 
##                       Khác          Nhân sự, biên chế 
##                       6422                        227 
##    Tài chính, lương thưởng         Thể chế, pháp luật 
##                        338                         61 
##             Tổ chức bộ máy         Ứng dụng công nghệ 
##                        662                        228
ggplot(data_clean, aes(x = chude)) +
  geom_bar(fill = "steelblue") +
  coord_flip() +  # Xoay ngang cho dễ đọc
  theme_minimal() +
  labs(title = "Phân loại ý kiến theo chủ đề",
       x = "Chủ đề", y = "Số ý kiến")

Vẽ đồ thị các từ phổ biến

textstat_frequency(dfm_ngrams) %>%
  head(20) %>%
  ggplot(aes(x = reorder(feature, frequency), y = frequency)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(title = "Top 20 từ/cụm phổ biến", x = "Từ/Cụm", y = "Tần suất")

Sử dụng mô hình LDA (Latent Dirich Allocation)

Latent Dirichlet Allocation (LDA) là một mô hình xác suất thống kê được dùng để tự động phát hiện các chủ đề tiềm ẩn trong tập hợp văn bản, mà không cần gán nhãn trước. Nó giả định rằng: Mỗi văn bản là một tổ hợp của nhiều chủ đề (topics) Mỗi chủ đề là một tổ hợp của nhiều từ khóa (words) Mỗi từ trong văn bản được sinh ra từ một chủ đề ẩn nào đó

👉 Nói đơn giản: LDA sẽ tìm ra các nhóm từ khóa thường đi chung và gán thành một chủ đề tiềm ẩn.

chontopic=convert(dfm_ngrams, to = "topicmodels")
lda_model <- LDA(chontopic, k = 5, control = list(seed = 1234))
terms(lda_model, 10)
##       Topic 1         Topic 2           Topic 3      Topic 4        
##  [1,] "tăng_lương"    "sắp_xếp"         "quy_trình"  "phù_hợp"      
##  [2,] "không_đề_xuất" "giải_quyết"      "hỗ_trợ"     "không_có"     
##  [3,] "tiếp_tục"      "quan_tâm"        "thực_hiện"  "ủng_hộ"       
##  [4,] "nhanh_gọn"     "đồng_thuận"      "hiệu_quả"   "tinh_gọn"     
##  [5,] "đề_xuất"       "tinh_giản"       "chuyên_môn" "chưa_có"      
##  [6,] "ưu_tiên"       "cũng_như"        "kịp_thời"   "đánh_giá"     
##  [7,] "hợp_lý"        "xã_hội"          "minh_bạch"  "chính_sách"   
##  [8,] "thành_phố"     "khong_y_kien"    "giấy_tờ"    "giữ_nguyên"   
##  [9,] "ổn_định"       "không_có_ý_kiến" "phát_triển" "không_biết"   
## [10,] "đào_tạo"       "cụ_thể"          "quản_lý"    "tăng_thu_nhập"
##       Topic 5           
##  [1,] "đồng_ý"          
##  [2,] "không_có_đề_xuất"
##  [3,] "thủ_tục"         
##  [4,] "đối_tượng"       
##  [5,] "thống_nhất"      
##  [6,] "thiết_kế_cụ_thể" 
##  [7,] "tán_thành"       
##  [8,] "khong_co"        
##  [9,] "nhất_trí"        
## [10,] "rất_tốt"

Sử dụng gói wordcloud2 cho nó màu mè

library("wordcloud2")
freq <- textstat_frequency(dfm_ngrams)
word_data <- data.frame(
  word = freq$feature,
  freq = freq$frequency
)
wordcloud2(word_data,
           size = 1.5,
           shape = "star",
           color = "random-light",
           backgroundColor = "white")

Túm lại là hay nha, nên học. Trên đây chỉ là cơ bản, mọi người nghiên cứu thêm, thấy gì hay nhớ share