(python) 绘制 3d 表面,颜色图作为第 4 维,x、y、z 的函数
(python) plot 3d surface with colormap as 4th dimension, function of x,y,z
我正在尝试绘制一个 3d 表面,其中三个维度中的每一个都在一个单独的值数组中,并且表面在每个坐标处的着色是 x、y、z 的函数。一种 numpy.pcolormesh 但在 4D 中,而不是 3D。
3D 图由以下公式给出:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.logspace(-1.,np.log10(5),50)
y = np.linspace(6,9,50)
z = np.linspace(-1,1,50)
colors = LikeBeta(y,range(50),range(50))
ax.plot_trisurf(x,y,z,cmap=colors,linewidth=0.2)
哪里
def LikeBeta(rho0,r0,beta):
M0 = 10**rho0*r0_array[r0]**3
I = cst*M0*sigma_los_beta[beta,:,r0]
S = dv**2+I
res = (np.log(S) + (v-u)**2/S).sum()
return res/2.
可能cmap=colors
是错误的,但问题出在其他地方。我收到以下错误:
----> 8 colors = LikeBeta(y,range(50),range(50))
----> 4 I = cst*M0*sigma_los_beta[beta,:,r0]
ValueError: operands could not be broadcast together with shapes (50,) (50,353)
的确 sigma_los_beta
是一个我单独评估的数组,其形状为 (50,353,50)
并且那 353 个是我必须拥有的数据。
如何将此函数转换为与 plot_trisurf
的其他条目兼容的形式?
抱歉,我无法提供最小的工作代码,因为 dv、v 和 u 是数据。
非常感谢您的帮助。干杯
此代码基于 trisurf 演示
http://matplotlib.org/examples/mplot3d/trisurf3d_demo.html
我在 SO Create own colormap using matplotlib and plot color scale
的基础上添加了一个函数 make_colormap()
还添加了一个序列 w=tan(-x*y),该序列基于该函数生成灰度颜色图。
您可以尝试构建 cdict 以向其添加更多颜色,但我认为灰度可以很好地证明概念...
抱歉,由于缺少最少的工作代码,我无法直接使用您的示例。
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
###################
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
#%
cdict = {'red': [], 'green': [], 'blue': []}
# make a lin_space with the number of records from seq.
x = np.linspace(0,1, len(seq))
#%
for i in range(len(seq)):
segment = x[i]
tone = seq[i]
cdict['red'].append([segment, tone, tone])
cdict['green'].append([segment, tone, tone])
cdict['blue'].append([segment, tone, tone])
#%
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
#############################
n_angles = 36
n_radii = 8
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.125, 1.0, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z = np.sin(-x*y)
w = np.tan(-x*y)
colors = make_colormap(w)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, cmap=colors, linewidth=0.2)
plt.show()
This 答案解决了 4d 曲面图问题。它使用 matplotlib 的 plot_surface
函数而不是 plot_trisurf
.
基本上,您想将 x、y 和 z 变量重塑为相同维度的二维数组。要将第四维添加为颜色图,您必须提供另一个与轴变量具有相同维数的二维数组。
下面是 3d 图的示例代码,其中颜色图对应于 x 值。 facecolors
参数用于根据您的喜好更改颜色图。注意它的值是从matplotlib.cm.ScalarMappable
class.
中的to_rgba()
函数获取的
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# domains
x = np.logspace(-1.,np.log10(5),50) # [0.1, 5]
y = np.linspace(6,9,50) # [6, 9]
z = np.linspace(-1,1,50) # [-1, 1]
# convert to 2d matrices
Z = np.outer(z.T, z) # 50x50
X, Y = np.meshgrid(x, y) # 50x50
# fourth dimention - colormap
# create colormap according to x-value (can use any 50x50 array)
color_dimension = X # change to desired fourth dimension
minn, maxx = color_dimension.min(), color_dimension.max()
norm = matplotlib.colors.Normalize(minn, maxx)
m = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors = m.to_rgba(color_dimension)
# plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
fig.canvas.show()
我引用的答案(和其他答案)提到您应该规范化第四维数据。似乎可以通过像我在代码示例中那样显式设置颜色图的限制来避免这种情况。
非常感谢@Frik 的伟大 ,它帮助我实现了 OP 要求的类似情节。
但是,我发现可以对代码进行一些简化并且可能会引起人们的兴趣。片段和下图。
import matplotlib.pyplot as plt
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
from mpl_toolkits.mplot3d.axes3d import get_test_data
import numpy as np
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
X, Y, Z = get_test_data(0.05)
C = np.linspace(-5, 5, Z.size).reshape(Z.shape)
scamap = plt.cm.ScalarMappable(cmap='inferno')
fcolors = scamap.to_rgba(C)
ax.plot_surface(X, Y, Z, facecolors=fcolors, cmap='inferno')
fig.colorbar(scamap)
plt.show()
最后,我也想评论一下@Frik写的:
The answer I referenced (and others) mentions that you should normalize your fourth dimension data. It seems that this may be avoided by explicitly setting the limits of the colormap as I did in the code sample.
我发现这个说法是不正确的。实际上,如果看一下 to_rgba
,就会发现有一个 norm
关键字,默认设置为 True
。这正是规范化发生的地方。还包括以下声明:
If norm is False, no normalization of the input data is performed, and it is assumed to be in the range (0-1).
您确实希望您的数据位于 (0-1)。
截至 2022 年 5 月,此问题的前三个答案各有不同的问题。我发现 example provided in the matplotlib 3.5.0 documentation 要简单得多,并且实际上可以按预期工作,使用 LightSource
class.
计算阴影 facecolors
只需覆盖传递给 ls.shade
的特定 z
:
from matplotlib import cbook
from matplotlib import cm
from matplotlib.colors import LightSource
import matplotlib.pyplot as plt
import numpy as np
# Load and format data
dem = cbook.get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
nrows, ncols = z.shape
x = np.linspace(dem['xmin'], dem['xmax'], ncols)
y = np.linspace(dem['ymin'], dem['ymax'], nrows)
x, y = np.meshgrid(x, y)
region = np.s_[5:50, 5:50]
x, y, z = x[region], y[region], z[region]
# Set up plot
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ls = LightSource(270, 45)
# To use a custom hillshading mode, override the built-in shading and pass
# in the rgb colors of the shaded surface calculated from "shade".
rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb,
linewidth=0, antialiased=False, shade=False)
plt.show()
我正在尝试绘制一个 3d 表面,其中三个维度中的每一个都在一个单独的值数组中,并且表面在每个坐标处的着色是 x、y、z 的函数。一种 numpy.pcolormesh 但在 4D 中,而不是 3D。 3D 图由以下公式给出:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.logspace(-1.,np.log10(5),50)
y = np.linspace(6,9,50)
z = np.linspace(-1,1,50)
colors = LikeBeta(y,range(50),range(50))
ax.plot_trisurf(x,y,z,cmap=colors,linewidth=0.2)
哪里
def LikeBeta(rho0,r0,beta):
M0 = 10**rho0*r0_array[r0]**3
I = cst*M0*sigma_los_beta[beta,:,r0]
S = dv**2+I
res = (np.log(S) + (v-u)**2/S).sum()
return res/2.
可能cmap=colors
是错误的,但问题出在其他地方。我收到以下错误:
----> 8 colors = LikeBeta(y,range(50),range(50))
----> 4 I = cst*M0*sigma_los_beta[beta,:,r0]
ValueError: operands could not be broadcast together with shapes (50,) (50,353)
的确 sigma_los_beta
是一个我单独评估的数组,其形状为 (50,353,50)
并且那 353 个是我必须拥有的数据。
如何将此函数转换为与 plot_trisurf
的其他条目兼容的形式?
抱歉,我无法提供最小的工作代码,因为 dv、v 和 u 是数据。 非常感谢您的帮助。干杯
此代码基于 trisurf 演示 http://matplotlib.org/examples/mplot3d/trisurf3d_demo.html
我在 SO Create own colormap using matplotlib and plot color scale
的基础上添加了一个函数 make_colormap()还添加了一个序列 w=tan(-x*y),该序列基于该函数生成灰度颜色图。
您可以尝试构建 cdict 以向其添加更多颜色,但我认为灰度可以很好地证明概念...
抱歉,由于缺少最少的工作代码,我无法直接使用您的示例。
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
###################
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
#%
cdict = {'red': [], 'green': [], 'blue': []}
# make a lin_space with the number of records from seq.
x = np.linspace(0,1, len(seq))
#%
for i in range(len(seq)):
segment = x[i]
tone = seq[i]
cdict['red'].append([segment, tone, tone])
cdict['green'].append([segment, tone, tone])
cdict['blue'].append([segment, tone, tone])
#%
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
#############################
n_angles = 36
n_radii = 8
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.125, 1.0, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z = np.sin(-x*y)
w = np.tan(-x*y)
colors = make_colormap(w)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, cmap=colors, linewidth=0.2)
plt.show()
This 答案解决了 4d 曲面图问题。它使用 matplotlib 的 plot_surface
函数而不是 plot_trisurf
.
基本上,您想将 x、y 和 z 变量重塑为相同维度的二维数组。要将第四维添加为颜色图,您必须提供另一个与轴变量具有相同维数的二维数组。
下面是 3d 图的示例代码,其中颜色图对应于 x 值。 facecolors
参数用于根据您的喜好更改颜色图。注意它的值是从matplotlib.cm.ScalarMappable
class.
to_rgba()
函数获取的
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# domains
x = np.logspace(-1.,np.log10(5),50) # [0.1, 5]
y = np.linspace(6,9,50) # [6, 9]
z = np.linspace(-1,1,50) # [-1, 1]
# convert to 2d matrices
Z = np.outer(z.T, z) # 50x50
X, Y = np.meshgrid(x, y) # 50x50
# fourth dimention - colormap
# create colormap according to x-value (can use any 50x50 array)
color_dimension = X # change to desired fourth dimension
minn, maxx = color_dimension.min(), color_dimension.max()
norm = matplotlib.colors.Normalize(minn, maxx)
m = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors = m.to_rgba(color_dimension)
# plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
fig.canvas.show()
我引用的答案(和其他答案)提到您应该规范化第四维数据。似乎可以通过像我在代码示例中那样显式设置颜色图的限制来避免这种情况。
非常感谢@Frik 的伟大
但是,我发现可以对代码进行一些简化并且可能会引起人们的兴趣。片段和下图。
import matplotlib.pyplot as plt
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
from mpl_toolkits.mplot3d.axes3d import get_test_data
import numpy as np
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
X, Y, Z = get_test_data(0.05)
C = np.linspace(-5, 5, Z.size).reshape(Z.shape)
scamap = plt.cm.ScalarMappable(cmap='inferno')
fcolors = scamap.to_rgba(C)
ax.plot_surface(X, Y, Z, facecolors=fcolors, cmap='inferno')
fig.colorbar(scamap)
plt.show()
最后,我也想评论一下@Frik写的:
The answer I referenced (and others) mentions that you should normalize your fourth dimension data. It seems that this may be avoided by explicitly setting the limits of the colormap as I did in the code sample.
我发现这个说法是不正确的。实际上,如果看一下 to_rgba
,就会发现有一个 norm
关键字,默认设置为 True
。这正是规范化发生的地方。还包括以下声明:
If norm is False, no normalization of the input data is performed, and it is assumed to be in the range (0-1).
您确实希望您的数据位于 (0-1)。
截至 2022 年 5 月,此问题的前三个答案各有不同的问题。我发现 example provided in the matplotlib 3.5.0 documentation 要简单得多,并且实际上可以按预期工作,使用 LightSource
class.
facecolors
只需覆盖传递给 ls.shade
的特定 z
:
from matplotlib import cbook
from matplotlib import cm
from matplotlib.colors import LightSource
import matplotlib.pyplot as plt
import numpy as np
# Load and format data
dem = cbook.get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
nrows, ncols = z.shape
x = np.linspace(dem['xmin'], dem['xmax'], ncols)
y = np.linspace(dem['ymin'], dem['ymax'], nrows)
x, y = np.meshgrid(x, y)
region = np.s_[5:50, 5:50]
x, y, z = x[region], y[region], z[region]
# Set up plot
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ls = LightSource(270, 45)
# To use a custom hillshading mode, override the built-in shading and pass
# in the rgb colors of the shaded surface calculated from "shade".
rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb,
linewidth=0, antialiased=False, shade=False)
plt.show()