使用R

时间:2017-09-10 15:06:14

标签: r rcharts sankey-diagram htmlwidgets networkd3

当我点击节点以了解特定节点的整个故事时,我想强调整个路径,这是一个示例 - http://bl.ocks.org/git-ashish/8959771

请检查此链接,你会发现在javscript中突出显示路径的功能,但请注意,此功能不能做我想要的,它突出显示与被点击的节点相关的链接以及与目标相关的链接节点。我想要的是突出显示与点击的节点相关的所有链接。

d3 Sankey - Highlight all connected paths from start to end

这是我需要的一个例子, enter image description here 这是整个图表,我需要的是,当我点击曼谷时,它会突出显示与数据框中曼谷相同的所有节点,例如突出显示与ClimateChange和EnergyShortage的链接,然后突出显示基础设施&生态系统,领导力和战略,...... 这就是我想要的。 这是另一张图片,显示与曼谷相关的节点使用闪亮来分析它。

enter image description here

当我在bl.ocks和链接的问题中使用highlight_node_links时会发生什么,这是错误的,并且没有显示节点和曼谷之间的关系。 enter image description here

以下是曼谷的数据,向您展示列如何相互关联,以及当您使用此数据时,它将仅生成第二张图片。

structure(list(City = c("Bangkok", "Bangkok", "Bangkok", "Bangkok", 
"Bangkok", "Bangkok", "Bangkok", "Bangkok", "Bangkok", "Bangkok", 
"Bangkok", "Bangkok", "Bangkok", "Bangkok", "Bangkok", "Bangkok"
), ResiliencyChallenge = c("ClimateChange", "ClimateChange", 
"ClimateChange", "ClimateChange", "ClimateChange", "InfrastructureFaliure", 
"EnergyShortage", "Pollution", "Pollution", "Pollution", "TransportationSystemFailure", 
"TransportationSystemFailure", "TransportationSystemFailure", 
"TransportationSystemFailure", "TransportationSystemFailure", 
"TransportationSystemFailure"), CRI.Dimesnsion.1 = c("Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Infrastructure & Ecosystems", 
"Leadership & Strategy", "Leadership & Strategy", "Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Infrastructure & Ecosystems", 
"Infrastructure & Ecosystems", "Leadership & Strategy"), Implementation.time.frame = c("Short-term", 
"Short-term", "Short-term", "Short-term", "Short-term", "Mid-term", 
"Long-term", "Short-term", "Short-term", "Mid-term", "Mid-term", 
"Short-term", "Short-term", "Short-term", "Short-term", "Short-term"
), Goal = c("Goal13", "Goal13", "Goal13", "Goal13", "Goal13", 
"Goal12", "Goal12", "Goal11", "Goal11", "Goal11", "Goal11", "Goal11", 
"Goal11", "Goal11", "Goal11", "Goal11")), .Names = c("City", 
"ResiliencyChallenge", "CRI.Dimesnsion.1", "Implementation.time.frame", 
"Goal"), class = "data.frame", row.names = c(NA, -16L))

2 个答案:

答案 0 :(得分:1)

鉴于您提供的R代码数据结构......

首先,sankeyNetwork期望列出边/链接的数据以及通过这些链接连接的节点。您的数据有......让我们称之为“旅行者”中心格式,其中您的每一行数据都与特定的“路径”相关。因此,首先您需要将该数据转换为sankeyNetwork所需的数据类型,同时保留识别它们来自路径的链接所需的信息。此外,您的数据中只有一个城市,因此除非数据中的路径至少有两个不同的来源,否则很难看到结果,因此我将复制它并将第二组归因于另一个城市。这是一个例子......

library(tidyverse)

# duplicate the data for another city so we have more than 1 origin
links <-
  df %>%
  full_join(mutate(df, City = "Hong Kong")) %>%
  mutate(row = row_number()) %>%
  mutate(origin = .[[1]]) %>%
  gather("column", "source", -row, -origin) %>%
  mutate(column = match(column, names(df))) %>%
  arrange(row, column) %>%
  group_by(row) %>%
  mutate(target = lead(source)) %>%
  ungroup() %>%
  filter(!is.na(target)) %>%
  select(source, target, origin) %>%
  group_by(source, target, origin) %>%
  summarise(count = n()) %>%
  ungroup()

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

现在,links期望的表单中有nodessankeyNetwork数据框,而links数据框有一个额外的列origin,确定每个链接在路径上的哪个城市。您现在可以使用sankeyNetwork绘制此内容,在原始数据中添加,因为它被剥离,然后使用htmlwidgets::onRender指定一个点击行为,该行为会更改源自城市节点的任何链接的不透明度点击了......

library(networkD3)
library(htmlwidgets)

sn <- sankeyNetwork(Links = links, Nodes = nodes, Source = 'source',
                    Target = 'target', Value = 'count', NodeID = 'name')

# add origin back into the links data because sankeyNetwork strips it out
sn$x$links$origin <- links$origin


