17 文本数据分析
R 语言任务视图中以自然语言处理(Natural Language Processing)涵盖文本分析(Text Analysis)的内容。R 语言社区中有两本文本分析相关的著作,分别是《Text Mining with R》(Silge 和 Robinson 2017)和《Supervised Machine Learning for Text Analysis in R》(Hvitfeldt 和 Silge 2021)。
本文有两个目的:其一分析谢益辉 10 多年日志,挖掘写作主题及其变化;其二挖掘湘云的日志主题,计算与益辉日志的风格相似度。事实上,益辉在个人主页中是明确说了自己的兴趣范围的,本文借用文本挖掘中的主题建模方法,不过是一点实证,熟悉文本建模的操作过程。
从谢益辉公开的日志中,探索成功人士的经历,从中汲取一些经验、教训。最近才知道他有 300 多万字的日志,数字惊讶到我了,遂决定抽取最近 10 年的日志数据进行分析。中英文分开,首先处理、分析中文日志。文本操作、分析的内容有数据清理、文本分词、词频统计、词云展示、词向量化、主题建模、相似度度量等。对作者来说,感兴趣的主题与写作的内容有直接的关系。益辉的日志都是没有分类标签的,主题挖掘可以洞察作者的兴趣。
17.1 数据获取
- 总体规模:益辉每年的日志数量、日志平均字数,益辉发布书籍的年份
- 过程细节:发布时间、日志字数的日历图、日志年度主题
下载益辉的日志数据
git clone git@github.com:yihui/yihui.org.git
经过整理后,打包成 Rdata 数据供 R 软件使用。
17.2 日志概况
代码
library(ggplot2)
library(ggrepel)
ggplot() +
geom_label_repel(
data = df2, aes(x = year, y = file_name, label = event_wrap),
max.overlaps = 150, segment.colour = "gray", seed = 2023
) +
geom_point(data = df1, aes(x = file_year, y = file_name)) +
geom_line(data = df1, aes(x = file_year, y = file_name)) +
scale_x_continuous(n.breaks = 15) +
theme_bw() +
labs(x = "年份", y = "篇数")
2006 年获得中国人民大学学士学位,2009 和 2013 年分别获得中国人民大学硕士和爱荷华州立大学博士学位,在校期间,日志数量持续增加,又陆续创立统计之都,举办中国 R 语言大会。在毕业那年需要完成毕业论文,因此,日志数量明显减少。2013 -2016 年,每年都有书籍出版,期间,有博士毕业、找工作、安家等重要事情,因此,日志数量持续处于低位。稳定后,2017-2018 年除了正常出两本书以外,写了大量的日志,迎来第二个高峰,2018 年,中英文日志数量超过 300 篇。2019-2020 年集中精力在写一本食谱。2021 年第一本中文书《现代统计图形》在10年后出版,这主要是 2007-2011 年的工作。2021-2023 年日志数量(2023年中文日志未发布)处于较低水平。
17.3 数据清洗
以 2001 年的一篇日志为例,展开数据清洗的过程。移除文章的 YAML 元数据,对于文本分析来说,主要是没啥信息含量。
移除「我」 「是」 「你」 「的」 「了」 「也」 等高频的人称、助词、虚词。这些词出现的规律对表现个人风格很重要,且看红楼梦关于后40回作者归属的研究,通过比较一些助词、虚词的出现规律,从而看出作者的习惯、文风。这种东西是在长期的潜移默化中形成的,对作者自己来说,都可能是无意识的。
添加新词,比如「歪贼」、「谢益辉」等,主要是人名、外号等实体。
new_words <- readLines(file("data/text/new_word.txt"))
new_user_word(worker = jieba_seg, words = new_words)
#> [1] TRUE
分词后,再移除数字和英文
词频统计
#> char freq
#> 285 一个 7
#> 397 同学 5
#> 178 一篇 4
#> 356 没有 4
#> 67 鼻音 3
#> 106 田春雨 3
ggwordcloud 包绘制词云图可视化词频统计的结果。
library(ggwordcloud)
head(tmp, 150) |>
ggplot(aes(label = char, size = freq)) +
geom_text_wordcloud(seed = 2022, grid_size = 8, max_grid_size = 24) +
scale_size_area(max_size = 10)
计算 TF-IDF 值
17.4 主题的探索
益辉的日志是没有分类和标签的,所以,先聚类,接着逐个分析每个类代表的实际含义。然后,将聚类的结果作为结果标签,再应用多分类回归模型,最后联合聚类、分类模型,从无监督转化到有监督模型。
topicmodels (Grün 和 Hornik 2011) 基于 tm (Feinerer, Hornik, 和 Meyer 2008) 支持潜在狄利克雷分配(Latent Dirichlet Allocation,简称 LDA) 和 Correlated Topics Models (CTM) 文本主题建模,这一套工具比较适合英文文本分词、向量化和建模。text2vec 包支持多个统计模型,如LDA 、LSA 、GloVe 等,文本向量化后,结合统计学习模型,可用于分类、回归、聚类等任务,更多详情见 https://text2vec.org。
接下来使用 David M. Blei 等提出 LDA 算法做主题建模,详情见 LDA 算法原始论文。
首先将所有日志分词、向量化,构建文档-词矩阵 document-term matrix (DTM)
# 移除链接
remove_links <- function(x) {
gsub(pattern = "(<http.*?>)|(\\(http.*?\\))|(<www.*?>)|(\\(www.*?>\\))", replacement = "", x)
}
# 清理、分词、清理
file_list1 <- lapply(file_list, remove_yaml)
file_list1 <- lapply(file_list1, remove_links)
file_list1 <- lapply(file_list1, segment, jiebar = jieba_seg)
file_list1 <- lapply(file_list1, remove_number_english)
去掉没啥实际意义的词(比如单个字),极高频词和极低频词。
采用 LDA(Latent Dirichlet Allocation)算法建模
# 词向量化
vectorizer <- vocab_vectorizer(v)
# 文档-词矩阵 DTM
dtm <- create_dtm(it, vectorizer, type = "dgTMatrix")
# 10 个主题
lda_model <- LDA$new(n_topics = 9, doc_topic_prior = 0.1, topic_word_prior = 0.01)
# 训练模型
doc_topic_distr <- lda_model$fit_transform(
x = dtm, n_iter = 1000, convergence_tol = 0.001,
n_check_convergence = 25, progressbar = FALSE
)
#> INFO [08:15:01.825] early stopping at 175 iteration
#> INFO [08:15:02.121] early stopping at 50 iteration
下图展示主题的分布,各个主题及其所占比例。
barplot(
doc_topic_distr[1, ], xlab = "主题", ylab = "比例",
ylim = c(0, 1), names.arg = 1:ncol(doc_topic_distr)
)
将 9 个主题的 Top 12 词分别打印出来。
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] "例子" "一首" "社会" "统计" "代码" "记得" "吱吱" "时代" "网站"
#> [2,] "翻译" "歌词" "观点" "会议" "函数" "不知" "照片" "意义" "数据"
#> [3,] "字符" "手机" "痛苦" "模型" "文档" "同学" "好吃" "媒体" "图形"
#> [4,] "特征" "这首" "教育" "论文" "文件" "阿姨" "我家" "文化" "域名"
#> [5,] "作品" "首歌" "人类" "老师" "变量" "居然" "家里" "现实" "软件"
#> [6,] "中文" "遗憾" "追求" "分布" "字体" "看见" "味道" "社交" "服务器"
#> [7,] "排版" "艺术" "思考" "小子" "元素" "学校" "厨房" "社区" "邮件"
#> [8,] "意思" "小说" "强烈" "统计学" "语法" "路上" "在家" "眼中" "提供"
#> [9,] "风格" "生活" "成功" "参加" "编译" "听说" "黄瓜" "避免" "编辑"
#> [10,] "主题" "诗词" "接受" "报告" "图片" "印象" "包子" "造成" "系统"
#> [11,] "伟大" "鸡蛋" "工作" "检验" "参数" "当时" "叶子" "事实" "浏览器"
#> [12,] "表示" "人间" "努力" "学生" "生成" "名字" "辣椒" "政治" "注册"
结果有点意思,说明益辉喜欢读书写作(主题 1、3、8)、诗词歌赋(主题 2)、统计图形(主题 4)、代码编程(主题 5)、回忆青春(主题 6)、做菜吃饭(7)、倒腾网站(主题 9)。
提示:参考论文 (Zhang, Li, 和 Zhang 2023) 根据 perplexities 做交叉验证选择最合适的主题数量。
17.5 相似性度量
我与益辉日志的相似性度量
17.6 习题
text2vec 包内置的电影评论数据集
movie_review
中 sentiment(表示正面或负面评价)列作为响应变量,构建二分类模型,对用户的一段评论分类。(提示:词向量化后,采用 glmnet 包做交叉验证调整参数、模型)根据 CRAN 上发布的 R 包元数据分析 R 包的描述字段,实现 R 包主题分类。
接习题 2,根据任务视图对 R 包的标记,建立有监督的多分类模型,评估模型的分类效果,并对尚未标记的 R 包分类。(提示:一个 R 包可能同时属于多个任务视图,考虑使用 xgboost 包)