How to create 3D animation of particles position vs time in python?

  • I am trying to make a 3-D animation of stars' position vs time. I have the information of the stars' x, y and z co-ordinates over time. I was looking at the example of Matplotlib and did understood what I need to do but I don't want the connecting lines between different times. All I need is to show the position of the stars at different time and make a movie of it. This is the example I was looking at



    https://matplotlib.org/gallery/animation/simple_3danim.html



    After that I want to make it a bit complicated. I have the stars size and mass as well. I was wondering if I can change the size of the circles in the plot according to the radius and use different Colors for different mass range?


    The answer you seek is going to be in the mplo3d documentation, not on this stackexchange.


  • After that I want to make it a bit complicated. I have the stars size and mass as well. I was wondering if I can change the size of the circles in the plot according to the radius and use different Colors for different mass range?




    That part has been addressed in previous questions and answers here, I remember reading maybe a year ago (+/- 0.9 years).






    Here is something you may find helpful reprinted from my answer in Stack Overflow. It's based on astronomer and data scientist Jake VanderPlas' blogpost from 2013.



    For your needs keep the dots but not the lines. What I recommend is that you start from this and get as far as you can, then ask a question in Stack Overflow showing what you've tried and explaining what you still need.




    Here is the Lorenz attractor both in 3D and animated. The script is in the following link (along with many goodies) in Jake VanderPlas' Pythonic Perambulations. You can learn a lot by going line-by-line through the script - it's an elegant use of matplotlib objects.



    https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/



    I added these two lines just before return in the animate function, and then used ImageJ to import the "image stack" and save the "animated GIF":




    fname = "Astro_Jake_" + str(i+10000)[1:]
    fig.savefig(fname)



    Note: For OSX it seems to be necessary to set blit = False in animation.FuncAnimation.



    Astro Jake



    Here is a minimal, simplified example of plotting lines in 3D based on the above:




    def lorentz_deriv((x, y, z), t0, sigma=10., beta=8./3, rho=28.0):
    """Compute the time-derivative of a Lorentz system."""
    return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z]

    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from scipy.integrate import odeint as ODEint

    x = np.linspace(0, 20, 1000)
    y, z = 10.*np.cos(x), 10.*np.sin(x) # something simple

    fig = plt.figure()
    ax = fig.add_subplot(1,2,1,projection='3d')
    ax.plot(x, y, z)

    # now Lorentz
    times = np.linspace(0, 4, 1000)

    start_pts = 30. - 15.*np.random.random((20,3)) # 20 random xyz starting values

    trajectories = []
    for start_pt in start_pts:
    trajectory = ODEint(lorentz_deriv, start_pt, times)
    trajectories.append(trajectory)

    ax = fig.add_subplot(1,2,2,projection='3d')
    for trajectory in trajectories:
    x, y, z = trajectory.T # transpose and unpack
    # x, y, z = zip(*trajectory) # this also works!
    ax.plot(x, y, z)

    plt.show()


    I wrote this before I knew of PIL. Apparently you can use PIL to generate your GIFs instead of using an external program like I did.



    See





    and have fun!



    An example of a GIF with stars in it from the first blogpost (Nyan Cat):



    An example of a GIF (Nyan Cat)


  • Hi I did manage to write the code. Here is what I did



    import numpy as np
    import matplotlib.pyplot as plt
    import mpl_toolkits.mplot3d.axes3d as p3
    import matplotlib.animation as animation
    import pandas as pd

    df1=pd.read_pickle("properties.csv")
    x=np.array(df1['x'])*3.24078e-17
    y=np.array(df1['y'])*3.24078e-17
    z=np.array(df1['z'])*3.24078e-17

    data=[]
    for i in range(len(x)):
    blank=[]
    for j in range(len(x[i])):
    dummy=[]
    dummy.append(x[i][j])
    dummy.append(y[i][j])
    dummy.append(z[i][j])
    blank.append((dummy))

    data.append(np.array(blank))



    def animate_scatters(iteration, data, scatters):
    for i in range(data[0].shape[0]):
    scatters[i]._offsets3d = (data[iteration][i,0:1], data[iteration][i,1:2], data[iteration][i,2:])
    return scatters

    def main(data, save=False):
    fig = plt.figure()
    ax = p3.Axes3D(fig)

    # Initialize scatters
    scatters = [ ax.scatter(data[0][i,0:1], data[0][i,1:2], data[0][i,2:]) for i in range(data[0].shape[0]) ]

    # Number of iterations
    iterations = len(data)

    # Setting the axes properties
    ax.set_xlim3d([-10, 10])
    ax.set_xlabel('X')

    ax.set_ylim3d([-10, 10])
    ax.set_ylabel('Y')

    ax.set_zlim3d([-10, 10])
    ax.set_zlabel('Z')

    ax.set_title('Nuclear Star Cluster')

    # Provide starting angle for the view.
    ax.view_init(25, 10)

    ani = animation.FuncAnimation(fig, animate_scatters, iterations, fargs=(data, scatters),
    interval=50, blit=False, repeat=True)

    if save:
    Writer = animation.writers['ffmpeg']
    writer = Writer(fps=30, metadata=dict(artist='Me'), bitrate=1800, extra_args=['-vcodec', 'libx264'])
    ani.save('3d-scattered-animated.mp4', writer=writer)

    plt.show()

    #print data[1]
    main(data, save=True)


    Now my problem is my data shape is changing with time, the number of particles is getting reduced. How do I incorporate that in the code?


    Is this an answer? Shouldn't this be part of the question? Then edit your question accordingly.

    @planetmaker Good point. However, this is looking more like a coding question now, and it may be better to ask it on Stack Overflow, where there are plenty of Python users who know matplotlib. OTOH, asking on SO can be a nerve-wracking experience, we're a bit gentler here on Astronomy. ;)

    Okay well I think this is the end of the road for Astronomy SE for your question, since this is no longer even a little bit about Astronomy. You have some experience with Stack Overflow, they need fairly specific questions, and there is also Code Review SE and they require *running code* that only needs improvement. We should't post follow up questions in answer posts.

    @PM2Ring ya that's why I posted at least something *here* to at least get them started.

    I've only had a brief look at your code, but I noticed a couple of things. There's no need to import `pandas` just to read a .csv file if you aren't actually using any other Pandas features. You can use the `DictReader` in the `csv` module for that. Also, your `data` conversion looks very inefficient. If you find yourself using `for` loops on a Numpy array, you're probably doing it wrong. Especially nested `for` loops.

    @PM2Ring There is nothing wrong using for loop or nested for loop with numpy arrays. I am just grouping the [x,y,z] for each particle which could have been done using np.hstack as well I guess

    @ArpanDas when your data gets big you will have much better results if you follow that advice. "There's nothing wrong" will turn into "My script takes forever to run" to which the answer will be "You're *definitely* doing it wrong." Learn to let numpy do the work for you because numpy executes fast compiled code whereas nested python loops are far slower. Stacking and moving the order of axes of arrays have simple numpy syntax, and when you are done you can consider using `.copy()` to make a new instance of your reordered array (if you have enough memory) if caching/paging slows you down.

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM