我正在尝试创建一个类似于下面摘自本文的图,本质上是一个具有两个不同 y 轴的 3d 图。按照本博客中的指导,我创建了一个最小的示例。
模块
from mpl_toolkits import mplot3d
import numpy as np
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
Run Code Online (Sandbox Code Playgroud)
创建一些数据
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Z2 = Z*100+100
Run Code Online (Sandbox Code Playgroud)
绘图
这会创建一个漂亮的 3D 绘图,但显然只有一个 y 轴。我在网上找不到任何关于如何使用 python 的建议,尽管有一些关于 matlab 的建议。
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z2, rstride=1, cstride=1,
cmap='viridis', edgecolor='none')
ax.set_title('surface');
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
Run Code Online (Sandbox Code Playgroud)
代码给出:
这并不容易。一种可能的解决方法如下:
axes仍然是单一和共享的(出于必要/明显的 matplotlib 3d 图限制),但数据被绘制为好像在相同的连续值尺度上,但轴刻度和标签被自定义覆盖以反映不同的尺度价值观。例如,
import numpy as np
import matplotlib.pyplot as plt
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
def g(x, y):
return -np.cos(np.sqrt(x ** 2 + y ** 2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Z_new = g(X, Y)
offset = 5
Z_new_offset = Z_new + Z.max() + offset
fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection="3d")
surf1 = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1, cmap="viridis", edgecolor="none", alpha=0.7
)
surf2 = ax.plot_surface(
X,
Y,
Z_new_offset,
rstride=1,
cstride=1,
cmap="plasma",
edgecolor="none",
alpha=0.7,
)
z_ticks_original = np.linspace(Z.min(), Z.max(), 5)
# Add custom tick labels and tick marks for the new plot on the left
z_ticks_new = np.linspace(Z_new_offset.min(), Z_new_offset.max(), 5)
for z_tick in z_ticks_new:
ax.text(
X.min() - 0.5,
Y.min() - 2.5,
z_tick + 0.25,
f"{z_tick - (offset+1):.1f}",
color="k",
verticalalignment="center",
)
ax.plot(
[X.min() - 0.5, X.min()],
[Y.min() - 0.5, Y.min()],
[z_tick, z_tick],
color="k",
)
ax.set_zticks(np.block([z_ticks_original, z_ticks_new]))
fig.canvas.draw()
labels = []
for lab, tick in zip(ax.get_zticklabels(), ax.get_zticks()):
if float(tick) >= 1.0:
lab.set_text("")
labels += [lab]
ax.set_zticklabels(labels)
# Draw the left Z-axis line
ax.plot(
[X.min() - 0.5] * 2,
[Y.min() - 0.5] * 2,
[z_ticks_new.min(), z_ticks_new.max()],
color="k",
)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
cbar1 = fig.colorbar(
surf1, ax=ax, pad=-0.075, orientation="vertical", shrink=0.5
)
cbar1.set_label("Z Values (primary z-axis)")
cbar2 = fig.colorbar(
surf2,
ax=ax,
pad=0.12,
orientation="vertical",
shrink=0.5,
ticks=z_ticks_original,
)
cbar2.set_label("Z Values (secondary z-axis)")
plt.show()
Run Code Online (Sandbox Code Playgroud)