小言_互联网的博客

【R语言文本挖掘】:tidy数据格式及词频计算

751人阅读  评论(0)

【R语言文本挖掘】:tidy数据格式及词频计算


  • 🌸个人主页:JoJo的数据分析历险记
  • 📝个人介绍:小编大四统计在读,目前保研到统计学top3高校继续攻读统计研究生
  • 💌如果文章对你有帮助,欢迎✌关注、👍点赞、✌收藏、👍订阅专栏
  • ✨本文收录于【R语言文本挖掘】本系列主要介绍R语言在文本挖掘领域的应用包括:情感分析、TF-IDF、主题模型等。本系列会坚持完成下去,请大家多多关注点赞支持,一起学习~,尽量坚持每周持续更新,欢迎大家订阅交流学习!

引言

使用tidydata原则是一种使处理数据更容易、更有效的强大方法,在处理文本时也是如此。tidydata具有特定的结果

  • 每个变量都是一列
  • 每个观察都是一行
  • 每种类型的观察单元都是一个表格

我们将整洁的文本格式定义为每行一个标记的表格。以这种方式构建文本数据意味着它符合tidy data原则,并且可以使用一系列的r语言内置函数处理,这使得文本挖掘更方便。

  • 字符串:文本当然可以在 r 中存储为字符串,即字符向量,并且通常文本数据首先以这种形式读入内存。
  • 语料库:这些类型的对象通常包含带有附加元数据和详细信息的原始字符串。
  • 文档-单词矩阵:这是一个稀疏矩阵,描述文档的集合(即语料库),每个文档一行,每个词一列。

1.unnest_tokens分词函数

unnest_tokens函数是用于分词处理的的函数。我们下面先通过一个简短的例子来了解

text <- c("JoJo loves YoYo",
           "小明喜欢打篮球")
text
  1. 'JoJo loves YoYo'
  2. '小明喜欢打篮球'

这是我们可能想要分析的典型特征向量。为了把它变成一个整洁的文本数据集,我们首先需要把它放到一个数据框中。

library(dplyr)
text_df <- tibble(line = 1:2, text = text)
text_df
A tibble: 2 × 2
line text
<int> <chr>
1 JoJo loves YoYo
2 小明喜欢打篮球

tibble 是 r 中的一个数据框类,在 dplyr 和 tibble 包中可用,具有方便的打印方法,不会将字符串转换为因子,并且不使用行名。 Tibbles 非常适合与tidydata一起使用。
注意,这个包含文本的数据框还不能直接与tidydata文本分析兼容。我们无法过滤掉最频繁出现的单词或计数,因为每一行都是由多个组合单词组成的。我们需要将其转换为每个文档每行一个单词 (one-row-one-token)
token是一个有意义的文本单元,通常是一个词,我们有兴趣将其用于进一步分析,而标记化是将文本拆分为标记的过程。
在第一个示例中,我们只有一个文档,在tidydata数据集框架中,我们需要将文本分解为单独的单词(称为tokenization的过程)并将其转换为整洁的数据结构。为此,我们使用 tidytext 的 unnest_tokens() 函数。

library(tidytext)
text_df %>%
  unnest_tokens(word, text)
A tibble: 7 × 2
line word
<int> <chr>
1 jojo
1 loves
1 yoyo
2 小明
2 喜欢
2
2 篮球

这里使用的 unnest_tokens 的两个基本参数是列名。首先,我们有将在文本未嵌套到其中时创建的输出列名称(在本例中为 word),然后是文本来自的输入列(在本例中为 text)。请记住,上面的 text_df 有一个名为 text 的列,其中包含感兴趣的数据。
使用 unnest_tokens 后,我们对每一行进行了拆分,以便在新数据帧的每一行中都有一个标记(单词); unnest_tokens() 中的默认标记化是针对单个单词的,如此处所示。另请注意:

  • 保留其他列,例如每个单词来自的行号。
  • 标点符号已被删除。
  • 默认情况下,unnest_tokens() 将标记转换为小写,这使得它们更容易与其他数据集进行比较或组合。 (使用 to_lower = FALSE 参数关闭此行为)。
    拥有这种格式的文本数据,我们可以使用标准的 tidy 工具集(即 dplyr、tidyr 和 ggplot2)来操作、处理和可视化文本,如图 所示。

