简体   繁体   English

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

[英]Plotting mplot3d / axes3D xyz surface plot with log scale?

I've been looking high and low for a solution to this simple problem but I can't find it anywhere!我一直在寻找解决这个简单问题的方法,但在任何地方都找不到! There are a loads of posts detailing semilog / loglog plotting of data in 2D eg plt.setxscale('log') however I'm interested in using log scales on a 3d plot(mplot3d).有很多帖子详细介绍了 2D 中数据的 semilog/loglog 绘图,例如 plt.setxscale('log') 但是我对在 3d plot(mplot3d) 上使用对数刻度很感兴趣。

I don't have the exact code to hand and so can't post it here, however the simple example below should be enough to explain the situation.我手头没有确切的代码,所以不能在这里发布,但是下面的简单示例应该足以解释这种情况。 I'm currently using Matplotlib 0.99.1 but should shortly be updating to 1.0.0 - I know I'll have to update my code for the mplot3d implementation.我目前正在使用 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()

The above code will plot fine in 3D, however the three scales (X, Y, Z) are all linear.上面的代码可以在 3D 中很好地绘制,但是三个尺度(X、Y、Z)都是线性的。 My 'Y' data spans several orders of magnitude (like 9!), so it would be very useful to plot it on a log scale.我的“Y”数据跨越了几个数量级(比如 9!),所以在对数刻度上绘制它会非常有用。 I can work around this by taking the log of the 'Y', recreating the numpy array and plotting the log(Y) on a linear scale, but in true python style I'm looking for smarter solution which will plot the data on a log scale.我可以通过获取“Y”的日志、重新创建 numpy 数组并在线性刻度上绘制 log(Y) 来解决此问题,但在真正的 Python 风格中,我正在寻找更智能的解决方案,它将数据绘制在一个对数刻度。

Is it possible to produce a 3D surface plot of my XYZ data using log scales, ideally I'd like X & Z on linear scales and Y on a log scale?是否可以使用对数刻度生成我的 XYZ 数据的 3D 曲面图,理想情况下我希望 X 和 Z 在线性刻度上,而 Y 在对数刻度上?

Any help would be greatly appreciated.任何帮助将不胜感激。 Please forgive any obvious mistakes in the above example, as mentioned I don't have my exact code to have and so have altered a matplotlib gallery example from my memory.请原谅上面示例中的任何明显错误,如前所述,我没有确切的代码,因此根据我的记忆更改了 matplotlib 库示例。

Thanks谢谢

Since I encountered the same question and Alejandros answer did not produced the desired Results here is what I found out so far.由于我遇到了同样的问题,而 Alejandros 的回答没有产生预期的结果,这是我目前发现的结果。

The log scaling for Axes in 3D is an ongoing issue in matplotlib. 3D 轴的对数缩放是 matplotlib 中的一个持续问题。 Currently you can only relabel the axes with:目前,您只能使用以下内容重新标记轴:

ax.yaxis.set_scale('log')

This will however not cause the axes to be scaled logarithmic but labeled logarithmic.然而,这不会导致轴被缩放为对数,而是标记为对数。 ax.set_yscale('log') will cause an exception in 3D ax.set_yscale('log')将导致 3D 异常

See on github issue 209请参阅 github问题 209

Therefore you still have to recreate the numpy array因此,您仍然必须重新创建 numpy 数组

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

All you have to do is to define the scale of the axis you want.你所要做的就是定义你想要的轴的比例。 For instance, if you want that x and y axis are on log scale, you should write:例如,如果您希望 x 和 y 轴在对数刻度上,您应该编写:

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

and eventually:并最终:

ax.zaxis.set_scale('log')

I wanted a symlog plot and, since I fill the data array by hand, I just made a custom function to calculate the log to avoid having negative bars in the bar3d if the data is < 1:我想要一个符号图,并且由于我手动填充数据数组,因此我只是创建了一个自定义函数来计算日志,以避免在数据 < 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)

Since I have no negative values, I did not implement handling this values in this function, but it should not be hard to change it.由于我没有负值,所以我没有在这个函数中实现处理这个值,但是改变它应该不难。

I came up with a nice and easy solution taking inspiration from Issue 209 .我从第209期中汲取灵感,想出了一个简单易用的解决方案。 You define a small formatter function in which you set your own notation.您定义一个小型格式化程序函数,您可以在其中设置自己的符号。

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 sets the exponential to only use integers 10⁻¹, 10⁻² without 10^-1.5 etc. Source set_major_locator将指数设置为仅使用整数set_major_locator而不使用 10^-1.5 等。 来源

Important! remove the cast int() in the return statement if you don't use set_major_locator and you want to display 10^-1.5 otherwise it will still print 10⁻¹ instead of 10^-1.5.如果您不使用set_major_locator并且想要显示 10^-1.5,则删除 return 语句中的 cast int()否则它仍将打印 10⁻¹ 而不是 10^-1.5。

Example: 线性对数

Try it yourself!自己试试吧!

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

There is no solution because of the issue 209. However, you can try doing this:由于问题209,没有解决办法。但是,您可以尝试这样做:

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

If in "Y" there is a 0, it is going to appear a warning but still works.如果在“Y”中有一个 0,它会出现警告但仍然有效。 Because of this warning color maps don´t work, so try to avoid 0 and negative numbers.由于此警告色图不起作用,因此请尽量避免使用 0 和负数。 For example:例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM