GraphRAG生成的graphml文件转echarts脚本

Python写的GraphRAG生成的graphml文件转echarts脚本,方便用于查看GraphRAG生成的graphml文件。

如果不太明白用途的,可以查看上手GraphRAG心得(非GPT-4o跑通)

针对需要更复杂关系图谱或者更多样式的, 可以自行修改html_template中的html样式。

脚本
import xml.etree.ElementTree as ET
import json
import os
from jinja2 import Template
from tkinter import Tk
from tkinter import filedialog


# 解析GraphML文件
def parse_graphml(file_path):
    tree = ET.parse(file_path)
    root = tree.getroot()

    ns = {"graphml": "http://graphml.graphdrawing.org/xmlns"}

    nodes = {}
    for node in root.findall("graphml:graph/graphml:node", ns):
        node_id = node.get("id")
        nodes[node_id] = {
            "id": node_id,
            "value": len(node.findall("graphml:data", ns)[2].text.split(",")),
        }

    links = []
    for edge in root.findall("graphml:graph/graphml:edge", ns):
        source = edge.get("source")
        target = edge.get("target")
        value = len(edge.findall("graphml:data", ns)[2].text.split(","))
        links.append({"source": source, "target": target, "value": value})

    return nodes, links


# 构建ECharts所需的JSON数据结构
def build_echarts_data(nodes, links):
    return {
        "nodes": [
            {"name": node["id"], "value": node["value"]} for node in nodes.values()
        ],
        "links": links,
    }


# 生成HTML文件
def generate_html(title, data):
    html_template = """
    <!DOCTYPE html>
    <html lang="zh-CN">

    <head>
        <meta charset="UTF-8">
        <title>{{ title }}</title>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
        <style>
            html, body {
                height: 100%;
                margin: 0;
                padding: 0;
            }
            #main {
                width: 100%;
                height: 100vh;
            }

        </style>
    </head>

    <body>
        <div id="main"></div>
        <script type="text/javascript">
            var myChart = echarts.init(document.getElementById('main'));

            var data = {{ nodes | tojson }};
            var links = {{ links | tojson }};
            var max_node_value = Math.max(...data.map(node => node.value));
            var max_link_value = Math.max(...links.map(link => link.value));

            function getNodeStyle(node) {
                var color;
                var size;
                if (node.value > max_node_value*0.8) {
                    color = 'red';
                    size = node.value * 10;
                } else if (node.value > max_node_value*0.6) {
                    color = 'orange';
                    size = node.value * 6;
                } else if (node.value > max_node_value*0.4) {
                    color = 'yellow';
                    size = node.value * 3;
                } else if (node.value > max_node_value*0.2) {
                    color = 'green';
                    size = node.value * 2;
                } else {
                    color = 'blue';
                    size = node.value * 1;
                }

                return {
                    symbolSize: size,
                    itemStyle: {
                        color: color
                    }
                };
            }

            function getLinkStyle(link) {
                var color;
                if (link.value > max_link_value*0.8) {
                    color = 'red';
                } else if (link.value > max_link_value*0.6) {
                    color = 'orange';
                } else if (link.value > max_link_value*0.4) {
                    color = 'yellow';
                } else if (link.value > max_link_value*0.2) {
                    color = 'green';
                } else {
                    color = 'brown';
                }

                return {
                    lineStyle: {
                        width: link.value * 2,
                        color: color
                    }
                };
            }

            var option = {
                title: {
                    text: '{{ title }}'
                },

                series: [{
                    type: 'graph',
                    layout: 'force',
                    data: data.map(function (node) {
                        return Object.assign(node, getNodeStyle(node));
                    }),
                    links: links.map(function (link) {
                        return Object.assign(link, getLinkStyle(link));
                    }),
                    roam: true,
                    draggable: true,
                    label: {
                        show: true,
                        fontSize: 12,
                        fontFamily: 'sans-serif',
                        formatter: function (node) {
                            return node.name;
                        }
                    },
                    force: {
                        repulsion: 100
                    }
                }]
            };

            myChart.setOption(option);
            window.addEventListener('resize', function () {
                myChart.resize();
            });
        </script>
    </body>

    </html>
    """
    template = Template(html_template)
    return template.render(title=title, nodes=data["nodes"], links=data["links"])


# 主函数
def main():
    # 使用tkinter打开文件对话框
    root = Tk()
    root.withdraw()  # 隐藏主窗口
    file_path = filedialog.askopenfilename(
        title="选择GraphML文件",
        filetypes=[("GraphML files", "*.graphml"), ("All files", "*.*")],
    )
    if not file_path:
        print("没有选择文件")
        return

    graphml_file = file_path
    graphml_file_name = os.path.splitext(os.path.basename(graphml_file))[0]

    nodes, links = parse_graphml(graphml_file)
    echarts_data = build_echarts_data(nodes, links)
    html_content = generate_html(graphml_file_name, echarts_data)

    output_file = os.path.join(
        os.path.dirname(graphml_file), f"{graphml_file_name}.html"
    )
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(html_content)

    print(f"HTML文件生成: {output_file}")


if __name__ == "__main__":
    main()

效果展示:

3 Likes

前排打call

4 Likes

你是真快,
我都想举办你是机器人

2 Likes

我货真价实手打 :tieba_114:

4 Likes

太强啦!

3 Likes

太厉害,一会儿玩一下

厉害

你好强

为什么我用的时候各种报错