使用 matplotlib 散点图绘制 3D Numpy 数组
Scatter plotting 3D Numpy array using matplotlib
我有一个数值大小为 (75, 150, 150) 的 3D numpy 数组,它表示 75 个层,x 方向有 150 个网格单元,y 方向有 150 个网格单元。有没有办法在 x y 和 z 3D 地图中绘制此 3D 数组,其中 z 维度代表我模型的层,x 和 y 是 x 和 y 维度?我有以下代码,但它给我错误:ValueError: shape mismatch: objects cannot be broadcast to a single shape.
array.shape = (75, 150, 150)
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 1687500
z=np.arange(76)
x=np.arange(151)
y=np.arange(151)
for i, j, k in x, y, z:
y=array[k,i, :]
x=array[k,:, j]
colors = np.random.randint(0, 10, size=n)
ax.scatter(z, x, y, c=colors, marker='o')
plt.show()
您有一个包含 75*150*150
(1687500
) 个元素的数组。在散点图的第一个版本中,您使用一列 75 个元素作为 Z,两列 150 个值作为 X 和 Y。您几乎忽略了 3D 数组中的所有信息。
在编辑后的版本中,x、y 和 z 均包含完整的 3D 数组。所有值都将相同。
假设数组包含75层150x150的网格,你可以绘制如下。请注意,由于点数很大,因此速度会很慢。而且,很难理解。 np.meshgrid
创建位置值数组。
要过滤掉零值,您可以将它们替换为np.nan
。要根据 bins 着色,您可以使用 BoundaryNorm
.
from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')
# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[(x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 > (num_layers - z) ** 2] = 0
array[array == 0] = np.nan # replace zeros by NaN to make them invisible
bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format='%.4f')
plt.show()
当有很多零时,有助于将它们完全过滤掉。您需要将所有数组转换为一维数组:
from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import FuncFormatter
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')
# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[np.abs((x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 - (num_layers - z) ** 2) > 5] = 0
# make the arrays 1D, so they are easier to filter
array = array.ravel()
filter = array != 0
x = x.ravel()[filter]
y = y.ravel()[filter]
z = z.ravel()[filter]
array = array[filter]
bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format=FuncFormatter(lambda x, pos: f'{x * 100:3g} %'))
plt.show()
我有一个数值大小为 (75, 150, 150) 的 3D numpy 数组,它表示 75 个层,x 方向有 150 个网格单元,y 方向有 150 个网格单元。有没有办法在 x y 和 z 3D 地图中绘制此 3D 数组,其中 z 维度代表我模型的层,x 和 y 是 x 和 y 维度?我有以下代码,但它给我错误:ValueError: shape mismatch: objects cannot be broadcast to a single shape.
array.shape = (75, 150, 150)
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 1687500
z=np.arange(76)
x=np.arange(151)
y=np.arange(151)
for i, j, k in x, y, z:
y=array[k,i, :]
x=array[k,:, j]
colors = np.random.randint(0, 10, size=n)
ax.scatter(z, x, y, c=colors, marker='o')
plt.show()
您有一个包含 75*150*150
(1687500
) 个元素的数组。在散点图的第一个版本中,您使用一列 75 个元素作为 Z,两列 150 个值作为 X 和 Y。您几乎忽略了 3D 数组中的所有信息。
在编辑后的版本中,x、y 和 z 均包含完整的 3D 数组。所有值都将相同。
假设数组包含75层150x150的网格,你可以绘制如下。请注意,由于点数很大,因此速度会很慢。而且,很难理解。 np.meshgrid
创建位置值数组。
要过滤掉零值,您可以将它们替换为np.nan
。要根据 bins 着色,您可以使用 BoundaryNorm
.
from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')
# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[(x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 > (num_layers - z) ** 2] = 0
array[array == 0] = np.nan # replace zeros by NaN to make them invisible
bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format='%.4f')
plt.show()
当有很多零时,有助于将它们完全过滤掉。您需要将所有数组转换为一维数组:
from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import FuncFormatter
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')
# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[np.abs((x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 - (num_layers - z) ** 2) > 5] = 0
# make the arrays 1D, so they are easier to filter
array = array.ravel()
filter = array != 0
x = x.ravel()[filter]
y = y.ravel()[filter]
z = z.ravel()[filter]
array = array[filter]
bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format=FuncFormatter(lambda x, pos: f'{x * 100:3g} %'))
plt.show()