Удалить пустые документы из DocumentTermMatrix в темах R?
Я занимаюсь тематическим моделированием с помощью пакета topicmodels в R. Я создаю объект Corpus, выполняя некоторую базовую предварительную обработку и создавая DocumentTermMatrix:
corpus <- Corpus(VectorSource(vec), readerControl=list(language="en"))
corpus <- tm_map(corpus, tolower)
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeWords, stopwords("english"))
corpus <- tm_map(corpus, stripWhitespace)
corpus <- tm_map(corpus, removeNumbers)
...snip removing several custom lists of stopwords...
corpus <- tm_map(corpus, stemDocument)
dtm <- DocumentTermMatrix(corpus, control=list(minDocFreq=2, minWordLength=2))
И затем выполнение LDA:
LDA(dtm, 30)
Этот последний вызов LDA() возвращает ошибку
"Each row of the input matrix needs to contain at least one non-zero entry".
Я предполагаю, что это означает, что после предварительной обработки есть хотя бы один документ, в котором нет терминов. Есть ли простой способ удалить документы, не содержащие терминов из DocumentTermMatrix?
Я просмотрел документацию для пакета topicmodels и нашел функцию removeSparseTerms, которая удаляет термины, которые не отображаются в каком-либо документе, но нет аналога для удаления документов.
Ответы
Ответ 1
"Each row of the input matrix needs to contain at least one non-zero entry"
Ошибка означает, что разреженная матрица содержит строку без записей (слов). одна идея состоит в том, чтобы вычислить сумму слов по строке
rowTotals <- apply(dtm , 1, sum) #Find the sum of words in each Document
dtm.new <- dtm[rowTotals> 0, ] #remove all docs without words
Ответ 2
Ответ agstudy отлично работает, но использование его на медленном компьютере оказалось довольно проблематичным.
tic()
row_total = apply(dtm, 1, sum)
dtm.new = dtm[row_total>0,]
toc()
4.859 sec elapsed
(это было сделано с размером 4000x15000 точек на дюйм)
Узкое место, кажется, применяет sum()
к разреженной матрице.
Макет документа-терма, созданный пакетом tm
, содержит имена я и j, которые являются индексами, где записи находятся в разреженной матрице. Если dtm$i
не содержит определенного индекса строки p
, то строка p
пуста.
tic()
ui = unique(dtm$i)
dtm.new = dtm[ui,]
toc()
0.121 sec elapsed
ui
содержит все ненулевые индексы, а так как dtm$i
уже упорядочен, dtm.new
будет в том же порядке, что и dtm
. Увеличение производительности может не иметь значения для матричных матриц меньшего документа, но может стать значительным с более крупными матрицами.
Ответ 3
Это просто для того, чтобы уточнить ответ, заданный agstudy.
Вместо того, чтобы удалять пустые строки из матрицы dtm, мы можем идентифицировать документы в нашем корпусе, которые имеют нулевую длину, и удалять документы непосредственно из корпуса, перед выполнением второго dtm с только непустыми документами.
Это полезно для поддержания соответствия 1:1 между dtm и корпусом.
empty.rows <- dtm[rowTotals == 0, ]$dimnames[1][[1]]
corpus <- corpus[-as.numeric(empty.rows)]
Ответ 4
Просто удалите разреженные термины из DTM, и все будет хорошо работать.
dtm <- DocumentTermMatrix(crude, sparse=TRUE)
Ответ 5
Просто небольшое добавление к ответу Дарио Лакана:
empty.rows <- dtm[rowTotals == 0, ]$dimnames[1][[1]]
будет собирать запись id
, а не порядковые номера. Попробуйте следующее:
library(tm)
data("crude")
dtm <- DocumentTermMatrix(crude)
dtm[1, ]$dimnames[1][[1]] # return "127", not "1"
Если вы создаете свой собственный корпус с последовательной нумерацией, после очистки данных некоторые документы могут быть удалены, а нумерация также будет нарушена. Поэтому лучше использовать id
напрямую:
corpus <- tm_filter(
corpus,
FUN = function(doc) !is.element(meta(doc)$id, empty.rows))
# !( meta(doc)$id %in% emptyRows )
)