web-dev-qa-db-fra.com

Supprimer les documents vides de DocumentTermMatrix dans les modèles R?

Je fais de la modélisation de sujet à l'aide du paquet topicmodels de R. Je crée un objet Corpus, effectue un prétraitement de base, puis crée un 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))

Et puis effectuer LDA: 

LDA(dtm, 30)

Ce dernier appel à LDA () renvoie l'erreur 

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

Je suppose que cela signifie qu’il existe au moins un document qui ne contient aucun terme après le prétraitement. Existe-t-il un moyen simple de supprimer des documents ne contenant aucun terme d'un DocumentTermMatrix? 

J'ai consulté la documentation du package topicmodels et trouvé la fonction removeSparseTerms, qui supprime les termes n'apparaissant dans aucun document, mais il n'y a pas d'analogique pour la suppression de documents. 

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

L'erreur signifie que la matrice fragmentée contient une ligne sans entrées (mots). une idée est de calculer la somme des mots par rangée

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

la réponse de agstudy fonctionne très bien, mais son utilisation sur un ordinateur lent s'est révélée légèrement problématique.

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

(cela a été fait avec un 4000x15000 dtm)

Le goulot d'étranglement semble appliquer sum() à une matrice fragmentée.

Une matrice de termes de document créée par le package tm contient les noms i et j, qui sont des indices de l'emplacement où les entrées se trouvent dans la matrice fragmentée. Si dtm$i ne contient pas d'index de ligne particulier p, la ligne p est vide.

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

ui contient tous les index non nuls et, étant donné que dtm$i est déjà commandé, dtm.new sera dans le même ordre que dtm. Le gain de performance peut ne pas importer pour les matrices de termes de document plus petites, mais peut devenir significatif avec des matrices plus grandes.

22
SylphFeather

Ceci est juste pour élaborer sur la réponse donnée par agstudy.

Au lieu de supprimer les lignes vides de la matrice dtm, nous pouvons identifier les documents de longueur nulle dans notre corpus et les supprimer directement du corpus, avant d'exécuter un deuxième fichier DTM avec uniquement des documents non vides.

Ceci est utile pour garder une correspondance 1: 1 entre le DTM et le corpus.

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

11
Dario Lacan

Supprimez simplement les termes clairsemés du DTM et tout fonctionnera correctement.

dtm <- DocumentTermMatrix(crude, sparse=TRUE)
2
Arijay Chaudhry

Juste un petit ajout à la réponse de Dario Lacan:

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

va collecter le id de l'enregistrement, plutôt que les numéros d'ordre. Essaye ça:

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

Si vous construisez votre propre corpus avec une numérotation consécutive, après le nettoyage des données, certains documents peuvent être supprimés et la numérotation sera également interrompue. Donc, il vaut mieux utiliser id directement:

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