Bry*_*ger 7 python plotly sankey-diagram
我想弄清楚是否有办法在 python 中为 Plotly Sankey 图指定节点标签的位置。在下面链接的图像中,我在每个节点的右侧自动放置了带有标签的图表,但是我想看看是否可以将它们完全放在节点的左侧。
我已经到处搜索文档,但节点标签似乎没有任何其他规范,但我可能遗漏了一些东西。理想情况下,如果可以将它们定位在节点的右侧或左侧,那将会很棒,但是将它们放在左侧是最初的目标。
截至 2023 年 3 月,Plotly 仍然没有提供指定 sankey 节点标签位置的方法,但是有一种方法可以使用一些 javascript 覆盖它们的坐标,我们可以将其嵌入到 python 中,因此下面的解决方案适用于 Plotly.js和 Plotly.py(现场演示)。
首先我们需要引入两个参数:
position - 'left' | 'center' | 'right'forcePos - true | false默认情况下,所有标签都位于各自节点的右侧,除了最后一层节点的标签位于左侧(我猜是为了防止文本溢出并最大化节点之间的间距),允许精确指定是否forcePos或无论节点层如何,都不强制放置标签。
以下函数根据这些参数重新排列所有节点标签:
const TEXTPAD = 3; // constant used in Plotly.js
function sankeyNodeLabelsAlign(position, forcePos) {
const textAnchor = {left: 'end', right: 'start', center: 'middle'}[position];
const nodes = gd.getElementsByClassName('sankey-node');
for (const node of nodes) {
const d = node.__data__;
const label = node.getElementsByClassName('node-label').item(0);
// Ensure to reset any previous modifications
label.setAttribute('x', 0);
if (!d.horizontal)
continue;
// This is how Plotly's default text positioning is computed (coordinates
// are relative to that of the cooresponding node).
const padX = d.nodeLineWidth / 2 + TEXTPAD;
const posX = padX + d.visibleWidth;
let x;
switch (position) {
case 'left':
if (d.left || d.node.originalLayer === 0 && !forcePos)
continue;
x = -posX - padX;
break;
case 'right':
if (!d.left || !forcePos)
continue;
x = posX + padX;
break;
case 'center':
if (!forcePos && (d.left || d.node.originalLayer === 0))
continue;
x = (d.nodeLineWidth + d.visibleWidth)/2 + (d.left ? padX : -posX);
break;
}
label.setAttribute('x', x);
label.setAttribute('text-anchor', textAnchor);
}
}
Run Code Online (Sandbox Code Playgroud)
这个想法是在每次(重新)绘制图表时触发该函数,为此,事件plotly_afterplot非常适合:
const gd = document.getElementById('graphDivId');
const position = 'left';
const forcePos = true;
gd.on('plotly_afterplot', sankeyNodeLabelsAlign.bind(gd, position, forcePos));
gd.emit('plotly_afterplot'); // manual trigger for initial rendering
Run Code Online (Sandbox Code Playgroud)
对于使用 Plotly.js 构建的桑基图表,就是这样。
对于使用 Plotly.py 构建的桑基图表,我们需要的只是将此 javascript 代码嵌入到一个字符串中,我们可以Figure.show()通过参数将其传递给plotly 的方法post_script。唯一的区别是为了识别在本例中生成 id 的图 div,因此我们使用占位符{plot_id}:
fig = go.Figure(data, layout)
js = '''
const TEXTPAD = 3; // constant used by Plotly.js
function sankeyNodeLabelsAlign(position, forcePos) { ... }
const gd = document.getElementById('{plot_id}');
const position = 'left';
const forcePos = true;
gd.on('plotly_afterplot', sankeyNodeLabelsAlign.bind(gd, position, forcePos));
gd.emit('plotly_afterplot');
'''
fig.show(post_script=[js])
Run Code Online (Sandbox Code Playgroud)
注意。传递后脚本show()仅适用于输出 HTML 的渲染器(静态导出显然不会运行 javascript)。write_html()我们还可以在使用方法或时传递后脚本to_html()。
position='left'带有和 的桑基图示例forcePos=true: