Dmi*_*try 6 python plot data-visualization scatter matplotlib
在 Python 中绘制 3d 数组的最有效方法是什么?
例如:
volume = np.random.rand(512, 512, 512)
Run Code Online (Sandbox Code Playgroud)
其中数组项表示每个像素的灰度颜色。
以下代码运行速度太慢:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
volume = np.random.rand(20, 20, 20)
for x in range(len(volume[:, 0, 0])):
for y in range(len(volume[0, :, 0])):
for z in range(len(volume[0, 0, :])):
ax.scatter(x, y, z, c = tuple([volume[x, y, z], volume[x, y, z], volume[x, y, z], 1]))
plt.show()
Run Code Online (Sandbox Code Playgroud)
为了获得更好的性能ax.scatter,请尽可能避免多次调用。相反,将所有x, y,z坐标和颜色打包到一维数组(或列表)中,然后调用ax.scatter一次:
ax.scatter(x, y, z, c=volume.ravel())
Run Code Online (Sandbox Code Playgroud)
问题(就 CPU 时间和内存而言)增长为size**3,其中size是立方体的边长。
此外,ax.scatter将尝试渲染所有size**3点,而不考虑大多数点被外壳上的点遮挡的事实。
volume在渲染它之前,这将有助于减少点的数量——也许通过以某种方式总结或重新采样/插值它。
我们还O(size**3)可以O(size**2)
通过只绘制外壳来减少从到所需的 CPU 和内存:
import functools
import itertools as IT
import numpy as np
import scipy.ndimage as ndimage
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def cartesian_product_broadcasted(*arrays):
"""
http://stackoverflow.com/a/11146645/190597 (senderle)
"""
broadcastable = np.ix_(*arrays)
broadcasted = np.broadcast_arrays(*broadcastable)
dtype = np.result_type(*arrays)
rows, cols = functools.reduce(np.multiply, broadcasted[0].shape), len(broadcasted)
out = np.empty(rows * cols, dtype=dtype)
start, end = 0, rows
for a in broadcasted:
out[start:end] = a.reshape(-1)
start, end = end, end + rows
return out.reshape(cols, rows).T
# @profile # used with `python -m memory_profiler script.py` to measure memory usage
def main():
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
size = 512
volume = np.random.rand(size, size, size)
x, y, z = cartesian_product_broadcasted(*[np.arange(size, dtype='int16')]*3).T
mask = ((x == 0) | (x == size-1)
| (y == 0) | (y == size-1)
| (z == 0) | (z == size-1))
x = x[mask]
y = y[mask]
z = z[mask]
volume = volume.ravel()[mask]
ax.scatter(x, y, z, c=volume, cmap=plt.get_cmap('Greys'))
plt.show()
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)

但请注意,即使只绘制外壳,要实现绘制,
size=512我们仍然需要大约 1.3 GiB 的内存。还要注意,即使您有足够的总内存,但由于 RAM 不足,程序使用交换空间,程序的整体速度也会显着降低。如果您发现自己处于这种情况,那么唯一的解决方案是找到一种更智能的方法,使用更少的点来渲染可接受的图像,或者购买更多的 RAM。
首先,512x512x512 点的密集网格需要绘制的数据太多,这不是从技术角度来看,而是从观察绘图时能够从中看到任何有用的信息来看。您可能需要提取一些等值面,查看切片等。如果大多数点都是不可见的,那么可能没问题,但是您应该要求ax.scatter仅显示非零点以使其更快。
也就是说,您可以通过以下方法更快地完成此操作。技巧是消除所有 Python 循环,包括隐藏在itertools.
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
# Make this bigger to generate a dense grid.
N = 8
# Create some random data.
volume = np.random.rand(N, N, N)
# Create the x, y, and z coordinate arrays. We use
# numpy's broadcasting to do all the hard work for us.
# We could shorten this even more by using np.meshgrid.
x = np.arange(volume.shape[0])[:, None, None]
y = np.arange(volume.shape[1])[None, :, None]
z = np.arange(volume.shape[2])[None, None, :]
x, y, z = np.broadcast_arrays(x, y, z)
# Turn the volumetric data into an RGB array that's
# just grayscale. There might be better ways to make
# ax.scatter happy.
c = np.tile(volume.ravel()[:, None], [1, 3])
# Do the plotting in a single call.
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x.ravel(),
y.ravel(),
z.ravel(),
c=c)
Run Code Online (Sandbox Code Playgroud)