matplotlib 如何在一个图中对齐两个x轴的值

cigdeys3  于 2023-11-22  发布在  其他
关注(0)|答案(2)|浏览(202)

我尝试在matplotlib中使用twiny()来绘制一条带有两个x轴的曲线,该曲线来自一个由以下数据块组成的XML文件:

  1. <data>
  2. <meas>
  3. <utc>2018-11-10T22:27:06.500003</utc>
  4. <ra_j2000>23.9722686269</ra_j2000>
  5. <dec_j2000>-1.23845121893</dec_j2000>
  6. <mag>9.96074403533</mag>
  7. </meas>
  8. <meas>
  9. <utc>2018-11-10T22:27:54.500002</utc>
  10. <ra_j2000>23.9930913364</ra_j2000>
  11. <dec_j2000>-1.03788334773</dec_j2000>
  12. <mag>11.356437889</mag>
  13. </meas>
  14. <meas>
  15. <utc>2018-11-10T22:38:36.500002</utc>
  16. <ra_j2000>0.267638646848</ra_j2000>
  17. <dec_j2000>1.56055091433</dec_j2000>
  18. <mag>11.1642458641</mag>
  19. </meas>
  20. <meas>
  21. <utc>2018-11-10T22:46:18.500000</utc>
  22. <ra_j2000>0.462353662364</ra_j2000>
  23. <dec_j2000>3.34334963425</dec_j2000>
  24. <mag>11.1082534741</mag>
  25. </meas>
  26. <meas>
  27. <utc>2018-11-10T22:57:18.500001</utc>
  28. <ra_j2000>0.740393528722</ra_j2000>
  29. <dec_j2000>5.78641590694</dec_j2000>
  30. <mag>11.0688955214</mag>
  31. </meas>
  32. <meas>
  33. <utc>2018-11-10T23:03:06.499995</utc>
  34. <ra_j2000>0.888541738338</ra_j2000>
  35. <dec_j2000>7.03265231497</dec_j2000>
  36. <mag>10.2358937709</mag>
  37. </meas>
  38. <meas>
  39. <utc>2018-11-10T23:05:42.500002</utc>
  40. <ra_j2000>0.955591973177</ra_j2000>
  41. <dec_j2000>7.5832430461</dec_j2000>
  42. <mag>10.86206725</mag>
  43. </meas>
  44. <meas>
  45. <utc>2018-11-10T23:06:48.499999</utc>
  46. <ra_j2000>0.984093767077</ra_j2000>
  47. <dec_j2000>7.81466175077</dec_j2000>
  48. <mag>10.3466108708</mag>
  49. </meas>
  50. </data>

