在动画的某些帧中,虚线绘出的图形线消失

2024-10-02 14:21:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个基于Python的虚线图“动画”(多帧,显示不同的日期),当帧发生变化时,其线条不会显示出来。我已经使用Plotly Dash好几年了,以前从未遇到过这个问题,但下面有一个可复制的示例

以下是所有帧的应该是的样子(注意顶部的红线和底部的紫线): enter image description here

这是上面的第一帧。右边的第二帧看起来像这样,紫色线上方没有红线。我可以向你保证那里有数据;它只是没有出现!使用print(tablate())的输出检查您自己。 enter image description here

要设置回答此问题,请安装以下库:

pip install dash flask plotly pandas colour tabulate

共享一个相当大的数据帧没有完美的方法,但是共享文本比提供下载链接更可取(感谢@vesland从他的答案here中获得的提示)

下面是完整的代码,供您复制和粘贴,并查看“bug”:

from flask import Flask
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import pandas as pd
from colour import Color
from tabulate import tabulate


# Create the "list_of_dicts" for Pandas
list_of_dicts = [
    {
        "Unnamed: 0": 1499,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.33,
        "up_down": "Downstroke",
        "hour": 20,
        "load": -241.0,
    },
    {
        "Unnamed: 0": 21615,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.33,
        "up_down": "Upstroke",
        "hour": 20,
        "load": 165.9,
    },
    {
        "Unnamed: 0": 1687,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.73,
        "up_down": "Downstroke",
        "hour": 20,
        "load": -239.0,
    },
    {
        "Unnamed: 0": 21803,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.73,
        "up_down": "Upstroke",
        "hour": 20,
        "load": 147.76,
    },
    {
        "Unnamed: 0": 1875,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.13,
        "up_down": "Downstroke",
        "hour": 20,
        "load": -242.0,
    },
    {
        "Unnamed: 0": 21991,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.13,
        "up_down": "Upstroke",
        "hour": 20,
        "load": 128.0,
    },
    {
        "Unnamed: 0": 2063,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.53,
        "up_down": "Downstroke",
        "hour": 20,
        "load": -244.0,
    },
    {
        "Unnamed: 0": 22179,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.53,
        "up_down": "Upstroke",
        "hour": 20,
        "load": 109.25,
    },
    {
        "Unnamed: 0": 2251,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.94,
        "up_down": "Downstroke",
        "hour": 20,
        "load": -243.0,
    },
    {
        "Unnamed: 0": 22367,
        "timestamp_local": "2021-01-19 20:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.94,
        "up_down": "Upstroke",
        "hour": 20,
        "load": 92.6206896551724,
    },
    {
        "Unnamed: 0": 1500,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.33,
        "up_down": "Downstroke",
        "hour": 21,
        "load": -245.0,
    },
    {
        "Unnamed: 0": 21616,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.33,
        "up_down": "Upstroke",
        "hour": 21,
        "load": 183.84615384615384,
    },
    {
        "Unnamed: 0": 1688,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.73,
        "up_down": "Downstroke",
        "hour": 21,
        "load": -244.0,
    },
    {
        "Unnamed: 0": 21804,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 6.73,
        "up_down": "Upstroke",
        "hour": 21,
        "load": 163.5,
    },
    {
        "Unnamed: 0": 1876,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.13,
        "up_down": "Downstroke",
        "hour": 21,
        "load": -244.0,
    },
    {
        "Unnamed: 0": 21992,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.13,
        "up_down": "Upstroke",
        "hour": 21,
        "load": 145.44444444444446,
    },
    {
        "Unnamed: 0": 2064,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.53,
        "up_down": "Downstroke",
        "hour": 21,
        "load": -246.0,
    },
    {
        "Unnamed: 0": 22180,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.53,
        "up_down": "Upstroke",
        "hour": 21,
        "load": 128.21052631578948,
    },
    {
        "Unnamed: 0": 2252,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.94,
        "up_down": "Downstroke",
        "hour": 21,
        "load": -246.0,
    },
    {
        "Unnamed: 0": 22368,
        "timestamp_local": "2021-01-19 21:00:00-07:00",
        "timestamp_local_day": "2021-01-19 00:00:00-07:00",
        "inches": 7.94,
        "up_down": "Upstroke",
        "hour": 21,
        "load": 110.55555555555556,
    },
    {
        "Unnamed: 0": 1315,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.05,
        "up_down": "Downstroke",
        "hour": 16,
        "load": -202.0,
    },
    {
        "Unnamed: 0": 21431,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.05,
        "up_down": "Upstroke",
        "hour": 16,
        "load": 176.0,
    },
    {
        "Unnamed: 0": 1503,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.45,
        "up_down": "Downstroke",
        "hour": 16,
        "load": -204.0,
    },
    {
        "Unnamed: 0": 21619,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.45,
        "up_down": "Upstroke",
        "hour": 16,
        "load": 166.0,
    },
    {
        "Unnamed: 0": 1691,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.85,
        "up_down": "Downstroke",
        "hour": 16,
        "load": -202.0,
    },
    {
        "Unnamed: 0": 21807,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 6.85,
        "up_down": "Upstroke",
        "hour": 16,
        "load": 154.0,
    },
    {
        "Unnamed: 0": 1879,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 7.25,
        "up_down": "Downstroke",
        "hour": 16,
        "load": -202.0,
    },
    {
        "Unnamed: 0": 21995,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 7.25,
        "up_down": "Upstroke",
        "hour": 16,
        "load": 142.0,
    },
    {
        "Unnamed: 0": 2067,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 7.66,
        "up_down": "Downstroke",
        "hour": 16,
        "load": -202.0,
    },
    {
        "Unnamed: 0": 22183,
        "timestamp_local": "2021-01-20 16:00:00-07:00",
        "timestamp_local_day": "2021-01-20 00:00:00-07:00",
        "inches": 7.66,
        "up_down": "Upstroke",
        "hour": 16,
        "load": 130.0,
    },
]

