如何消除 3D 投影图中的偏移?

发布于 2025-01-11 01:32:11 字数 1295 浏览 2 评论 0原文

我意识到我用 matplotlib 制作的 3D 绘图有轻微的“错位”。这是 MWE:

import numpy as np
from matplotlib import pyplot as plt
figure = plt.figure(figsize=(8,10.7))
ax = plt.gca(projection='3d')
ax.plot_surface(np.array([[0, 0], [30, 30]]),
                np.array([[10, 10], [10, 10]]),
                np.array([[10, 20], [10, 20]]), 
                rstride=1, cstride=1
)
ax.plot_surface(np.array([[0, 0], [30, 30]]),
                np.array([[20, 20], [20, 20]]),
                np.array([[10, 20], [10, 20]]), 
                rstride=1, cstride=1
)
plt.show()
plt.close()

显然,箱没有正确居中,因为表面似乎从 10.5 开始并以 20.5 结束,而不是急剧的 10 和 20。如何才能实现后者呢?

输入图片此处描述

编辑:恐怕建议的答案有问题。 x 轴没有黑色实线,默认情况下就是这样:

在此处输入图像描述

当我取出建议的包装时,我得到:

在此处输入图像描述

不幸的是,当我拿出我的东西时在绘图时,这个问题在 Jupyter 笔记本中无法重现,但尽管如此,我想知道您是否能够向我指出我必须做什么,以便在我的情况下,x 轴有一个黑色又线了?

I realized a slight "misalignment" of a plot I'm making in 3D with matplotlib. Here is an MWE:

import numpy as np
from matplotlib import pyplot as plt
figure = plt.figure(figsize=(8,10.7))
ax = plt.gca(projection='3d')
ax.plot_surface(np.array([[0, 0], [30, 30]]),
                np.array([[10, 10], [10, 10]]),
                np.array([[10, 20], [10, 20]]), 
                rstride=1, cstride=1
)
ax.plot_surface(np.array([[0, 0], [30, 30]]),
                np.array([[20, 20], [20, 20]]),
                np.array([[10, 20], [10, 20]]), 
                rstride=1, cstride=1
)
plt.show()
plt.close()

Clearly, the bins are not correctly centered, as the surfaces seem to start at 10.5 and end at 20.5 instead of 10 and 20 sharply. How could one achieve the latter?

enter image description here

EDIT: I'm afraid that there is an issue with the suggested answer. The x-axis does not have a solid black line, as is the case by default:

enter image description here

When I take out the suggested wrapping, I get:

enter image description here

Unfortunately, when I take out the stuff that I'm plotting, this issue is not reproducible in a Jupyter notebook, but nevertheless, I was wondering about whether you might be able to point out to me what I'd have to do so that in my case, the x-axis has a black line again?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岁月染过的梦 2025-01-18 01:32:12

这是由 matplotlib 对 3D Axis 坐标的处理引起的。它故意改变 minsmaxs 来创建一些人工填充:

<代码> axis3d.py#L190-L194

类轴(maxis.XAxis):
   ...
   def _get_coord_info(自身,渲染器):
       ...
       # 在最小/最大点和绘图边缘之间添加一个小的偏移量
       增量 =(最大值 - 分钟)/ 12
       分钟 -= 0.25 * 增量
       最大值 += 0.25 * 增量
       ...
       返回最小值、最大值、中心、增量、bounds_proj、最高值

从 v3.5.1 开始,没有参数可以控制此行为。

但是,我们可以使用 functools.wraps< /a> 创建一个围绕 Axis._get_coord_info 的包装器,用于取消移动 minsmaxs。为了防止此包装器多次恢复原位(例如,重新运行其 Jupyter 单元时),请通过 跟踪包装器状态 >_unpadded 属性:

from functools import wraps

def unpad(f): # where f will be Axis._get_coord_info
    @wraps(f)
    def wrapper(*args, **kwargs):
        mins, maxs, centers, deltas, bounds_proj, highs = f(*args, **kwargs)
        mins += 0.25 * deltas # undo original subtraction
        maxs -= 0.25 * deltas # undo original addition
        return mins, maxs, centers, deltas, bounds_proj, highs

    if getattr(f, '_unpadded', False): # bypass if already unpadded
        return f
    else:
        wrapper._unpadded = True # mark as unpadded
        return wrapper

在绘图之前应用 unpad 包装器:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axis3d import Axis

X = np.array([[0, 0], [30, 30]])
Z = np.array([[10, 20], [10, 20]])
y1, y2, y3 = 10, 16, 20

# wrap Axis._get_coord_info with our unpadded version
Axis._get_coord_info = unpad(Axis._get_coord_info)

fig, ax = plt.subplots(figsize=(8, 10.7), subplot_kw={'projection': '3d'})
ax.plot_surface(X, np.tile(y1, (2, 2)), Z, rstride=1, cstride=1)
ax.plot_surface(X, np.tile(y2, (2, 2)), Z, rstride=1, cstride=1)
ax.plot_surface(X, np.tile(y3, (2, 2)), Z, rstride=1, cstride=1)

plt.show()

未填充的 3d 图

This is caused by matplotlib's processing of 3D Axis coordinates. It deliberately shifts mins and maxs to create some artificial padding:

axis3d.py#L190-L194

class Axis(maxis.XAxis):
   ...
   def _get_coord_info(self, renderer):
       ...
       # Add a small offset between min/max point and the edge of the plot
       deltas = (maxs - mins) / 12
       mins -= 0.25 * deltas
       maxs += 0.25 * deltas
       ...
       return mins, maxs, centers, deltas, bounds_proj, highs

As of v3.5.1, there is no parameter to control this behavior.

However, we can use functools.wraps to create a wrapper around Axis._get_coord_info that unshifts mins and maxs. To prevent this wrapper from unshifting multiple times (e.g., when rerunning its Jupyter cell), track the wrapper state via an _unpadded attribute:

from functools import wraps

def unpad(f): # where f will be Axis._get_coord_info
    @wraps(f)
    def wrapper(*args, **kwargs):
        mins, maxs, centers, deltas, bounds_proj, highs = f(*args, **kwargs)
        mins += 0.25 * deltas # undo original subtraction
        maxs -= 0.25 * deltas # undo original addition
        return mins, maxs, centers, deltas, bounds_proj, highs

    if getattr(f, '_unpadded', False): # bypass if already unpadded
        return f
    else:
        wrapper._unpadded = True # mark as unpadded
        return wrapper

Apply the unpad wrapper before plotting:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axis3d import Axis

X = np.array([[0, 0], [30, 30]])
Z = np.array([[10, 20], [10, 20]])
y1, y2, y3 = 10, 16, 20

# wrap Axis._get_coord_info with our unpadded version
Axis._get_coord_info = unpad(Axis._get_coord_info)

fig, ax = plt.subplots(figsize=(8, 10.7), subplot_kw={'projection': '3d'})
ax.plot_surface(X, np.tile(y1, (2, 2)), Z, rstride=1, cstride=1)
ax.plot_surface(X, np.tile(y2, (2, 2)), Z, rstride=1, cstride=1)
ax.plot_surface(X, np.tile(y3, (2, 2)), Z, rstride=1, cstride=1)

plt.show()

unpadded 3d plot

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文