如何在单个散点图中为多个移动点设置动画

How to animate multiple moving points in single scatter plot

这是一个显示 5 个不同点坐标的数据框。如何逐行动画?

        x1  y1  x2  y2  x3  y3  x4  y4  x5  y5

   0    12  22  12  22  12  22  12  22  12  22
   1    6   29  17  10  24  19  17  10  6   29
   2    24  19  24  19  21  26  24  19  21  26
   3    21  26  6   29  17  10  6   29  24  19
   4    17  10  21  26  6   29  21  26  17  10
  ...   ... ... ... ... ... ... ... ... ... ...
11995   17  10  17  10  17  10  17  10  17  10
11996   24  19  24  19  24  19  24  19  24  19
11997   21  26  21  26  21  26  21  26  21  26
11998   6   29  6   29  6   29  6   29  6   29
11999   12  22  12  22  12  22  12  22  12  22

x1,y1列是第一只蚂蚁的坐标。依次x2,y2 x3,y3 ..代表其他蚂蚁的坐标。索引显示了蚂蚁在每一步中从一个节点到另一个节点的步骤。 我用 plotly 来制作数据动画

import plotly.express as px
px.scatter(dftempt, x=dftempt.x1, y=dftempt.y1, animation_frame=dftempt.index, range_x=[0,30], range_y=[0,30])

但它只显示第一个点 (x1,y1),我如何在单个图中为所有 5 个点设置动画?

这是前 100 行的数据:

