多对多关联关系

基于D3实现的多对多关联关系:github地址

各人员之间的互动关系、各公司上下游企业的关联关系、各地之间的交互关系等等,都是多对多关联关系的表达,他们之间可以通过矩阵图的形式来表达, 如下图所示 (用python的heatmap

而利用 D3库函数可以实现更为酷炫的弧线,如下图所示:

    1. 要画多方关系图,首先需要确定绘制的数据,是一个 N * N 的矩阵。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
matrix = [
const matrix = [
[1, 41, 0, 0, 8, 4, 2, 0, 5, 5],
[6, 0, 1, 1, 1, 0, 4, 1, 0, 1],
[1, 12, 0, 0, 3, 0, 1, 0, 1, 3],
[0, 11, 3, 0, 4, 1, 1, 0, 0, 2],
[1, 1, 0, 0, 1, 0, 0, 2, 2, 0],
[4, 0, 4, 0, 0, 0, 0, 1, 0, 1],
[0, 6, 0, 0, 1, 1, 0, 0, 1, 0],
[1, 4, 2, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 3, 0, 0, 1, 0],
]
]
    1. 然后对 i 行 j 列对应的数据设计一个对象字典检索其对应的名字:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const nameByIndex = {
    0: 332,
    1: 229,
    2: 333,
    3: 343,
    4: 313,
    5: 357,
    6: 344,
    7: 318,
    8: 312,
    9: 329
    };
    const indexByName = {
    332: 0,
    229: 1,
    333: 2,
    343: 3,
    313: 4,
    357: 5,
    344: 6,
    318: 7,
    312: 8,
    329: 9
    };
    1. 定义好 svg 图片的长和宽。
    1. 利用D3绘制svg,核心代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
if (!this.d3 || !this.config || !this.matrix || !this.nameByIndex || !this.indexByName) {
console.error('initial error!');
}

let color = this.d3.scaleOrdinal(this.d3.schemeCategory10);
let outerRadius = Math.min(this.width, this.height) * 0.5;
let innerRadius = outerRadius - 124;
let ribbon = this.d3.ribbon().radius(innerRadius);
let arc = this.d3.arc().innerRadius(innerRadius).outerRadius(innerRadius + 20);
let chord = this.d3.chord().padAngle(.04).sortSubgroups(this.d3.descending).sortChords(this.d3
.descending);
let svg = this.d3.select("svg").attr("viewBox", [-this.width / 2, -this.height / 2, this.width, this
.height
])
.attr("font-size", this.font_size);

const chords = chord(this.matrix);
const group = svg.append("g").selectAll("g").data(chords.groups).join("g");

group.append("path").attr("fill", d => color(d.index)).attr("stroke", d => color(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(${innerRadius + 26})
${d.angle > Math.PI ? "rotate(180)" : ""}`)
.attr("text-anchor", d => d.angle > Math.PI ? "end" : null)
.text(d => this.nameByIndex[d.index]);

svg.append("g")
.attr("fill-opacity", 0.67)
.selectAll("path")
.data(chords)
.join("path")
.attr("stroke", d => this.d3.rgb(color(d.source.index)).darker())
.attr("fill", d => color(d.source.index))
.attr("d", ribbon);
this.svg = svg;
return svg;

html代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="ChordDependencyChart.js" charset="utf-8"></script>

<body>
<svg style="width: 1000px; height:1000px"></svg>
</body>
<script>
const matrix = [
[1, 41, 0, 0, 8, 4, 2, 0, 5, 5],
[6, 0, 1, 1, 1, 0, 4, 1, 0, 1],
[1, 12, 0, 0, 3, 0, 1, 0, 1, 3],
[0, 11, 3, 0, 4, 1, 1, 0, 0, 2],
[1, 1, 0, 0, 1, 0, 0, 2, 2, 0],
[4, 0, 4, 0, 0, 0, 0, 1, 0, 1],
[0, 6, 0, 0, 1, 1, 0, 0, 1, 0],
[1, 4, 2, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 3, 0, 0, 1, 0],
]

const nameByIndex = {
0: 332,
1: 229,
2: 333,
3: 343,
4: 313,
5: 357,
6: 344,
7: 318,
8: 312,
9: 329
};
const indexByName = {
332: 0,
229: 1,
333: 2,
343: 3,
313: 4,
357: 5,
344: 6,
318: 7,
312: 8,
329: 9
};

let config = {
matrix: matrix,
nameByIndex: nameByIndex,
indexByName: indexByName,
width: 1000,
height: 1000
}
let chart = new Chart(d3, config);
let svg = chart.draw();
chart.saveAsPng(svg, name = "local");
</script>

</html>
  • 其中 d3 是 D3 库函数依赖,可以使用离线包或者在线引用的方式。

  • ChordDependencyChart是我自定义的一个库函数,可以实现多对多关联关系弧线图绘制,并可以保存为 png 图片,效果如下:

github地址