структурирование двоичных данных для графика Санки

У меня возникли проблемы с определением того, как построить график для данных, для которых существует несколько возможностей успеха (1) или неудачи (0). Вы можете сгенерировать мой образец с помощью следующего кода:

# example
library(networkD3)
library(tidyverse)
library(tidyr)

set.seed(900)
n=1000
example.data<-data.frame("A" = rep(1,n),
                         "B" = sample(c(0,1),n,replace = T),
                         "C" = rep(NA,n),
                         "D" = rep(NA,n),
                         "E" = rep(NA,n),
                         "F" = rep(NA,n),
                         "G" = rep(NA,n))

for (i in 1:n){
  example.data$C[i]<- ifelse(example.data$B[i]==1,
                                   sample(c(0,1),1,prob = c(0.3,0.7),replace = F),
                                   sample(c(0,1),1,prob = c(0.55,0.45),replace = F))
  example.data$D[i]<-ifelse(example.data$C[i]==1,
                                              sample(c(0,1),1,prob = c(0.95,0.05),replace = F),
                                              sample(c(0,1),1,prob = c(0.65,0.35),replace = F))
  example.data$E[i]<-ifelse(example.data$C[i]==0 & example.data$D[i]==0,
                                    sample(c(0,1),1,prob = c(.9,.1),replace = F),
                                    ifelse(example.data$C[i]==0 & example.data$D[i]==1,
                                           sample(c(0,1),1,prob = c(.3,.7),replace = F),
                                           ifelse(example.data$C[i]==1 & example.data$D[i]==0,
                                                  sample(c(0,1),1,prob = c(.9,.1),replace = F),
                                                  sample(c(0,1),1,prob = c(.1,.9),replace = F))))
  example.data$F[i]<-ifelse(example.data$E==1,
                                         sample(c(1,0),1,prob=c(.85,.15),replace = F),
                                         sample(c(1,0),1,prob = c(.01,.99),replace = F))
  example.data$G[i]<-sample(c(1,0),1,prob = c(.78,.22),replace = F)
}


example.data.1<-example.data%>%
  gather()%>%
  mutate(ORDER = c(rep(0,n),rep(1,n),rep(2,n),rep(3,n),rep(4,n),rep(5,n),rep(6,n)))%>%
  dplyr::select("Event" = key,
                "Success" = value,
                ORDER)%>%
  group_by(ORDER)%>%
  summarise("YES" = sum(Success==1),
            "NO" = sum(Success==0))

Сложность для меня заключается в том, как я могу сгенерировать данные ссылок без необходимости вручную указывать исходные цели и значения.

Я использовал пример sankey с этого веб-сайта и приступил к работе с данными моего собственного примера в наименее элегантных возможный способ:

links<-data.frame("source" = sort(rep(seq(0,10,1),2)),
           "target" = c(1,2,3,4,3,4,5,6,5,6,7,8,7,8,9,10,9,10,11,12,11,12),
           "value" = c(sum(example.data$A==1 &example.data$B==1), #1
                       sum(example.data$A==1 & example.data$B==0),#2
                       sum(example.data$B==1 & example.data$C==1),#3
                       sum(example.data$B==1 & example.data$C==0),#4
                       sum(example.data$B==0 & example.data$C==1),#5
                       sum(example.data$B==0 & example.data$C==0),#6
                       sum(example.data$C==1 & example.data$D==1),#7
                       sum(example.data$C==1 & example.data$D==0),#8
                       sum(example.data$C==0 & example.data$D==1),#9
                       sum(example.data$C==0 & example.data$D==0),#10
                       sum(example.data$D==1 & example.data$E==1),#11
                       sum(example.data$D==1 & example.data$E==0),#12
                       sum(example.data$D==0 & example.data$E==1),#13
                       sum(example.data$D==0 & example.data$E==0),#14
                       sum(example.data$E==1 & example.data$F==1),#15
                       sum(example.data$E==1 & example.data$F==0),#16
                       sum(example.data$E==0 & example.data$F==1),#17
                       sum(example.data$E==0 & example.data$F==0),#18
                       sum(example.data$F==1 & example.data$G==1),#19
                       sum(example.data$F==1 & example.data$G==0),#20
                       sum(example.data$F==0 & example.data$G==1),#21
                       sum(example.data$F==0 & example.data$G==0)))#22

