如何在 Plotly 饼图的任意位置添加标签和线条?

问题描述 投票:0回答:1

我想通过 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
为它们添加标签。简而言之,我希望看到这样的事情:

我怎样才能做到这一点(如果可能的话)?

python plotly plotly-python
1个回答
0
投票

我重构了您的代码,以便我们使用

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()

© www.soinside.com 2019 - 2024. All rights reserved.