字符串
我的问题是我在这些x轴上得到了不对齐的值。下面是我的Python脚本:

  1. import math
  2. import xml.etree.ElementTree as ET
  3. from astropy.time import Time
  4. from astropy.coordinates import get_sun
  5. from datetime import datetime, timedelta
  6. import matplotlib.pyplot as plt
  7. import matplotlib.ticker as ticker
  8. from matplotlib import dates
  9. tree = ET.parse('20181110_10241.xml')
  10. root = tree.getroot()
  11. x_ut = []
  12. x_phi = []
  13. y_brightness = []
  14. def convert_time(obs_time):
  15. obs_time = str(obs_time)
  16. d, t = obs_time.split('T')
  17. year, month, day = map(int, d.split('-'))
  18. hour, minute, second = t.split(':')
  19. return datetime(year, month, day, int(hour), int(minute)) + \
  20. timedelta(seconds=float(second))
  21. def get_sun_coords(obs_time):
  22. sun_coords = get_sun(obs_time)
  23. sun_ra = sun_coords.ra.degree
  24. sun_dec = sun_coords.dec.degree
  25. return sun_ra, sun_dec
  26. def get_phase_angle(sun_ra, sun_dec, target_ra, target_dec):
  27. phase_angle = math.degrees(math.acos(-math.sin(math.radians(sun_dec))*math.sin(math.radians(target_dec)) - math.cos(math.radians(sun_dec))*math.cos(math.radians(target_dec))*math.cos(math.radians(sun_ra-target_ra))))
  28. return phase_angle
  29. for meas in root.findall('meas'):
  30. obs_time = Time(meas.find('utc').text, format='isot', scale='utc')
  31. target_ra = float(meas.find('ra_j2000').text)*15
  32. target_dec = float(meas.find('dec_j2000').text)
  33. mag = float(meas.find('mag').text)
  34. sun_ra, sun_dec = get_sun_coords(obs_time)
  35. phase_angle = get_phase_angle(sun_ra, sun_dec, target_ra, target_dec)
  36. obs_time = convert_time(obs_time)
  37. x_ut.append(obs_time)
  38. x_phi.append(phase_angle)
  39. y_brightness.append(mag)
  40. fig, ax1 = plt.subplots()
  41. ax1.plot(x_ut, y_brightness, marker='o', label='apparent brightness')
  42. ax1.set_xlim(x_ut[0],x_ut[-1])
  43. ax1.xaxis.set_major_locator(dates.MinuteLocator(interval=1))
  44. ax1.xaxis.set_major_formatter(dates.DateFormatter('%H:%M'))
  45. ax1.tick_params(axis='x', rotation=45)
  46. ax1.minorticks_on()
  47. ax1.legend()
  48. ax1.grid()
  49. ax1.set_xlabel('time [h:m, UT]')
  50. ax1.set_ylabel('apparent brightness [mag, CR]')
  51. ax2 = ax1.twiny()
  52. ax2.plot(x_phi,y_brightness, marker='^', color='red')
  53. ax2.set_xlim(x_phi[0],x_phi[-1])
  54. ax2.xaxis.set_major_locator(ticker.MultipleLocator(1))
  55. ax2.minorticks_on()
  56. ax2.set_xlabel('phase angle (phi) [deg]')
  57. plt.gca().invert_yaxis()
  58. plt.tight_layout(pad=0)
  59. plt.show()


这将产生以下图:
x1c 0d1x的数据
我打算稍后隐藏红色曲线(通过使用visibility=False),在这里我绘制它只是为了查看x轴值的正确对齐,也就是说,两条曲线必须(!)重叠,因为相位角(x_phi)值取决于相应的时间戳(x_ut)值,但正如您可以清楚地看到的那样,只有开头和结尾正确对齐,但中间的大部分数据都没有对齐(相位曲线向右移动)。
我做错了什么?
最初,我假设相位角(x_phi)随时间非线性变化,因此两条曲线的set_xlim()以不同的方式拉伸它们,但这不是真的,我绘制了x_phix_ut,并且有明显的线性变化:



提前感谢您的任何帮助!

**编辑:**非线性已经被tel在下面的回答中证明了。因此,我稍微改变了我的问题。

如果我从两个子图ax1ax2中删除set_xlim(),那么:
1)上面的x轴自动反转,从最小值开始,尽管给出值的列表x_phi从最大值开始--我如何避免这种反转而不使用invert_axis()?(在不同的情况下,我在x_phi列表中总是只有增加或减少的值)
2)总共有3个列表:x_utx_phiy_brightness;我只需要实际绘制y_brightnessx_ut的曲线,另外还需要将x_phi(带有ticker.MultipleLocator(1))的值与x_ut中相应的时刻值对齐--我该怎么做呢?
我的问题类似于这个:How do I align gridlines for two y-axis scales using Matplotlib?,但是在我的例子中,x轴上部的刻度之间没有线性间距,所以我不能使用该解决方案。
另外,这个问题也涉及到一个类似的问题:trouble aligning ticks for matplotlib twinx axes但是我不知道如何表达两个x轴之间的关系,因为数据类型非常不同:datetime和float。它们之间的唯一关系是一对一的,也就是说,来自x_ut的第一个值与来自x_phi的第一个值相关,第二个值与第二个值相关,等等;这种关系是非线性的。

