简体   繁体   English

我们如何从 GV 文件创建具有非重叠子图的 GV 文件?

[英]How do we create GV files with non-overlapping subgraphs from a GV file?

Graph图形

I am having a GV file containing 3 subgraphs:我有一个包含 3 个子图的 GV 文件:

  1. cluster_1
  2. cluster_2
  3. cluster_3

Source of Final_Graph.gv : Final_Graph.gv的来源:

digraph Final_Graph {
    graph [center=true rankdir=LR ratio=compress size="15,10"]
    a
    b
    c
    d
    a -> b [label = 1]
    a -> c [label = 2]
    a -> d [label = 3]
    b -> d [label = 4]
    c -> d [label = 5]

    subgraph cluster_1{
        color=lightgrey style=filled
        label="A"
        a
        b
    }
    
    subgraph cluster_2{
        color=lightgrey style=filled
        label="B"
        a
        b
    }
    
    subgraph cluster_3{
        color=lightgrey style=filled
        label="C"
        c
        d
    }
}

Rendered:渲染:

渲染GV

Wanted通缉

I am looking to create other GV files with subgraphs being non-overlapping (that is subgraphs with no similar nodes, so for this case, the first file could have clusters 1 and 3, and the second file could have clusters 2 and 3).我希望创建子图不重叠的其他 GV 文件(即没有相似节点的子图,因此对于这种情况,第一个文件可以有簇 1 和 3,第二个文件可以有簇 2 和 3)。

Code代码

I am using this function in Python to do this task:我在 Python 中使用这个 function 来完成这个任务:

import networkx as nx
import itertools
def draw_graph_combinations():

# Load the original graph
  G = nx.drawing.nx_agraph.read_dot("Final_Graph.gv")

# Create an empty dictionary to store the subgraphs
  subgraphs = {}
  

# Iterate over the edges of the graph
  for u, v, data in G.edges(data=True):
    label = data.get("label")
    if label not in subgraphs:
        subgraphs[label] = nx.DiGraph()
   

  for node in G.nodes:
    # Add the node to each subgraph
    for label, subgraph in subgraphs.items():
        subgraph.add_node(node)
        
  for label, subgraph in subgraphs.items():
    for edge in G.edges:
        subgraph.add_edge(edge[0], edge[1])

 # Get all combinations of subgraphs
  combinations = itertools.combinations(subgraphs.items(), len(subgraphs))

# Iterate over the combinations
  for i, subgraph_items in enumerate(combinations):
    combined_subgraph = nx.DiGraph()
    for label, subgraph in subgraph_items:
        combined_subgraph = nx.compose(combined_subgraph, subgraph)
    nx.drawing.nx_agraph.write_dot(combined_subgraph, f"combined_subgraph_{i}.gv")

Issue问题

However, when I run this function in Python, the files printed out only contains the nodes and edges of the original file, without the subgraphs being shown.但是,当我在 Python 中运行这个 function 时,打印出来的文件只包含原始文件的节点和边,没有显示子图。

Question问题

Is there any method in Python to divide this GV file into other files with non-overlapping subgraphs? Python有什么方法可以把这个GV文件分成子图不重叠的其他文件吗?

Update:更新:

I have tried to use PyGraphviz (1.10) to remove subgraphs (similar to the answer from @hc_dev below), but the remove method could not find the subgraphs (cluster_1 and cluster_2).我尝试使用 PyGraphviz (1.10) 删除子图(类似于下面@hc_dev 的回答),但是删除方法找不到子图(cluster_1 和 cluster_2)。 I also would like to find a way to find combination of non-overlapping subgraphs and paste result in new files.我还想找到一种方法来找到非重叠子图的组合并将结果粘贴到新文件中。 Here is my current version of the code:这是我当前的代码版本:

G = pgv.AGraph("Final_Graph.gv")
   
  combinations = itertools.combinations(G.subgraphs(), 2)

  for index, subgraph in enumerate(combinations):
    if index == 0:
        continue
    if any(i in subgraph[1] for i in subgraph[0]):
      G.remove_subgraph(subgraph[1].name)
      G.write(f"new_file{index}.gv")

However, I did not get any GV file from this code, and my files that I got from this code with another GV file still have overlapping subgraphs.但是,我没有从这段代码中得到任何 GV 文件,而且我从这段代码中得到的文件和另一个 GV 文件仍然有重叠的子图。 Do you have any ideas on how to fix this problem?您对如何解决此问题有任何想法吗?

You can use package PyGraphviz for reading, modifying, writing and rendering Graphviz graphs.您可以使用 package PyGraphviz读取、修改、编写和渲染 Graphviz 图形。

PyGraphviz PyGraphviz

Prerequisite: graphviz binaries have to be installed on your system.先决条件:必须在您的系统上安装graphviz二进制文件。 Install the Python package using pip:使用 pip 安装 Python package:

pip install pygraphviz

