简体   繁体   中英

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. 单独称为散点图

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

在此处输入图像描述

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.

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.

We can use a categorical color map for your purposes,

from random import random
import matplotlib.pyplot as plt

def rxy(): return [random() for _ in range(10)], [random() for _ in range(10)]

colors =  plt.cm.tab20c(range(20))
for n in range(6):
    plt.scatter(*rxy(), c=[colors[n]], label=str(n))
plt.legend()
plt.show()

note that the value of c is a list with a single entry, the (4,) Numpy array representing in RGBA format the current color.

在此处输入图像描述

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)

This code below is currently working for me, for the most part.

I changed plt.scatter... to ax.scatter... and it fixed the z-axis scaling issue that I mentioned in EDIT 2 above.

I also changed to ax = Axes3D(fig)

Thanks to everyone's help. I will work with this for now. 在此处输入图像描述

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('path/to/vtk/files/*_mesh.vtk'))

meshes = []

fig = plt.figure(figsize = (16, 10))
ax = Axes3D(fig)

colors = iter(cm.rainbow(np.linspace(0, 1, len(files))))

for fyle in files:
    ax.scatter(*meshio.read(fyle).points.T, c=[next(colors)])

plt.legend() #legend isn't plotting, will have to fix this
plt.show()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM