Rのtopicmodelsパッケージを使用してトピックモデリングを行っています。コーパスオブジェクトを作成し、基本的な前処理を行ってから、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".
これは、前処理後に用語が含まれていないドキュメントが少なくとも1つあることを意味していると思います。 DocumentTermMatrixから用語を含まないドキュメントを削除する簡単な方法はありますか?
Topicmodelsパッケージのドキュメントを調べて、ドキュメントに表示されない用語を削除するremoveSparseTerms関数を見つけましたが、ドキュメントを削除するための類似物はありません。
"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
agstudyの答えはうまく機能しますが、遅いコンピューターでそれを使用すると、やや問題があります。
_tic()
row_total = apply(dtm, 1, sum)
dtm.new = dtm[row_total>0,]
toc()
4.859 sec elapsed
_
(これは4000x15000 dtmで行われました)
ボトルネックは、スパース行列にsum()
を適用しているようです。
tm
パッケージによって作成されたdocument-term-matrixには、名前iおよび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
と同じ順序になります。パフォーマンスが向上するのは、ドキュメントの用語マトリックスが小さい場合は重要ではありませんが、マトリックスが大きい場合は重要になる場合があります。
これは、agstudyによって与えられた答えを詳しく説明するためのものです。
Dtmマトリックスから空の行を削除する代わりに、空でないドキュメントのみで2番目のdtmを実行する前に、長さがゼロのコーパス内のドキュメントを特定し、コーパスから直接ドキュメントを削除できます。
これは、dtmとコーパスを1対1で対応させるのに役立ちます。
empty.rows <- dtm[rowTotals == 0, ]$dimnames[1][[1]] corpus <- corpus[-as.numeric(empty.rows)]
DTMからスパース用語を削除するだけで、すべて正常に機能します。
dtm <- DocumentTermMatrix(crude, sparse=TRUE)
ダリオ・ラカンの答えに対するちょっとした補遺:
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 )
)