我正在 Python 中使用 Matplotlib 绘制一些 3D 曲面图,并注意到一个恼人的现象。根据我设置视点(相机位置)的方式,垂直 (z) 轴在左侧和右侧之间移动。以下是两个示例:示例 1,轴左,示例 2,轴右。第一个示例有 ax.view_init(25,-135),第二个示例有 ax.view_init(25,-45)。
我想保持观点相同(查看数据的最佳方式)。有什么方法可以将轴强制向一侧或另一侧吗?
我需要类似的东西:在两侧绘制 z 轴。感谢 @crayzeewulf 的回答,我找到了以下解决方法(左侧、右侧或两侧):
首先根据需要绘制 3d,然后在调用
show()
之前,使用仅覆盖 Axes3D
方法的 Wrapper 类包装 draw()
。
包装类调用只是将某些功能的可见性设置为 False,它会绘制自身并最终使用修改后的平面绘制 z 轴。这个包装类允许您在左侧、右侧或两侧绘制 z 轴。
import matplotlib
matplotlib.use('QT4Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
class MyAxes3D(axes3d.Axes3D):
def __init__(self, baseObject, sides_to_draw):
self.__class__ = type(baseObject.__class__.__name__,
(self.__class__, baseObject.__class__),
{})
self.__dict__ = baseObject.__dict__
self.sides_to_draw = list(sides_to_draw)
self.mouse_init()
def set_some_features_visibility(self, visible):
for t in self.w_zaxis.get_ticklines() + self.w_zaxis.get_ticklabels():
t.set_visible(visible)
self.w_zaxis.line.set_visible(visible)
self.w_zaxis.pane.set_visible(visible)
self.w_zaxis.label.set_visible(visible)
def draw(self, renderer):
# set visibility of some features False
self.set_some_features_visibility(False)
# draw the axes
super(MyAxes3D, self).draw(renderer)
# set visibility of some features True.
# This could be adapted to set your features to desired visibility,
# e.g. storing the previous values and restoring the values
self.set_some_features_visibility(True)
zaxis = self.zaxis
draw_grid_old = zaxis.axes._draw_grid
# disable draw grid
zaxis.axes._draw_grid = False
tmp_planes = zaxis._PLANES
if 'l' in self.sides_to_draw :
# draw zaxis on the left side
zaxis._PLANES = (tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
zaxis.draw(renderer)
if 'r' in self.sides_to_draw :
# draw zaxis on the right side
zaxis._PLANES = (tmp_planes[3], tmp_planes[2],
tmp_planes[1], tmp_planes[0],
tmp_planes[4], tmp_planes[5])
zaxis.draw(renderer)
zaxis._PLANES = tmp_planes
# disable draw grid
zaxis.axes._draw_grid = draw_grid_old
def example_surface(ax):
""" draw an example surface. code borrowed from http://matplotlib.org/examples/mplot3d/surface3d_demo.html """
from matplotlib import cm
import numpy as np
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
if __name__ == '__main__':
fig = plt.figure(figsize=(15, 5))
ax = fig.add_subplot(131, projection='3d')
ax.set_title('z-axis left side')
ax = fig.add_axes(MyAxes3D(ax, 'l'))
example_surface(ax) # draw an example surface
ax = fig.add_subplot(132, projection='3d')
ax.set_title('z-axis both sides')
ax = fig.add_axes(MyAxes3D(ax, 'lr'))
example_surface(ax) # draw an example surface
ax = fig.add_subplot(133, projection='3d')
ax.set_title('z-axis right side')
ax = fig.add_axes(MyAxes3D(ax, 'r'))
example_surface(ax) # draw an example surface
plt.show()
正如OP在下面的评论中指出的,下面建议的方法没有为原始问题提供足够的答案。
正如this注释中提到的,axis3d中有很多硬编码值,这使得自定义其行为变得困难。所以,我认为在当前的 API 中没有一个好的方法可以做到这一点。您可以通过修改
_PLANES
的 zaxis
参数来“破解”它,如下所示:
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
view_1 = (25, -135)
view_2 = (25, -45)
init_view = view_2
ax.view_init(*init_view)
现在,无论您如何旋转图形,z 轴将始终位于图形的左侧(只要 z 轴正方向朝上)。但 x 轴和 y 轴将继续翻转。您可以使用
_PLANES
,并且可能能够获得所有轴所需的行为,但这可能会在 matplotlib
的未来版本中中断。
我写了一个解决方案,因为我对提供的解决方案不满意。它似乎没有在正确的一侧正确显示刻度和刻度标签(要么在两侧显示,要么根本不显示)。
这个“黑客”涉及简单地切换 x 和 y 轴,并反转 y 轴。注意:您必须按 Y、X、Z 顺序绘制曲面,但对于提供的示例而言,这并不重要,因为提供的曲面是对称的(跨 x 和 y)
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def example_surface(ax):
""" draw an example surface. code borrowed from http://matplotlib.org/examples/mplot3d/surface3d_demo.html """
from matplotlib import cm
import numpy as np
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(Y, X, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
if __name__ == '__main__':
f, ax = plt.subplots(1, 1, figsize=(5, 5), subplot_kw={"projection": "3d"})
ax.invert_yaxis()
ax.view_init(elev=15, azim=-130)
example_surface(ax) # draw an example surface
plt.show()
print("here")