簡體   English   中英

使用對數刻度繪制 mplot3d/axes3D xyz 曲面圖?

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

我一直在尋找解決這個簡單問題的方法,但在任何地方都找不到! 有很多帖子詳細介紹了 2D 中數據的 semilog/loglog 繪圖,例如 plt.setxscale('log') 但是我對在 3d plot(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 數組並在線性刻度上繪制 log(Y) 來解決此問題,但在真正的 Python 風格中,我正在尋找更智能的解決方案,它將數據繪制在一個對數刻度。

是否可以使用對數刻度生成我的 XYZ 數據的 3D 曲面圖,理想情況下我希望 X 和 Z 在線性刻度上,而 Y 在對數刻度上?

任何幫助將不勝感激。 請原諒上面示例中的任何明顯錯誤,如前所述,我沒有確切的代碼,因此根據我的記憶更改了 matplotlib 庫示例。

謝謝

由於我遇到了同樣的問題,而 Alejandros 的回答沒有產生預期的結果,這是我目前發現的結果。

3D 軸的對數縮放是 matplotlib 中的一個持續問題。 目前,您只能使用以下內容重新標記軸:

ax.yaxis.set_scale('log')

然而,這不會導致軸被縮放為對數,而是標記為對數。 ax.set_yscale('log')將導致 3D 異常

請參閱 github問題 209

因此,您仍然必須重新創建 numpy 數組

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

你所要做的就是定義你想要的軸的比例。 例如,如果您希望 x 和 y 軸在對數刻度上,您應該編寫:

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

並最終:

ax.zaxis.set_scale('log')

我想要一個符號圖,並且由於我手動填充數據數組,因此我只是創建了一個自定義函數來計算日志,以避免在數據 < 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)

由於我沒有負值,所以我沒有在這個函數中實現處理這個值,但是改變它應該不難。

我從第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將指數設置為僅使用整數set_major_locator而不使用 10^-1.5 等。 來源

Important! 如果您不使用set_major_locator並且想要顯示 10^-1.5,則刪除 return 語句中的 cast int()否則它仍將打印 10⁻¹ 而不是 10^-1.5。

Example: 線性對數

自己試試吧!

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

由於問題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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM