我想通过 Plotly 制作我所在城市今天日长的饼图,使用以下代码:
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()
我想在饼图中添加两条线,灰色部分与金色部分相接,分别用
sunset
和sunrise
为它们添加标签。简而言之,我希望看到这样的事情:
我怎样才能做到这一点(如果可能的话)?
我重构了您的代码,以便我们使用
go.Barpolar
创建扇区,并使用 go.Scatterpolar
为扇区添加文本标签以及指示 sunrise
和 sunset
的行。
为了帮助 plotly 将时间字符串放置在正确的位置,我编写了一个辅助函数,将您的时间字符串转换为度数(例如,plotly 需要知道
"06:11"
是从正北顺时针方向旋转的 92.75 degrees
),我设置极坐标图的参数,使0度为正北,正角的方向顺时针移动——这样可以确保我们与时钟极坐标系一致。
由于
sunrise
和 sunset
标签与其他刻度线位于相同的径向位置,我们可以扩展 tickvals
和 ticktext
数组以包含这些附加标签的角度和文本。需要注意的一件事是,因为 plotly 从左到右书写文本,所以 ticktext 应该看起来像 [f"{sunrise} sunrise", f"sunset {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()