{'x1': {0: 5, 1: 29, 2: 14, 3: 21, 4: 26, 5: 5, 6: 5, 7: 29, 8: 14, 9: 21, 10: 26, 11: 5, 12: 5, 13: 29, 14: 14, 15: 21, 16: 26, 17: 5, 18: 5, 19: 29, 20: 14, 21: 21, 22: 26, 23: 5, 24: 5, 25: 29, 26: 14, 27: 21, 28: 26, 29: 5, 30: 5, 31: 29, 32: 14, 33: 21, 34: 26, 35: 5, 36: 5, 37: 29, 38: 14, 39: 21, 40: 26, 41: 5, 42: 5, 43: 29, 44: 14, 45: 21, 46: 26, 47: 5, 48: 5, 49: 29, 50: 14, 51: 21, 52: 26, 53: 5, 54: 5, 55: 29, 56: 14, 57: 21, 58: 26, 59: 5, 60: 5, 61: 29, 62: 14, 63: 21, 64: 26, 65: 5, 66: 5, 67: 29, 68: 14, 69: 21, 70: 26, 71: 5, 72: 5, 73: 29, 74: 14, 75: 21, 76: 26, 77: 5, 78: 5, 79: 29, 80: 14, 81: 21, 82: 26, 83: 5, 84: 5, 85: 29, 86: 14, 87: 21, 88: 26, 89: 5, 90: 5, 91: 29, 92: 14, 93: 21, 94: 26, 95: 5, 96: 5, 97: 29, 98: 14, 99: 21}, 'y1': {0: 20, 1: 9, 2: 29, 3: 23, 4: 24, 5: 20, 6: 20, 7: 9, 8: 29, 9: 23, 10: 24, 11: 20, 12: 20, 13: 9, 14: 29, 15: 23, 16: 24, 17: 20, 18: 20, 19: 9, 20: 29, 21: 23, 22: 24, 23: 20, 24: 20, 25: 9, 26: 29, 27: 23, 28: 24, 29: 20, 30: 20, 31: 9, 32: 29, 33: 23, 34: 24, 35: 20, 36: 20, 37: 9, 38: 29, 39: 23, 40: 24, 41: 20, 42: 20, 43: 9, 44: 29, 45: 23, 46: 24, 47: 20, 48: 20, 49: 9, 50: 29, 51: 23, 52: 24, 53: 20, 54: 20, 55: 9, 56: 29, 57: 23, 58: 24, 59: 20, 60: 20, 61: 9, 62: 29, 63: 23, 64: 24, 65: 20, 66: 20, 67: 9, 68: 29, 69: 23, 70: 24, 71: 20, 72: 20, 73: 9, 74: 29, 75: 23, 76: 24, 77: 20, 78: 20, 79: 9, 80: 29, 81: 23, 82: 24, 83: 20, 84: 20, 85: 9, 86: 29, 87: 23, 88: 24, 89: 20, 90: 20, 91: 9, 92: 29, 93: 23, 94: 24, 95: 20, 96: 20, 97: 9, 98: 29, 99: 23}, 'x2': {0: 5, 1: 26, 2: 14, 3: 21, 4: 29, 5: 5, 6: 5, 7: 26, 8: 14, 9: 21, 10: 29, 11: 5, 12: 5, 13: 26, 14: 14, 15: 21, 16: 29, 17: 5, 18: 5, 19: 26, 20: 14, 21: 21, 22: 29, 23: 5, 24: 5, 25: 26, 26: 14, 27: 21, 28: 29, 29: 5, 30: 5, 31: 26, 32: 14, 33: 21, 34: 29, 35: 5, 36: 5, 37: 26, 38: 14, 39: 21, 40: 29, 41: 5, 42: 5, 43: 26, 44: 14, 45: 21, 46: 29, 47: 5, 48: 5, 49: 26, 50: 14, 51: 21, 52: 29, 53: 5, 54: 5, 55: 26, 56: 14, 57: 21, 58: 29, 59: 5, 60: 5, 61: 26, 62: 14, 63: 21, 64: 29, 65: 5, 66: 5, 67: 26, 68: 14, 69: 21, 70: 29, 71: 5, 72: 5, 73: 26, 74: 14, 75: 21, 76: 29, 77: 5, 78: 5, 79: 26, 80: 14, 81: 21, 82: 29, 83: 5, 84: 5, 85: 26, 86: 14, 87: 21, 88: 29, 89: 5, 90: 5, 91: 26, 92: 14, 93: 21, 94: 29, 95: 5, 96: 5, 97: 26, 98: 14, 99: 21}, 'y2': {0: 20, 1: 24, 2: 29, 3: 23, 4: 9, 5: 20, 6: 20, 7: 24, 8: 29, 9: 23, 10: 9, 11: 20, 12: 20, 13: 24, 14: 29, 15: 23, 16: 9, 17: 20, 18: 20, 19: 24, 20: 29, 21: 23, 22: 9, 23: 20, 24: 20, 25: 24, 26: 29, 27: 23, 28: 9, 29: 20, 30: 20, 31: 24, 32: 29, 33: 23, 34: 9, 35: 20, 36: 20, 37: 24, 38: 29, 39: 23, 40: 9, 41: 20, 42: 20, 43: 24, 44: 29, 45: 23, 46: 9, 47: 20, 48: 20, 49: 24, 50: 29, 51: 23, 52: 9, 53: 20, 54: 20, 55: 24, 56: 29, 57: 23, 58: 9, 59: 20, 60: 20, 61: 24, 62: 29, 63: 23, 64: 9, 65: 20, 66: 20, 67: 24, 68: 29, 69: 23, 70: 9, 71: 20, 72: 20, 73: 24, 74: 29, 75: 23, 76: 9, 77: 20, 78: 20, 79: 24, 80: 29, 81: 23, 82: 9, 83: 20, 84: 20, 85: 24, 86: 29, 87: 23, 88: 9, 89: 20, 90: 20, 91: 24, 92: 29, 93: 23, 94: 9, 95: 20, 96: 20, 97: 24, 98: 29, 99: 23}, 'x3': {0: 5, 1: 26, 2: 14, 3: 21, 4: 29, 5: 5, 6: 5, 7: 26, 8: 14, 9: 21, 10: 29, 11: 5, 12: 5, 13: 26, 14: 14, 15: 21, 16: 29, 17: 5, 18: 5, 19: 26, 20: 14, 21: 21, 22: 29, 23: 5, 24: 5, 25: 26, 26: 14, 27: 21, 28: 29, 29: 5, 30: 5, 31: 26, 32: 14, 33: 21, 34: 29, 35: 5, 36: 5, 37: 26, 38: 14, 39: 21, 40: 29, 41: 5, 42: 5, 43: 26, 44: 14, 45: 21, 46: 29, 47: 5, 48: 5, 49: 26, 50: 14, 51: 21, 52: 29, 53: 5, 54: 5, 55: 26, 56: 14, 57: 21, 58: 29, 59: 5, 60: 5, 61: 26, 62: 14, 63: 21, 64: 29, 65: 5, 66: 5, 67: 26, 68: 14, 69: 21, 70: 29, 71: 5, 72: 5, 73: 26, 74: 14, 75: 21, 76: 29, 77: 5, 78: 5, 79: 26, 80: 14, 81: 21, 82: 29, 83: 5, 84: 5, 85: 26, 86: 14, 87: 21, 88: 29, 89: 5, 90: 5, 91: 26, 92: 14, 93: 21, 94: 29, 95: 5, 96: 5, 97: 26, 98: 14, 99: 21}, 'y3': {0: 20, 1: 24, 2: 29, 3: 23, 4: 9, 5: 20, 6: 20, 7: 24, 8: 29, 9: 23, 10: 9, 11: 20, 12: 20, 13: 24, 14: 29, 15: 23, 16: 9, 17: 20, 18: 20, 19: 24, 20: 29, 21: 23, 22: 9, 23: 20, 24: 20, 25: 24, 26: 29, 27: 23, 28: 9, 29: 20, 30: 20, 31: 24, 32: 29, 33: 23, 34: 9, 35: 20, 36: 20, 37: 24, 38: 29, 39: 23, 40: 9, 41: 20, 42: 20, 43: 24, 44: 29, 45: 23, 46: 9, 47: 20, 48: 20, 49: 24, 50: 29, 51: 23, 52: 9, 53: 20, 54: 20, 55: 24, 56: 29, 57: 23, 58: 9, 59: 20, 60: 20, 61: 24, 62: 29, 63: 23, 64: 9, 65: 20, 66: 20, 67: 24, 68: 29, 69: 23, 70: 9, 71: 20, 72: 20, 73: 24, 74: 29, 75: 23, 76: 9, 77: 20, 78: 20, 79: 24, 80: 29, 81: 23, 82: 9, 83: 20, 84: 20, 85: 24, 86: 29, 87: 23, 88: 9, 89: 20, 90: 20, 91: 24, 92: 29, 93: 23, 94: 9, 95: 20, 96: 20, 97: 24, 98: 29, 99: 23}, 'x4': {0: 5, 1: 26, 2: 14, 3: 21, 4: 29, 5: 5, 6: 5, 7: 26, 8: 14, 9: 21, 10: 29, 11: 5, 12: 5, 13: 26, 14: 14, 15: 21, 16: 29, 17: 5, 18: 5, 19: 26, 20: 14, 21: 21, 22: 29, 23: 5, 24: 5, 25: 26, 26: 14, 27: 21, 28: 29, 29: 5, 30: 5, 31: 26, 32: 14, 33: 21, 34: 29, 35: 5, 36: 5, 37: 26, 38: 14, 39: 21, 40: 29, 41: 5, 42: 5, 43: 26, 44: 14, 45: 21, 46: 29, 47: 5, 48: 5, 49: 26, 50: 14, 51: 21, 52: 29, 53: 5, 54: 5, 55: 26, 56: 14, 57: 21, 58: 29, 59: 5, 60: 5, 61: 26, 62: 14, 63: 21, 64: 29, 65: 5, 66: 5, 67: 26, 68: 14, 69: 21, 70: 29, 71: 5, 72: 5, 73: 26, 74: 14, 75: 21, 76: 29, 77: 5, 78: 5, 79: 26, 80: 14, 81: 21, 82: 29, 83: 5, 84: 5, 85: 26, 86: 14, 87: 21, 88: 29, 89: 5, 90: 5, 91: 26, 92: 14, 93: 21, 94: 29, 95: 5, 96: 5, 97: 26, 98: 14, 99: 21}, 'y4': {0: 20, 1: 24, 2: 29, 3: 23, 4: 9, 5: 20, 6: 20, 7: 24, 8: 29, 9: 23, 10: 9, 11: 20, 12: 20, 13: 24, 14: 29, 15: 23, 16: 9, 17: 20, 18: 20, 19: 24, 20: 29, 21: 23, 22: 9, 23: 20, 24: 20, 25: 24, 26: 29, 27: 23, 28: 9, 29: 20, 30: 20, 31: 24, 32: 29, 33: 23, 34: 9, 35: 20, 36: 20, 37: 24, 38: 29, 39: 23, 40: 9, 41: 20, 42: 20, 43: 24, 44: 29, 45: 23, 46: 9, 47: 20, 48: 20, 49: 24, 50: 29, 51: 23, 52: 9, 53: 20, 54: 20, 55: 24, 56: 29, 57: 23, 58: 9, 59: 20, 60: 20, 61: 24, 62: 29, 63: 23, 64: 9, 65: 20, 66: 20, 67: 24, 68: 29, 69: 23, 70: 9, 71: 20, 72: 20, 73: 24, 74: 29, 75: 23, 76: 9, 77: 20, 78: 20, 79: 24, 80: 29, 81: 23, 82: 9, 83: 20, 84: 20, 85: 24, 86: 29, 87: 23, 88: 9, 89: 20, 90: 20, 91: 24, 92: 29, 93: 23, 94: 9, 95: 20, 96: 20, 97: 24, 98: 29, 99: 23}, 'x5': {0: 5, 1: 14, 2: 21, 3: 26, 4: 29, 5: 5, 6: 5, 7: 14, 8: 21, 9: 26, 10: 29, 11: 5, 12: 5, 13: 14, 14: 21, 15: 26, 16: 29, 17: 5, 18: 5, 19: 14, 20: 21, 21: 26, 22: 29, 23: 5, 24: 5, 25: 14, 26: 21, 27: 26, 28: 29, 29: 5, 30: 5, 31: 14, 32: 21, 33: 26, 34: 29, 35: 5, 36: 5, 37: 14, 38: 21, 39: 26, 40: 29, 41: 5, 42: 5, 43: 14, 44: 21, 45: 26, 46: 29, 47: 5, 48: 5, 49: 14, 50: 21, 51: 26, 52: 29, 53: 5, 54: 5, 55: 14, 56: 21, 57: 26, 58: 29, 59: 5, 60: 5, 61: 14, 62: 21, 63: 26, 64: 29, 65: 5, 66: 5, 67: 14, 68: 21, 69: 26, 70: 29, 71: 5, 72: 5, 73: 14, 74: 21, 75: 26, 76: 29, 77: 5, 78: 5, 79: 14, 80: 21, 81: 26, 82: 29, 83: 5, 84: 5, 85: 14, 86: 21, 87: 26, 88: 29, 89: 5, 90: 5, 91: 14, 92: 21, 93: 26, 94: 29, 95: 5, 96: 5, 97: 14, 98: 21, 99: 26}, 'y5': {0: 20, 1: 29, 2: 23, 3: 24, 4: 9, 5: 20, 6: 20, 7: 29, 8: 23, 9: 24, 10: 9, 11: 20, 12: 20, 13: 29, 14: 23, 15: 24, 16: 9, 17: 20, 18: 20, 19: 29, 20: 23, 21: 24, 22: 9, 23: 20, 24: 20, 25: 29, 26: 23, 27: 24, 28: 9, 29: 20, 30: 20, 31: 29, 32: 23, 33: 24, 34: 9, 35: 20, 36: 20, 37: 29, 38: 23, 39: 24, 40: 9, 41: 20, 42: 20, 43: 29, 44: 23, 45: 24, 46: 9, 47: 20, 48: 20, 49: 29, 50: 23, 51: 24, 52: 9, 53: 20, 54: 20, 55: 29, 56: 23, 57: 24, 58: 9, 59: 20, 60: 20, 61: 29, 62: 23, 63: 24, 64: 9, 65: 20, 66: 20, 67: 29, 68: 23, 69: 24, 70: 9, 71: 20, 72: 20, 73: 29, 74: 23, 75: 24, 76: 9, 77: 20, 78: 20, 79: 29, 80: 23, 81: 24, 82: 9, 83: 20, 84: 20, 85: 29, 86: 23, 87: 24, 88: 9, 89: 20, 90: 20, 91: 29, 92: 23, 93: 24, 94: 9, 95: 20, 96: 20, 97: 29, 98: 23, 99: 24}}

