如何避免未完全或正确连接到 sankeyNetwork 中的节点的链接?

How can I avoid links not fully or properly connecting to nodes in sankeyNetwork?

我有一个来自 networkd3 渲染的 Sankey 网络,它在一个 Shiny 应用程序中呈现,显示了一年内的转换,并为用户提供了多种过滤数据的选项。我遇到了一个问题,链接没有完全连接到节点和 运行 到其他链接,特别是当用户 select 一小部分组导致绘图水平比垂直长(即,每年显示的年份多于组 - 请参见下面的示例)。

我无法在 Stack Overflow 上找到修复程序。我相信问题可能出在底层 Javascript,所以我无法理解。任何帮助将不胜感激。谢谢!下面的 Reprex。

## Create links dataframe 
# Note that the 0 count links are for displaying nodes in the proper year
links <- data.frame(
  from = c(
    "A 2015", "A 2016", "A 2017", "A 2018", "A 2019",
    "A 2015", "A 2016", "A 2017", "A 2018"
  ),
  to = c(
    "B 2016", "B 2017", "B 2018", "B 2019", "B 2020",
    "A 2016", "A 2017", "A 2018", "A 2019"
  ),
  count = c(48, 36, 31, 46, 24, 0, 0, 0, 0)
)

## Create node dataframe
nodes <- data.frame(
  name = unique(
    c(
      as.character(links$from), 
      as.character(links$to)
    )
  )
)

## Add node indices to link data (zero-indexed)
links$source = match(links$from, nodes$name)-1
links$target = match(links$to, nodes$name)-1

## Render Sankey
library(networkD3)
sankeyNetwork(
  Links = links,
  Nodes = nodes,
  Source = "source",
  Target = "target",
  Value = "count",
  NodeID = "name",
  iterations = 0, 
  sinksRight = FALSE
)

D3 的 sankey 模块创建的链接是带有贝塞尔曲线的 SVG 路径。在您的示例中,浏览器的 SVG 引擎试图在非常紧凑的水平 space 和非常粗的 (stroke-width) 路径内创建弯曲路径。例如比较以下两个 SVG(其中唯一的区别是 stroke-width)...

<svg style="width: 100%; height: 100%;">

    <path d="M 0 50 C 25,50 25,100 50,100" style="stroke-width: 10px; fill: none; stroke: rgb(0, 0, 0); stroke-opacity: 0.2;"></path>
    
    <g transform="translate(100,0)">
    
        <path d="M 0 50 C 25,50 25,100 50,100" style="stroke-width: 80px; fill: none; stroke: rgb(0, 0, 0); stroke-opacity: 0.2;"></path>
    
    </g>
    
</svg>

默认情况下,networkD3 的 sankey 图会尝试使用其容器允许的尽可能多的水平和垂直 space,在您的情况下,这导致了低于最佳的高宽比,这使得节点之间的水平距离相对较小,节点的高度(因此链接的厚度)相对较大。

您可以通过更改容器的大小并刷新(即调整浏览器 window 或更改容器的 HTML/CSS 属性)或指定宽度来“修复”此问题和高度,以便该比率适用于您拥有的数据,例如...

sankeyNetwork(
  Links = links,
  Nodes = nodes,
  Source = "source",
  Target = "target",
  Value = "count",
  NodeID = "name",
  iterations = 0, 
  sinksRight = FALSE,
  height = 250,
  width = 1000
)