Visualisation des résultats de plusieurs modèles de classes latentes

9

J'utilise l'analyse de classe latente pour regrouper un échantillon d'observations basé sur un ensemble de variables binaires. J'utilise R et le package poLCA. Dans LCA, vous devez spécifier le nombre de clusters que vous souhaitez rechercher. Dans la pratique, les gens exécutent généralement plusieurs modèles, chacun spécifiant un nombre différent de classes, puis utilisent divers critères pour déterminer quelle est la "meilleure" explication des données.

Je trouve souvent très utile de parcourir les différents modèles pour essayer de comprendre comment les observations classées dans le modèle de classe = (i) sont distribuées par le modèle de classe = (i + 1). À tout le moins, vous pouvez parfois trouver des clusters très robustes qui existent quel que soit le nombre de classes dans le modèle.

Je voudrais un moyen de représenter graphiquement ces relations, de communiquer plus facilement ces résultats complexes dans des articles et à des collègues qui ne sont pas statistiquement orientés. J'imagine que c'est très facile à faire dans R en utilisant une sorte de package graphique réseau simple, mais je ne sais tout simplement pas comment.

Quelqu'un pourrait-il m'orienter dans la bonne direction? Ci-dessous, le code pour reproduire un exemple de jeu de données. Chaque vecteur xi représente la classification de 100 observations, dans un modèle avec i classes possibles. Je veux représenter graphiquement comment les observations (lignes) se déplacent d'une classe à l'autre à travers les colonnes.

x1 <- sample(1:1, 100, replace=T)
x2 <- sample(1:2, 100, replace=T)
x3 <- sample(1:3, 100, replace=T)
x4 <- sample(1:4, 100, replace=T)
x5 <- sample(1:5, 100, replace=T)

results <- cbind (x1, x2, x3, x4, x5)

J'imagine qu'il existe un moyen de produire un graphique où les nœuds sont des classifications et les bords reflètent (en poids, ou en couleur peut-être) le% d'observations passant des classifications d'un modèle au suivant. Par exemple

entrez la description de l'image ici

MISE À JOUR: Avoir des progrès avec le package igraph. À partir du code ci-dessus ...

Les résultats poLCA recyclent les mêmes nombres pour décrire l'appartenance à la classe, vous devez donc faire un peu de recodage.

N<-ncol(results) 
n<-0
for(i in 2:N) {
results[,i]<- (results[,i])+((i-1)+n)
n<-((i-1)+n)
}

Ensuite, vous devez obtenir toutes les tabulations croisées et leurs fréquences, et les lier dans une matrice définissant tous les bords. Il existe probablement une manière beaucoup plus élégante de procéder.

results <-as.data.frame(results)

g1           <- count(results,c("x1", "x2"))

g2           <- count(results,c("x2", "x3"))
colnames(g2) <- c("x1", "x2", "freq")

g3           <- count(results,c("x3", "x4"))
colnames(g3) <- c("x1", "x2", "freq")

g4           <- count(results,c("x4", "x5"))
colnames(g4) <- c("x1", "x2", "freq")

results <- rbind(g1, g2, g3, g4)

library(igraph)

g1 <- graph.data.frame(results, directed=TRUE)

plot.igraph(g1, layout=layout.reingold.tilford)

entrez la description de l'image ici

Il est temps de jouer plus avec les options igraph, je suppose.

DL Dahly
la source
1
Si vous trouvez une solution qui vous satisfait, vous pouvez également poster votre code comme réponse
Gala
2
Cela se transforme en quelque chose comme des parsets . Voir ggparallel pour une implémentation R.
Andy W
1
Jusqu'à ce que je remarque le commentaire de @ Andy, je pensais à quelque chose comme un clustergram (avec l'ID des sujets vs no. Clusters) ou peut-être un streamgraph (probablement moins attrayant si vous avez peu de clusters). Ceci, bien sûr, suppose que vous êtes disposé à travailler au niveau individuel.
chl

Réponses:

3

Jusqu'à présent, les meilleures options que j'ai trouvées, grâce à vos suggestions, sont les suivantes:

  library (igraph)
  library (ggparallel)

# Generate random data

  x1 <- sample(1:1, 1000, replace=T)
  x2 <- sample(2:3, 1000, replace=T)
  x3 <- sample(4:6, 1000, replace=T)
  x4 <- sample(7:10, 1000, replace=T)
  x5 <- sample(11:15, 1000, replace=T)
  results <- cbind (x1, x2, x3, x4, x5)
  results <-as.data.frame(results)

# Make a data frame for the edges and counts

  g1           <- count (results, c("x1", "x2"))

  g2           <- count (results, c("x2", "x3"))
  colnames(g2) <- c     ("x1", "x2", "freq")

  g3           <- count (results, c("x3", "x4"))
  colnames(g3) <- c     ("x1", "x2", "freq")

  g4           <- count (results, c("x4", "x5"))
  colnames(g4) <- c     ("x1", "x2", "freq")

  edges        <- rbind (g1, g2, g3, g4)

# Make a data frame for the class sizes

  h1            <- count (results, c("x1"))

  h2            <- count (results, c("x2"))
  colnames (h2) <- c     ("x1", "freq")

  h3            <- count (results, c("x3"))
  colnames (h3) <- c     ("x1", "freq")

  h4            <- count (results, c("x4"))
  colnames (h4) <- c     ("x1", "freq")

  h5            <- count (results, c("x5"))
  colnames (h5) <- c     ("x1", "freq")

  cSizes        <- rbind (h1, h2, h3, h4, h5)

# Graph with igraph

  gph    <- graph.data.frame (edges, directed=TRUE)

  layout <- layout.reingold.tilford (gph, root = 1)
  plot (gph,
        layout           = layout,
        edge.label       = edges$freq, 
        edge.curved      = FALSE,
        edge.label.cex   = .8,
        edge.label.color = "black",
        edge.color       = "grey",
        edge.arrow.mode  = 0,
        vertex.label     = cSizes$x1 , 
        vertex.shape     = "square",
        vertex.size      = cSizes$freq/20)

# The same idea, using ggparallel

  a <- c("x1", "x2", "x3", "x4", "x5")

  ggparallel (list (a), 
              data        = results, 
              method      = "hammock", 
              asp         = .7, 
              alpha       = .5, 
              width       = .5, 
              text.angle = 0)

Fait avec igraph

Avec Igraph

Fait avec ggparallel

Avec ggparallel

Encore trop difficile à partager dans un journal, mais j'ai certainement trouvé utile de les consulter rapidement.

Il y a aussi une option possible de cette question sur le débordement de pile , mais je n'ai pas encore eu l'occasion de l'implémenter; et une autre possibilité ici .

DL Dahly
la source
1
Merci d'avoir posté les exemples. Ce post sur CV montre un code plus agréable pour les tracés ParSets dans R (désolé aurait dû le signaler en premier). Mon incursion dans le paquet ggparallel suggère qu'il est assez rude jusqu'à présent (bien que les données aléatoires comme vous le montrez n'auront pas tendance à avoir l'air bien IMO pour les ParSets).
Andy W