nodes<-data.frame("name" = names(example.data))


example.list<-list(nodes,links)

names(example.list)<-c("nodes","links")

Моя проблема вот в чем. 1) попытка использовать эти данные в функции sankeyNetwork на самом деле не приводит к построению графика и 2) Очевидно, что этот метод будет подвержен множеству ошибок, особенно если имеется более двух целей на узел.

Я нашел пример в стеке, где человек использовал вызов сопоставления в функции dplyr :: mutate, которая выглядела многообещающей для того, что я пытаюсь выполнить, но данные имели немного другую структуру, и я действительно не знал, как получить вызов соответствия для работы с моими собственными данными.

Результатом, который я собираюсь получить, является график, показывающий количество наблюдений, перемещающихся между каждым из событий / результатов [A: F]. Итак, представьте, что каждая из колонок представляет собой успешное или неуспешное событие. Сюжет саке проиллюстрировал бы сводку общих успехов и неудач каждого события. Таким образом, все 1000 наблюдений, начиная с A, при этом 493 идут к узлу B = 1, а оставшиеся 507 идут к узлу, указывающему B = 0. Из 493 в B = 1 345 переходят к узлу, указывающему C = 1, и 148 переходят к узлу C = 0. Из 507 в B = 0 263 переходят к C = 1, а 244 переходят к C = 0, и так далее до конца событий от A до F. Надеюсь, я сделал это достаточно ясно. Любая помощь по этому вопросу будет принята с благодарностью.


person slap-a-da-bias    schedule 19.10.2018    source источник
comment
@CJ Спасибо за помощь по первой части. Чтобы прояснить график, который я ищу, представьте, что все 1000 наблюдений проходят через 1 из 2 дверей, помеченных ДА или НЕТ для каждой из переменных столбца. Итак, все 1000 начинаются с A и проходят либо B = YES, либо B = NO. Затем все 1000 проходят через C = YES или C = NO. Затем все 1000 проходят через D = YES или D = NO и т. Д.   -  person slap-a-da-bias    schedule 19.10.2018
comment
Я обновил свой ответ   -  person CJ Yetman    schedule 20.10.2018


Ответы (1)


График Санки не работает, потому что вы ссылаетесь на узлы в ваших target и source столбцах, которые не существуют в вашем nodes фрейме данных.

показывать...

sort(unique(c(links$source, links$target)))
# [1]  0  1  2  3  4  5  6  7  8  9 10 11 12

nrow(nodes)
# [1] 7

Чтобы преобразовать исходные данные в правильный формат ...

Причина, по которой с вашими исходными данными трудно работать, заключается в том, что важная информация, которую вы хотите использовать, неявно закодирована в форме ваших данных, но не включена в данные явно. Каждая точка данных в заданной строке неявно связана с тем, что они были выбраны одним и тем же объектом, но эта информация не существует явно в ваших данных. Точно так же каждый столбец неявно представляет одно из последовательной цепочки действий. Хороший тест для этой ситуации - спросить себя, изменили ли вы форму данных, или отсортировали их по столбцу, или изменили порядок столбцов, будет ли у вас та же информация? Если бы вы поменяли местами столбец B на столбец D, была бы у вас все та же информация? Игнорируя тот факт, что можно неявно предполагать предполагаемый порядок ваших столбцов, потому что они названы в алфавитном порядке, ответ будет отрицательным ... так что вам нужно начать с кодирования этой информации в свои данные.

Добавьте номер строки как переменную / столбец, затем соберите все столбцы в длинный формат и добавьте номер столбца ...

