英文:
How do I add labels and lines in arbitrary positons to Plotly's pie chart?
问题
我想在饼图上添加两条线,分别连接灰色部分和金色部分,并使用“日落”和“日出”标签对它们进行标注。简而言之,我希望看到类似这样的结果:
英文:
I want to make a pie chart of daylength in my city today via Plotly, using this code:
import typing as t
import plotly.graph_objects as go
def add_circular_labels(fig: go.Figure,
labels: t.List[str]):
fig.add_trace(go.Scatterpolar())
fig.update_layout(
polar = dict(
radialaxis_visible=False,
angularaxis = dict(
type="category",
categoryarray=labels,
direction="clockwise",
showticklabels=True,
ticks="outside",
ticklen=8,
tickcolor="white",
showgrid=False)
)
)
return fig
def main():
values = [0.258, 0.542, 0.201]
labels = ["night-sunrise", "day", "night-sunset"]
sunset, sunrise = "06:11", "19:11"
fig = go.Figure(data=[go.Pie(labels=labels,
values=values,
hole=.7,
direction="clockwise",
sort=False)])
fig.update_layout(template="plotly_dark")
fig.update_traces(marker=dict(colors=["#7D8491", "#DEB841", "#7D8491"]))
fig = add_circular_labels(fig, [f"{i:02d}:00" for i in range(24)])
fig.show()
if __name__ == "__main__":
main()
I want to add two lines to the pie chart, where the grey part meets the golden part, and add labels to them with sunset
and sunrise
, respectively. In short, I would like to see something like this:
How could I achieve that (if that's possible at all)?
答案1
得分: 1
我重构了你的代码,使用了go.Barpolar
来创建扇区,以及go.Scatterpolar
来添加扇区的文本标签以及表示日出和日落的线条。
为了帮助Plotly将时间字符串放在正确的位置,我编写了一个将你的时间字符串转换为度数的函数(例如,Plotly 需要知道"06:11"
是从正北方顺时针旋转92.75度
),并设置了极坐标图的参数,使得0度
指向正北,正角度的方向为顺时针 - 这样确保了我们与钟表极坐标系统保持一致。
由于日出
和日落
标签与其他刻度位于相同的径向位置,我们可以扩展tickvals
和ticktext
数组,以包含这些额外标签的角度和文本。需要注意的一点是,由于Plotly 从左到右写文本,ticktext
应该像这样:[f"{日出} 日出", f"sunset {日落}"]
,这样日出和日落的时间字符串才能更接近扇区。
from datetime import datetime
import typing as t
import plotly.graph_objects as go
def get_clock_theta(time_str: str):
"""
将格式为 '%H:%M' 的时间字符串转换为度数
get_clock_theta("12:00") 返回 180.0
"""
time = datetime.strptime(time_str, '%H:%M')
total_hours = time.hour + time.minute/60
return (360 * total_hours / 24)
def main():
# ...
# (你的主要功能部分)
# ...
if __name__ == "__main__":
main()
请注意,这只是代码的部分翻译,如有其他需要请继续提问。
英文:
I refactored your code so that we use go.Barpolar
to create the sectors, and go.Scatterpolar
to add the text labels for the sectors as well as the lines indicating sunrise
and sunset
.
To help plotly place time strings at the correct location, I wrote a function that converts your time string to a degree measure (for example, plotly needs to know that "06:11"
is 92.75 degrees
clockwise from due north), and I set the parameters of polar chart so that 0 degrees is due north and the direction of positive angles moves clockwise – this ensures that we are consistent with the clock polar coordinate system.
Since the sunrise
and sunset
labels are at the same radial location as the other tickmarks, we can extend the tickvals
and ticktext
arrays to include the angles and text for these additional labels. One thing to note is that because plotly writes text from left to right, the ticktext should look like [f"{sunrise} sunrise", f"sunset {sunset}"]
so that the time string is closer to the sector for both sunrise and sunset.
from datetime import datetime
import typing as t
import plotly.graph_objects as go
def get_clock_theta(time_str: str):
"""
converts time string in the format of '%H:%M' to degrees
get_clock_theta("12:00") returns 180.0
"""
time = datetime.strptime(time_str, '%H:%M')
total_hours = time.hour + time.minute/60
return (360 * total_hours / 24)
def main():
values = [0.258, 0.542, 0.201]
labels = ["night-sunrise", "day", "night-sunset"]
sunrise, sunset = "06:11", "19:11"
colors = ["#7D8491", "#DEB841", "#7D8491"]
start_theta, end_theta = 0.0, 360.0
sunrise_theta = get_clock_theta(sunrise)
sunset_theta = get_clock_theta(sunset)
all_angles = [start_theta, sunrise_theta, sunset_theta, end_theta]
thetas, widths = [], []
for i in range(len(all_angles)-1):
thetas.append((all_angles[i] + all_angles[i+1]) / 2)
widths.append(abs(all_angles[i+1] - all_angles[i]))
r_min, r_max = 0.7, 1.0
fig = go.Figure()
for theta, width, label, color, value in zip(thetas, widths, labels, colors, values):
fig.add_trace(go.Barpolar(
r=[r_max],
theta=[theta],
width=[width],
base=[r_min],
name=label,
marker=dict(color=color),
text=value,
legendgroup=label,
))
fig.add_trace(go.Scatterpolar(
r=[(r_min+r_max)/2],
theta=[theta],
mode='text',
text=f"{value:.1%}",
textfont=dict(color='rgb(50,50,50)'),
showlegend=False,
legendgroup=label,
))
for theta, label in zip(
[sunrise_theta, sunset_theta],
["sunrise","sunset"]
):
fig.add_trace(go.Scatterpolar(
r=[r_min,r_max],
theta=[theta, theta],
mode='lines',
marker=dict(color='white'),
showlegend=False,
))
fig.update_layout(template="plotly_dark")
## set 0 degrees to be due north, and go clockwise
## the default is 0 degrees being east, and going counterclockwise
## add 24 tickvals for each hour spaced evenly around the polar chart
## and also add your labels for sunrise and sunset
fig.update_polars(
angularaxis = dict(
tickvals=[i*360/24 for i in range(24)] + [sunrise_theta, sunset_theta],
ticktext=[f"{i:02d}:00" for i in range(24)] + [f"{sunrise} sunrisee", f"sunset {sunset}"],
tickcolor="white",
ticklen=10,
rotation=90,
direction='clockwise',
gridcolor="rgba(0,0,0,0)"
),
radialaxis = dict(
visible=False,
range=[0,1],
)
)
fig.show()
if __name__ == "__main__":
main()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论