0

I am trying to create a 3D animation scatter plot where each point is plotted as a sphere with radius of r proportional to value M (please see the code below), I guess it should be done by using argument s in ax.scatter, but since this value is unique for each (x,y,z), I don't know how to pass that to graph._offsets3d which accepts (x,y,z) touple. This is the first part of the task, the other part is that the data should appear at their specific time t (please see the code below).

  1. I am currently struggling to change the size of each point according to their corresponding value in M, and color code the point with its corresponding time t, do you know how could I do this?

  2. It would my next task to add a play/pause button to the figure and be able to rotate the the graph?

Does anyone have similar experiences that I could benefit from? Many thanks!

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation

#####Data Generation####

# Space Coordinate
X = np.random.random((100,)) * 255 * 2 - 255
Y = np.random.random((100,)) * 255 * 2 - 255
Z = np.random.random((100,)) * 255 * 2 - 255


# Magnitude of each point
M = np.random.random((100,))*-1+0.5

# Time
t = np.sort(np.random.random((100,))*10)

#ID each point should be color coded. Moreover, each point belongs to a cluster `ID`
ID = np.sort(np.round([np.random.random((100,))*5]))

def update_lines(num):
    for i in range (df_IS["EASTING [m]"].size):
        dx = X[i]
        dy = Y[i]
        dz = Z[i] 
        text.set_text("{:d}: [{:.0f}] Mw[{:.2f}]".format(ID[i], t[i],ID[i]))  # for debugging
        x.append(dx) 
        y.append(dy) 
        z.append(dz) 
        graph._offsets3d = (x, y, z) 
        return graph,




fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(X, Y, Z, color='orange')  # s argument here 
text = fig.text(0, 1, "TEXT", va='top')  # for debugging

ax.set_xlim3d(X.min(), X.max())
ax.set_ylim3d(Y.min(), Y.max())
ax.set_zlim3d(Z.min(),Z.max())

# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, frames=200, interval=500, blit=False)
plt.show()
Pygin
  • 907
  • 6
  • 12

1 Answers1

2

In the animation function was looped by the size of the data frame, but rewrote your code partly because the animation argument is linked to the number of frames. Please correct me if I'm wrong. You can also pass in the size with graph.set_sizes(), which you can specify there. Your size variable had a negative value, so I'm recreating it as an integer. I've used a separate library in part because of my working environment.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
from IPython.display import HTML # Animation on jupyter lab 
from matplotlib.animation import PillowWriter # For GIF animation 
#####Data Generation####

# Space Coordinate
X = np.random.random((100,)) * 255 * 2 - 255
Y = np.random.random((100,)) * 255 * 2 - 255
Z = np.random.random((100,)) * 255 * 2 - 255

# Magnitude of each point
# M = np.random.random((100,))*-1+0.5
M = np.random.randint(1,70, size=100)
# Time
t = np.sort(np.random.random((100,))*10)

#ID each point should be color coded. Moreover, each point belongs to a cluster `ID`
ID = np.sort(np.round([np.random.random((100,))*5]))

x = []
y = []
z = []
m = []

def update_lines(i):
#     for i in range (df_IS["EASTING [m]"].size):
    dx = X[i]
    dy = Y[i]
    dz = Z[i]
    dm = M[i]
#     text.set_text("{:d}: [{:.0f}] Mw[{:.2f}]".format(ID[i], t[i],ID[i]))  # for debugging
    x.append(dx) 
    y.append(dy) 
    z.append(dz)
    m.append(dm)
    graph._offsets3d = (x, y, z)
    graph.set_sizes(m)
    return graph,

fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(X, Y, Z, s=M, color='orange')  # s argument here 
text = fig.text(0, 1, "TEXT", va='top')  # for debugging

ax.set_xlim3d(X.min(), X.max())
ax.set_ylim3d(Y.min(), Y.max())
ax.set_zlim3d(Z.min(), Z.max())

# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, frames=100, interval=500, blit=False, repeat=False)
# plt.show()
ani.save('test3Dscatter.gif', writer='pillow')
plt.close()
HTML(ani.to_html5_video())

enter image description here

Edit:

# Time
t = np.sort(np.random.random((100,))*10)

# datapoint for color
cm_name = 'jet'
cm = plt.get_cmap(cm_name, 100)
C = [cm(n) for n in range(cm.N)]

# list for colors add
x = []
y = []
z = []
m = []
c = []

# animation function update

dm = M[i]
dc = C[i] # update

m.append(dm)
c.append(dc) # update

graph._facecolor3d = c # scatter color defined
return graph,
r-beginners
  • 31,170
  • 3
  • 14
  • 32
  • Thank you, this looks awesome. The other thing is that I need to add color to each point and have them color coded based on the vector t. Is there something like `graph.set_color` that you are aware of? – Pygin Nov 23 '20 at 20:53
  • Running the above code I am having "RuntimeError: Requested MovieWriter (ffmpeg) not available". I ran the library imports and I have them all installed and get no error in that front. Am I missing something here? – Pygin Nov 23 '20 at 21:03
  • In order to turn them into GIF images, the introduction of the PILLOW library is necessary. Check the library you are importing. I'll look into the colors in the future. – r-beginners Nov 24 '20 at 00:12
  • `graph.set_color()`It is now configurable.See [this page](https://matplotlib.org/3.1.1/api/collections_api.html#matplotlib.collections.PathCollection). – r-beginners Nov 24 '20 at 00:41
  • How would you invert the Z axis in this code? – Pygin Jan 22 '21 at 06:25
  • 1
    `ax.invert_zaxis()`I think this will reverse the z-axis. Add this after `set_zlim3d()`. – r-beginners Jan 22 '21 at 07:16
  • Thanks so much it works, now I am trying to add the time of the occurrence of each data point using `graph.set_color()`, hopefully it works. – Pygin Jan 30 '21 at 00:48
  • I tried to add the `graph.set_color()` to the function, the argument to pass here must be a tuple, so t in your code as time is a vector (I tried tuple(t) to convert the numpy array to tuple but did not work). I am getting so many errors trying to add the t as the time and mapping it with color so that the bar appears to the side and showing the time each event has happened. Do you mind letting me know how would you add the time as a color to each point whose size is related to its magnitude (i.e., M). Cheers – Pygin Apr 29 '21 at 17:57
  • by the way I am reading [this post](https://stackoverflow.com/a/52115846/13597899) which might be related to color mapping in my case. i am still trying to wrap my head around the concept of how to get the color mapped with time and size with their magnitude. – Pygin Apr 29 '21 at 18:12
  • 1
    To add color to a 3d scatter plot, prepare the color data as you would any other data. We have prepared 100 colors in the 'jet' color map. We prepare variables for it in the animation function and add the colors sequentially. The point is to specify the 3D color. `graph._facecolor3d = c` – r-beginners May 02 '21 at 12:44
  • 1
    At r-beginners, thank you for your reply. Ideally I want to have all the time data to be normalized and colormap to the points with a fixed color bar. Right now I think the code would generate 100 colors randomly from the `jet`, which would not appreciate the time series in the color. Something like [this post](https://stackoverflow.com/a/66026854/15811497) with a proper color bar would be awesome. I added `graph = ax.scatter(X, Y, Z, s=M, c=0.5*t, cmap=plt.cm.magma)` and `fig.colorbar(graph, ax=ax)` and it is not just going through the animation – Pygin May 02 '21 at 16:38