问题描述
我发现 quanteda 中的 tokens_compound()
会更改不同 R 会话中令牌的顺序。也就是说,即使种子值是固定的,每次重新启动会话后结果都会有所不同,尽管它在单个会话中不会改变。
这里是复制过程:
- 查找搭配、复合标记并保存它们。
library(quanteda)
set.seed(12345)
data(data_corpus_inaugural)
toks <- data_corpus_inaugural %>%
tokens(remove_punct = TRUE,remove_symbol = TRUE,padding = TRUE) %>%
tokens_tolower()
col <- toks %>%
textstat_collocations()
toks.col <- toks %>%
tokens_compound(pattern = col[col$z > 3])
write(attr(toks.col,"types"),"col1.txt")
-
结束并重新启动 R 会话并再次运行上述代码,将“col1.txt”替换为“col2.txt”。
-
比较两组令牌,发现它们不同。
col1 <- read.table("col1.txt")
col2 <- read.table("col2.txt")
identical(col1$V1,col2$V1) # This should return FALSE.
col1$V1[head(which(col1$V1 != col2$V1))]
col2$V1[head(which(col1$V1 != col2$V1))]
这在很多情况下无关紧要,但 LDA(通过 {topicmodels})的结果在不同会话中会发生变化。我猜是因为如果我将 tokens
中的特征顺序重置为 as.list()
和之后的 as.tokens()
(dfm_sort()
不适用于此),LDA 的结果是恒定的。
我想知道这是否只发生在我身上(Ubuntu 18.04.5、R 4.0.4 和 quanteda 2.1.2)并且很高兴听到另一个(更简单的)解决方案。
2 月 20 日更新
比如LDA的输出就不复现了。
lis <- list()
for (i in seq_len(2)) {
set.seed(123)
lis[[i]] <- tokens_compound(toks,pattern = col[col$z > 3]) %>%
dfm() %>%
convert(to = "topicmodels") %>%
LDA(k = 5,method = "Gibbs",control = list(seed = 12345,iter = 100))
}
head(lis[[1]]@gamma)
head(lis[[2]]@gamma)
解决方法
一项有趣的调查,但这既不是错误也不是值得关注的事情。在 quanteda 令牌对象中,类型在处理步骤(例如 textstat_compound()
)之后不是按顺序确定的。这是因为这个函数在 C++ 中是并行化的,并且这些线程如何操作不是由 R 中的 set.seed()
固定的。但这不会影响重要的部分,即类型集,或任何关于令牌本身的内容。如果您希望提取的类型的顺序相同,则应在提取时对它们进行排序。
library("quanteda")
## Package version: 2.1.2
toks <- data_corpus_inaugural %>%
tokens(
remove_punct = TRUE,remove_symbol = TRUE,padding = TRUE
) %>%
tokens_tolower()
col <- quanteda.textstats::textstat_collocations(toks)
事实证明,您不需要保存输出或重新启动 R - 这发生在单个会话中。
# types are differently indexed,but are the same set
lis <- list()
for (i in seq_len(2)) {
set.seed(123)
toks.col <- tokens_compound(toks,pattern = col[col$z > 3])
lis <- c(lis,list(types = types(toks.col)))
}
dframe <- data.frame(lis)
sum(dframe$types != dframe$types.1)
## [1] 19898
head(dframe[dframe$types != dframe$types.1,])
## types types.1
## 8897 at_this_second my_fellow_citizens
## 8898 to_take_the_oath_of_the_presidential_office no_people
## 8899 there_is on_earth
## 8900 occasion_for cause_to_be_thankful
## 8901 an_extended this_is_said
## 8902 there_was spirit_of
但是(无序的)类型集是相同的:
# but
setequal(dframe$types,dframe$types.1)
## [1] TRUE
更重要的是,当我们比较每个有序令牌的值时,它们是相同的:
# tokens are the same
lis <- list()
for (i in seq_len(2)) {
set.seed(123)
toks.col <- tokens_compound(toks,list(toks = as.character(toks.col)))
}
dframe <- data.frame(lis)
all.equal(dframe$toks,dframe$toks.1)
## [1] TRUE
由 reprex package (v1.0.0) 于 2021 年 2 月 18 日创建
另外一条评论,此分析强调了其重要性:我们强烈反对直接访问对象属性。如上所述使用 types(x)
,而不是 attr(x,"types")
。前者将始终有效。后者依赖于我们对对象的实现,这可能会随着我们改进包而改变。