【问题标题】:Changing position of vertical (z) axis of 3D plot (Matplotlib)?更改 3D 图(Matplotlib)的垂直(z)轴的位置?
【发布时间】:2013-02-09 03:07:46
【问题描述】:

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

我想保持观点不变(查看数据的最佳方式)。有什么办法可以强制轴到一侧或另一侧?

【问题讨论】:

    标签: python matplotlib mplot3d


    【解决方案1】:

    我需要类似的东西:在两侧绘制 zaxis。感谢@crayzeewulf 的回答,我找到了以下解决方法(左侧、右侧或两侧):

    首先根据需要绘制 3d,然后在调用 show() 之前使用 Wrapper 类包装 Axes3D,该类仅覆盖 draw() 方法。

    Wrapper 类调用只是将某些特征的可见性设置为 False,它会绘制自身并最终使用修改后的 PLANES 绘制 z 轴。这个 Wrapper 类允许您在左侧、右侧或两侧绘制 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()
    

    【讨论】:

    【解决方案2】:

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

    正如this 注意中提到的,axis3d 中有很多硬编码值,这使得自定义其行为变得困难。所以,我认为在当前的 API 中没有很好的方法来做到这一点。您可以通过修改zaxis_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 轴将继续翻转。您可以使用_PLANES,并且可能能够为所有轴获得所需的行为,但这可能会在matplotlib 的未来版本中中断。

    【讨论】:

    • 非常感谢crayzeewulf。这有点奇怪,我能够让它工作,但是,它的行为不像预期的那样。当我在未经您修改的情况下运行脚本时,z 轴位于左侧。当我实施您的修改时,z 轴停留在左侧。我更改了 ax.zaxis._PLANES=(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]) 的顺序并将轴向右移动边(我想要的)。似乎我只是重置了 ax.zaxis._PLANES 的相同值,但它实现了不同的结果。无论如何,再次感谢,这正是我想要的正是,我很感激。
    • 糟糕,我错了。为了改变轴,我使用了 ax.zaxis._PLANES=(tmp[1],tmp[2],tmp[3],tmp[4],tmp[5],tmp[0])。这行得通;但是,我刚刚意识到它也会改变平面上的水平/垂直线。您可以看到here 后轴平面没有水平线,但前轴平面有。是否有任何网站描述 ._PLANES 是如何定义的?我已经尝试将大约 50 种不同的组合放入其中以弄清楚,但我无法得到我想要的结果(右侧的垂直轴和背面的水平线)
    • 跟进我的最后一条评论。我刚刚完成了 tmp_planes 的 [0,1,2,3,4,5] 所有排列的蛮力测试,共有 720 种不同的排列。 没有一个导致正确的数字。我想我可能只是被这个问题困住了。
    • William,很抱歉,没有任何排列可以使 z 轴保持在右侧,而不会破坏情节的其他一些特征。与_PLANES 混在一起确实是一个丑陋的组合。确定轴位置的机制在matplotlib 的名为axis3d.py 的文件中实现。您可以从这段代码中看到它目前不是很灵活,但您可以通过阅读此文件了解更多关于 _PLANES 的使用方式。您也许可以在此文件中修补 Axis 类,使其按您想要的方式工作。但这又是丑陋的。
    • @crayzeewulf:感谢 PLANES 的创意。在了解了它们的工作原理之后。我发布了一个解决方法,它不会弄乱其他功能并避免修补源代码。
    猜你喜欢
    • 1970-01-01
    • 2016-04-21
    • 2023-04-06
    • 2018-11-30
    • 1970-01-01
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多