# Create the DataFrame from the list_of_dicts
df = pd.DataFrame(list_of_dicts)
df = df.sort_values(["timestamp_local", "inches", "up_down"])

# For viewing and verifying DataFrame contents in VS Code:
print(df["timestamp_local"].unique())
print(
    tabulate(
        df[["timestamp_local", "inches", "up_down", "hour", "load"]],
        headers="keys",
        tablefmt="psql",
    )
)

#############################################################################
# Create the animation in Plotly-Dash
frames = []
slider_steps = []
slider_distinct_days_set = set()

mode = "lines"
marker = dict(
    size=5,
    opacity=0.5,
)
line = dict(
    shape="spline",
    smoothing=0.4,
)

# Transition in milliseconds for the animation (default 500)
duration_frame = 1000
duration_transition = 0
duration_transition_slider = 1000

# Docs say redraw not needed for scatterplots, but if it doesn't redraw,
# the annotations stay the same as for the first frame...
redraw = True

easing = "exp-in-out"
ordering = "layout first"  # default
mode_animate = "immediate"  # default

bootstrap_blue_base = Color("blue")
bootstrap_blue_lum = Color("blue")
bootstrap_blue_lum.luminance = 0.8

bootstrap_red_base = Color("red")
bootstrap_red_lum = Color("red")
bootstrap_red_lum.luminance = 0.8

# Add scatters to the animation by day
for gname_day, gdf_day in df.groupby("timestamp_local_day"):
    frame = {"data": [], "name": gname_day, "layout": {}}

    hours_in_day = gdf_day["timestamp_local"].nunique()
    up_colors = list(bootstrap_red_lum.range_to(bootstrap_red_base, hours_in_day))
    down_colors = list(bootstrap_blue_lum.range_to(bootstrap_blue_base, hours_in_day))

    for gname_isup, gdf_isup in gdf_day.groupby("up_down"):
        i = 0
        colors = down_colors if gname_isup == "Downstroke" else up_colors
        for label, gdf_ts in gdf_isup.groupby("timestamp_local"):
            print(f"{gname_day} {gname_isup} {label} color: {colors[i].hex}")
            frame["data"].append(
                go.Scatter(
                    name=label,
                    mode=mode,  # lines or markers
                    x=gdf_ts["inches"],
                    y=gdf_ts["load"],
                    marker=dict(
                        color=colors[i].hex,
                    ),
                    line=line,
                )
            )
            i += 1

    frames.append(frame)

    if gname_day not in slider_distinct_days_set:
        slider_distinct_days_set.add(gname_day)
        slider_steps.append(
            {
                "method": "animate",
                "label": gname_day,  # text label to appear on the slider
                "args": [
                    [gname_day],
                    {
                        "mode": mode_animate,
                        "frame": {"duration": duration_frame, "redraw": redraw},
                        "transition": {
                            "duration": duration_transition_slider,
                            "easing": easing,
                        },
                        "ordering": ordering,
                    },
                ],
            }
        )

