用对数刻度绘制mplot3d / axes3D xyz曲面图?

时间:2010-10-11 20:42:08

标签: python numpy matplotlib

我一直在寻找解决这个简单问题的高低,但我无法在任何地方找到它!有大量的帖子详细描述了2D中的数据的semilog / loglog绘图,例如plt.setxscale('log')但是我有兴趣在3d图上使用对数刻度(mplot3d)。

我没有准确的代码,因此无法在此处发布,但下面的简单示例应该足以解释这种情况。我目前正在使用Matplotlib 0.99.1,但很快就会更新到1.0.0 - 我知道我将不得不更新我的mplot3d实现代码。

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = Axes3D(fig)
X = np.arange(-5, 5, 0.025)
Y = np.arange(-5, 5, 0.025)
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.jet, extend3d=True)
ax.set_zlim3d(-1.01, 1.01)

ax.w_zaxis.set_major_locator(LinearLocator(10))
ax.w_zaxis.set_major_formatter(FormatStrFormatter('%.03f'))

fig.colorbar(surf)

plt.show()

以上代码将在3D中精细绘制,但三个刻度(X,Y,Z)都是线性的。我的'Y'数据跨越了几个数量级(如9!),因此在对数刻度上绘制它非常有用。我可以通过获取'Y'的日志,重建numpy数组并在线性刻度上绘制日志(Y)来解决这个问题,但是在真正的python风格中我正在寻找更智能的解决方案,它将绘制数据对数表。

是否可以使用对数刻度生成我的XYZ数据的3D表面图,理想情况下我喜欢X&线性刻度上的Z和对数刻度的Y?

非常感谢任何帮助。请原谅上面示例中的任何明显错误,如上所述,我没有确切的代码,因此从我的记忆中改变了matplotlib库示例。

由于

6 个答案:

答案 0 :(得分:11)

由于我遇到了同样的问题而Alejandros的答案没有产生预期的结果,这是我到目前为止所发现的。

3D中Axes的日志缩放是matplotlib中的一个持续问题。目前,您只能通过以下方式重新标记轴:

ax.yaxis.set_scale('log')

但是,这不会导致轴按比例缩放,而是标记为对数。 ax.set_yscale('log')会在3D中导致异常

请参阅github issue 209

因此,您仍然需要重新创建numpy数组

答案 1 :(得分:3)

您所要做的就是定义所需轴的比例。例如,如果您希望x轴和y轴是对数刻度,则应编写:

ax.xaxis.set_scale('log')
ax.yaxis.set_scale('log')

并最终:

ax.zaxis.set_scale('log')

答案 2 :(得分:1)

osx中的

:运行ax.zaxis._set_scale(' log')(注意下划线)

答案 3 :(得分:1)

我从问题 209 中汲取灵感,想出了一个很好且简单的解决方案。您定义一个小型格式化程序函数,您可以在其中设置自己的符号。

import matplotlib.ticker as mticker

# My axis should display 10⁻¹ but you can switch to e-notation 1.00e+01
def log_tick_formatter(val, pos=None):
    return f"$10^{{{int(val)}}}$"  # remove int() if you don't use MaxNLocator
    # return f"{10**val:.2e}"      # e-Notation

ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))

set_major_locator 将指数设置为仅使用整数 10⁻¹、10⁻² 而不使用 10^-1.5 等。Source

Important! 如果您不使用 int() 并且想要显示 10^-1.5,则在 return 语句中删除强制转换 set_major_locator 否则它仍然会打印 10⁻¹ 而不是10^-1.5。

Example: LinearLog

自己试试吧!

from mpl_toolkits.mplot3d import axes3d
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker

fig = plt.figure(figsize=(11,8))
ax1 = fig.add_subplot(121,projection="3d")

# Grab some test data.
X, Y, Z = axes3d.get_test_data(0.05)
# Now Z has a range from 10⁻³ until 10³, so 6 magnitudes
Z = (np.full((120, 120), 10)) ** (Z / 20)
ax1.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
ax1.set(title="Linear z-axis (small values not visible)")


def log_tick_formatter(val, pos=None):
    return f"$10^{{{int(val)}}}$"


ax2 = fig.add_subplot(122,projection="3d")

# You still have to take log10(Z) but thats just one operation
ax2.plot_wireframe(X, Y, np.log10(Z), rstride=10, cstride=10)
ax2.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax2.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
ax2.set(title="Logarithmic z-axis (much better)")
plt.savefig("LinearLog.png", bbox_inches='tight')
plt.show()

答案 4 :(得分:0)

我想要一个符号图,由于我是手工填充数据数组,因此我只是创建了一个自定义函数来计算对数,以避免如果数据<1:bar3d中出现负条。

import math as math

def manual_log(data):
  if data < 10: # Linear scaling up to 1
    return data/10
  else: # Log scale above 1
    return math.log10(data)

由于我没有负值,因此我没有在此函数中实现处理此值,但是更改它并不难。

答案 5 :(得分:-1)

由于问题209,没有解决方案。但是,您可以尝试这样做:

ax.plot_surface(X, np.log10(Y), Z, cmap='jet', linewidth=0.5)

如果在“Y”中有一个0,它将出现警告但仍然有效。由于此警告颜色贴图不起作用,因此请尽量避免使用0和负数。例如:

   Y[Y != 0] = np.log10(Y[Y != 0])
ax.plot_surface(X, Y, Z, cmap='jet', linewidth=0.5)