matplotlib 使用色图绘制3d表面作为第四维,x,y,z的函数

e4yzc0pl  于 2023-05-01  发布在  其他
关注(0)|答案(4)|浏览(274)

我试图绘制一个三维表面,其中三维中的每一个都在一个单独的值数组中,每个坐标处的表面颜色是x,y,z的函数。有点麻木。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是数据。非常感谢你的帮助。干杯

dgtucam1

dgtucam11#

This答案解决了4D曲面图问题。它使用matplotlib的plot_surface函数而不是plot_trisurf
基本上,你想将x,y和z变量整形为相同维度的2d数组。要将第四维添加为色彩Map表,必须提供另一个维度相同的2d数组作为轴变量。
下面是一个3d图的示例代码,其中颜色Map表对应于x值。facecolors参数用于根据您的喜好更改色彩Map表。请注意,它的值是从matplotlib.cm.ScalarMappable类中的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()

我引用的答案(和其他人)提到你应该规范化你的第四维数据。看起来,通过像我在代码示例中所做的那样显式地设置色彩Map表的限制,可以避免这种情况。

nhn9ugyo

nhn9ugyo2#

非常感谢@Frik的伟大answer,它帮助我实现了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写的:
我引用的答案(和其他人)提到你应该规范化你的第四维数据。看起来,通过像我在代码示例中所做的那样显式地设置色彩Map表的限制,可以避免这种情况。
我发现这个说法是不正确的。事实上,如果你看一下to_rgba,你可以看到有一个norm关键字,默认设置为True。这正是正常化发生的地方。还包括以下声明:
如果norm为False,则不执行输入数据的归一化,并且假定其在范围(0-1)内。
您确实希望数据位于(0-1)中。

wdebmtf2

wdebmtf23#

此代码基于trisurf demo 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()
hm2xizp9

hm2xizp94#

截至2022年5月,这个问题的前三个答案都有不同的问题。我发现example provided in the matplotlib 3.5.0 documentation要简单得多,实际上可以像预期的那样使用LightSource类计算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()

相关问题