我想在Python中使用两个不同的x轴绘制一些数据。为了便于解释,我会说我想绘制光吸收数据,这意味着我绘制吸光度与波长(nm)或能量(eV)的关系。我想绘制一个图,其中底轴表示波长(nm),顶轴表示能量(eV)。这两个不是线性相关的(正如你在下面的MWE中看到的)。
完整的MWE:
import numpy as np
import matplotlib.pyplot as plt
import scipy.constants as constants
# Converting wavelength (nm) to energy (eV)
def WLtoE(wl):
# E = h*c/wl
h = constants.h # Planck constant
c = constants.c # Speed of light
J_eV = constants.e # Joule-electronvolt relationship
wl_nm = wl * 10**(-9) # convert wl from nm to m
E_J = (h*c) / wl_nm # energy in units of J
E_eV = E_J / J_eV # energy in units of eV
return E_eV
x = np.arange(200,2001,5)
x_mod = WLtoE(x)
y = 2*x + 3
fig, ax1 = plt.subplots()
ax2 = ax1.twiny()
ax1.plot(x, y, color='red')
ax2.plot(x_mod, y, color = 'green')
ax1.set_xlabel('Wavelength (nm)', fontsize = 'large', color='red')
ax1.set_ylabel('Absorbance (a.u.)', fontsize = 'large')
ax1.tick_params(axis='x', colors='red')
ax2.set_xlabel('Energy (eV)', fontsize='large', color='green')
ax2.tick_params(axis='x', colors='green')
ax2.spines['top'].set_color('green')
ax2.spines['bottom'].set_color('red')
plt.tight_layout()
plt.show()
字符串
这产生:
x1c 0d1x的数据
这已经接近我想要的了,但是我想解决以下两个问题:
1.其中一个轴需要反转-高波长等于低能量,但图中不是这样。我试过使用x_mod = WLtoE(x)[::-1]
,但这并不能解决这个问题。
1.由于轴不是线性相关的,我希望顶部和底部的轴“匹配”。(或多或少),但实际上1000 nm对应于1.24 eV。(优选地,底部,波长轴)需要被压缩/扩展以匹配顶部处的能量的正确值。我希望红色和绿色曲线重合。
我感谢任何和所有的提示和技巧,帮助我做出一个很好的情节!提前感谢。
编辑DeX 97的答案完美地解决了我的问题,尽管我做了一些小的修改,正如你在下面看到的。我只是在我绘制东西的方式上做了一些改变,定义了像DeX 97一样完美的功能。
编辑的绘图代码
fig, ax1 = plt.subplots()
ax1.plot(WLtoE(x), y)
ax1.set_xlabel('Energy (eV)', fontsize = 'large')
ax1.set_ylabel('Absorbance (a.u.)', fontsize = 'large')
# Create the second x-axis on which the wavelength in nm will be displayed
ax2 = ax1.secondary_xaxis('top', functions=(EtoWL, WLtoE))
ax2.set_xlabel('Wavelength (nm)', fontsize='large')
# Invert the wavelength axis
ax2.invert_xaxis()
# Get ticks from ax1 (energy)
E_ticks = ax1.get_xticks()
E_ticks = preventDivisionByZero(E_ticks)
# Make own array of wavelength ticks, so they are round numbers
# The values are not linearly spaced, but that is the idea.
wl_ticks = np.asarray([200, 250, 300, 350, 400, 500, 600, 750, 1000, 2000])
# Set the ticks for ax2 (wl)
ax2.set_xticks(wl_ticks)
# Make the values on ax2 (wavelength) integer values
ax2.xaxis.set_major_formatter(FormatStrFormatter('%i'))
plt.tight_layout()
plt.show()
型
1条答案
按热度按时间xv8emn3q1#
在你的代码示例中,你绘制了两次相同的数据(尽管使用
E=h*c/wl
进行了转换)。我认为只绘制一次数据就足够了,但是创建了两个x轴:一个以nm为单位显示波长,一个以eV为单位显示相应的能量。考虑下面调整后的代码:
字符串
首先,我定义了
preventDivisionByZero
效用函数。这个函数接受一个数组作为输入,并检查(近似)等于零的值。随后,它将用一个 * 不 * 等于零的小数字(sys.float_info.epsilon
)替换这些值。这个函数将在一些地方使用,以防止被零整除。我稍后会回到为什么这很重要。在这个函数之后,定义了
WLtoE
函数。注意,我在你的函数顶部添加了preventDivisionByZero
函数。此外,我定义了一个EtoWL
函数,它与你的WLtoE
函数相反。然后,您生成虚拟数据并将其绘制在
ax1
上,这是 * 波长 * 的x轴。在设置一些标签后,ax1
被反转(如您在原始帖子中所要求的那样)。现在,我们使用
ax2 = ax1.secondary_xaxis('top', functions=(WLtoE, EtoWL))
为 energy 创建第二个轴。第一个参数表示轴应该放在图的顶部。第二个(关键字)参数是一个包含两个函数的元组:第一个功能是前向变换,而第二个函数是反向变换。更多信息请参见Axes.secondary_axis
。注意,matplotlib会在必要时将值传递给这两个函数。作为这些值可以等于零,处理这些情况很重要。因此,preventDivisionByZero
函数!在创建第二个轴之后,设置标签。现在我们有两个x轴,但是两个轴上的刻度在不同的位置。为了解决这个问题,我们将 wavelength x轴的刻度位置存储在
wl_ticks
中。在使用preventDivisionByZero
函数确保没有零元素后,我们使用WLtoE
函数计算相应的能量值。这些相应的能量值存储在E_ticks
中。现在我们只需设置刻度位置第二个x轴的值等于E_ticks
中的值,使用ax2.set_xticks(E_ticks)
。为了在第二个x轴(能量)上保留两位小数,我们使用
ax2.xaxis.set_major_formatter(FormatStrFormatter('%.2f'))
。当然,您可以自己选择所需的小数位数。上面给出的代码生成以下图形:x1c 0d1x