如果您要构建的内容的第一帧如下所示:

最后一帧应该是这样的:

然后你应该为每个类别 xy 将你的数据框从宽格式重塑为长格式,这样 animation_frame 除了连续索引之外还有其他东西可以使用。然后将从 x1, y1 中提取的数字分配给 px.scatter:

中的 animation_group 属性
px.scatter(dftempt, x='x', y='y', animation_frame='index', animation_group='variable', range_x=[0,30], range_y=[0,30])

完整代码:

import pandas as pd
import plotly.express as px

df = pd.DataFrame({'x1': {0: 12, 1: 6, 2: 24, 3: 21, 4: 17},
                     'y1': {0: 22, 1: 29, 2: 19, 3: 26, 4: 10},
                     'x2': {0: 12, 1: 17, 2: 24, 3: 6, 4: 21},
                     'y2': {0: 22, 1: 10, 2: 19, 3: 29, 4: 26},
                     'x3': {0: 12, 1: 24, 2: 21, 3: 17, 4: 6},
                     'y3': {0: 22, 1: 19, 2: 26, 3: 10, 4: 29},
                     'x4': {0: 12, 1: 17, 2: 24, 3: 6, 4: 21},
                     'y4': {0: 22, 1: 10, 2: 19, 3: 29, 4: 26},
                     'x5': {0: 12, 1: 6, 2: 21, 3: 24, 4: 17},
                     'y5': {0: 22, 1: 29, 2: 26, 3: 19, 4: 10}})

