From f31f6c71077f797588138e4be293387bbe4537fc Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sun, 22 Jun 2025 18:37:22 +0800 Subject: [PATCH] feat: export graph as graphml format --- misc/all-graph/03-convert_graphml.py | 88 ++++++++++++++++++++++++++++ misc/all-graph/compare.py | 30 ---------- 2 files changed, 88 insertions(+), 30 deletions(-) create mode 100755 misc/all-graph/03-convert_graphml.py delete mode 100644 misc/all-graph/compare.py diff --git a/misc/all-graph/03-convert_graphml.py b/misc/all-graph/03-convert_graphml.py new file mode 100755 index 0000000..941c294 --- /dev/null +++ b/misc/all-graph/03-convert_graphml.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +import igraph as ig +from lxml import etree +from dataclasses import dataclass + + +class GraphML: + @dataclass(frozen=True) + class Node: + id: str + code: str + step: int + + @dataclass(frozen=True) + class Edge: + src: str # node id + dst: str # node id + + def __init__(self, gid: str, graph: ig.Graph): + self.__gid = gid + + self.__nodes = [] + id_len = len(str(graph.vcount() - 1)) + for index in range(graph.vcount()): + info = graph.vs[index] + self.__nodes.append(GraphML.Node(f'n{index:0{id_len}d}', info['code'], info['step'])) + + self.__edges = [] + for n1, n2 in graph.get_edgelist(): + node_1 = self.__nodes[n1] + node_2 = self.__nodes[n2] + if node_1.step < node_2.step: + node_1, node_2 = node_2, node_1 + self.__edges.append(GraphML.Edge(node_1.id, node_2.id)) + + def __dump_node(self, node: Node) -> etree.Element: + node_xml = etree.Element('node', id=node.id) + etree.SubElement(node_xml, 'data', key='v_code').text = node.code + etree.SubElement(node_xml, 'data', key='v_step').text = str(node.step) + return node_xml + + def __dump_edge(self, edge: Edge) -> etree.Element: + return etree.Element('edge', source=edge.src, target=edge.dst) + + def __dump_graph(self) -> etree.Element: + graph_xml = etree.Element('graph', id=self.__gid, edgedefault='undirected') + for node in self.__nodes: + graph_xml.append(self.__dump_node(node)) + for edge in self.__edges: + graph_xml.append(self.__dump_edge(edge)) + return graph_xml + + def build_and_save(self, output: str) -> None: + root = etree.Element('graphml', nsmap={ + None: 'http://graphml.graphdrawing.org/xmlns', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance' + }) + root.set( + '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation', + 'http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd' + ) + + etree.SubElement(root, 'key', attrib={ + 'id': 'v_code', + 'for': 'node', + 'attr.name': 'code', + 'attr.type': 'string' + }) + etree.SubElement(root, 'key', attrib={ + 'id': 'v_step', + 'for': 'node', + 'attr.name': 'step', + 'attr.type': 'int' + }) + root.append(self.__dump_graph()) + + tree = etree.ElementTree(root) + tree.write(output, pretty_print=True, xml_declaration=True, encoding='utf-8') + + +def to_graphml(tag: str, input: str, output: str) -> None: + gml = GraphML(tag, ig.Graph.Read_Pickle(input)) + gml.build_and_save(output) + + +if __name__ == "__main__": + to_graphml('0-00M-005X', 'output-ig/0-00M-005X_DAAFE0C.pkl', 'output-gml/0-00M-005X_DAAFE0C.graphml') diff --git a/misc/all-graph/compare.py b/misc/all-graph/compare.py deleted file mode 100644 index 7e9df37..0000000 --- a/misc/all-graph/compare.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 - -import os -import igraph as ig - - -def compare(file_1: str, file_2: str) -> None: - print(f'{file_1} vs {file_2}') - g1 = ig.Graph.Read_Pickle(file_1) - g2 = ig.Graph.Read_Pickle(file_2) - - assert g1.vcount() == g2.vcount() - assert g1.ecount() == g2.ecount() - assert g1.isomorphic(g2) - - for edge in g1.es: - assert edge.attributes() == {} - - for edge in g2.es: - assert edge.attributes() == {} - - gv1 = {x['code']: x.attributes() for x in g1.vs} - gv2 = {x['code']: x.attributes() for x in g2.vs} - assert gv1 == gv2 - - -if __name__ == '__main__': - assert sorted(os.listdir('legacy-ig')) == sorted(os.listdir('modern-ig')) - for name in sorted(os.listdir('modern-ig')): - compare(f'legacy-ig/{name}', f'modern-ig/{name}')