Visualización de resultados de múltiples modelos de clase latente

9

Estoy usando un análisis de clase latente para agrupar una muestra de observaciones basadas en un conjunto de variables binarias. Estoy usando R y el paquete poLCA. En LCA, debe especificar el número de clústeres que desea encontrar. En la práctica, las personas usualmente ejecutan varios modelos, cada uno especificando un número diferente de clases, y luego usan varios criterios para determinar cuál es la "mejor" explicación de los datos.

A menudo me resulta muy útil mirar a través de los diversos modelos para tratar de comprender cómo las observaciones clasificadas en el modelo con clase = (i) se distribuyen por el modelo con clase = (i + 1). Como mínimo, a veces puede encontrar clústeres muy robustos que existen independientemente del número de clases en el modelo.

Me gustaría una forma de graficar estas relaciones, para comunicar más fácilmente estos resultados complejos en documentos y colegas que no están orientados estadísticamente. Me imagino que esto es muy fácil de hacer en R usando algún tipo de paquete de gráficos de red simple, pero simplemente no sé cómo.

¿Podría alguien señalarme en la dirección correcta? A continuación hay un código para reproducir un conjunto de datos de ejemplo. Cada vector xi representa la clasificación de 100 observaciones, en un modelo con i clases posibles. Quiero graficar cómo las observaciones (filas) se mueven de una clase a otra a través de las columnas.

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)

Me imagino que hay una manera de producir un gráfico donde los nodos son clasificaciones y los bordes reflejan (por pesos, o color tal vez) el% de observaciones que se mueven de clasificaciones de un modelo a otro. P.ej

ingrese la descripción de la imagen aquí

ACTUALIZACIÓN: Tener algún progreso con el paquete igraph. A partir del código anterior ...

Los resultados de poLCA reciclan los mismos números para describir la membresía de la clase, por lo que debe hacer un poco de recodificación.

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

Luego debe obtener todas las tabulaciones cruzadas y sus frecuencias, y vincularlas en una matriz que defina todos los bordes. Probablemente haya una forma mucho más elegante de hacer esto.

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)

ingrese la descripción de la imagen aquí

Es hora de jugar más con las opciones de igraph, supongo.

DL Dahly
fuente
1
Si encuentra una solución que lo satisfaga, también puede publicar su código como respuesta
Gala
2
Esto se está convirtiendo en algo así como parsets . Ver ggparallel para una implementación de R.
Andy W
1
Hasta que noté el comentario de @ Andy, estaba pensando en algo así como un clustergram (con ID de los sujetos frente a no. Clusters ) o tal vez un diagrama de flujo (probablemente menos atractivo si tiene pocos clusters). Esto, por supuesto, supone que está dispuesto a trabajar a nivel individual.
chl

Respuestas:

3

Hasta ahora, las mejores opciones que he encontrado, gracias a sus sugerencias, son estas:

  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)

Hecho con igraph

Con Igraph

Hecho con ggparallel

Con ggparallel

Todavía es demasiado difícil para compartir en un diario, pero ciertamente he echado un vistazo rápido a estos muy útil.

También hay una posible opción de esta pregunta sobre el desbordamiento de la pila , pero aún no he tenido la oportunidad de implementarlo; y otra posibilidad aquí .

DL Dahly
fuente
1
Gracias por publicar los ejemplos. Esta publicación en CV muestra un código más agradable para las parcelas ParSets en R (lo siento debería haber señalado eso primero). Mi incursión en el paquete ggparallel sugiere que es bastante irregular hasta el momento (aunque los datos aleatorios como muestra no tenderán a verse bien como OMI para ParSets).
Andy W