3D scatter plot of multiple files with each file having unique color

I have seen this thread but my data are a little different. I want to create a 3D plot of multiple files containing x,y,z coordinates and color code each file with a unique color, not each point coordinate

Code thus far:

import meshio
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import glob
import matplotlib.cm as cm

files = sorted(glob.glob('mesh_files/*.vtk'))

mesh = []

fig = plt.figure(figsize = (16, 10))
ax = plt.axes(projection = '3d')

colors = cm.rainbow(np.linspace(0, 1, 16))

for file in files:
    mesh.append(meshio.read(file))

    x = [m.points[:, 0] for m in mesh]
    y = [m.points[:, 1] for m in mesh]
    z = [m.points[:, 2] for m in mesh]

    for a,b,c,d in zip(x,y,z,colors):
        plt.scatter(a,b,c,color=d)

Background

x, y and z are all lists containing numpy arrays

<<len(x)
16

<<len(x[0])
99937

<<x[0].shape
(99937,)

<<type(x)
<class 'list'>

<<type(x[0])
<class 'numpy.ndarray'>

I believe the issue is with the colors and a possible mismatch in sizes

<<len(colors)
16

<<len(colors[0])
4

Error

RuntimeWarning: invalid value encountered in sqrt

EDIT: I can individually call scatter and manually enter a different color to create the below plot, but this would take forever with 10+ files, so I want it in a loop or function of some sort. individually called scatter plots

EDIT2: I was able to get this plot, which is nice that the colors are different for each files' data, but the z scale is too small, compared to the first plot, and it looks like data are missing, it should like like the first plot in terms of z depth values, but with 16 unique colors as in the second plot. The first plot is only plotting 3 files manually

enter image description here

3 answers

  • answered 2019-11-08 14:17 Liris

    I think you mistake comes from the mesh list that you are updating at every step. You plot the whole mesh list every step, such that your first file is plotted 16 times, in 16 different colors.

    The simplest code could be:

    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    import numpy as np
    import glob
    import matplotlib.cm as cm
    
    files = sorted(glob.glob('mesh_files/*.vtk'))
    
    fig = plt.figure(figsize = (16, 10))
    ax = plt.axes(projection = '3d')
    
    colors = cm.rainbow(np.linspace(0, 1, len(files)))
    
    for file in files:
        data = meshio.read(file).points
    
        x = data[:, 0]
        y = data[:, 1]
        z = data[:, 2]
    
        plt.scatter(x, y, z, color = colors[files.index(file)])
    

    If you want to store all the points in a list called mesh, you can modify it as :

    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    import numpy as np
    import glob
    import matplotlib.cm as cm
    
    files = sorted(glob.glob('mesh_files/*.vtk'))
    mesh = []
    
    fig = plt.figure(figsize = (16, 10))
    ax = plt.axes(projection = '3d')
    
    colors = cm.rainbow(np.linspace(0, 1, len(files)))
    
    for file in files:
        mesh.append(meshio.read(file).points)
    
        x = mesh[-1][:, 0]
        y = mesh[-1][:, 1]
        z = mesh[-1][:, 2]
    
        plt.scatter(x, y, z, color = colors[files.index(file)])
    

    such that you only plot the points corresponding the file you just read at every step.

  • answered 2019-11-08 14:50 mauve

    As was mentioned previously, the problem you're experiencing is which loop the color selection is occurring in.

    color = iter(cm.rainbow(np.linspace(0, 1, len(files))))
    
    for file in files:
        d = next(color) #set the color for each file instead of inside the loop
        mesh.append(meshio.read(file))
    
        x = [m.points[:, 0] for m in mesh]
        y = [m.points[:, 1] for m in mesh]
        z = [m.points[:, 2] for m in mesh]
    
        for a,b,c in zip(x,y,z):
            plt.scatter(a,b,c,color=d)
    

  • answered 2019-11-08 15:43 gboffi

    If you don't need the meshes afterwards you can avoid allocating a bunch of memory

    ...
    colors = iter(cm.rainbow(np.linspace(0, 1, 16)))
    for file in files:
        plt.scatter(*meshio.read(file).points.T, c=[next(colors)], label=file)
    plt.legend()
    plt.show()
    

    or, if you need the meshes afterwards we can use a container

    ...
    meshes = []
    colors = iter(cm.rainbow(np.linspace(0, 1, 16)))
    for file in files:
        meshes.append(meshio.read(file))
        plt.scatter(*meshes[-1].points.T, c=[next(colors)], label=file)
    plt.legend()
    plt.show()
    

    NB scatter in 3D needs x, y and z, all with shape (N,), while meshobj.points has shape (N, 3) so we first transpose it (shape is now (3, N)) and finally we unpack (using the star "*" operator) the 2D array to get the requested three (N,) arrays.