python mplot3D中的2D图未与3D轴壁齐平 [英] 2D plots are not sitting flush against 3D-axis walls in python mplot3D

查看:84
本文介绍了python mplot3D中的2D图未与3D轴壁齐平的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将2D数据绘制到3D轴上. 我可以使用ax.plot_surface运行3D形状,但是无法使用ax.plot使2D数据与轴壁齐平放置.

I'm attempting to plot 2D data onto a 3D axis. I have the 3D shape working using ax.plot_surface but I'm unable to get the 2D data to sit flush against the axis walls using ax.plot.

这是一个简化的示例代码,显示了我在2D数据上遇到的问题:

Here is a stripped down example code showing the issue I'm having with the 2D data:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Generate Example Data
x = [0.04,0,-0.04]
y = [0.04,0,-0.04]
z = [0.04,0,-0.04]

# Start plotting environment
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# Plot 3 lines positioned against the axes "walls"
ax.plot(x,y,-0.08,zdir='z',c='r')
ax.plot(x,z, 0.08,zdir='y',c='g')
ax.plot(y,z,-0.08,zdir='x',c='b')

# Label each axis
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

# Set each axis limits
ax.set_xlim([-0.08,0.08])
ax.set_ylim([-0.08,0.08]) 
ax.set_zlim([-0.08,0.08]) 

# Equally stretch all axes
ax.set_aspect("equal")

# Set plot size for saving to disk
plt.gcf().set_size_inches(11.7,8.3) 

# Save figure in .eps and .png format
plt.savefig('test.eps', format='eps')
plt.savefig('test.png', format='png', dpi=300)

# Display figure
plt.show()

这给出以下结果,从中您可能可以看到数据线的末端不在轴线上(即,不与0.04和-0.04对齐):

This gives the following result from which you might be able to see that the ends of the data lines don't sit on the axis lines (ie. don't line up with 0.04 and -0.04):

通过交互式浏览图,我确定将ax.plot调用中的0.08更改为0.083(同时保持相关符号)可以使图更加平坦.

By interactively exploring the plot I determined that changing the 0.08's in the ax.plot calls, to a magnitude of 0.083 (while keeping the relevant signs) allows the plots to fit much flatter to the wall.

我对此的解释是,该图并没有强制执行我的轴限制,通过查看轴相交的间距,该图在图上很明显,但是使用ax.get_xlim()等显示了我设置的值,所以我我错过了一些东西.

My interpretation of this is that the plot isn't enforcing my axis limits which appears evident on the plot by looking at the spacings where the axes meet, but using ax.get_xlim() etc, shows the values that I set so I'm missing something.

关于如何让这些地块与墙壁齐平放置的任何想法?

Any ideas on how I can get these plots to sit flush against the walls?

非常感谢,

蒂姆

我也尝试使用设置轴限制

I've also attempted to set the axis limits using

ax.set_xlim3d(-0.08,0.08)
ax.set_ylim3d(-0.08,0.08) 
ax.set_zlim3d(-0.08,0.08) 

 ax.set_xlim3d([-0.08,0.08])
 ax.set_ylim3d([-0.08,0.08]) 
 ax.set_zlim3d([-0.08,0.08])

没有任何运气.

我绝对倾向于将其归因于轴相交处的填充问题,但是我找不到有关此的任何文档. IE.我将绘图位置设置为-0.08,将轴限制设置为-0.08,但是该绘图在末尾添加了一些填充,以使限制在-0.082和-0.083之间.

I'm definitely leaning towards attributing this to a padding issue where the axes meet but I'm unable to find any documentation regarding this. ie. I'm setting the plot position to -0.08 and the axis limit to -0.08 but the plot is adding a little padding at the end to make the limits somewhere between -0.082 and -0.083.

我想删除填充或获取填充值,以便可以将其输入到ax.plot命令中.

I either want to remove the padding or get the padding value so that I can input it into ax.plot command.

遇到此问题但尚未解决的其他人 在mplot3d中更改网格墙的位置图

Somebody else who has encountered this issue but hasn't had the issue resolved Changing the position of the grid walls in an mplot3d figure

推荐答案

您是正确的,mplot3d模块包含一个函数,该函数在渲染轴之前将填充添加到轴的最小值和最大值.

You are correct, the mplot3d module contains a function that adds padding to your axis minimum and maximum values before it renders the axis.

不幸的是,填充量是硬编码的,当前在matplotlib(v2.0)的最新可用版本中用户无法更改.

Unfortunately, the amount of padding is hard coded in and is not currently user-changeable in the latest available version of matplotlib (v2.0).

解决方案1:修改源代码

我发现可以通过注释 axis3d.py 源代码文件中的两行源代码来禁用额外填充. (在matplotlib源目录中,可在mpl_toolkits> mplot3d> axis3d.py下找到)

I've found it is possible to disable the extra-padding by commenting out two lines of source code in the axis3d.py source code file. (In the matplotlib source directory this is found under mpl_toolkits > mplot3d > axis3d.py)

