how to align nodes that are in different subgraphs in graphviz?

the code below creates this diagram. I would like to to make all 3 subgraphs 5 nodes tall. so that, for example data_source_1, process_1 and product_1 are all horizontally aligned properly (same with process_5 and product_4). That would make the diagram better readable. is there a way of doing that? (I am using R to make the plot, but the issue is with the graphviz syntax).


library(DiagrammeR)

my_graph <- grViz(paste0("
  digraph {

  graph[splines = ortho,
  ordering = 'in',
  rankdir='LR',
  concentrate=true,
  labeljust= 'c',
  layout = dot,
  overlap =true,
  outputorder = nodesfirst]
  node [shape = rectangle, style = filled, fillcolor = 'blanchedalmond']
  edge[color = black, arrowhead=vee, minlen = 1]
  
  # draw lines 
  
  # from survey data to inpu
    data_source_1 -> process_1
    data_source_2 -> process_2
    data_source_3 -> process_2
    data_source_3 -> process_2
    data_source_4 -> process_2
    data_source_5 -> process_3
    data_source_6 -> process_4
    data_source_6 -> process_5

  # from input to cleaning
    process_1 -> product_1
    process_2 -> product_2
    process_3 -> product_3
    process_4 -> product_3
    process_5 -> product_4
    
  
# add clusters    
subgraph cluster_1 {
        node [style=filled]
        'data_source_1' 'data_source_2' 'data_source_3' 'data_source_4' 'data_source_5' 'data_source_6' 
        color='red';
        label = 'Data';
        style=filled;

}
    
    subgraph cluster_2 {
        node [style=filled]
        'process_1' 'process_2' 'process_3' 'process_4' 'process_5'
        color='lightblue';
        label = 'process';
        style=filled;

    }
        subgraph cluster_3 {
        node [style=filled]
        'product_1' 'product_2' 'product_3' 'product_4'
        color='yellow';
        label = 'process';
        style=filled;

    }
}"))


my_graph

This should have been easier, I thought the “nodes appear in reverse order” bug had been fixed, but nope. So within the clusters, we define the nodes in reverse order, sigh.
Adding invisible nodes helped, but the clusters started moving vertically. So:

digraph {

  graph[splines = ortho,
//  ordering = "in",        // probably does not help
        rankdir="LR",
        concentrate=true,         // probably does not help
        labeljust= "c",
        layout = dot,
// overlap =true,          // ignored by dot
        outputorder = nodesfirst  // probably not needed
        ranksep=1
       ]

  node [shape = rectangle, style = filled, fillcolor = "blanchedalmond"]
  edge[color = black, arrowhead=vee, minlen = 1]


# add clusters
  edge [style=invis]
  subgraph cluster_1 {

    node [style=filled]
    color="red";
    label = "Data";
    style=filled;
    {
      rank=same
      "data_source_6" -> "data_source_5" -> "data_source_4" -> "data_source_3" -> "data_source_2" -> "data_source_1"
      // "data_source_1" "data_source_2" "data_source_3" "data_source_4" "data_source_5" "data_source_6"
    }
  }

  subgraph cluster_2 {
    packmode=array_it1;
    node [style=filled]
    color="lightblue";
    label = "process";
    style=filled;
    {
      rank=same
      "process_6" -> "process_5" -> "process_4" -> "process_3" -> "process_2" -> "process_1"
      "process_6" [style=invis]
      //  "process_1" "process_2" "process_3" "process_4" "process_5" "process_6"
    }
  }

  subgraph cluster_3 {
    //margin=20
    color="yellow";
    label = "process";
    style=filled;

    node [style=filled]
    {
      rank=same
      "product_6" -> "product_5" -> "product_4" -> "product_3" -> "product_2" -> "product_1"
      // "product_1" "product_2" "product_3" "product_4" "product_5" "product_6"

      "product_5"  [style=invis]
      "product_6"  [style=invis]
    }
  }

  /***********************************************
    define nodes before defining edges - to keep sequence as desired
   ***********************************************/
# draw lines
  edge[constraint=false style=solid]   //added
# from survey data to inpu
  data_source_1 -> process_1 [constraint=true]
  data_source_2 -> process_2
  data_source_3 -> process_2
  data_source_3 -> process_2
  data_source_4 -> process_2
  data_source_5 -> process_3
  data_source_6 -> process_4
  data_source_6 -> process_5

# from input to cleaning
  process_1 -> product_1  [constraint=true]
  process_2 -> product_2
  process_3 -> product_3
  process_4 -> product_3
  process_5 -> product_4
}

Giving:
yaalignClusters1

Thanks, this is clearly better, but still does not align process_5 and product_4. the more edges are straight lines, the easier the graph is to follow.

adding invisible nodes wont really help, as the actually nodes in my data (sorry cant share that here) are tables of various size, that also change in size from day to day.

First, note that ortho splines work if

An alternative. Had to drop the clusters to get better alignment left-to-right and added “fake” header labels.

digraph {

//newrank=true

  graph[splines = ortho,
        rankdir="LR",
        labeljust= "c",
        ranksep=1
       ]

  node [shape = rectangle, style = filled, fillcolor = "blanchedalmond"]
  edge[color = black, arrowhead=vee, minlen = 1]

 subgraph clusterXX{

# add clusters
  subgraph NOTcluster_1 {
    node [style=filled]
    color="red";
    label = "Data";
    style=filled;

      rank=same
      "data_source_6"  "data_source_5"  "data_source_4"  "data_source_3" "data_source_2"  "data_source_1"
      head1 [label=Data shape=plain style=""]
    }

  subgraph NOTcluster_2 {
    node [style=filled]
    color="lightblue";
    label = "process";
    style=filled;

      rank=same
      "process_5"  "process_4"  "process_3"  "process_2"  "process_1"
      head2 [label=Process shape=plain style=""]
    }

  subgraph NOTcluster_3 {

    node [style=filled]
    color="yellow";
    label = "process";
    style=filled;

      rank=same
      "product_4"  "product_3"  "product_2"  "product_1"
      head3 [label=Process shape=plain style=""]

    }
  }
  /***********************************************
    define nodes before defining edges - to keep sequence as desired
   ***********************************************/
# draw lines

# from survey data to inpu
  data_source_1 -> process_1
  data_source_2 -> process_2
  data_source_3 -> process_2
  data_source_3 -> process_2
  data_source_4 -> process_2
  data_source_5 -> process_3
  data_source_6 -> process_4
  data_source_6 -> process_5

# from input to cleaning
  process_1 -> product_1  
  process_2 -> product_2
  process_3 -> product_3
  process_4 -> product_3
  process_5 -> product_4
}

Giving: