How to Custom Order and Sort Nodes in googleVis gvisSankey Diagrams in R
Understanding Node Ordering in googleVis Sankey Diagrams
Creating Sankey diagrams in R using googleVis::gvisSankey() is a popular choice for visualization due to its clean Google Charts integration. However, a common challenge developers face is controlling the vertical node order. By default, Google Charts uses a layout optimization algorithm that automatically rearranges nodes to minimize link crossings. While this produces clean diagrams mathematically, it often violates specific user-defined layout requirements—such as ordering categorical stages consistently (e.g., A → B → C) across time points.
The Root Cause: Google Charts Engine
The underlying Google Charts Sankey engine automatically determines node placement using iterative relaxation algorithms. When rendering a Sankey chart, it calculates the optimal layout over multiple iterations. Standard configuration options do not provide an explicit nodeOrder property, which leads to unpredictable vertical positioning when rendering complex flows.
Method 1: Combining iterations: 0 with Row Pre-Ordering
Google Charts provides an internal parameter called iterations. Setting iterations: 0 disables the auto-layout layout engine. When disabled, the engine places nodes based on the first time a node appears in the dataset from top to bottom.
To leverage this, you must structure your input data frame so that the links originating from or arriving at your desired top-most nodes appear in the first rows of your dataset:
library(googleVis)
library(tibble)
# Order data rows by the desired node order (A -> B -> C)
example_data_sorted <- tribble(
~FROM, ~TO, ~n,
"Line1 A", "Line2 A", 0.00001, # Minimal flow to establish presence
"Line1 B", "Line2 B", 0.00001,
"Line1 C", "Line2 C", 0.00001
)
# Plot with iterations set to 0
sankey_plot <- gvisSankey(
example_data_sorted,
from = "FROM",
to = "TO",
weight = "n",
options = list(sankey = "{iterations: 0}")
)
plot(sankey_plot)Method 2: Explicit Structural Anchors (Zero/Micro-Weight Rows)
If simple data reordering does not strictly guarantee vertical alignment across all columns, introducing zero-weight or micro-weight structural rows (e.g., n = 0 or n = 0.000001) at the top of your dataset is the standard workaround. Adding dummy rows ensures that nodes are encountered in your exact preferred sequence (A, then B, then C) for every stage column.
example_data_lines_ordered <- tribble(
~FROM, ~TO, ~n,
# Stage 1 to Stage 2 order anchors
"Line1 A", "Line2 A", 0,
"Line2 A", "Line3 A", 0,
"Line1 B", "Line2 B", 0,
"Line2 B", "Line3 B", 0,
"Line1 C", "Line2 C", 0,
"Line2 C", "Line3 C", 0,
# Actual dataset links
"Line1 A", "Line2 C", 2,
"Line1 B", "Line2 A", 1,
"Line1 B", "Line2 B", 1,
"Line1 C", "Line2 B", 1,
"Line1 C", "Line2 A", 2,
"Line2 A", "Line3 A", 1,
"Line2 A", "Line3 B", 2,
"Line2 B", "Line3 B", 1,
"Line2 B", "Line3 C", 1,
"Line2 C", "Line3 A", 2,
"Line2 C", "Line3 C", 1
)
sankey_fixed <- gvisSankey(
example_data_lines_ordered,
from = "FROM",
to = "TO",
weight = "n",
options = list(sankey = "{iterations: 0}")
)
plot(sankey_fixed)Alternative Modern Approaches in R
While the dummy row trick solves the issue within googleVis, legacy restrictions in Google Charts can make complex layouts cumbersome. If you have flexibility in package choice, consider these modern R visualization packages that support explicit node positioning out of the box:
- networkD3 (sankeyNetwork): Allows passing explicit node data frames where order is strictly controlled by index.
- plotly: The
plot_ly(type = 'sankey')function accepts explicitxandycoordinates for every node, granting absolute layout control over positioning and orientation. - ggsankey: A ggplot2 extension that respects factor level ordering for seamless ggplot-style customization.
Conclusion
For existing googleVis implementations, combining options = list(sankey = "{iterations: 0}") with zero-weight structural links at the top of your dataset remains the most reliable solution to enforce custom vertical node sorting.