双Y轴与匹配网格线

5
我想制作一个matplotlib折线图,其中显示了两个变量,分别在两个独立的y轴上,并且具有匹配的网格线。这很简单,除了匹配的网格线部分。以下是一些可重现的代码片段,包括我的最佳尝试。完整的代码片段可以在最后找到。

使用初始图形创建可重现的数据框

按照在matplotlib中为次要y轴添加y轴标签的建议,我能够制作出这个图形:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


# Dataframe with some random numbers
np.random.seed(123)
rows = 4
df = pd.DataFrame(np.random.randint(90,110,size=(rows, 2)), columns=list('AB'))
datelist = pd.date_range(pd.datetime(2017, 1, 1).strftime('%Y-%m-%d'), periods=rows).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df['B'] = df['A'] * np.random.uniform(0.6,1.4, size = 4)

# Plot 1
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')
ax2.yaxis.grid(which="major", color='green', linestyle='--')

ax.legend(loc='upper left')
ax2.legend(loc='upper right')
plt.show()

情节 1

enter image description here

正如您所看到的,matplotlib为两个变量选择了合理的间隔,但是y轴的网格线不太匹配。根据Align secondary y-axis ticks with stripplot on primary x-axis的建议,我能够对齐它们,但这自然会遗漏一些值: 图表2
# Plot 2
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')
#--
ax2.set_ylim(ax.get_ylim())

ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')
plt.show()

enter image description here

我的下一个尝试是使用ax2int = ax2.get_ylim()[1] - ax2.get_ylim()[0]检索次要y轴的间隔,并将其除以主轴上网格线的数量以使它们匹配。然后遵循在matplotlib中更改x或y轴上的“刻度频率”建议,使用np.arange(start, stop, steps)。但是我无法完全弄清楚如何完美地匹配间隔,如下所示:
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')

# The math
firstStep = ax.get_yticks()[1] - ax.get_yticks()[0]
ax2int = ax2.get_ylim()[1] - ax2.get_ylim()[0]
axSteps = len(ax.get_yticks()) 
newIntervals = ax2int / axSteps

# My best attempt
myticks = np.arange(min(df['B']), max(df['B']), newIntervals)
ax2.set(yticks=myticks)
ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')

plt.show()

情节 3

enter image description here

任何建议都是很好的!也许在matplotlib中已经有一些内置功能来完成这些事情?谢谢!

这是整个内容,方便复制粘贴:

# Libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


# Dataframe with some random numbers
np.random.seed(123)
rows = 4
df = pd.DataFrame(np.random.randint(90,110,size=(rows, 2)), columns=list('AB'))
datelist = pd.date_range(pd.datetime(2017, 1, 1).strftime('%Y-%m-%d'), periods=rows).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df['B'] = df['A'] * np.random.uniform(0.6,1.4, size = 4)

# Plot 1
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')
ax2.yaxis.grid(which="major", color='green', linestyle='--')

ax.legend(loc='upper left')
ax2.legend(loc='upper right')
plt.show()
#%%

# Plot 2
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')
#--
ax2.set_ylim(ax.get_ylim())

ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')
plt.show()
#%%

# Plot 3 
fig, ax = plt.subplots()
t = df.index
ax.plot(t, df['A'])
ax2 = ax.twinx()
ax2.plot(t, df['B'], color='red')

# The math
firstStep = ax.get_yticks()[1] - ax.get_yticks()[0]
ax2int = ax2.get_ylim()[1] - ax2.get_ylim()[0]
axSteps = len(ax.get_yticks()) 
newIntervals = ax2int / axSteps

# My best attempt
myticks = np.arange(min(df['B']), max(df['B']), newIntervals)
ax2.set(yticks=myticks)
ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')

plt.show()

1
这个问题的第二个答案(由Scott Howard提供,特别是最后的编辑)可能会有所帮助? - DavidG
谢谢你的提示!这有点尴尬,因为我的问题结果是多余的。我确信我已经查看了所有相关的帖子。 - vestland
1个回答

2

手动对齐第二个轴的yticks很棘手,因为(a)并非所有的yticks都会显示(例如执行print(ax.get_yticks())并将其与您的图形进行比较),以及(b)因为set_yticks()也会影响ylims。用下面的代码替换你的#The math#My best attempt部分对我有用:

# The math
ylim1 = ax.get_ylim()
len1 = ylim1[1]-ylim1[0]
yticks1 = ax.get_yticks()
rel_dist = [(y-ylim1[0])/len1 for y in yticks1]
ylim2 = ax2.get_ylim()
len2 = ylim2[1]-ylim2[0]
yticks2 = [ry*len2+ylim2[0] for ry in rel_dist]

#My best attempt
ax2.set_yticks(yticks2)
ax2.set_ylim(ylim2)  #<-- this line is needed to re-adjust the limits to the original values
ax.yaxis.grid(which="major", color='black', linestyle='-')
ax2.yaxis.grid(which="major", color='green', linestyle='--')
ax.legend(loc='upper left')
ax2.legend(loc='upper right')

并且生成的图表看起来是这样的:

enter image description here

希望这能有所帮助。


太棒了!谢谢! - vestland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接