我认为 Plotly 旭日形图没有任何内置注释,因此您需要手动添加注释。
在您的情况下,我认为使用 go.Scatter() 和参数 mode='text' 将允许您将注释放置在旭日形图上。这种方法的优点是可以将旭日形图放置在最方便的任何坐标上。
例如,如果您将 x 轴和 y 轴的范围设置为 [-1,1],这将确保旭日形图以 (0,0) 为中心,半径约为 1(这取决于不幸的是,您的浏览器窗口的纵横比)。您可能需要在这些范围上进行一些填充,以确保文本在接近范围的上端或下端时不会被截断。
然后你可以使用极坐标根据r和theta来确定x和y坐标。所以如果你想把注解"1227"放在45度角,那么设置x=r*cos(45˚)和y=r*sin(45˚).,然后把你想放的所有注解都重复这个过程。
更新:虽然 Plotly 以正确的顺序呈现带有类别的旭日形图,但似乎这些信息并没有存储在可访问的对象中,这让我们的任务是确定类别的顺序及其各自角度我们自己。
对于 Plotly sunburst 图表,其父类别中的类别(day、time 和 sex)的总和决定了它们在图表上从 0 度开始的放置顺序。例如,total_tips 类别 day 的总和最大的是 Sat,其次是 Sun, Thur, Fri,这是这些类别及其值在图表上的排列顺序。对于父类别中的子类别,同样的模式适用:例如,Sat/Dinner/Male 的 total_tips 之和大于 Sat/Dinner/Female,因此 Sat/Dinner/Male 对应的值放在 Sat/Dinner 之前/女。
我们可以使用groupby 和sort_values 的组合重现此排序:
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from math import sin,cos,pi
df = px.data.tips()
fig = px.sunburst(df, path=['day', 'time', 'sex'], values='total_bill')
totals_groupby = df.groupby(['day', 'time', 'sex']).sum()
totals_groupby["day_sum"] = df.groupby(['day', 'time', 'sex']).total_bill.sum().groupby(level='day').transform('sum')
totals_groupby["day_time_sum"] = df.groupby(['day', 'time', 'sex']).total_bill.sum().groupby(level=['day','time']).transform('sum')
totals_groupby["day_time_sex_sum"] = df.groupby(['day', 'time', 'sex']).total_bill.sum().groupby(level=['day','time','sex']).transform('sum')
totals_groupby = totals_groupby.sort_values(by=["day_sum","day_time_sum","day_time_sex_sum"], ascending=[0,0,0])
下面是totals_groupby DataFrame,我们在其中复制了与 Plotly express sunburst 图表相同的类别顺序:
>>> totals_groupby
total_bill tip size day_sum day_time_sum day_time_sex_sum
day time sex
Sat Dinner Male 1227.35 181.95 156 1778.40 1778.40 1227.35
Female 551.05 78.45 63 1778.40 1778.40 551.05
Sun Dinner Male 1269.46 186.78 163 1627.16 1627.16 1269.46
Female 357.70 60.61 53 1627.16 1627.16 357.70
Thur Lunch Male 561.44 89.41 73 1096.33 1077.55 561.44
Female 516.11 79.42 77 1096.33 1077.55 516.11
Dinner Female 18.78 3.00 2 1096.33 18.78 18.78
Fri Dinner Male 164.41 21.23 16 325.88 235.96 164.41
Female 71.55 14.05 10 325.88 235.96 71.55
Lunch Female 55.76 10.98 9 325.88 89.92 55.76
Male 34.16 5.70 5 325.88 89.92 34.16
我们想要的注解是totals_groupby的total_bill列中的值,其顺序与plotly.express sunburst图对应。
然后我们可以通过将total_bill 列除以total_bill 的总数并乘以360 以度数为单位来计算每个类别的角度对向。请注意,这不是我们要放置注释的最终角度:要获得它,我们需要从 0 开始对这些角度进行滚动平均。
annotations = [format(v,".0f") for v in totals_groupby.total_bill.values]
## calculate the angle subtended by each category
sum_total_bill = df.total_bill.sum()
delta_angles = 360*totals_groupby["total_bill"] / sum_total_bill
## calculate cumulative sum starting from 0, then take a rolling mean
## to get the angle where the annotations should go
angles_in_degrees = pd.concat([pd.DataFrame(data=[0]),delta_angles]).cumsum().rolling(window=2).mean().dropna().values
>>> annotations
['1227', '551', '1269', '358', '561', '516', '19', '164', '72', '56', '34']
>>> list(angles_in_degrees[:,0])
[45.76087924652581, 112.06726915325291, 179.94370071482274, 240.6112138730718, 274.8807006133266, 315.0563924959142, 334.9993889518348, 341.82949891979104, 350.6271011253642, 355.3737646988153, 358.726368488971]
现在我们可以使用辅助函数将所有这些信息放在旭日图上,将角度转换为 x,y 坐标。
def get_xy_coordinates(angles_in_degrees, r=1):
return [r*cos(angle*pi/180) for angle in angles_in_degrees], [r*sin(angle*pi/180) for angle in angles_in_degrees]
x_coordinates, y_coordinates = get_xy_coordinates(angles_in_degrees, r=1.13)
fig.add_trace(go.Scatter(
x=x_coordinates,
y=y_coordinates,
mode="text",
text=annotations,
hoverinfo="skip",
textfont=dict(size=14)
))
padding = 0.20
fig.update_layout(
xaxis=dict(
range=[-1 - padding, 1 + padding],
showticklabels=False
),
yaxis=dict(
range=[-1 - padding, 1 + padding],
showticklabels=False
),
plot_bgcolor='rgba(0,0,0,0)'
)
fig.show()