在函数_get_coord_info()中,该函数首先使用吸气剂函数get_w_lims()来检索已设置的x,y和z限制.它不会直接修改它们,因此为什么当您检查ax.get_xlim()时,它仍然返回0.08和-0.08的值.

In the function _get_coord_info(), the function first uses the getter function get_w_lims() to retrieve the x, y, and z limits that you've set. It doesn't modify them directly, hence why when you check ax.get_xlim() for example, it still returns the values of 0.08 and -0.08.

def _get_coord_info(self, renderer):
    minx, maxx, miny, maxy, minz, maxz = self.axes.get_w_lims()
    if minx > maxx:
        minx, maxx = maxx, minx
    if miny > maxy:
        miny, maxy = maxy, miny
    if minz > maxz:
        minz, maxz = maxz, minz
    mins = np.array((minx, miny, minz))
    maxs = np.array((maxx, maxy, maxz))
    centers = (maxs + mins) / 2.
    deltas = (maxs - mins) / 12.
    mins = mins - deltas / 4.
    maxs = maxs + deltas / 4.

    vals = mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2]
    tc = self.axes.tunit_cube(vals, renderer.M)
    avgz = [tc[p1][2] + tc[p2][2] + tc[p3][2] + tc[p4][2] for \
            p1, p2, p3, p4 in self._PLANES]
    highs = np.array([avgz[2*i] < avgz[2*i+1] for i in range(3)])

    return mins, maxs, centers, deltas, tc, highs

请注意,它以某种任意方式计算填充.因此,填充不是固定数字,而是 取决于您设置的轴限制.

Note it calculates the padding in a somewhat arbitrary way. So the padding is not a fixed number, but is dependent on the axis limits you have set.

    deltas = (maxs - mins) / 12.
    mins = mins - deltas / 4.
    maxs = maxs + deltas / 4.

当调用draw()函数渲染轴时,它使用这些修改后的minsmaxs来构造您看到的实际线,因此为什么总是在轴的每一端都获得填充.

When the draw() function is called to render the axis, it uses these modified mins and maxs to construct the actual line that you see, hence why you always get the padding at each end of the axis.

我的hacky解决方案是像这样注释掉两行:

My hacky solution is to just comment out the two lines like so:

    #mins = mins - deltas / 4.
    #maxs = maxs + deltas / 4.

为您提供线条与3D轴壁齐平的图形.

Giving you the figure with the lines flush against the 3D axis walls.

但是请注意下角的轴标签如何相互重叠,并且y标签似乎未对齐...我怀疑这就是为什么硬编码填充是一个功能. 可以对y-具有旋转属性的轴刻度标签set_yticklabels(...)方法中,直到看起来完全适合您的需求.

But note how the axis labels in the bottom corner overlap each other, and the y labels seem to be mis-aligned...I suspect this is why the hard-coded padding is a feature. It may possible to tweak the y-axis tick labels with the rotation property in the set_yticklabels(...) method until it looks right for your needs.

解决方案2:在交互式绘图窗口中使用缩放

另一种不需要修改源代码的解决方案是将图形绘制在一个交互式窗口中,然后稍微放大,直到线条与墙齐平为止.这是一种通过眼睛"的方法,因此需要反复试验.请注意,这通常会删除最高刻度标记标签,但可以避免上述解决方案中出现重叠的问题.

Another (sort-of) solution that doesn't require modifying the source code is to plot the figure in an interactive window, then zoom in slightly until the lines appear flush with the wall. It requires a bit of trial and error as it's a 'by-eye' method. Note that this usually removes the highest tick mark labels, but it avoids the overlapping issue in the above solution.:

解决方案3:结合以上所有内容

因此,既然我们知道mplot3d如何计算填充量,是否可以使用它来将轴限制设置为恰好合适的数量以避免填充问题,而不必使用交互模式或修改源代码?

So given that we know how mplot3d calculates the padding amounts, can we use this to set the axis limits to just the right amount to avoid the padding issues and without having to use the interactive mode or modify the source code?

是的,还有一些额外的功能:

Yes, with a little extra function:

def get_fixed_mins_maxs(mins, maxs):
    deltas = (maxs - mins) / 12.
    mins = mins + deltas / 4.
    maxs = maxs - deltas / 4.

    return [mins, maxs]

minmax = get_fixed_mins_maxs(-0.08, 0.08)

# gives us: [-0.07666666666666667, 0.07666666666666667]

# Set each axis limits with the minmax value from our function
ax.set_xlim(minmax)
ax.set_ylim(minmax) 
ax.set_zlim(minmax) 

从解决方案2中获得相同的图形,而不必打开交互式绘图窗口并通过肉眼对其进行判断.

Which gives the same figure from Solution 2, without having to open the interactive plot window and judge it by eye.

这篇关于python mplot3D中的2D图未与3D轴壁齐平的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