Plotly比例圆盘图遇悬殊坐标轴时的可视化优化方案咨询
优化比例圆盘图的可视化方案(解决轴量级差异问题)
问题根源
你的代码中使用了scaleanchor="x", scaleratio=1强制x/y轴等比例缩放,当x轴数值量级远小于y轴(比如x是0.x、y是几百)时,y轴会被极度压缩,导致圆盘变成细长条,完全无法辨认。即使做了y轴归一化,固定半径的圆盘也可能因为x轴范围过小而重叠或显示异常。
优化方案及代码示例
以下是几种针对性的优化思路,结合代码实现:
1. 动态适配圆盘半径,取消强制轴比例
去掉轴等比例约束,根据x轴数据范围计算合适的圆盘半径,避免重叠或显示过小:
import numpy as np from numpy import pi, sin, cos import plotly.graph_objects as go def degree2rad(degrees): return degrees * pi / 180 def disk_part(center, radius, start_angle, end_angle, n_points=50): t = np.linspace(degree2rad(start_angle), degree2rad(end_angle), n_points) x = center[0] + radius * cos(t) y = center[1] + radius * sin(t) return np.append(x, (center[0], x[0])), np.append(y, (center[1], y[0])) # 原始数据 x_points = [0.39, 0.17, 0.12, 1.2] y_points = [210, 160, 150, 250] ab = [[1, 1], [1, 2], [3, 7], [15, 2]] colors = ["yellow", "red"] # y轴归一化+自定义刻度 y_max = max(y_points) y_norm = [y / y_max for y in y_points] # 基于x轴范围计算圆盘半径(避免重叠,可调整比例系数) x_range = max(x_points) - min(x_points) radius = x_range * 0.1 # 取x轴范围的10%作为半径,可根据需求调整 fig = go.Figure() for x, y, (a, b) in zip(x_points, y_norm, ab): ratio = a / (a + b) for start_angle, end_angle, color in zip( (0, 360 * ratio), (360 * ratio, 359.9), colors ): x_disk, y_disk = disk_part([x, y], radius, start_angle, end_angle) fig.add_trace( go.Scatter( x=x_disk, y=y_disk, fill="toself", fillcolor=color, line={"color": color}, name=f"{(end_angle-start_angle)/360 * 100:.1f}%", hoverinfo="name+x+y" ) ) # 设置y轴显示原始数值 fig.update_yaxes( tickvals=y_norm, ticktext=y_points, title="Y轴(原始值)" ) fig.update_xaxes(title="X轴") # 去掉强制等比例,让轴自适应 fig.update_layout( legend_title="比例", hovermode="closest" ) fig.show()
2. 使用Shapes绘制扇形(更简洁且保证正圆)
Plotly的shapes.sector可以直接绘制扇形,无需手动生成点,还能保证圆盘在任何轴比例下都显示为正圆:
import numpy as np import plotly.graph_objects as go def add_ratio_sector(fig, center, radius, ratio, color_pair, name_suffix): # 绘制第一部分扇形 fig.add_shape( type="circle", xref="x", yref="y", x0=center[0]-radius, y0=center[1]-radius, x1=center[0]+radius, y1=center[1]+radius, fillcolor=color_pair[0], line_color=color_pair[0], sector=(0, 360*ratio), name=f"{ratio*100:.1f}%{name_suffix}" ) # 绘制第二部分扇形 fig.add_shape( type="circle", xref="x", yref="y", x0=center[0]-radius, y0=center[1]-radius, x1=center[0]+radius, y1=center[1]+radius, fillcolor=color_pair[1], line_color=color_pair[1], sector=(360*ratio, 359.9), name=f"{(1-ratio)*100:.1f}%{name_suffix}" ) # 原始数据 x_points = [0.39, 0.17, 0.12, 1.2] y_points = [210, 160, 150, 250] ab = [[1, 1], [1, 2], [3, 7], [15, 2]] color_pairs = ["yellow", "red"] # y轴归一化+自定义刻度 y_max = max(y_points) y_norm = [y / y_max for y in y_points] # 计算自适应半径 x_range = max(x_points) - min(x_points) radius = x_range * 0.1 fig = go.Figure() for idx, (x, y, (a, b)) in enumerate(zip(x_points, y_norm, ab)): ratio = a / (a + b) add_ratio_sector(fig, [x, y], radius, ratio, color_pairs, f" (点{idx+1})") # 设置轴显示 fig.update_yaxes( tickvals=y_norm, ticktext=y_points, title="Y轴(原始值)" ) fig.update_xaxes(title="X轴") fig.update_layout( legend_title="比例", showlegend=True, hovermode="closest" ) fig.show()
3. 使用Colorscale实现比例渐变(可选)
如果需要用颜色渐变体现比例差异,可以替换固定颜色为colorscale映射:
from plotly.colors import sequential # 选择一个colorscale,比如红黄绿渐变 colorscale = sequential.RdYlGn # 在循环中根据比例映射颜色 for x, y, (a, b) in zip(x_points, y_norm, ab): ratio = a / (a + b) # 从colorscale中选取对应比例的颜色 color1 = colorscale[int(ratio * (len(colorscale)-1))] color2 = colorscale[int((1-ratio) * (len(colorscale)-1))] # 后续绘制逻辑同上...
关键优化点总结
- 移除
scaleanchor和scaleratio,避免轴强制等比例导致的圆盘变形 - 基于x轴数据范围动态计算圆盘半径,确保圆盘大小合适且不重叠
- 保留y轴归一化+自定义刻度,兼顾数据准确性和可视化可读性
- 可选使用shapes简化扇形绘制,或用colorscale丰富比例的颜色表达
内容的提问来源于stack exchange,提问作者hesoyami
相关产品推荐
相关产品推荐

