如何基于Pandas DataFrame在Python中绘制Spotfire风格对比圆环图?
基于Pandas DataFrame实现Spotfire风格对比圆环图
可以用Matplotlib或Plotly实现这类对比圆环图,核心是将DataFrame中的指标映射为圆的大小(半径)和圆环宽度(与基准值的差异),同时按环形布局排列对比项。
示例数据准备
先构造模拟数据,包含基准项(Center)和多个对比项,Value用于计算差异(圆环宽度),Size用于决定圆的半径:
import pandas as pd data = { "Category": ["Center", "A", "B", "C", "D"], "Value": [100, 85, 120, 90, 110], "Size": [150, 120, 160, 130, 140] } df = pd.DataFrame(data)
用Matplotlib实现
Matplotlib适合静态可视化,通过计算布局坐标、圆半径和圆环宽度来绘制:
import matplotlib.pyplot as plt import numpy as np # 提取基准值 base_value = df.loc[df["Category"] == "Center", "Value"].iloc[0] base_size = df.loc[df["Category"] == "Center", "Size"].iloc[0] # 分离对比项 comparison_df = df[df["Category"] != "Center"].reset_index(drop=True) n_comparisons = len(comparison_df) # 计算对比项的布局角度(环形分布) angles = np.linspace(0, 2 * np.pi, n_comparisons, endpoint=False) # 布局半径(控制对比圆与中心圆的距离) layout_radius = base_size * 1.8 fig, ax = plt.subplots(figsize=(8, 8)) ax.set_aspect("equal") # 绘制中心基准圆 base_radius = base_size / 20 # 缩放比例适配画布 circle_base = plt.Circle((0, 0), base_radius, color="white", ec="black", lw=2) ax.add_artist(circle_base) # 中心标注 ax.text(0, 0, f"{base_value}\n{base_size}", ha="center", va="center", fontsize=12) # 绘制每个对比圆环 for i in range(n_comparisons): cat = comparison_df.loc[i, "Category"] val = comparison_df.loc[i, "Value"] size = comparison_df.loc[i, "Size"] # 计算圆环宽度:基于与基准值的差异比例 diff_ratio = abs(val - base_value) / base_value ring_width = base_radius * diff_ratio # 对比圆半径 comp_radius = size / 20 # 计算对比圆的坐标 x = layout_radius / 20 * np.cos(angles[i]) y = layout_radius / 20 * np.sin(angles[i]) # 绘制外环(基础圆) circle_outer = plt.Circle((x, y), comp_radius, color="white", ec="gray", lw=1) ax.add_artist(circle_outer) # 绘制差异圆环 color = "red" if val > base_value else "blue" circle_ring = plt.Circle((x, y), comp_radius - ring_width/2, width=ring_width, color=color, alpha=0.6) ax.add_artist(circle_ring) # 标注类别和数值 ax.text(x, y + comp_radius + 0.5, f"{cat}\n{val}", ha="center", va="bottom", fontsize=10) # 调整坐标轴范围 max_radius = max(layout_radius, base_size, max(comparison_df["Size"])) / 20 + 2 ax.set_xlim(-max_radius, max_radius) ax.set_ylim(-max_radius, max_radius) ax.axis("off") plt.title("Spotfire Style Comparison Circles", fontsize=14) plt.show()
代码说明
- 中心圆为基准项,周围对比圆按环形均匀分布
- 圆的大小由
Size字段映射,圆环宽度由Value与基准值的差异比例决定 - 颜色区分差异方向:红色表示高于基准值,蓝色表示低于
用Plotly实现(交互式)
Plotly支持交互式可视化,鼠标悬停可查看详细数据,布局逻辑类似但更简洁:
import plotly.graph_objects as go import numpy as np # 提取基准值 base_value = df.loc[df["Category"] == "Center", "Value"].iloc[0] base_size = df.loc[df["Category"] == "Center", "Size"].iloc[0] comparison_df = df[df["Category"] != "Center"].reset_index(drop=True) n_comparisons = len(comparison_df) # 计算对比项坐标 angles = np.linspace(0, 2 * np.pi, n_comparisons, endpoint=False) layout_radius = base_size * 1.8 x = layout_radius / 20 * np.cos(angles) y = layout_radius / 20 * np.sin(angles) fig = go.Figure() # 添加中心基准圆 fig.add_trace(go.Scatter( x=[0], y=[0], mode="markers+text", marker=dict(size=base_size/5, color="white", line=dict(color="black", width=2)), text=[f"{base_value}\nSize: {base_size}"], textposition="middle center", name="Center" )) # 添加对比圆环 for i in range(n_comparisons): cat = comparison_df.loc[i, "Category"] val = comparison_df.loc[i, "Value"] size = comparison_df.loc[i, "Size"] diff_ratio = abs(val - base_value) / base_value ring_width = size/5 * diff_ratio # 圆环宽度 # 外层基础圆 fig.add_trace(go.Scatter( x=[x[i]], y=[y[i]], mode="markers+text", marker=dict(size=size/5, color="white", line=dict(color="gray", width=1)), text=[cat], textposition="top center", showlegend=False )) # 差异圆环 color = "#ff4d4d" if val > base_value else "#4d79ff" fig.add_trace(go.Scatter( x=[x[i]], y=[y[i]], mode="markers", marker=dict(size=size/5 - ring_width, color=color, line=dict(color=color, width=ring_width)), hovertext=f"Category: {cat}<br>Value: {val}<br>Size: {size}<br>Difference: {val - base_value}", hoverinfo="text", name=f"{cat} ({val})" )) # 调整布局 fig.update_layout( title="Interactive Comparison Circles", showlegend=True, xaxis=dict(visible=False, range=[-15, 15]), yaxis=dict(visible=False, range=[-15, 15]), width=700, height=700, hovermode="closest" ) fig.show()
代码说明
- 交互式悬停可查看每个对比项的详细指标
- 用两层 marker 实现圆环效果:外层白色基础圆,内层带宽度的颜色环
- 同样通过颜色区分与基准值的差异方向
内容的提问来源于stack exchange,提问作者user6386155
相关产品推荐
相关产品推荐