# add onRender JavaScript to set the click behavior
htmlwidgets::onRender(
  sn,
  '
  function(el, x) {
    var nodes = d3.selectAll(".node");
    var links = d3.selectAll(".link");
    nodes.on("mousedown.drag", null); // remove the drag because it conflicts
    nodes.on("click", clicked);
    function clicked(d, i) {
      links
        .style("stroke-opacity", function(d1) {
            return d1.origin == d.name ? 0.5 : 0.2;
          });
    }
  }
  '
)

以上是上述答案的简化版本(使用较小的示例数据集),它将每个“路径”分开,而不是像路径一样聚合并递增计数/值变量。

library(dplyr)
library(tidyr)
library(networkD3)
library(htmlwidgets)

df <- read.csv(header = T, as.is = T, text = '
name,origin,layover,destination
Bob,Baltimore,Chicago,Los Angeles
Bob,Baltimore,Chicago,Seattle
Bob,New York,St Louis,Austin
Bob,New York,Chicago,Seattle
Tom,Baltimore,Chicago,Los Angeles
Tom,New York,St Louis,San Diego
Tom,New York,Chicago,Seattle
Tom,New York,New Orleans,Austin
')

links <-
  df %>%
  mutate(row = row_number()) %>%
  mutate(traveler = .[[1]]) %>%
  gather("column", "source", -row, -traveler) %>%
  mutate(column = match(column, names(df))) %>%
  arrange(row, column) %>%
  group_by(row) %>%
  mutate(target = lead(source)) %>%
  ungroup() %>%
  filter(!is.na(target)) %>%
  select(source, target, traveler) %>%
  group_by(source, target, traveler) %>%
  summarise(count = n()) %>%
  ungroup()

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

sn <- sankeyNetwork(Links = links, Nodes = nodes, Source = 'source',
                    Target = 'target', Value = 'count', NodeID = 'name')

# add origin back into the links data because sankeyNetwork strips it out
sn$x$links$traveler <- links$traveler

# add onRender JavaScript to set the click behavior
htmlwidgets::onRender(
  sn,
  '
  function(el, x) {
    var nodes = d3.selectAll(".node");
    var links = d3.selectAll(".link");
    nodes.select("rect").style("cursor", "pointer");
    nodes.on("mousedown.drag", null); // remove the drag because it conflicts
    //nodes.on("mouseout", null);
    nodes.on("click", clicked);
    function clicked(d, i) {
      links
        .style("stroke-opacity", function(d1) {
            return d1.traveler == d.name ? 0.5 : 0.2;
          });
    }
  }
  '
)

enter image description here enter image description here

答案 1 :(得分:1)

这个问题的实现是在这个闪亮的应用程序中。

https://setsna2.shinyapps.io/sankey-shinyforallcities/

我必须从内部修改networkD3,我通常将其安装并复制到包含闪亮应用程序的目录中,然后将软件包放入R-lib中。

我对sankeyNetwork.js函数进行了一些修改,以绘制sankey图。 这是目录的图片,它显示了到达sankeyNetwork.js手动更改位置的目录结构。

请注意,我在此问题中使用和上传的sankeyNetwork.js的版本是旧的,它是2年前的,所以您可以下载新版本的networkD3并只需修改我的部分接下来会提到。 enter image description here 我在sankeyNetwork.js中所做的更改是添加

    .on('mouseover', function(node) {
        Shiny.onInputChange("node_name", node.name);
    })

这意味着如果有人将鼠标悬停在节点上,我将使用Shiny.onInputChange将节点名称作为“ node_name”变量传输到我的R会话中,那么您可以在线阅读有关此闪亮功能的更多信息。

这里是我曾经知道我的意思的sankeyNetwork.js

现在,如果有人将鼠标悬停在节点上,我可以获取该节点的名称并将其发送给R,如果他移开了光标,我将不会得到任何名称,这就是核心思想。

您可以通过单击here

来查看我闪亮的应用程序的代码

您可以看到Data0变量here的一部分,也可以看到here中的Goals变量。

在R代码中,您会在代码中找到一些注释,例如“供调试使用此代码”或注释,因此,如果您运行这些注释,则在运行闪亮的应用程序之前,您将了解数据的外观,从而充分了解其运行方式。 sankey图读取数据及其外观。

在R代码中,您会找到从sankeyNetwork.js读取node_name的部分

        NodeName <- reactive({ 
                if(length(input$node_name)>0){return(as.character(input$node_name))}
                else{return(0)}
        })

然后代码中的下一部分是检查NodeName是否在我的Nodes数据框中,如果存在,那么我将得到与该节点相关的所有节点,然后会获得将这些节点相互连接的链接ID,请注意,链接ID从0开始而不是从1开始,因为javascript从0开始,R从1开始。

现在我们有了用户悬停的NodeName和与此节点相关的Links,现在我们可以制作sankey图并将其保存在sn中,然后我删除了旧的工具提示,然后添加了一个新的提示。

在使用Shiny时使用onRender修改sankey图,我使用它来使 Highlighting功能在运行Shiny时以及当用户在节点上悬停时修改sankey图,将获取节点的名称,然后获取链接ID,并在现有的sankey图中搜索链接ID,并增加其不透明度。

请注意,如果您运行该应用程序,将会出错,您必须将其上传到Shinyapps.io上进行调试,这就是我检查我的应用程序是否正确运行的方式,也许您可​​以找到另一个调试方式。