Clustering textuel avec R

Dans l’article précédent, Oracle Text a été utilisé pour assurer le partitionnement d’un ensemble de recettes de cuisines. Dans ce post, la même opération va être réalisée à l’aide de R. Les données utilisées seront identiques (on les rapatrie avec ROracle depuis la base Oracle de l’article précédent):

> library(ROracle)
Loading required package: DBI
> ora = Oracle()
> cnx = dbConnect(ora, username="c##rafa", password="Password1#", dbname="//clorai2-scan:1521/pdb_hodba08")
> data_set <- dbGetQuery(cnx, "select * from RECETTES_CLEAN")
>

On va se servir des fonctions de la librairie tm pour réaliser les opérations de manipulation de texte. On commence par créer un Corpus de termes à partir des listes d’ingrédients:

> library(tm)
> IngredientsCorpus <- Corpus(VectorSource(data_set$INGREDIENTS), readerControl = list(language = "fr"))
>

Ce Corpus va ensuite être re-travaillé en réalisant les opérations suivantes:

  • Homogénéisation de la casse
  • Suppression des symboles de ponctuation
  • Suppression des chiffres
  • Conversion des caractères accentués en caractères non-accentués
  • Suppression des mots vides (on récupère depuis CTX_STOPWORDS la liste des mots vides générée dans le billet précédent)
  • Suppression des espaces
  • Racinisation des termes
> IngredientsCorpus <- tm_map(IngredientsCorpus, content_transformer(tolower))
>
> replacePunctuation <- function(x) {
+   gsub("[[:punct:]]+", " ", x)
+ }
>
> IngredientsCorpus <- tm_map(IngredientsCorpus, content_transformer(replacePunctuation))
> 
> IngredientsCorpus <- tm_map(IngredientsCorpus, removeNumbers)
>
> replaceAccent <- function(x) {
+   iconv(x, to="ASCII//TRANSLIT//IGNORE")
+ }
>
> IngredientsCorpus <- tm_map(IngredientsCorpus, replaceAccent)
> 
> mots_vides <- dbGetQuery(cnx, "select upper(spw_word) mot from CTX_STOPWORDS where spw_stoplist='RECETTE_STOPLIST'")
> IngredientsCorpus <- tm_map(IngredientsCorpus, removeWords, tolower(mots_vides$MOT))
>
> IngredientsCorpus <- tm_map(IngredientsCorpus, stripWhitespace)
>
> IngredientsCorpus <- tm_map(IngredientsCorpus, stemDocument, "fr")
>

A partir du Corpus ainsi obtenu, on produit une matrice Documents/Termes (on extrait au passage les mots de moins de 3 lettres). On lui applique ensuite la fonction wigthTfIdf pour déterminer les poids Td-Idf de chaque terme:

 
> IngredientsDTM <- DocumentTermMatrix(IngredientsCorpus, control=list(minWordLength=3))
>
> IngredientsDTM_TfIdf <- weightTfIdf(IngredientsDTM)
>

Pour chaque document, on normalise les poids par la norme euclidienne/L2 du document:

> normalisation_L2 <- function(x) {
+   x / apply(x, MARGIN=1, 
+             FUN=function(y) 
+             {
+               norm(y, type="2")
+             })
+ }
>
> IngredientsDTM_TfIdf_norm <- normalisation_L2(as.matrix(IngredientsDTM_TfIdf))
> 

La matrice peut alors être utilisée par la fonction kmeans. On indique que l’on souhaite obtenir 2 clusters:

> recettes_cluster <- kmeans(IngredientsDTM_TfIdf_norm, 2)
> table(recettes_cluster$cluster, data_set$CATEGORIE_PLAT)
   
    Salé Sucré
  1    2   169
  2  354    19
> 

On peut voir à l’aide de la table de contingence que le résultat du partitionnement est très similaire à celui obtenu dans le billet précédent.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

+ 1 = eleven