2. 简奥斯汀数据集

为了方便说明,这里我们直接使用Jane Austen 的 6 部已完成、已出版的小说从janeaustenr包中的文本,并将它们转换为整洁的格式。janeaustenr包以每行一行的格式提供这些文本,在这种情况下,一行类似于实体书中的文字印刷行。让我们从这个开始,并且还使用mutate()来注释行号数量以跟踪原始格式的行,并使用章节 (使用正则表达式) 来查找所有章节的位置。

library(janeaustenr)
library(dplyr)
library(stringr)
original_books <- austen_books() %>%#加载数据集
    group_by(book) %>%
    mutate(linenumber = row_number(),#计算行数
            chapter = cumsum(str_detect(text, 
                                     regex("^chapter [\\divxlc]",
                                           ignore_case = TrUE)))) %>%#使用正则来提取章节信息
   ungroup()
original_books %>% head(10)#查看数据集
A tibble: 10 × 4
text book linenumber chapter
<chr> <fct> <int> <int>
SENSE AND SENSIBILITY Sense & Sensibility 1 0
Sense & Sensibility 2 0
by Jane Austen Sense & Sensibility 3 0
Sense & Sensibility 4 0
(1811) Sense & Sensibility 5 0
Sense & Sensibility 6 0
Sense & Sensibility 7 0
Sense & Sensibility 8 0
Sense & Sensibility 9 0
CHAPTEr 1 Sense & Sensibility 10 1

为了将这个文本转换成tidy数据集,我们需要将这个数据结构转换成一行一个token的形式,因此需要使用unnest_tokens()函数

library(tidytext)
tidy_books <- original_books %>%
  unnest_tokens(word, text)#将text进行分词处理
#查看数据集
tidy_books %>% head(10)
A tibble: 10 × 4
book linenumber chapter word
<fct> <int> <int> <chr>
Sense & Sensibility 1 0 sense
Sense & Sensibility 1 0 and
Sense & Sensibility 1 0 sensibility
Sense & Sensibility 3 0 by
Sense & Sensibility 3 0 jane
Sense & Sensibility 3 0 austen
Sense & Sensibility 5 0 1811
Sense & Sensibility 10 1 chapter
Sense & Sensibility 10 1 1
Sense & Sensibility 13 1 the

此函数使用 tokenizers 包将原始数据帧中的每一行文本分隔为单词。默认token是针对单词的,但其他选项包括string、n-gram、句子、行、段落或围绕正则表达式模式的分隔。 现在数据是每行一个单词的格式,我们可以使用 dplyr 等整洁的工具对其进行操作。
通常在文本分析中,我们会想要移除停用词;停用词是对分析没有太大的词,通常是英语中非常常见的词,例如 “the”、“of”、“to” 等这些介词。我们可以使用 anti_join() 删除停用词,包含在tidytext的stop_words数据中
我们现在先来看一下停用词

stop_words %>% head() 
A tibble: 6 × 2
word lexicon
<chr> <chr>
a SMArT
a's SMArT
able SMArT
about SMArT
above SMArT
according SMArT

然后我们使用anti_join,反连接来将文本中的停用词删除

tidy_books <- tidy_books %>%
  anti_join(stop_words)#删除常见的停用词

tidytext包中的stop_words数据集包含来自三个词典的停用词。我们可以一起使用它们,就像我们在上面使用的那样,或者filter()只使用一组停用词。这取决于某个具体的分析情况。
我们还可以使用dplyr的**count()**来查找所有书籍中最常见的单词。

tidy_books %>%
  count(word, sort = TrUE) %>%#按照单词出现的次数排序
  head()
A tibble: 6 × 2
word n
<chr> <int>
miss 1855
time 1337
fanny 862
dear 822
lady 817
sir 806

我们使用ggplot2来进行可视化分析,进一步分析各个单词的情况

library(ggplot2)

tidy_books %>%
  count(word, sort = TrUE) %>%
  filter(n > 600) %>%#这里我们筛选出现次数大于600的词
  mutate(word = reorder(word, n)) %>%#这里需要重新再进行排序,因为过滤之后重新排序了。
  ggplot(aes(n, word)) +
  geom_col() +
  labs(y = NULL)