# isolate and reshape x
xx = [c for c in df.columns if 'x' in c]
dfx = df[xx]
dfxm =pd.melt(dfx.reset_index(), id_vars=['index'], value_vars=dfx.columns)
dfxm['variable'] = dfxm['variable'].str.extract('(\d+)')
dfxm.rename(columns={"value": "x"}, inplace=True)
# dfxm

# isolate and reshape y
yy = [c for c in df.columns if 'y' in c]
dfy = df[yy]
dfym =pd.melt(dfy.reset_index(), id_vars=['index'], value_vars=dfy.columns)
dfym['variable'] = dfym['variable'].str.extract('(\d+)')
dfym.rename(columns={"value": "y"}, inplace=True)
dfym['x'] = dfxm['x']

dftempt = dfym

px.scatter(dftempt, x='x', y='y', animation_frame='index', animation_group='variable', range_x=[0,30], range_y=[0,30])

试图编辑@vestland 的答案,但取消了,现在队列已满...我的错。重塑的方式似乎有点不雅,但我是这样做的。您只能在图像中看到四个点,因为两个点位于同一位置。

import plotly_express as px
import pandas as pd

df = pd.DataFrame({'x1': {0: 12, 1: 6, 2: 24, 3: 21, 4: 17},
                     'y1': {0: 22, 1: 29, 2: 19, 3: 26, 4: 10},
                     'x2': {0: 12, 1: 17, 2: 24, 3: 6, 4: 21},
                     'y2': {0: 22, 1: 10, 2: 19, 3: 29, 4: 26},
                     'x3': {0: 12, 1: 24, 2: 21, 3: 17, 4: 6},
                     'y3': {0: 22, 1: 19, 2: 26, 3: 10, 4: 29},
                     'x4': {0: 12, 1: 17, 2: 24, 3: 6, 4: 21},
                     'y4': {0: 22, 1: 10, 2: 19, 3: 29, 4: 26},
                     'x5': {0: 12, 1: 6, 2: 21, 3: 24, 4: 17},
                     'y5': {0: 22, 1: 29, 2: 26, 3: 19, 4: 10}})

dftempt = df

point_lst = []
for i in range(len(dftempt.columns) // 2):
    point = 'point' + str(i+1)
    dftempt[point] = df[df.columns[2*i:2*i+2]].apply(lambda x: list(x) + [point], axis=1)
    point_lst.append(point)

dftempt = pd.concat(dftempt[point] for point in point_lst).reset_index().rename(columns={0:'data'})
dftempt[['x','y','points']] = pd.DataFrame(dftempt.data.tolist(), index= dftempt.index)
px.scatter(dftempt, x='x', y='y', animation_frame='index', animation_group='points', range_x=[0,30], range_y=[0,30])