Подтвердить что ты не робот

Удалить пустые документы из 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, которая удаляет термины, которые не отображаются в каком-либо документе, но нет аналога для удаления документов.

4b9b3361

Ответ 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 )
)