Візуалізація результатів декількох моделей латентного класу


9

Я використовую аналіз прихованого класу для кластеризації вибірки спостережень на основі набору бінарних змінних. Я використовую R і пакет poLCA. У LCA потрібно вказати кількість кластерів, які ви хочете знайти. На практиці люди зазвичай запускають кілька моделей, в кожній із яких вказано різну кількість класів, а потім використовують різні критерії, щоб визначити, що є "найкращим" поясненням даних.

Мені часто буває дуже корисно переглядати різні моделі, щоб спробувати зрозуміти, як спостереження, класифіковані в моделі з класом = (i), розподіляються моделлю з класом = (i + 1). Принаймні, іноді можна знайти дуже надійні кластери, які існують незалежно від кількості класів у моделі.

Мені б хотілося, як можна побудувати ці відносини, легше повідомляти ці складні результати у документах та колегам, які не є статистично орієнтованими. Я думаю, це зробити дуже просто в R, використовуючи якийсь простий мережевий графічний пакет, але я просто не знаю як.

Може хтось, будь ласка, вказав мене в правильному напрямку. Нижче наведено код для відтворення прикладу набору даних. Кожен вектор xi представляє класифікацію 100 спостережень у моделі з i можливими класами. Я хочу графікувати, як спостереження (рядки) переміщуються від класу до класу через стовпці.

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)

Я думаю, є спосіб створити графік, де вузли є класифікацією, а краї відображають (за вагою або кольором)% спостережень, що рухаються від класифікацій від однієї моделі до іншої. Напр

введіть тут опис зображення

ОНОВЛЕННЯ: Досягнення певного прогресу в пакеті igraph. Починаючи з наведеного вище коду ...

Результати poLCA переробляють однакові номери для опису членства в класі, тому потрібно трохи перекодувати.

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

Тоді вам потрібно отримати всі перехресні табуляції та їх частоти і зв'язати їх в одну матрицю, що визначає всі ребра. Напевно, існує набагато більш елегантний спосіб зробити це.

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)

введіть тут опис зображення

Час грати більше з варіантами igraph, я думаю.


1
Якщо ви знайдете рішення, яке вас задовольняє, ви також можете опублікувати свій код як відповідь
Гала

2
Це перетворюється на щось на зразок парсетів . Див. Ggparallel для реалізації R.
Енді Ш

1
Поки я не помітив коментар @ Енді, я думав про щось на зразок кластерграми (з ідентифікатором предметів проти кластерів №) або, можливо, потоковою програмою (можливо, менш привабливою, якщо у вас мало кластерів). Це, звичайно, передбачає, що ви готові працювати на індивідуальному рівні.
chl

Відповіді:


3

Поки найкращі варіанти, які я знайшов, завдяки вашим пропозиціям, такі:

  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)

Зроблено з іграфом

З Іграфом

Зроблено з ggparallel

З ggparallel

Все ще занадто грубо, щоб поділитися з журналом, але я, безумовно, знайшов короткий огляд цих дуже корисних.

Можливий варіант цього питання щодо переповнення стека , але я ще не мав шансу його реалізувати; і ще одна можливість тут .


1
Дякуємо за публікацію прикладів. Ця публікація в CV показує якийсь приємний код для сюжетів ParSets в R (вибачте, мав би вказати на це першим). Мій набіг до пакету ggparallel дозволяє припустити, що він поки що доволі шорсткий по краях (хоча випадкові дані, як ви показуєте, не будуть виглядати добре IMO для ParSets).
Енді Ш
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.