如何在Python中添加动画以绘制图形

Posted

技术标签:

【中文标题】如何在Python中添加动画以绘制图形【英文标题】:How to add animation to plotly figure in Python 【发布时间】:2022-01-01 03:08:10 【问题描述】:

目前我有这个代码:

fig= go.Figure()
for idx in range(len(reference)):
    df = reference.loc[idx]
    if df.ObjClass != 0:
        x,y = df.Polygon.exterior.xy
        fig.add_trace(go.Scatter(x=np.array(x), y=np.array(y)))
return fig

该图用于使用多边形的边界框。最有可能不是最有效的,也有待改进。

我的问题是如何向现有的 Plotly 人物添加动画?在我的 df 中,我有一个适合该时间的时间列(df.Time)。添加动画后,我只想绘制该时间戳的多边形。

Current plot without animation

编辑:添加数据结构

所以这在绘图时不起作用,正如我在下面的评论中解释的那样。但是,如果我更改为按 ObjID 而不是时间排序,它确实有效,但动画帧未排序。我希望最低的时间排在第一位,时间最高的排在最后。

ObjClass    Time    ObjID   Corner  Lat Long
0   3.0 5.9 54.0    RR  21.549906   129.418088
563 3.0 5.9 54.0    LR  23.569243   129.523022
1126    3.0 5.9 54.0    LF  23.569243   136.523022
1689    3.0 5.9 54.0    RF  21.549906   136.418088
1   3.0 5.95    54.0    RR  17.946687   114.856340
564 3.0 5.95    54.0    LR  19.726662   114.919184
1127    3.0 5.95    54.0    LF  19.726662   121.919184
1690    3.0 5.95    54.0    RF  17.946687   121.856340
2   3.0 6.0 54.0    RR  17.685873   121.115523
565 3.0 6.0 54.0    LR  19.556310   121.182149
1128    3.0 6.0 54.0    LF  19.556310   128.182149
1691    3.0 6.0 54.0    RF  17.685873   128.115523
3   3.0 6.05    54.0    RR  17.685873   121.115523
566 3.0 6.05    54.0    LR  19.556310   121.182149
1129    3.0 6.05    54.0    LF  19.556310   128.182149
1692    3.0 6.05    54.0    RF  17.685873   128.115523
4   3.0 6.1 54.0    RR  17.375609   127.925362
567 3.0 6.1 54.0    LR  19.322351   127.848100
1130    3.0 6.1 54.0    LF  19.322351   134.848100
1693    3.0 6.1 54.0    RF  17.375609   134.925362

【问题讨论】:

【参考方案1】:

您没有提供样本数据。隐式定义了一个数据框

Polygon ObjectId ObjClass time
POLYGON ((58 91, 58 77, 0 73, -0 87, 58 91)) 0 1 00:00
POLYGON ((51 102, 58 90, 7 62, 0 74, 51 102)) 0 1 01:00
POLYGON ((40 110, 52 101, 18 54, 6 63, 40 110)) 0 1 02:00

这假定了一个附加列 ObjectId,它是相同的 多边形,每个 时间

具有不同的坐标

创建 plotly 动画人物的最简单方法是使用 Plotly Express。因此,将数据重组为长数据帧中的 xy 序列

import io
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
import shapely.wkt


df_p = pd.read_csv(io.StringIO("""Polygon,ObjectId,ObjClass,time
"POLYGON ((58 91, 58 77, 0 73, -0 87, 58 91))",0,1,00:00
"POLYGON ((51 102, 58 90, 7 62, 0 74, 51 102))",0,1,01:00
"POLYGON ((40 110, 52 101, 18 54, 6 63, 40 110))",0,1,02:00
"POLYGON ((28 112, 41 109, 30 52, 17 55, 28 112))",0,1,03:00
"POLYGON ((32 29, 20 33, 38 93, 50 89, 32 29))",1,1,00:00
"POLYGON ((45 31, 34 29, 25 91, 36 93, 45 31))",1,1,01:00
"POLYGON ((57 38, 47 32, 13 84, 23 90, 57 38))",1,1,02:00
"POLYGON ((65 50, 58 39, 5 72, 12 83, 65 50))",1,1,03:00
"POLYGON ((1 90, 7 90, 12 48, 6 48, 1 90))",2,1,00:00
"POLYGON ((-7 85, -2 89, 20 53, 15 49, -7 85))",2,1,01:00
"POLYGON ((-13 78, -10 83, 26 60, 23 55, -13 78))",2,1,02:00
"POLYGON ((-15 69, -14 75, 28 69, 27 63, -15 69))",2,1,03:00
"POLYGON ((59 12, 49 18, 84 92, 94 86, 59 12))",3,1,00:00
"POLYGON ((77 11, 65 11, 66 93, 78 93, 77 11))",3,1,01:00
"POLYGON ((94 17, 83 12, 49 87, 60 92, 94 17))",3,1,02:00
"POLYGON ((107 30, 99 21, 36 74, 44 83, 107 30))",3,1,03:00
"POLYGON ((9 29, 3 37, 13 43, 19 35, 9 29))",4,1,00:00
"POLYGON ((12 28, 4 34, 10 44, 18 38, 12 28))",4,1,01:00
"POLYGON ((15 29, 5 31, 7 43, 17 41, 15 29))",4,1,02:00
"POLYGON ((17 32, 8 29, 5 40, 14 43, 17 32))",4,1,03:00"""))