请注意,austen_books()函数以我们想要分析的文本开始,但在其他情况下,我们可能需要执行文本数据的清理,例如删除版权标题或格式。

现在我们已经使用janeaustenr 包来探索文本,让我们介绍一下 gutenbergr 包(robinson 2016)。 Gutenbergr 包提供对 Project Gutenberg 收藏中公共领域作品的访问。该软件包包括用于下载书籍的工具(去除无用的页眉/页脚信息),以及可用于查找感兴趣的作品的古腾堡项目元数据的完整数据集。在本书中,我们将主要使用函数 gutenberg_download(),通过 ID 从 Project Gutenberg 下载一个或多个作品,但您也可以使用其他函数来探索数据,将 Gutenberg ID 与标题、作者、语言等配对,或者收集有关作者的信息。

3.词频计算及相关性分析

文本挖掘中的一个常见任务是查看词频,就像我们在上面对简·奥斯汀的小说所做的那样,并比较不同文本中的词频。我们可以使用 tidy data 原则直观而流畅地做到这一点。我们已经有了简奥斯汀的作品;让我们再获取两组文本进行比较。首先,让我们看一下19 世纪末和 20 世纪初 H.G. Wells 的一些科幻和奇幻小说。让我们来看看时间机器、世界大战、隐形人和莫罗博士岛。 我们可以使用gutenberg_download() 和每本小说的Project Gutenberg ID 号访问这些作品。

library(gutenbergr)

hgwells <- gutenberg_download(c(35, 36, 5230, 159))

tidy_hgwells <- hgwells %>%
  unnest_tokens(word, text) %>%
  anti_join(stop_words)

我们来看看H.G. Wells的小说中的单词情况

tidy_hgwells %>%
  count(word, sort = TrUE) %>% filter(n>200)
A tibble: 11 × 2
word n
<chr> <int>
time 461
people 302
door 260
heard 249
black 232
stood 229
white 224
hand 218
kemp 213
eyes 210
suddenly 210

现在让我们来看看勃朗特的一些著名作品,他和简奥斯汀的生活时期有些重叠,但写作风格却截然不同。让我们来看看简·爱、呼啸山庄、荒野庄园的房客、维莱特和艾格尼丝·格雷。我们将再次为每部小说使用 Project Gutenberg ID 编号,并使用 gutenberg_download() 访问文本。

bronte <- gutenberg_download(c(1260, 768, 969, 9182, 767))
tidy_bronte <- bronte %>%
  unnest_tokens(word, text) %>%
  anti_join(stop_words)#删除停用词
tidy_bronte %>%
  count(word, sort = TrUE) %>%head()
A tibble: 6 × 2
word n
<chr> <int>
time 1065
miss 854
day 825
hand 767
eyes 714
don’t 666

有趣的是,H.G. Wells 和 Bronte的 “time”、“eyes”和“hand” 出现的次数都在前 10 名。
现在,让我们通过将数据框连接在一起来计算 Jane Austen、Bronte和 H.G. Wells 作品中每个单词的频率。我们可以使用 tidyr 中的 pivot_wider() 和 pivot_longer() 来重塑我们的数据框,以便我们需要绘制和比较三组小说。

library(tidyr)
frequency <- bind_rows(mutate(tidy_bronte, author = "Bronte Sisters"),
                       mutate(tidy_hgwells, author = "H.G. Wells"), 
                       mutate(tidy_books, author = "Jane Austen")) %>% 
  mutate(word = str_extract(word, "[a-z']+")) %>%#筛选单词
  count(author, word) %>%
  group_by(author) %>%#根据作者进行分组
  mutate(proportion = n / sum(n)) %>% #计算词频
  select(-n) %>% 
  pivot_wider(names_from = author, values_from = proportion) %>%#将数据转换
  pivot_longer(`Bronte Sisters`:`H.G. Wells`,
               names_to = "author", values_to = "proportion")

frequency%>%head()
A tibble: 6 × 4
word Jane Austen author proportion
<chr> <dbl> <chr> <dbl>
a 9.190796e-06 Bronte Sisters 5.869785e-05
a 9.190796e-06 H.G. Wells 1.471844e-05
aback NA Bronte Sisters 3.913190e-06
aback NA H.G. Wells 1.471844e-05
abaht NA Bronte Sisters 3.913190e-06
abaht NA H.G. Wells NA