**编辑2:**我之前编辑中的数字1)现在已经解决了。对于剩下的问题,看起来我必须使用register_scale()来重新缩放次x轴相对于主x轴的比例。要做到这一点,我还必须定义matplotlib.scale.ScaleBase的子类。到目前为止,我只找到了两个复杂的(对我来说)示例:

https://matplotlib.org/examples/api/custom_scale_example.html
https://stackoverrun.com/es/q/8578801(西班牙语,但代码中有英文注解)
我不确定我是否能够自己实现这一点,所以我仍然寻求任何帮助。

wh6knrhe

wh6knrhe1#

好耶!我已经成功地得到了所寻求的结果,而无需定义新的scale类!以下是从问题中添加/修改的脚本中的相关代码部分(变量step稍后将从用户命令行输入中读取,或者我可能会找到另一种自动设置滴答频率的方法):

  1. x_ut = []
  2. x_phi = []
  3. x_phi_ticks = []
  4. x_phi_ticklabels = []
  5. y_brightness = []
  6. # populate lists for the phase angle ticks and labels
  7. i = 0
  8. step = 15
  9. while i <= (len(x_ut)-step):
  10. x_phi_ticks.append(x_ut[i])
  11. x_phi_ticklabels.append(x_phi[i])
  12. i += step
  13. x_phi_ticks.append(x_ut[-1])
  14. x_phi_ticklabels.append(x_phi[-1])
  15. # plot'em all
  16. fig, ax1 = plt.subplots()
  17. ax1.plot(x_ut, y_brightness, marker='o', label='apparent brightness')
  18. ax1.xaxis.set_major_locator(dates.MinuteLocator(interval=1))
  19. ax1.xaxis.set_major_formatter(dates.DateFormatter('%H:%M'))
  20. ax1.tick_params(axis='x', rotation=45)
  21. ax1.minorticks_on()
  22. ax1.legend()
  23. ax1.grid(which='major', linestyle='-', color='#000000')
  24. ax1.grid(which='minor', linestyle='--')
  25. ax1.set_xlabel('time [h:m, UT]')
  26. ax1.set_ylabel('apparent brightness [mag, CR]')
  27. ax2 = ax1.twiny()
  28. ax2.set_xlim(ax1.get_xlim())
  29. ax2.set_xticks(x_phi_ticks)
  30. ax2.set_xticklabels(x_phi_ticklabels)
  31. ax2.set_xlabel('phase angle (phi) [deg]')
  32. plt.gca().invert_yaxis()
  33. plt.tight_layout(pad=0)
  34. plt.show()

字符串
x1c 0d1x的数据

展开查看全部
ghhkc1vu

ghhkc1vu2#

您的绘图程序看起来是正确的。相反,看起来问题在于时间和相位角之间的关系存在 *(非常小)偏离线性。您可以通过在时间与Angular 图上覆盖一条黑色直线来查看(单击下面以在单独的窗口中打开并放大以清楚地看到它):


的数据
这里有一个放大,突出显示的偏差:



让两个x轴对齐的唯一方法是:

  • 操纵数据。
  • 操纵第二个x轴的刻度使其不一致,使图上49和48度之间的实际距离与图上45和44度之间的实际距离不同,依此类推。

这两种方法都是一个坏主意™,你不应该做任何一个。你需要按原样绘制数据,而不是试图用聪明的技巧来隐藏任何不一致的地方。否则会被认为是学术上的不诚实。️
你唯一的选择是找出时间和Angular 不像你期望的那样匹配的原因。这可能是你的代码中的问题,也许是你在get_phase_angle中使用的公式中的问题。或者它可能是你的底层数据集中的问题,例如,如果度测量中有一些噪音。或者它可能只是,在现实中,时间和Angular 并不具有线性关系。毕竟,地球不是完美的圆。

展开查看全部

相关问题