See Install — PyGraphviz 1.10 documentation .请参阅 安装 — PyGraphviz 1.10 文档

Removal of overlapping subgraphs去除重叠子图

Note: Instead loading from source string, you can also pass a file-handle or file-name as parameter to constructor AGraph(filename='Final_Graph.gv') or AGraph('Final_Graph.gv') .注意:除了从源字符串加载之外,您还可以将文件句柄或文件名作为参数传递给构造函数AGraph(filename='Final_Graph.gv')AGraph('Final_Graph.gv')

import pygraphviz as pgv
import itertools

s = '''digraph Final_Graph {
    graph [center=true rankdir=LR ratio=compress size="15,10"]
    a
    b
    c
    d
    a -> b [label = 1]
    a -> c [label = 2]
    a -> d [label = 3]
    b -> d [label = 4]
    c -> d [label = 5]
    subgraph cluster_1{
        color=lightgrey style=filled
        label="A"
        a
        b
    }
    
    subgraph cluster_2{
        color=lightgrey style=filled
        label="B"
        a
        b
    }
    
    subgraph cluster_3{
        color=lightgrey style=filled
        label="C"
        c
        d
    }
}'''


# print some information of the parsed graph 
def print_info(G):
    print(f"Graph '{G.get_name()}' has:")
    print(f"* {len(G.nodes())} nodes")
    print(f"* {len(G.edges())} edges")
    print(f"* {len(G.subgraphs())} subgraphs")


def combine_subgraphs(G):
    combinations = itertools.combinations(G.subgraphs(), 2)  # iterator returned
    pairs = list(combinations)  # store iterator results in a list
    return pairs
    

def overlapping(pair_of_subgraphs):
    left_nodes = pair_of_subgraphs[0].nodes()
    right_nodes = pair_of_subgraphs[1].nodes()
    shared_nodes = set(left_nodes).intersection(set(right_nodes))
    return shared_nodes


def remove_subgraph(G, subgraph_name):
    # the first file could have clusters 1 and 3
    G_copy = G.copy()  # Return a copy of the graph.
    G_copy.remove_subgraph(subgraph_name)  # Remove subgraph with given name.
    print(f"## Copy: without {subgraph_name}\n{G_copy.string()}")  # Return a string (unicode) representation of graph in dot format.
    return G_copy
    
    
G = pgv.AGraph(string=s)  # loading a graph from source string
print_info(G)

pairs = combine_subgraphs(G)

print(f"Searching for overlapps in {len(pairs)} combinations:")
for i, pair in enumerate(pairs):
    if overlapping(pair):
        print(f"{i}: Overlapped ❌️")
        left_name = pair[0].name
        right_name = pair[1].name
        print(f"Removing left subgraph: {left_name} ..")
        left_removed = remove_subgraph(G, left_name)
        #left_removed.write(f"without_{left_name}.dot")  # Write graph in dot format to file on path.
        print(f"Removing right subgraph: {right_name} ..")
        right_removed = remove_subgraph(G, right_name)
        #right_removed.write(f"without_{right_name}.dot")  # Write graph in dot format to file on path.
    else:
        print(f"{i}: Non-Overlapping ✔️")
print("Done.")

Output on console (tested with pygraphviz version 1.6):控制台上的 Output(使用 pygraphviz 1.6 版测试):

Graph 'Final_Graph' has:
* 4 nodes
* 5 edges
* 3 subgraphs
Searching for overlapps in 3 combinations:
0: Overlapped ❌
Removing left subgraph: cluster_1 ..
## Copy: without cluster_1
digraph Final_Graph {
    graph [center=true,
        rankdir=LR,
        ratio=compress,
        size="15,10"
    ];
    subgraph cluster_2 {
        graph [color=lightgrey,
            label=B,
            style=filled
        ];
        a;
        b;
    }
    subgraph cluster_3 {
        graph [color=lightgrey,
            label=C,
            style=filled
        ];
        c;
        d;
    }
    a -> b   [label=1];
    a -> c   [label=2];
    a -> d   [label=3];
    b -> d   [label=4];
    c -> d   [label=5];
}

Removing right subgraph: cluster_2 ..
## Copy: without cluster_2
digraph Final_Graph {
    graph [center=true,
        rankdir=LR,
        ratio=compress,
        size="15,10"
    ];
    subgraph cluster_1 {
        graph [color=lightgrey,
            label=A,
            style=filled
        ];
        a;
        b;
    }
    subgraph cluster_3 {
        graph [color=lightgrey,
            label=C,
            style=filled
        ];
        c;
        d;
    }
    a -> b   [label=1];
    a -> c   [label=2];
    a -> d   [label=3];
    b -> d   [label=4];
    c -> d   [label=5];
}

1: Non-Overlapping ✔️
2: Non-Overlapping ✔️
Done.

To save removal-results to files uncomment the respective write-lines.要将删除结果保存到文件,请取消注释相应的写入行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM