Python 更改三维绘图(Matplotlib)垂直(z)轴的位置?

Python 更改三维绘图(Matplotlib)垂直(z)轴的位置?,python,matplotlib,mplot3d,Python,Matplotlib,Mplot3d,我正在用Python中的Matplotlib进行一些3D曲面绘制,并注意到一个恼人的现象。根据我设置视点(相机位置)的方式,垂直(z)轴在左侧和右侧之间移动。这里有两个例子:。第一个示例具有ax.view_init(25,-135),而第二个示例具有ax.view_init(25,-45) 我希望保持视点不变(查看数据的最佳方式)。有没有办法将轴推到一边或另一边?正如OP在下面的评论中指出的那样,下面建议的方法没有为原始问题提供足够的答案。 如注释中所述,中有许多硬编码的值,这使得自定义其行为变

我正在用Python中的Matplotlib进行一些3D曲面绘制,并注意到一个恼人的现象。根据我设置视点(相机位置)的方式,垂直(z)轴在左侧和右侧之间移动。这里有两个例子:。第一个示例具有ax.view_init(25,-135),而第二个示例具有ax.view_init(25,-45)


我希望保持视点不变(查看数据的最佳方式)。有没有办法将轴推到一边或另一边?

正如OP在下面的评论中指出的那样,下面建议的方法没有为原始问题提供足够的答案。

如注释中所述,中有许多硬编码的值,这使得自定义其行为变得困难。因此,我认为在当前的API中没有一个好的方法来实现这一点。您可以通过修改
zaxis
\u PLANES
参数来“破解”它,如下所示:

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轴将继续翻转。您可以使用
\u平面
,并且可能能够获得所有轴的所需行为,但这可能会在未来版本的
matplotlib
中中断

正如OP在下面的评论中指出的那样,下面建议的方法没有为原始问题提供足够的答案。

如注释中所述,中有许多硬编码的值,这使得自定义其行为变得困难。因此,我认为在当前的API中没有一个好的方法来实现这一点。您可以通过修改
zaxis
\u PLANES
参数来“破解”它,如下所示:

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轴将继续翻转。您可以使用
\u平面
,并且可能能够获得所有轴的所需行为,但这可能会在未来版本的
matplotlib
中中断

我需要类似的东西:在两边画扎克斯。多亏了@crayzeewulf的回答,我找到了以下解决方法(适用于左侧、右侧或两侧):

首先根据需要绘制3d,然后在调用
show()
之前,使用一个简单覆盖
draw()
方法的包装类包装
Axes3D

包装器类调用只是将某些特征的可见性设置为False,它会绘制自身,最后绘制带有修改平面的zaxis。此包装器类允许您在左侧、右侧或两侧绘制zaxis

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()

我需要类似的东西:在两边画扎克斯。多亏了@crayzeewulf的回答,我找到了以下解决方法(适用于左侧、右侧或两侧):

首先根据需要绘制3d,然后在调用
show()
之前,使用一个简单覆盖
draw()
方法的包装类包装
Axes3D

包装器类调用只是将某些特征的可见性设置为False,它会绘制自身,最后绘制带有修改平面的zaxis。此包装器类允许您在左侧、右侧或两侧绘制zaxis

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()

非常感谢crayzeewulf。这有点奇怪,我能够让它工作,但是,它的行为并不像预期的那样。当我在没有修改的情况下运行脚本时,z轴位于左侧。当我实施您的修改时,z轴保持在左侧。我更改了ax.zaxis的顺序。_平面=(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]),它将轴移动到右侧(我想要的)。看起来我只是重置了ax.zaxis.的相同值,但结果不同。无论如何,再次感谢,这正是我想要的,我很感激。呜呜,我错了。为了改变轴,我使用了ax.zaxis._平面=(tmp[1],tmp[2],tmp[3],tmp[4],tmp[5],tmp[0])。这起作用了;但是,我刚刚意识到它也会移动平面上的水平/垂直线。您可以看到,后轴平面没有水平线,但前轴平面有水平线。是否有网站描述了飞机是如何定义的?我试着将大约50种不同的组合放入其中来解决这个问题,但在我最后的评论之后,我还没有得到我想要的结果(右边是垂直轴,后面是水平线)。我刚刚完成了一个蛮力测试,测试了tmp_飞机[0,1,2,3,4,5]的所有排列,总共720种不同的排列。没有一个结果是正确的数字。我想我可能只是被这个问题困住了。威廉,很抱歉,没有一个排列能够在不破坏情节其他特征的情况下保持z轴在右侧。与
\u飞机
混在一起真是一件难看的事。用于确定轴位置的机制在
matplotlib
的名为
axis3d.py
的文件中实现。从这段代码中可以看出,它目前不是很灵活,但您可以通过阅读此文件了解更多有关
\u PLANES
使用方式的信息。您可能可以在此文件中对
Axis
类进行猴子补丁,以使其按您想要的方式工作。“但这又是丑陋的。”克雷齐乌夫:多亏了飞机的创意。在了解了他们的工作原理之后。我已经发布了一个解决方案,它不会破坏其他功能,也不会修补源代码。非常感谢crayzeewulf。这有点奇怪,我能够让它工作,但是,它的行为并不像预期的那样。当我在没有修改的情况下运行脚本时,z轴位于左侧。当我实施您的修改时,z轴保持在左侧。我更改了ax.zaxis的顺序。_平面=(tmp[0]、tmp[1]、tmp[2]、tmp[3]、tmp[4]、tmp[5]),它将轴移动到