在 matplotlib 上缩放两个不同的 y 轴的问题
Problem with scaling two different y-axis on matplotlib
我想在一个 x 轴和两个 y 轴(eV
和 nm
)上绘制数据集。两个 y 轴与等式链接在一起:nm = 1239.8/eV
.
正如您从我的图片输出中看到的那样,值的位置不正确。例如,在 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()
结果图如下。我只想用第一个轴除以 1239.8 得到第二个轴,我不知道还要找什么!
您可以使用 ax.secondary_yaxis
,如 this example. 中所述,请参阅下面的代码来解决您的问题。我只包含了与第二个 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
.
中的一组不同的值手动更改这些
我想出了解决这个问题的方法 (source of the hint here)。
因此,对于任何需要具有 一个 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
Output image here
我想在一个 x 轴和两个 y 轴(eV
和 nm
)上绘制数据集。两个 y 轴与等式链接在一起:nm = 1239.8/eV
.
正如您从我的图片输出中看到的那样,值的位置不正确。例如,在 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()
结果图如下。我只想用第一个轴除以 1239.8 得到第二个轴,我不知道还要找什么!
您可以使用 ax.secondary_yaxis
,如 this example. 中所述,请参阅下面的代码来解决您的问题。我只包含了与第二个 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
.
我想出了解决这个问题的方法 (source of the hint here)。
因此,对于任何需要具有 一个 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
Output image here