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.
Đâ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()
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
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
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
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
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
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.
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")
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")
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"
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")