df_p["Polygon"] = df_p["Polygon"].apply(shapely.wkt.loads)

# explode out ploygons to x & y for plotly express
df_plot = (
    df_p["Polygon"]
    .apply(
        lambda p: ["x": x, "y": y for x, y in zip(p.exterior.xy[0], p.exterior.xy[1])]
    )
    .explode()
    .apply(pd.Series)
    .join(df_p)
)


px.line(df_plot, x="x", y="y", color="ObjectId", animation_frame="time")

使用样本数据

样本数据没有使用 shapely https://shapely.readthedocs.io/en/stable/manual.html#polygons 因此在样本代码中使用 exterior.xy 是无关紧要的 样本数据将数据定义为 ObjID 的值序列,其中顶点由 Corner 限定 要绘制多边形,需要包含起点和终点相同点的 x/y 序列。这已插入 order 很重要,因此将 Corner 定义为 https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Categorical.html 那么使用plotly生成动画人物就很简单了
import pandas as pd
import io
import plotly.express as px

df = pd.read_csv(
    io.StringIO(
        """ObjClass    Time    ObjID   Corner  Lat Long
0   3.0 5.9 54.0    RR  21.549906   129.418088
563 3.0 5.9 54.0    LR  23.569243   129.523022
1126    3.0 5.9 54.0    LF  23.569243   136.523022
1689    3.0 5.9 54.0    RF  21.549906   136.418088
1   3.0 5.95    54.0    RR  17.946687   114.856340
564 3.0 5.95    54.0    LR  19.726662   114.919184
1127    3.0 5.95    54.0    LF  19.726662   121.919184
1690    3.0 5.95    54.0    RF  17.946687   121.856340
2   3.0 6.0 54.0    RR  17.685873   121.115523
565 3.0 6.0 54.0    LR  19.556310   121.182149
1128    3.0 6.0 54.0    LF  19.556310   128.182149
1691    3.0 6.0 54.0    RF  17.685873   128.115523
3   3.0 6.05    54.0    RR  17.685873   121.115523
566 3.0 6.05    54.0    LR  19.556310   121.182149
1129    3.0 6.05    54.0    LF  19.556310   128.182149
1692    3.0 6.05    54.0    RF  17.685873   128.115523
4   3.0 6.1 54.0    RR  17.375609   127.925362
567 3.0 6.1 54.0    LR  19.322351   127.848100
1130    3.0 6.1 54.0    LF  19.322351   134.848100
1693    3.0 6.1 54.0    RF  17.375609   134.925362"""
    ),
    sep="\s+",
    engine="python",
)

# if polygon is a quadrangle, need five points to define it. 4 is just a linestring
# duplicate first corner
df = pd.concat([df, df.loc[df["Corner"].eq("RR")].assign(Corner="RR2")])

# need to sort by Corner, hence make it a categorical
df["Corner"] = pd.Categorical(
    df["Corner"], ["RR", "RF", "LF", "LR", "RR2"], ordered=True
)

px.line(
    df.sort_values(["ObjID", "Time", "Corner"]),
    x="Long",
    y="Lat",
    color="ObjID",
    animation_frame="Time",
).update_layout(
    xaxis="range": [df["Long"].min(), df["Long"].max()],
    yaxis="range": [df["Lat"].min(), df["Lat"].max()],
)

【讨论】:

非常感谢您的回答!帮了大忙!但是现在我有一个新问题。使用您的示例,我将数据框重组为带有“Lat”和“Long”列的长格式。但是,我对数据的排序方式似乎非常挑剔。如果我按“ObjID”排序,它可以工作,但时间线全乱了。如果我改为按“时间”排序,则时间线没问题,但它只绘制每个时间值的对象之一,即使有多个。 你没有共享你的数据结构,所以我不得不推断它。看起来您有一个带有多边形的数据框,其中每个多边形都有一个 ObjId,该 ObjId 在时间/转换中是恒定的。因此,这就是我构建样本数据的方式。用你的数据框的head(10) 更新你的问题,我会更新。这就是为什么所有关于提问的 SO 指南都强调需要共享样本数据。不要做可怕的事情,将您的数据共享为图像,迫使任何回答者使用不可靠的 OCR 来使用它 在上面添加了我的数据结构。看起来有点可怕,但我希望你能轻松复制它。 更新了答案 - 你用 shapely 的示例代码完全把我扔给了我。您的示例代码和示例数据似乎不相关 呵呵,那是因为我改变了我的 Shapely Polygon 方法以更接近您的答案。原始数据确实包含 Polygon 对象,但在尝试使我的 df 适应您的示例时,我删除了这些对象。

以上是关于如何在Python中添加动画以绘制图形的主要内容,如果未能解决你的问题,请参考以下文章

绘制动画网络图的工具是什么

CSS 技巧一则 -- 在 CSS 中使用三角函数绘制曲线图形及展示动画

从零学python——python的基本图形绘制以及基本语法

利用canvas绘制图形

利用python绘制太阳花

番外3. Python OpenCV 中如何绘制各种图形?