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

huangapple go评论95阅读模式
英文:

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

The result looks like this:如何在Plotly的饼图中任意位置添加标签和线?

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:
如何在Plotly的饼图中任意位置添加标签和线?

How could I achieve that (if that's possible at all)?

答案1

得分: 1

我重构了你的代码,使用了go.Barpolar来创建扇区,以及go.Scatterpolar来添加扇区的文本标签以及表示日出和日落的线条。

为了帮助Plotly将时间字符串放在正确的位置,我编写了一个将你的时间字符串转换为度数的函数(例如,Plotly 需要知道"06:11"是从正北方顺时针旋转92.75度),并设置了极坐标图的参数,使得0度指向正北,正角度的方向为顺时针 - 这样确保了我们与钟表极坐标系统保持一致。

由于日出日落标签与其他刻度位于相同的径向位置,我们可以扩展tickvalsticktext数组,以包含这些额外标签的角度和文本。需要注意的一点是,由于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()

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

huangapple
  • 本文由 发表于 2023年4月6日 20:25:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75949487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定