most_recent_day_available_index = max(0, len(slider_distinct_days_set) - 1)
sliders = [
    {
        # IMPORTANT: this is the "active" step in the slider, which shows up on load
        "active": most_recent_day_available_index,
        "pad": {"b": 10, "t": 60},
        "len": 0.9,
        "x": 0.1,
        "xanchor": "left",
        "y": 0,
        "yanchor": "top",
        "steps": slider_steps,
        "transition": {"duration": duration_transition_slider},
    }
]

updatemenus = [
    {
        "type": "buttons",
        "direction": "left",
        "pad": {"r": 10, "t": 70},
        "showactive": False,
        "x": 0.1,
        "xanchor": "right",
        "y": 0,
        "yanchor": "top",
        "buttons": [
            {
                "label": "Play",
                "method": "animate",
                "args": [
                    None,
                    {
                        "mode": mode_animate,
                        "direction": "reverse",  # forward or reverse
                        "fromcurrent": True,
                        "frame": {"duration": duration_frame, "redraw": redraw},
                        "transition": {
                            "duration": duration_transition,
                            "easing": easing,
                        },
                        "ordering": ordering,
                    },
                ],
            },
            {
                "label": "Pause",
                "method": "animate",
                "args": [
                    [None],
                    {
                        "mode": "immediate",
                        "frame": {"duration": 0, "redraw": redraw},
                        "transition": {
                            "duration": 0,
                        },
                    },
                ],
            },
        ],
    }
]

fig = go.Figure(
    # Make the initial data, before the animation frames start
    data=frames[-1]["data"],
    frames=frames,
    layout=go.Layout(
        hovermode="closest",
        height=500,
        plot_bgcolor="white",
        showlegend=False,
        font={"family": "Segoe UI", "color": "#717174"},
        xaxis=dict(
            gridcolor="rgb(238,238,238)",
            range=[6, 8],
            title="position",
        ),
        yaxis=dict(
            gridcolor="rgb(238,238,238)",
            range=[-350, 350],
            title="Weight",
        ),
        margin=go.layout.Margin(l=0, r=10, b=0, t=0),
        sliders=sliders,
        updatemenus=updatemenus,
    ),
)

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]


def create_app():
    app = Flask(__name__)
    dashapp = dash.Dash(__name__, server=app, external_stylesheets=external_stylesheets)

    dashapp.layout = html.Div(
        [
            dcc.Graph(
                figure=fig,
            )
        ]
    )

    return app


app = create_app()

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=5000)


Tags: theinlocalloadtimestampdownsliderduration
2条回答

我没有足够的代表留下评论,所以我必须在这里回答。我在plotly上有一个这样的例子,实际上我的一条线在另一条线的正下方,所以它看起来好像不见了

当我点击图例中的覆盖线时,它发现了丢失的线。这里可能吗

Plotly Dash动画文档here中描述了该问题/错误:

Animations are designed to work well when each row of input is present across all animation frames, and when categorical values mapped to symbol, color and facet are constant across frames. Animations may be misleading or inconsistent if these constraints are not met.

我的示例有每日帧,但每天的数据小时数不同。1月19日有两个小时,而1月20日只有一个小时

为了解决这个问题,我需要每天都有相同数量的小时“图表”(例如,每天24个数据图表)

相关问题 更多 >