Question

Given a DAG, in which each node belongs to a category, how can this graph be transformed into a table with a column for each category? The transformation doesn't have to be reversible, but should preserve useful information about the structure of the graph; and should be a 'natural' transformation, in the sense that a person looking at the graph and the table should not be surprised by any of the rows. It should also be compact, i.e. have few rows.

For example given a graph of nodes a1,b1,b2,c1 with edges a1->b1, a1->b2, b1->c1, b2->c1 (i.e. a diamond-shaped graph) I would expect to see the following table:

a  b  c
--------
a1 b1 c1
a1 b2 c1

I've thought about this problem quite a bit, but I'm having trouble coming up with an algorithm that gives intuitive results on certain graphs. Consider the graph a1,b1,c1 with edges a1->c1, b1->c1. I'd like the algorithm to produce this table:

a  b  c 
--------
a1 b1 c1

But maybe it should produce this instead:

a  b  c 
--------
a1    c1
a1 b1

I'm looking for creative ideas and insights into the problem. Feel free to vary to simplify or constrain the problem if you think it will help.

Brainstorm away!

Edit:

The transformation should always produce the same set of rows, although the order of rows does not matter.

The table should behave nicely when sorting and filtering using, e.g., Excel. This means that mutliple nodes cannot be packed into a single cell of the table - only one node per cell.

Was it helpful?

Solution 4

Here's what I ended up doing:

  • Find all paths emanating from a node without in-edges. (Could be expensive for some graphs, but works for mine)
  • Traverse each path to collect a row of values
  • Compact the rows

Compacting the rows is dones as follows.

  • For each pair of columns x,y
    • Construct a map of every value of x to it's possible values of y
    • Create another map For entries that only have one distinct value of y, mapping the value of x to its single value of y.
  • Fill in the blanks using these maps. When filling in a value, check for related blanks that can be filled.

This gives a very compact output and seems to meet all my requirements.

OTHER TIPS

What you need is a variation of topological sorting. This is an algorithm that "sorts" graph vertexes as if a---->b edge meant a > b. Since the graph is a DAG, there is no cycles in it and this > relation is transitive, so at least one sorting order exists.

For your diamond-shaped graph two topological orders exist:

a1 b1 b2 c1
a1 b2 b1 c1

b1 and b2 items are not connected, even indirectly, therefore, they may be placed in any order.

After you sorted the graph, you know an approximation of order. My proposal is to fill the table in a straightforward way (1 vertex per line) and then "compact" the table. Perform sorting and pick the sequence you got as output. Fill the table from top to bottom, assigning a vertex to relevant column:

a  b  c
--------
a1 
   b2 
   b1
      c1

Now compact the table by walking from top to bottom (and then make similar pass from bottom to top). On each iteration, you take a closer look to a "current" row (marked as =>) and to the "next" row.

  1. If in a column nodes in current and next node differ, do nothing for this column:

         from     ---->      to
       X  b  c            X  b  c
       --------           --------
    => X1 .  .           X1 .  .
       X2 .  .        => X2 .  .
    
  2. If in a column X in the next row there is no vertex (table cell is empty) and in the current row there is vertex X1, then you sometimes should fill this empty cell with a vertex in the current row. But not always: you want your table to be logical, don't you? So copy the vertex if and only if there's no edge b--->X1, c--->X1, etc, for all vertexes in current row.

         from     --->      to
       X  b  c           X  b  c
       --------          --------
    => X1 b  c           X1 b  c
          b1 c1       => X1 b1 c1
    

(Edit:) After first (forward) and second (backward) passes, you'll have such tables:

 first       second
a  b  c     a  b  c 
--------    --------
a1          a1 b2 c1     
a1 b2       a1 b2 c1  
a1 b1       a1 b1 c1  
a1 b1 c1    a1 b1 c1

Then, just remove equal rows and you're done:

a  b  c 
--------
a1 b2 c1  
a1 b1 c1  

And you should get a nice table. O(n^2).

How about compacting all reachable nodes from one node together in one cell ? For example, your first DAG should look like:

a   b        c
---------------
a1  [b1,b2]  
    b1       c1
    b2       c1

It sounds like a train system map with stations within zones (a,b,c).

You could be generating a table of all possible routes in one direction. In which case "a1, b1, c1" would seem to imply a1->b1 so don't format it like that if you have only a1->c1, b1->c1

You could decide to produce a table by listing the longest routes starting in zone a, using each edge only once, ending with the short leftover routes. Or allow edges to be reused only if they connect unused edges or extend a route.

In other words, do a depth first search, trying not to reuse edges (reject any path that doesn't include unused edges, and optionally trim used edges at the endpoints).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top