Search code examples
plotlysankey-diagram

R plotly sankey - how to make the column annonation aligned with each column


I have below code, which is to add annonation for each column in the sankey chart.

library("plotly")
library("ggplot2")
library("dplyr")
library("xml2")
library("htmlwidgets")

a <- read.csv(header = TRUE, text = "date,Data Center,Customer,companyID,source,target,value 
")

node_names <- union(a$source, a$target)

node_names <- node_names[order(sub('.*_', '', node_names))]
nodes <- data.frame(name = node_names)
links <- data.frame(source = match(a$source, node_names) - 1,
                    target = match(a$target, node_names) - 1,
                    value = a$value)


definePosition <- function(nodeList){
  #  nodeList = node_names
  # unique name endings
  endings = unique(sub('.*_', '', nodeList))
  # define intervals
  steps = 1/length(endings)
  # x-values for each unique name ending
  # for input as node position
  nodes_x = {}
  xVal = 0
  for (e in endings) {
    nodes_x[e] = xVal
    xVal = xVal + steps
    
  }
  # x and y values in list form
  x_values <- 0
  y_values <- 0
  i =1
  for (n in nodeList) {
    last = sub('.*_', '', n)
    x_values[i] = nodes_x[last]
    y_values[i] = 0.001 * length(x_values)
    i = i + 1
  }
  
  return(list(x_values, y_values))
  
}

position = definePosition(node_names)
node_x = position[[1L]]
node_y = position[[2L]]

#Plot
plot_ly(type='sankey',
             orientation = "h",
             arrangement = "snap",
             node = list (
               label = node_names,
               x = node_x,
               y = node_y,
               color = "steelblue",
               pad = 15,
               thinkness = 15,
               line = list(color = "black", width = 0.5)),
               link = list(source = links$source, target = links$target, value = links$value, color = "gainsboro")) %>%
  
add_annotations(x=unique(node_x)[1],y=1,xref = "x",yref = "paper",text = "<b>step1</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[2],y=1,xref = "x",yref = "paper",text = "<b>step2</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[3],y=1,xref = "x",yref = "paper",text = "<b>step3</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[4],y=1,xref = "x",yref = "paper",text = "<b>step4</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[5],y=1,xref = "x",yref = "paper",text = "<b>step5</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[6],y=1,xref = "x",yref = "paper",text = "<b>step6</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[7],y=1,xref = "x",yref = "paper",text = "<b>step7</b>",xanchor = 'left',showarrow = F, align = "center") %>%
add_annotations(x=unique(node_x)[8],y=1,xref = "x",yref = "paper",text = "<b>step8</b>",xanchor = 'left',showarrow = F, align = "center") %>%



layout(
  title = "<b>Opportunity Marketing User Behavior Monitor(Prod environment)</b>",
  font = list(
    size = 10,
    color = 'grey'
  ),
  margin = list(
    l = 30,
    r = 50,
    b = 50,
    t = 100,
    pad = 20
  ),
  xaxis = list(showgrid = F, zeroline = F, visiable = F, showticklabels = F),
  yaxis = list(showgrid = F, zeroline = F, visiable = F, showticklabels = F)
)

but after running the code, the annonation added is not aligned with each column. My thought is to get the x-axis position for each column, and then assign to x parameters for each add_annonation. But how to know the x-axis value for each column??


Solution

  • It appears that your definePosition() function is putting your node labels in the expected places. It is doing its job. However, it is the nodes that are not appearing where they should.

    You are attempting to fix the node positions using calculated node_x and node_y vectors. However, plotly's handling of manual node positions is finicky and a bit opaque. In some cases it appears to override manual positions. I have found that in your situation, omitting one or both of the arguments x or y in node will align your nodes where expected. For example:

    plot_ly(type='sankey',
            orientation = "h",
            arrangement = "snap",
            node = list (
              label = node_names,
              # x = node_x, # No longer setting node.x manually
              y = node_y,
              color = "steelblue",
              pad = 15,
              thinkness = 15,
              line = list(color = "black", width = 0.5)),
            link = list(source = links$source, target = links$target, value = links$value, color = "gainsboro")) %>%
      
      add_annotations(x=unique(node_x)[1],y=1,xref = "x",yref = "paper",text = "<b>step1</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[2],y=1,xref = "x",yref = "paper",text = "<b>step2</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[3],y=1,xref = "x",yref = "paper",text = "<b>step3</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[4],y=1,xref = "x",yref = "paper",text = "<b>step4</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[5],y=1,xref = "x",yref = "paper",text = "<b>step5</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[6],y=1,xref = "x",yref = "paper",text = "<b>step6</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[7],y=1,xref = "x",yref = "paper",text = "<b>step7</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      add_annotations(x=unique(node_x)[8],y=1,xref = "x",yref = "paper",text = "<b>step8</b>",xanchor = 'right',showarrow = F, align = "center") %>%
      
      
      
      layout(
        title = "<b>Opportunity Marketing User Behavior Monitor(Prod environment)</b>",
        font = list(
          size = 10,
          color = 'grey'
        ),
        margin = list(
          l = 30,
          r = 50,
          b = 50,
          t = 100,
          pad = 20
        ),
        xaxis = list(showgrid = F, zeroline = F, visiable = F, showticklabels = F),
        yaxis = list(showgrid = F, zeroline = F, visiable = F, showticklabels = F)
      )
    

    This issue bugs me a lot; I ran into something similar before which was why your question interested me (Why are fixed positions for nodes in a plotly sankey graph being overridden or ignored?). I've opened an issue about it on the plotly.r github as well.