events <- 
  example.data %>% 
  as_tibble() %>% 
  mutate(row = row_number()) %>% 
  gather(column, choice, -row) %>% 
  mutate(column_num = match(column, names(example.data))) %>% 
  arrange(row, column_num) %>% 
  select(row, column_num, everything())

events
# # A tibble: 7,000 x 4
#      row column_num column choice
#    <int>      <int> <chr>   <dbl>
#  1     1          1 A           1
#  2     1          2 B           1
#  3     1          3 C           1
#  4     1          4 D           0
#  5     1          5 E           1
#  6     1          6 F           1
#  7     1          7 G           0
#  8     2          1 A           1
#  9     2          2 B           0
# 10     2          3 C           1
# # ... with 6,990 more rows

Теперь данные представляют собой одно событие / выбор для каждой строки со всей необходимой вам важной информацией. В желаемом выводе каждый «узел» определяется столбцом и выбором, сделанным на этом этапе ... так что A_1, B_0, B_1, C_0, C_1 и т. Д. Для каждого события в ваших измененных данных вы хотите знать на на каком узле произошел этот выбор / событие («цель») и из какого узла оно пришло («источник»). Целевой узел - это имя столбца и выбор этого события. Исходный узел - это имя столбца и выбор события, которое ему предшествовало (-1 column_num) в той же строке (человек / объект / наблюдение).

links <-
  events %>% 
  mutate(target = paste0(column, "_", choice)) %>% 
  group_by(row) %>% 
  mutate(source = lag(target)) %>% 
  filter(!is.na(source) & !is.na(target))

links
# # A tibble: 6,000 x 6
# # Groups:   row [1,000]
#      row column_num column choice target source
#    <int>      <int> <chr>   <dbl> <chr>  <chr> 
#  1     1          2 B           1 B_1    A_1   
#  2     1          3 C           1 C_1    B_1   
#  3     1          4 D           0 D_0    C_1   
#  4     1          5 E           1 E_1    D_0   
#  5     1          6 F           1 F_1    E_1   
#  6     1          7 G           0 G_0    F_1   
#  7     2          2 B           0 B_0    A_1   
#  8     2          3 C           1 C_1    B_0   
#  9     2          4 D           0 D_0    C_1   
# 10     2          5 E           1 E_1    D_0   
# # ... with 5,990 more rows

Теперь вы хотите резюмировать эти данные. Вы хотите подсчитать количество каждой уникальной ссылки / пути.

links <- 
  links %>% 
  select(source, target) %>% 
  group_by(source, target) %>% 
  summarise(value = n()) %>% 
  ungroup()

links
# # A tibble: 22 x 3
#    source target value
#    <chr>  <chr>  <int>
#  1 A_1    B_0      507
#  2 A_1    B_1      493
#  3 B_0    C_0      244
#  4 B_0    C_1      263
#  5 B_1    C_0      148
#  6 B_1    C_1      345
#  7 C_0    D_0      267
#  8 C_0    D_1      125
#  9 C_1    D_0      579
# 10 C_1    D_1       29
# # ... with 12 more rows

При этом вам просто нужно поместить его в формат, который требует sankeyNetwork ... фрейм данных узлов с одной строкой для каждого уникального узла и фрейм данных ссылок, где исходный и целевой столбцы являются числовыми и относятся к индексу (0 -based) узлов в кадре данных узлов (номер строки, в которой они появляются - 1).

nodes <- data.frame(name = unique(c(links$source, links$target)))

links$source <- match(links$source, nodes$name) - 1
links$target <- match(links$target, nodes$name) - 1

sankeyNetwork(Links = links, Nodes = nodes, Source = "source", 
              Target = "target", Value = "value", NodeID = "name")

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

person CJ Yetman    schedule 19.10.2018
comment
Большое спасибо! Не переходя на личности, но это был тяжелый день, и это единственное, что закончилось хорошо. Я бы пожал тебе руку, если бы мог - person slap-a-da-bias; 20.10.2018