如何在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。因此,将数据重组为长数据帧中的 x 和 y 序列
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 中使用三角函数绘制曲线图形及展示动画