首页 > 解决方案 > 为桑基图构建二进制数据

问题描述

我无法弄清楚如何为有多种成功机会(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) 显然这种方法容易出错,特别是如果每​​个节点有 2 个以上的目标。

我在堆栈上找到了一个示例,该人在 dplyr::mutate 函数中使用了 match 调用,该函数看起来很有希望完成我想要完成的工作,但是数据的结构略有不同,我真的不知道如何获得match 调用来处理我自己的数据。

我要的输出是一个 sankey 图,它显示了在每个事件/结果 [A:F] 之间移动的观察次数。所以想象每一列代表一个成功或不成功的事件。sakey 图将说明每个事件的总成功和失败的总结。因此,从 A 开始的所有 1000 个观测值,其中 493 个进入 B = 1 的节点,其余 507 个进入指示 B = 0 的节点。在 B = 1 的 493 个中,345 个进入指示 C = 1 的节点,并且148 到节点 C = 0。在 B = 0 中的 507 中,263 到 C = 1 和 244 到 C = 0,等等事件 A 到 F 的其余部分。我希望我已经做到了够清楚。对此的任何帮助将不胜感激。

标签: rdplyrsankey-diagramhtmlwidgetsnetworkd3

解决方案


sankey 图不起作用,因为您引用了您的节点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-基于)节点数据框中的节点(它们出现的行号 - 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")

在此处输入图像描述


推荐阅读