2012-12-19 10 views
32

robie temat modelowania przy użyciu pakietu topicmodels w R. tworzę obiekt Corpus, robiąc podstawowe wstępne przetwarzanie, a następnie tworząc DocumentTermMatrix:Usunąć puste dokumenty z DocumentTermMatrix w R topicmodels?

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)) 

a następnie wykonywanie LDA:

LDA(dtm, 30) 

to ostateczne wezwanie do LDA() zwraca błąd

"Each row of the input matrix needs to contain at least one non-zero entry". 

Zakładam, oznacza to, że istnieje co najmniej jeden dokument t kapelusz nie zawiera żadnych warunków po wstępnym przetworzeniu. Czy istnieje prosty sposób na usunięcie dokumentów, które nie zawierają terminów z DocumentTermMatrix?

Zajrzałem do dokumentacji pakietu topicmodels i znalazłem funkcję removeSparseTerms, która usuwa terminy, które nie pojawiają się w żadnym dokumencie, ale nie ma analogii do usuwania dokumentów.

+0

Kiedy przeprowadzasz kontrolę (korpus), widzisz jakiś dokument emty? a LDA jest w jakiej paczce? – agstudy

+0

Używam funkcji LDA z pakietu topicmodels. Mam około 51 000 dokumentów, więc nie mogę ręcznie sprawdzić przy inspekcji. Możliwe też, że niektóre terminy są usuwane przez parametry minDocFreq = 2, minWordLength = 2, więc to, co naprawdę chcę sprawdzić, to DocumentTermMatrix. Czy istnieje inny sposób na znalezienie pustych dokumentów? –

Odpowiedz

45
"Each row of the input matrix needs to contain at least one non-zero entry" 

Błąd oznacza, że ​​rzadka macierz zawiera wiersz bez wpisów (słów). jednym pomysłem jest obliczenie sumy słów według wiersza

rowTotals <- apply(dtm , 1, sum) #Find the sum of words in each Document 
dtm.new <- dtm[rowTotals> 0, ]   #remove all docs without words 
+1

Działa to dobrze. Nie zdawałem sobie sprawy, że DocumentTermMatrix może być indeksowany w ten sposób. Dzięki! –

+1

@BillM jest macierzą, specjalną tak (rzadką), ale wciąż macierzą. – agstudy

+3

To rozwiązanie ma jeden potencjalny problem: wynikowa macierz bez dokumentów zawierających słowa zerowe nie będzie już odpowiadać oryginalnemu korpusowi, tzn. Będzie zawierała mniejszą liczbę wierszy. Dlatego nie będzie możliwe wykorzystanie wyniku LDA do kategoryzacji korpusu (na przykład poprzez przypisanie najlepszego klastra do każdego dokumentu). Masz pomysł, jak rozwiązać ten problem? –

11

Jest to po prostu rozwinięcie odpowiedzi udzielonej przez agstudy.

Zamiast usuwać puste wiersze z macierzy dtm, możemy zidentyfikować dokumenty w naszym korpusie, które mają zerową długość i usunąć dokumenty bezpośrednio z korpusu, przed wykonaniem drugiego dtm z tylko niepustymi dokumentami.

Jest to przydatne, aby zachować korespondencję 1: 1 między dtm a korpusem.

empty.rows <- dtm[rowTotals == 0, ]$dimnames[1][[1]] corpus <- corpus[-as.numeric(empty.rows)]

+0

Dla mnie druga linia kodu wyrzuciła ten błąd Błąd w '[.VCorpus' (corpus, 1, 1): nieużywany argument (1). Zmodyfikowałem go do korpusu <- corpus [-as.numeric (empty.rows)] – user131476

+1

Korpus to lista, więc nie można używać wyboru 2D. Po prostu potrzebujesz 'corpus [-as.numeric (empty.rows)]' (tj. Jeden wymiar). – neverfox

+0

Dzięki, edytuję odpowiedź. –

0

Wystarczy niewielki dodatek do odpowiedzi Dario Lacana:

empty.rows <- dtm[rowTotals == 0, ]$dimnames[1][[1]] 

zbierze rekordu id zamiast numerów porządkowych. Spróbuj tego:

library(tm) 
data("crude") 
dtm <- DocumentTermMatrix(crude) 
dtm[1, ]$dimnames[1][[1]] # return "127", not "1" 

Jeśli konstruować własne korpus z kolejnymi numerami, po dane czyszczące niektóre dokumenty mogą być usunięte i numeracja również zostaną złamane. Tak, to lepiej użyć id bezpośrednio:

corpus <- tm_filter(
    corpus, 
    FUN = function(doc) !is.element(meta(doc)$id, empty.rows)) 
    # !(meta(doc)$id %in% emptyRows) 
) 
2

Wystarczy usunąć rozrzedzone warunki z DTM i wszystko będzie działać dobrze.

dtm <- DocumentTermMatrix(crude, sparse=TRUE) 
+0

dtm <-removeSparseTerms (dtm, sparse = 0.98) –

17

Odpowiedź agstudy działa świetnie, ale używanie jej na wolnym komputerze okazało się dość problematyczne.

tic() 
row_total = apply(dtm, 1, sum) 
dtm.new = dtm[row_total>0,] 
toc() 
4.859 sec elapsed 

(to zostało wykonane z 4000x15000 DTM)

Wąskim gardłem wydaje się być zastosowanie sum() do rzadkiego matrycy.

Macierz dokumentu-macierzy utworzona przez pakiet tm zawiera nazwy i oraz j, które są indeksami dla pozycji, które znajdują się w macierzy rzadkiej. Jeśli dtm$i nie zawiera określonego indeksu wiersza p, wiersz p jest pusty.

tic() 
ui = unique(dtm$i) 
dtm.new = dtm[ui,] 
toc() 
0.121 sec elapsed 

ui zawiera wszystkie niezerowe indeksów, a od dtm$i jest już uporządkowane, dtm.new będzie w takiej samej kolejności jak dtm. Wzrost wydajności może nie mieć znaczenia dla mniejszych macierzy terminów dokumentów, ale może stać się znaczący w przypadku większych macierzy.