Towsif Ahamed Labib
Towsif Ahamed Labib

Reputation: 686

How to animate multiple moving points in single scatter plot

Here is a dataframe showing coordinates of 5 different points. How can it be animated row by row?

        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

Column x1,y1 is the coordinates of the first ant. Sequentially x2,y2 x3,y3 .. represents the coordinates of other ants. Indexes show the steps where in every step ants go from one node to another node. I used plotly to animate the data

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

But it only shows the 1st point(x1,y1), How can I animate all 5 points in single plot?

Here is the data of first 100 rows:

{'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}}

Upvotes: 1

Views: 456

Answers (2)

vestland
vestland

Reputation: 61094

If the first frame of what you're aiming to build looks like this:

enter image description here

And the last frame should look like this:

enter image description here

Then you should reshape your dataframe from a wide to a long format for each category x and y so that animation_frame has something else than a continuous index to work with. And then assign extracted numbers from x1, y1 to the animation_group attribute in px.scatter:

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

Complete code:

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

Upvotes: 1

Frodnar
Frodnar

Reputation: 2252

Tried to edit @vestland's answer, but canceled and now the queue is full... My bad. Seems to be a little inelegant way to reshape, but here's how I did it. You can only see four points in the image because two are in the same location.

enter image description here

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

Upvotes: 2

Related Questions