我想在我的 3d 箭袋图中使用与颜色图相对应的颜色。绘图的 2d 版本有一个可选数组,用于将颜色映射到箭头。如何在 3d 版本中创建相同的效果?
4 回答
3D 颤动图是 1.4 中的一个全新功能,它(和它的文档)可能仍然有点粗糙。在这种情况下,我们可以尝试使用这样一个事实,即 quiver 被实现为LineCollection
which (最终)继承自ScalarMappable
这意味着它知道颜色图是什么,并且返回的艺术家有方法set_array
。
建立在此处的文档上
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) *
np.sin(np.pi * z))
q = ax.quiver(x, y, z, u, v, w, length=0.1, cmap='Reds', lw=2)
q.set_array(np.random.rand(np.prod(x.shape)))
plt.show()
但是,您会注意到头部的颜色与轴的颜色不同,这是由于其实现方式的实现细节,每个部分都被绘制为自己的线。
直接使用Norm
和颜色映射函数并将结果传递给colors
可能是更好的课程。
您可以使用colors
参数为每个箭头指定自定义颜色,尽管这样做的方法并不简单(对于matplotlib 2.0.0)。我在这个问题中指定了如何实际绘制箭袋图的逻辑以及指定着色的解决方法。您可以查看此要点以获取一个简单的示例,该示例可以生成类似于此的图形:
总而言之,以下是要遵循的步骤:
- 假设您有一个 3 元组(或 RGBA 的 4 元组)的列表(大小为 x*y*z),指定要绘制的每个向量的 RGB 值(0~1 之间)。
- 过滤掉与长度为 0 的向量相对应的 RGB(或 RGBA)元组,因为它们实际上不会被绘制。
- 假设
[color_1, color_2, ..., color_n]
是您在步骤 2 之后获得的列表,您应该指定colors=[color_1, color_2, ..., color_n, color_1, color_1, color_2, color_2, ..., color_n, color_n]
因为实际上所有非零箭头“->”的“-”部分(由 1 行组成)将首先绘制,然后是“>”部分(由 2 行组成)。
希望这可以帮助。
基于@tacaswell 和@sytrus 的答案,这里是一个为 3d 箭袋着色的例子
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
# Make the direction data for the arrows
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) *
np.sin(np.pi * z))
# Color by azimuthal angle
c = np.arctan2(v, u)
# Flatten and normalize
c = (c.ravel() - c.min()) / c.ptp()
# Repeat for each body line and two head lines
c = np.concatenate((c, np.repeat(c, 2)))
# Colormap
c = plt.cm.hsv(c)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(x, y, z, u, v, w, colors=c, length=0.1, normalize=True)
plt.show()
扩展来自@slek120 的答案。我遇到了一个长度为零的向量的问题。这些弄乱了箭头尖端颜色之间的对应关系。我的解决方案是给它们一个非零长度并使它们透明。由于某种我不明白的原因,简单地丢弃它们是行不通的。在最后一部分添加一个小改动,也可以包含一个颜色条。颜色栏明确要求q.set_array()
. 这会更改颜色,但q.set_edgecolor(c); q.set_facecolor(c)
允许您插入自定义颜色图。
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
cmap = 'hsv'
# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
# Make the direction data for the arrows
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * np.sin(np.pi * z)
# check what happens if all values are zero
# no quivers are plotted, colors don't match anymore
u[:,2:4] = v[:,2:4] = w[:,2:4] = 0
# change values that are zero to something close to zero
uvw = np.vstack((u[np.newaxis],v[np.newaxis],w[np.newaxis]))
norm = np.linalg.norm(uvw, axis = 0)
max_norm = np.max(norm)
mask = norm == 0
min_norm = 0.3 # you want every arrow to be longer than this fraction of max_norm
# rescale vs for illustrative purposes, so small vectors become visible
# and zero vectors become nonzero so colors of the arrow shaft and head correspond. Later these are made transparent
uvw = uvw + min_norm * np.tile(mask[np.newaxis], (3, 1, 1, 1)) / max_norm
# recalculate norms so you don't divide by zero
norm = np.linalg.norm(uvw, axis=0)
uvw = min_norm * uvw / norm + (1 - min_norm) * uvw / max_norm
u, v, w = uvw
# Color by azimuthal angle
c = np.arctan2(v, u)
# Flatten and normalize
c = (c.ravel() - c.min()) / c.ptp()
# Adjust for missing quivers
# c = c[np.nonzero((u.ravel() != 0) * (v.ravel() != 0) * (w.ravel() != 0))]
# Repeat for each body line and two head lines
c = np.concatenate((c, np.repeat(c, 2)))
repeated_mask = np.concatenate((mask.ravel(), np.repeat(mask.ravel(), 2)))
# Colormap
c = getattr(plt.cm, cmap)(c)
# set zero values transparent, you made them nonzero not to mess up the tip colors
c[repeated_mask, 3] = 0.1
fig = plt.figure()
ax = fig.gca(projection='3d')
q = ax.quiver(x, y, z, u, v, w, cmap = cmap, length=0.1)
q.set_array(np.linspace(0,max_norm,10))
fig.colorbar(q)
q.set_edgecolor(c)
q.set_facecolor(c)
plt.show()