You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Python d3blocks弦图迁移至React/D3.js前端

解决方案:从d3blocks导出弦图数据并在React前端重构

方案一:导出核心JSON数据,用D3.js在React中重构(推荐)

d3blocks生成的弦图本质基于D3.js,核心依赖**节点(nodes)和链接(links)**数据。我们可以直接从Python端提取这些数据并导出为JSON,再在React中用D3.js重新渲染,完全摆脱iframe的限制。

1. Python端提取并导出JSON数据

修改你的Python代码,从d3_c对象中提取弦图的核心数据并保存为JSON文件:

from d3blocks import D3Blocks
import json

d3 = D3Blocks()
df = d3.import_example('energy')
# 生成弦图但不保存HTML
d3_c = d3.chord(df, showfig=False, filepath=None)

# 提取节点和链接数据,转为字典格式
chord_data = {
    "nodes": d3_c.nodes.to_dict('records'),  # 包含节点名称、颜色等信息
    "links": d3_c.links.to_dict('records')   # 包含源节点、目标节点、数值等信息
}

# 保存为JSON文件
with open('chord_data.json', 'w', encoding='utf-8') as f:
    json.dump(chord_data, f, indent=2, ensure_ascii=False)

2. React前端用D3.js渲染弦图

首先在React项目中安装D3依赖:

npm install d3

然后创建一个可复用的弦图组件ChordDiagram.jsx:

import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';

const ChordDiagram = ({ data }) => {
  const svgRef = useRef();

  useEffect(() => {
    if (!data) return;

    // 清除旧图表内容
    d3.select(svgRef.current).selectAll('*').remove();

    // 图表尺寸配置
    const width = 600;
    const height = 600;
    const outerRadius = Math.min(width, height) * 0.5 - 40;
    const innerRadius = outerRadius - 30;

    // 创建SVG容器
    const svg = d3.select(svgRef.current)
      .attr('width', width)
      .attr('height', height)
      .append('g')
      .attr('transform', `translate(${width/2}, ${height/2})`);

    // 初始化D3弦图布局
    const chord = d3.chord()
      .padAngle(0.05)
      .sortSubgroups(d3.descending);

    const arc = d3.arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius);

    const ribbon = d3.ribbon()
      .radius(innerRadius);

    // 构建D3弦图需要的矩阵数据
    const nodeNames = data.nodes.map(n => n.name);
    const nodeColorMap = new Map(data.nodes.map(n => [n.name, n.color]));
    const matrix = Array.from({ length: nodeNames.length }, () => Array(nodeNames.length).fill(0));

    data.links.forEach(link => {
      const sourceIdx = nodeNames.indexOf(link.source);
      const targetIdx = nodeNames.indexOf(link.target);
      matrix[sourceIdx][targetIdx] = link.value;
      matrix[targetIdx][sourceIdx] = link.value; // 弦图默认双向,按需调整
    });

    const chords = chord(matrix);

    // 绘制分组弧(外圈)
    const group = svg.append('g')
      .selectAll('g')
      .data(chords.groups)
      .join('g');

    group.append('path')
      .style('fill', d => nodeColorMap.get(nodeNames[d.index]))
      .style('stroke', d => nodeColorMap.get(nodeNames[d.index]))
      .attr('d', arc);

    // 绘制节点标签
    group.append('text')
      .each(d => { d.angle = (d.startAngle + d.endAngle) / 2; })
      .attr('dy', '.35em')
      .attr('transform', d => `
        rotate(${(d.angle * 180 / Math.PI - 90)})
        translate(${outerRadius + 10})
        ${d.angle > Math.PI ? 'rotate(180)' : ''}
      `)
      .style('text-anchor', d => d.angle > Math.PI ? 'end' : 'start')
      .text(d => nodeNames[d.index]);

    // 绘制弦带(内圈连接)
    svg.append('g')
      .attr('fill-opacity', 0.67)
      .selectAll('path')
      .data(chords)
      .join('path')
      .style('fill', d => nodeColorMap.get(nodeNames[d.source.index]))
      .style('stroke', d => d3.color(nodeColorMap.get(nodeNames[d.source.index])).darker())
      .attr('d', ribbon);

  }, [data]);

  return <svg ref={svgRef}></svg>;
};

export default ChordDiagram;

最后在父组件中导入JSON数据并使用该组件:

import ChordDiagram from './ChordDiagram';
import chordData from './chord_data.json';

function App() {
  return (
    <div className="App">
      <ChordDiagram data={chordData} />
    </div>
  );
}

export default App;

方案二:复用d3blocks生成的JS代码片段

如果想直接复用d3blocks的渲染逻辑,可以先导出HTML文件:

d3_c = d3.chord(df, showfig=False, filepath='chord.html')

打开生成的chord.html,提取其中<script>标签内的渲染代码,将其改造为React的useEffect钩子逻辑(类似方案一的结构),这样可以保留d3blocks默认的样式和交互配置。


内容的提问来源于stack exchange,提问作者ntg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:17:53