[英]Problem with scaling two different y-axis on matplotlib
我想 plot 一個 x 軸和兩個 y 軸( eV
和nm
)上的數據集。 兩個 y 軸通過以下等式連接在一起: nm = 1239.8/eV
。
從我的圖片 output 可以看出,這些值不在正確的位置。 例如,在eV = 0.5
我需要有nm = 2479.6
,在eV = 2.9
, nm = 423
,等等......
我怎樣才能解決這個問題?
我的data.txt
:
number eV nm
1 2.573 481.9
2 2.925 423.9
3 3.174 390.7
4 3.242 382.4
5 3.387 366.1
我正在使用的代碼:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.ticker as tck
# data handling
file = "data.txt"
df = pd.read_csv(file, delimiter=" ") # generate a DataFrame with data
no = df[df.columns[0]]
eV = df[df.columns[1]].round(2) # first y-axis
nm = df[df.columns[2]].round(1) # second y-axis
# generate a subplot 1x1
fig, ax1 = plt.subplots(1,1)
# first Axes object, main plot (lollipop plot)
ax1.stem(no, eV, markerfmt=' ', basefmt=" ", linefmt='blue', label="Gas")
ax1.set_ylim(0.5,4)
ax1.yaxis.set_minor_locator(tck.MultipleLocator(0.5))
ax1.set_xlabel('Aggregation', labelpad=12)
ax1.set_ylabel('Transition energy [eV]', labelpad=12)
# adding second y-axis
ax2 = ax1.twinx()
ax2.set_ylim(2680,350) # set the corresponding ymax and ymin,
# but the values are not correct anyway
ax2.set_yticklabels(nm)
ax2.set_ylabel('Wavelength [nm]', labelpad=12)
# save
plt.tight_layout(pad=1.5)
plt.show()
生成的 plot 如下。 我只是想通過將第一個軸除以 1239.8 來獲得第二個軸,我不知道還要尋找什么!
您可以使用ax.secondary_yaxis
,如本示例中所述。 有關您的問題的實現,請參見下面的代碼。 我只包含了與第二個 y 軸相關的代碼部分。
# adding second y-axis
def eV_to_nm(eV):
return 1239.8 / eV
def nm_to_eV(nm):
return 1239.8 / nm
ax2 = ax1.secondary_yaxis('right', functions=(eV_to_nm, nm_to_eV))
ax2.set_yticks(nm)
ax2.set_ylabel('Wavelength [nm]', labelpad=12)
請注意,我也使用set_yticks
而不是set_yticklabels
。 此外,如果您刪除set_yticks
,matplotlib 將自動確定 y 刻度位置,假設 y 刻度呈線性分布。 然而,因為nm
與eV
成反比,這將導致(很可能)不希望的 y 刻度分布。 您可以使用set_yticks
中的一組不同值手動更改這些值。
我想出了如何解決這個問題( 這里的提示來源)。
因此,對於任何需要一個數據集具有一個 x 軸但有兩個 y 軸(一個在數學上與另一個相關)的人,報告了一個可行的解決方案。 基本上,問題在於與主 y 軸具有相同的刻度,但根據它們的數學關系(即,在這種情況下, nm = 1239.8/eV
)按比例更改它們。 以下代碼已經過測試並且可以正常工作。
如果您有兩個 x 軸和 1 個共享 y 軸等,這種方法當然有效。
重要提示:您必須定義一個 y 范圍(或 x 范圍,如果您想要相反的結果),否則您可能會遇到一些縮放問題。
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.ticker as tck
from matplotlib.text import Text
# data
file = "data.txt"
df = pd.read_csv(file, delimiter=" ") # generate a DataFrame with data
no = df[df.columns[0]]
eV = df[df.columns[1]].round(2) # first y-axis
nm = df[df.columns[2]].round(1) # second y-axis
# generate a subplot 1x1
fig, ax1 = plt.subplots(1,1)
# first Axes object, main plot (lollipop plot)
ax1.stem(no, eV, markerfmt=' ', basefmt=" ", linefmt='blue', label="Gas")
ax1.set_ylim(0.5,4)
ax1.yaxis.set_minor_locator(tck.MultipleLocator(0.5))
ax1.set_xlabel('Aggregation', labelpad=12)
ax1.set_ylabel('Transition energy [eV]', labelpad=12)
# function that correlates the two y-axes
def eV_to_nm(eV):
return 1239.8 / eV
# adding a second y-axis
ax2 = ax1.twinx() # share x axis
ax2.set_ylim(ax1.get_ylim()) # set the same range over y
ax2.set_yticks(ax1.get_yticks()) # put the same ticks as ax1
ax2.set_ylabel('Wavelength [nm]', labelpad=12)
# change the labels of the second axis by apply the mathematical
# function that relates the two axis to each tick of the first
# axis, and then convert it to text
# This way you have the same axis as y1 but with the same ticks scaled
ax2.set_yticklabels([Text(0, yval, f'{eV_to_nm(yval):.1f}')
for yval in ax1.get_yticks()])
# show the plot
plt.tight_layout(pad=1.5)
plt.show()
data.txt
同上:
number eV nm
1 2.573 481.9
2 2.925 423.9
3 3.174 390.7
4 3.242 382.4
5 3.387 366.1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.