Wizualizacja wyników z wielu ukrytych modeli klas

9

Używam ukrytej analizy klas do grupowania próbki obserwacji na podstawie zestawu zmiennych binarnych. Używam R i pakietu poLCA. W LCA musisz określić liczbę klastrów, które chcesz znaleźć. W praktyce ludzie zwykle uruchamiają kilka modeli, z których każdy określa inną liczbę klas, a następnie używają różnych kryteriów, aby ustalić, które jest „najlepszym” wyjaśnieniem danych.

Często uważam, że bardzo przydatne jest przeglądanie różnych modeli, aby spróbować zrozumieć, w jaki sposób obserwacje sklasyfikowane w modelu o klasie = (i) są rozkładane przez model o klasie = (i + 1). Przynajmniej czasami można znaleźć bardzo solidne klastry, które istnieją niezależnie od liczby klas w modelu.

Chciałbym znaleźć sposób na zobrazowanie tych związków, łatwiejsze przekazywanie tych złożonych wyników w artykułach i współpracownikom, którzy nie są zorientowani statystycznie. Wyobrażam sobie, że jest to bardzo łatwe w R przy użyciu jakiegoś prostego pakietu grafiki sieciowej, ale po prostu nie wiem jak.

Czy ktoś mógłby wskazać mi właściwy kierunek. Poniżej znajduje się kod do odtworzenia przykładowego zestawu danych. Każdy wektor xi reprezentuje klasyfikację 100 obserwacji w modelu z możliwymi klasami. Chcę wykreślić, w jaki sposób obserwacje (wiersze) przemieszczają się między klasami w kolumnach.

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)

Wyobrażam sobie, że istnieje sposób na stworzenie wykresu, w którym węzły są klasyfikacjami, a krawędzie odzwierciedlają (według wagi lub może koloru)% obserwacji przechodzących z klasyfikacji z jednego modelu do drugiego. Na przykład

wprowadź opis zdjęcia tutaj

AKTUALIZACJA: Trochę postępu w pakiecie igraph. Począwszy od powyższego kodu ...

Wyniki poLCA przetwarzają te same liczby, aby opisać członkostwo w klasie, więc trzeba trochę przekodować.

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

Następnie musisz zebrać wszystkie tabele krzyżowe i ich częstotliwości i powiązać je w jedną macierz definiującą wszystkie krawędzie. Prawdopodobnie jest to o wiele bardziej elegancki sposób.

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)

wprowadź opis zdjęcia tutaj

Czas na więcej zabawy z opcjami igraph.

DL Dahly
źródło
1
Jeśli znajdziesz rozwiązanie, które Cię zadowoli, możesz również opublikować swój kod jako odpowiedź
Gala
2
To zamienia się w coś w rodzaju zestawów . Zobacz ggparallel na implementację R.
Andy W
1
Dopóki nie zauważyłem komentarza @ Andy'ego, myślałem o czymś w rodzaju klastrergramu (z identyfikatorem badanych kontra liczba klastrów) lub może wykresu strumieniowego (prawdopodobnie mniej atrakcyjnego, jeśli masz kilka klastrów). To oczywiście zakłada, że ​​chcesz pracować na poziomie indywidualnym.
chl

Odpowiedzi:

3

Jak dotąd najlepsze opcje, które znalazłem, dzięki twoim sugestiom, to:

  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)

Zrobione z igraph

Z Igraphem

Zrobione z ggparallel

Z ggparallel

Wciąż zbyt szorstki, by móc go udostępnić w czasopiśmie, ale z pewnością uważam, że rzuciłem okiem na te bardzo przydatne.

Istnieje również możliwa opcja z tego pytania na temat przepełnienia stosu , ale nie miałem jeszcze okazji go wdrożyć; i inna możliwość tutaj .

DL Dahly
źródło
1
Dziękujemy za opublikowanie przykładów. Ten post na CV pokazuje trochę ładniejszy kod dla wykresów ParSets w R (przepraszam, że powinienem najpierw to wskazać). Moja wyprawa do pakietu ggparallel sugeruje, że do tej pory jest dość szorstki (chociaż losowe dane, takie jak Ty, nie będą wyglądały ładnie IMO dla ParSets).
Andy W