我们在这里使用 str_extract() 是因为来自 Project Gutenberg 的 UTF-8 编码文本有一些带有下划线的单词示例,以表示强调(如斜体)。分词器将这些视为单词,但我们不像我们在选择使用 str_extract() 之前的初始数据探索中看到的那样,将“_any_”与“any”分开计算。

library(scales)

# 会返回
ggplot(frequency, aes(x = proportion, y = `Jane Austen`, 
                      color = abs(`Jane Austen` - proportion))) +
  geom_abline(color = "gray20", lty = 2) +#拟合线
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.3, height = 0.3) +#抖动图
  geom_text(aes(label = word), check_overlap = TrUE, vjust = 1.5) +#增加文本
  scale_x_log10(labels = percent_format()) +#坐标轴取对数
  scale_y_log10(labels = percent_format()) +
  scale_color_gradient(limits = c(0, 0.001), 
                       low = "darkslategray4", high = "gray75") +#设置颜色
  facet_wrap(~author, ncol = 2) +#分面绘图
  theme(legend.position="none") +#去除图例
  labs(y = "Jane Austen", x = NULL)#y标签

在这些图中,靠近线的单词在两组文本中具有相似的频率,例如,在奥斯汀和勃朗特文本中 (“miss”、“time”、“day”在高频端) 或在奥斯汀文本中和 Wells 文本 (高频端的“time”、“date”、“brother”)。离线较远的词是在一组文本中比在另一组文本中更多的词。例如,在奥斯汀-勃朗特面板中,像 “elizabeth”、“emma”和“fanny”(都是专有名词) 这样的词在奥斯汀的文本中出现,但在勃朗特的文本中却不多,而像 “arthur”和“dog” 出现在勃朗特的文本中,但没有出现在奥斯汀的文本中。在将 H.G. Wells 与简·奥斯汀进行比较时,威尔斯使用了奥斯汀没有使用的 “beast”、“gun”、“fell”和“black” 等词,而奥斯汀使用了 “family”、“friend”、“letter” 等词,而威尔斯没有。

总体来说, 上图的 Austen-Bronte 面板中的单词比 Austen-Wells 面板中的更接近斜率线。 Austen-Wells 面板在低频时有空白空间。这些特征表明,奥斯汀和勃朗特使用的相似词比奥斯汀和威尔斯更多。此外,我们看到并非在所有三组文本中都找到了所有单词,并且 Austen 和 H.G. Wells 面板中的数据点较少。
让我们使用相关性测试来量化这些词频集的相似和不同之处。奥斯汀和勃朗特之间以及奥斯汀和威尔斯之间的词频有多相关?

cor.test(data = frequency[frequency$author == "Bronte Sisters",],
         ~ proportion + `Jane Austen`)


​ Pearson’s product-moment correlation

data:  proportion and Jane Austen
t = 111.06, df = 10346, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7285370 0.7461189
sample estimates:
      cor 
0.7374529 
cor.test(data = frequency[frequency$author == "H.G. Wells",],
         ~ proportion + `Jane Austen`)


​ Pearson’s product-moment correlation

data:  proportion and Jane Austen
t = 35.229, df = 6008, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3925914 0.4345047
sample estimates:
      cor 
0.4137673 

正如我们在图中看到的那样,奥斯汀和勃朗特小说之间的词频相关性(0.737) 比奥斯汀和 H.G.威尔斯之间相关性(0.414) 更高。

4.总结

在本文,我们探讨了关于tidy data的含义,以及如何将tidy data原则应用于自然语言处理。当文本以每行一个单词的格式组织时,删除停用词或计算词频等任务是tidy data生态系统中很自然的应用。每行一个token的框架可以从单个单词扩展到 n-gram 和其他有意义的文本单元,这些我们在后续继续讨论。如果文章对你有帮助,请多多点赞、收藏、评论支持,您的支持是我创作最大的动力!

参考资料:Text Mining with R


转载:https://blog.csdn.net/weixin_45052363/article/details/125774148
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场