如何在 seaborn 中为多个子图使用辅助 Y 轴

How to use secondary Y-axis for multiple subplots in seaborn

我有两个包含多列的数据框。我已经使用 pd.wide_to_long

将 DataFrame 变成了长格式
df1 = pd.wide_to_long(df_1.reset_index(), ['dvr','r'], i='index', j='Sample ID').reset_index()
df2 = pd.wide_to_long(df_2.reset_index(), ['dvlnr','r'], i='index', j='Sample ID').reset_index()

我想创建子图,并且由于其优雅的格式设置功能而倾向于使用 seaborn。 这是我生成带有辅助 Y 轴的子图的代码。

ax= sns.relplot(data=df1, x='r', y='dvlnr', col='Sample ID',  col_wrap=2, kind="line", height=4, aspect=1.5)
ax1 = plt.twinx()
ax1= sns.relplot(data=df2, x='r', y='dvr', ax=ax, col='Sample ID',  col_wrap=2, kind="line", height=4, aspect=1.5) 

但我总是得到 ValueError: Could not interpret value 'dvr' for parameter 'y'

我已经检查了几个例子 , 但我能够找到一个解决方案,因为我正在寻找具有辅助轴的多个子图。 有什么办法可以解决这个问题吗?提前致谢!

一个问题是您似乎混合了 df1df2。您不应该只将 y='dvlnrdf2 一起使用(反之亦然)吗?

另一个问题是 sns.relplot 没有 return 和 ax。相反,它 return 是 FacetGrid。对于 FacetGrid,您不能调用 twinx()。您需要通过 g = sns.relplot(data=df1, ...) 捕获 FacetGrid 并循环遍历轴,在每个轴上调用 twinx 并创建单独的散点图。如果数据范围足够相似,您可以合并数据帧并使用 hue 一次性绘制它们。

下面是一些示例代码,适用于数据范围差异太大而无法在同一 y 轴上绘制的情况:

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

df1 = pd.DataFrame({'r': np.random.randint(1, 31, 200),
                    'dvr': np.random.uniform(100, 1000, 200),
                    'Sample ID': np.random.randint(1, 5, 200)})
df2 = pd.DataFrame({'r': np.random.randint(1, 31, 300),
                    'dvlnr': np.random.uniform(1, 10, 300),
                    'Sample ID': np.random.randint(1, 5, 300)})

g = sns.relplot(data=df1, x='r', y='dvr', col='Sample ID', col_wrap=2, kind="line", height=4, aspect=1.5,
                color='dodgerblue')

for sample_id, ax in g.axes_dict.items():  # axes_dict is new in seaborn 0.11.2
    ax1 = ax.twinx()
    sns.lineplot(data=df2[df2['Sample ID'] == sample_id], x='r', y='dvlnr', color='crimson', ax=ax1)

plt.tight_layout()
plt.show()

如果 y 数据的范围足够相似,您可以合并数据框并使用 hue= 一次性绘制所有内容。 (这种方法也适用于更多数据帧。)

  • 需要添加一个新列(例如'source')以指示原始数据帧
  • 两个数据框中的相应列需要相同的名称

下面是一些示例代码:

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

df1 = pd.DataFrame({'r1': np.random.randint(1, 31, 200),
                    'dvr1': np.random.uniform(100, 1000, 200),
                    'Sample ID': np.random.randint(1, 5, 200)})
df2 = pd.DataFrame({'r1': np.random.randint(1, 31, 300),
                    'dvlnr1': np.random.uniform(300, 1200, 300),
                    'Sample ID': np.random.randint(1, 5, 300)})

# add an extra column to tell to which df the data belongs
df1['source'] = 'dvr'
# the corresponding columns in both df need to have the same name for the merge
df2 = df2.rename(columns={'dvlnr1': 'dvr1'})
df2['source'] = 'dvrnr'
df_merged = pd.concat([df1, df2]).reset_index()

g = sns.relplot(data=df_merged, x='r1', y='dvr1', hue='source', col='Sample ID', col_wrap=2,
                kind="line", height=4, aspect=1.5, palette='turbo')
plt.subplots_adjust(bottom=0.06, left=0.06)  # plt.tight_layout() doesn't work due to legend
plt.show()