10
10
在本章我们将用到一些新的R包来扩展我们处理文本和从推特得到的数据中建立我们自己的文字云的能力。如果你还没有阅读上一章节“字符串原理”,那么你应该在继续本章前先去阅读的,因为我们在那里建立了一些技巧。
11
11
12
12
无论你阅读完上一章的那个地方,你需要取得并预处理一个推文的数据集,用一些你已经写好的代码,或者新的。在上一章的结尾,我们提供了一些TweetFrame()函数的样本代码,这个函数利用一个查找术语和一个最大的推文限制,然后返回一个按时间排序的包含推文的数据帧。虽然函数中注释很多,但真正的只有三行功能代码用到了twitteR包的功能,并且为我们从推特上得到了数据。下面的活动中,我们仍然利用上一章我们通过这一命令得到的数据帧进行工作:
13
- < pre >
13
+ ``` r
14
14
tweetDF <- TweetFrame(" #solar" ,100 )
15
- </ pre >
15
+ ```
16
16
这就是数据帧,tweetDF,包含100条带"#solar"标签的推文,可推测出大多数关于太阳能和“绿色”相关的主题。在我们利用这两个新R包工作之前,我们可以通过丢弃大量对文字云没有任何意义的垃圾来改善显示质量。为了完成这个目的,我们调用了其他函数来去除多余的空格和所有的URL字符串,还去除了重复的推文标题,删除标签,消除了指向其他人的推文句柄。所有的这些变形,我们都采用了上一章中提到的字符串包中的字符串替换函数。举个例子,思考下下面这个口令,出现在CleanTweet()函数的倒数第二行:
17
- < pre >
17
+ ``` r
18
18
tweets <- str_replace_all(tweets , " @[a-z,A-Z]*" ," " )
19
- </ pre >
19
+ ```
20
20
你应该觉得这行代码很容易理解,否则,你应该做多一点练习。左边很简单:我们用赋值箭头把右边表达式的结果赋给“tweets”这个数据对象。请注意,当这个语句在这章末尾处的函数中被执行时,“tweets”只是个临时数据对象变量,它在CleanTweets()中使用之后就会自动消失。
21
21
22
22
右边表达式用了stringr包中的str_replace_all()这个函数。我们使用"all"这个函数而不使用str_replace(),原因是我们希望每个推文可以进行多重匹配。str_replace_all()这个函数有3个参数。第一个是输入,它是一个字符串矢量(我们用"tweets"这个临时数据变量作为文本资源同时也作为它的目标输出),第二个是要匹配的正则表达式,第三个是要替换匹配的字符串,其实是个空字符串,双引号中间什么也没有。本例中的正则表达式以“@”符号开头,后面跟着0个或多个小写或大写字母。最后的星号"* "就是代表0个或多个的意思。这个正则表达式会匹配推文中的任何相匹配的账户名字。
@@ -25,28 +25,28 @@ tweets <- str_replace_all(tweets, "@[a-z,A-Z]*","")
25
25
26
26
27
27
无论你是键入本章末尾处的函数还是打算一条一条口令单独的输入,让我们获得一个在原数据帧之外的文本分离矢量开始吧:
28
- < pre >
28
+ ``` r
29
29
cleanText <- tweetDF $ text
30
30
head(cleanText , 10 )
31
- </ pre >
31
+ ```
32
32
做这个的唯一目的是简化展示的剩余部分。你可以很容易拷贝tweetDF$text数据到同一个数据帧的另一列,如果你想的话。我们把这个练习独立起来是因为我们不需要担心围绕数据帧的剩余部分的信息。上面的head()口令给你返回一个你开始工作的预览。现在让我们运行我们自定义的cleaning函数吧:
33
- < pre >
33
+ ``` r
34
34
cleanText <- CleanTweets(cleanText )
35
35
head(cleanText , 10 )
36
- </ pre >
36
+ ```
37
37
既然上面第一行我们用“cleanText”这个数据对象作为参数输入和目标输出。这是一个老式的计算机科学技巧,它是为了减少用到的临时变量的个数。这样,它恰恰会完成我们想要的,首先执行右边的表达式:把"cleanText"作为参数输入到CleanTweets()函数中运行,然后把返回的结果也赋给"cleanText"这个变量,覆盖了它原来的内容。请记住,我们允许对“cleanText”这个变量的内容做任何操作,因为它只是原数据的一个副本,而且我们的原数据是原封不动的(例如,tweetDF数据帧中的文本列)。
38
38
39
39
head()函数应该返回一个简短的被过滤了大量无关垃圾的推文列表。如果你跟着这些 步骤,“cleanText”就是一个为下面的工作做准备的字符串矢量(在这个例子中,刚好是100个字符串)。我们现在用"tm"包去处理我们的文本。本例中的"tm"是指“文本挖掘”,它是R提供的众多的文本分析包中比较受欢迎的一个包。另外,文本挖掘时是指从文本内容中解剖有用的可分析的信息的操作(corpora是corpus的复数)。虽然有些人认为文本挖掘和自然语言处理可以交替使用,但它们还是有些细微的差别需要我们考虑的。首先,文本挖掘的“挖掘”部分是指从大数据集中寻找意想不到的模式这种行为的领域,或者是某些人认为的从数据库中发现知识。相反地,自然语言处理更关注的是机器为什么能够通过编程来理解(或机器自己学习)或怎样识别人类语言,使其有意义。同样地,文本挖掘通常关注的是分析文本数据的统计学函数,使用像统计文本内容中文字频次的策略。在自然语言处理中,我们听到更多的是根据语言学,我们把文本按语法分成一个一个部分,例如名词和动词。在R中的“tm”包,我们可以十分肯定它是属于统计学的阵营,它主要是把文本内容分成一个文字序列,然后统计我们找到的不同的文字和序列。
40
40
41
41
首先,确定你安装了“tm”包并导入了R的副本和R-studio库内。你可以R-studio中的图形接口或者我们前一章写的EnsurePackage()函数。一旦tm包已经准备就绪,你就可以执行以下口令了:
42
- < pre >
42
+ ``` r
43
43
tweetCorpus <- Corpus(VectorSource(cleanText ))
44
44
tweetCorpus
45
45
A corpus with 100 text documents
46
46
tweetCorpus <- tm_map(tweetCorpus , tolower )
47
47
tweetCorpus <- tm_map(tweetCorpus , removePunctuation )
48
48
tweetCorpus <- tm_map(tweetCorpus , removeWords , stopwords(' english' ))
49
- </ pre >
49
+ ```
50
50
在上一步中,我们用“tm”包将cleanText变量强制归入特定“Class”, 即语料库;并且将结果保存为一个新的数据对象”tweetCorpus“。这是我们第一次遇到一个”Class“。”Class“一词来源于计算机科学中的”面向对象编程“(OOP)。虽然R和面向对象编程语言(如Java)有很多不同,它仍然有很多基本功能来实现面向对象的编程。这里,我们介绍一些关于“Class”的基本知识。第一,“Class”只是一个关于数据结构的概念;第二,“Class”通过基本的数据形式来建立复杂的数据结构,例如数值型数据。例如,如果建立一个新的分类“Dashboard(数字仪表盘)”,我们可以引入“Miles Per Hour(小时里程数)“,”RPM“,以及“Fuel Level”来显示油位。由此,我们发现,R软件用户可以直接建立“类”。本例中,“tm”包的作者Ingo Feinerer,创建了一个新的门类,称之为语料库,成为了文本挖掘函数的核心数据结构(Feinerer,维也纳大学计算机科学教授,任职于数据库和人工智能研究组)。最后,本段讨论最重要的是,“类”不仅包含数据结构的概念,它还包含了函数的操作指南。换言之,“Class”是一个带有操作指引的数据对象,从简单操作,如添加、提取对象,到诸如画图之类的复杂操作过程。
51
51
52
52
在“tm”包的例子中,语料库定义了文本挖掘工作者所关心最基础的函数,一个包含了很多文件的语料集。我们一旦将文本储存到语料库中,“tm”包中自带的函数便可以使用了。上述中的最后三个命令展示了tm_map()函数的用法,该函数是tm包中非常有用的特性。在每一个用到tm_map()函数的例子中,我们将tweetCorpus作为自变量,另一个命令给语料数据变形。这里,我们进行了三次变换,首先将所有字母变成小写字母,然后去除标点符号,最后提取出所谓的“停用词”。
@@ -55,32 +55,32 @@ tweetCorpus <- tm_map(tweetCorpus, removeWords, stopwords('english'))
55
55
56
56
这样,我们将语料处理成为了一个没有大写字母,标点符号,和停用词的词语集合。现在我们可以通过创建“文档矩阵” 这些语料进行一些统计分析了。下面tm包中的命令就新建了一个矩阵:
57
57
58
- < pre >
58
+ ``` r
59
59
tweetTDM <- TermDocumentMatrix(tweetCorpus )
60
- </ pre >
60
+ ```
61
61
62
- < pre >
62
+ ``` r
63
63
# # Error: 没有"TermDocumentMatrix"这个函数
64
- </ pre >
64
+ ```
65
65
66
- < pre >
66
+ ``` r
67
67
tweetTDM
68
- </ pre >
68
+ ```
69
69
70
- < pre >
70
+ ``` r
71
71
# # Error: 找不到对象'tweetTDM'
72
- </ pre >
72
+ ```
73
73
74
74
一个文档矩阵,有时也被称为文件-条目矩阵,具有矩形数据结构,以条目s为行,以文件s为列(在别的地方若需要反过来也可以)。条目可以是一个单个单词,比如“biology”,也可以是一个复合词汇,比如“data analysis”。通过统计学的方法来观察哪些词汇通常组成词组,然后判断词汇的形式(是否是一个词组),或者也可以通过词典的方式来解决这个问题。Tm包支持词典的方式,但是我们在这个例子中并没有使用。所以,如果一个条目,比如“data”在第一个文件中出现一次,在第二个文件中出现两次,在第三个文件中没有出现,那么条目数据的栏就会包含1,2,0。
75
75
76
76
统计量会告诉我们什么时候需要在命令行中输入tweetTDM显示结果。条目文件Matrix()函数从100个tweets中提取了375个条目s。结果矩阵有很多0;在矩阵中的37500个单元中,只有610个包括非0条目,剩下的36890个包含0。单元中有0表示特定的文件没有对应的特定条目。根据对输入tweets的最大单词长度,最大条目长度是21个词。最后,在尾行,以“Weighting”开头的表示这类数据存放在文档矩阵中。在这个案例中,我们使用了默认最简单的选项,只是以条目的形式简单地记录了次数的数量,它会出现在文集中的文件中。可以使用下面的检查函数查看文档矩阵:
77
77
78
- < pre >
78
+ ``` r
79
79
inspect(tweetTDM )
80
- </ pre >
81
- < pre >
80
+ ```
81
+ ``` r
82
82
# # Error: 没有"inspect"这个函数
83
- </ pre >
83
+ ```
84
84
85
85
86
86
做好输出大量数据的准备。记住矩阵摘要中的条目“sparse”了吗?Sparse指大量包含0的单元——表明特点的条目并没有出现在给定的文件中。大多数的文档矩阵都是有许多0的。由于36890/37500 = 0.98,所以这个是98%稀疏的。大部分情况下,我们为了展示或者可视化的需求,需要精选或者填补文档矩阵。Tm工具包提供了多种方法来填补0条目,但是在这个例子中,我们将这个艰巨的任务交给词云工具包。
@@ -89,35 +89,35 @@ inspect(tweetTDM)
89
89
90
90
加载词云工具包之后,我们需要做一些准备,使得数据就绪,提交给云生成函数。这个函数需要两个向量作为输入变量,第一个是条目的一列,第二个是条目出现的频率。条目列和频率必须以频率优先的方式排序。为了完成这个工作,我们需要将矩阵变成无格式数据矩阵,这样才能以频率排序。下面的第一个命令可以完成这个:
91
91
92
- < pre >
92
+ ``` r
93
93
tdMatrix <- as.matrix(tweetTDM )
94
- </ pre >
95
- < pre >
94
+ ```
95
+ ``` r
96
96
# # Error: 找不到对象'tweetTDM'
97
- </ pre >
98
- < pre >
97
+ ```
98
+ ``` r
99
99
sortedMatrix <- sort(rowSums(tdMatrix ), decreasing = TRUE )
100
- </ pre >
100
+ ```
101
101
102
- < pre >
102
+ ``` r
103
103
# # Error: 找不到对象'tdMatrix'
104
- </ pre >
104
+ ```
105
105
106
- < pre >
106
+ ``` r
107
107
cloudFrame <- data.frame (word = names(sortedMatrix ), freq = sortedMatrix )
108
- </ pre >
108
+ ```
109
109
110
- < pre >
110
+ ``` r
111
111
# # Error: 找不到对象'sortedMatrix'
112
- </ pre >
112
+ ```
113
113
114
- < pre >
114
+ ``` r
115
115
wordcloud(cloudFrame $ word , cloudFrame $ freq )
116
- </ pre >
116
+ ```
117
117
118
- < pre >
118
+ ``` r
119
119
# # Error: 没有"wordcloud"这个函数
120
- </ pre >
120
+ ```
121
121
122
122
123
123
在上面的第二行,我们完成的是:计算每一行的总数,给出了所有tweets/文件一个条目的总频率。我们还将结果以高频优先排序。结果是一个命令序列:每个条目有一个频率而且每个条目的名称是频率对应的名字。
@@ -151,7 +151,7 @@ wordcloud(cloudFrame$word, cloudFrame$freq)
151
151
### 本章代码
152
152
153
153
154
- < pre >
154
+ ``` r
155
155
# CleanTweets() - Takes the junk out of a vector of tweet texts
156
156
CleanTweets <- function (tweets ) {
157
157
# Remove rudundant spaces
@@ -166,6 +166,6 @@ CleanTweets <- function(tweets) {
166
166
tweets <- str_replace_all(tweets , " @[a-z,A-Z]*" , " " )
167
167
return (tweets )
168
168
}
169
- </ pre >
169
+ ```
170
170
171
171
0 commit comments