matplotlib 使用Seaborn在一个图中绘制多个不同的图

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

我正试图使用seaborn

重现《统计学习入门》一书中的以下情节
我特别想使用seaborn的lmplot创建前两个图,boxplot创建第二个图。主要问题是lmplot根据to this answer创建FacetGrid,这迫使我为箱线图添加另一个matplotlib Axes。我想知道是否有更简单的方法来实现这一点。下面,我不得不做相当多的手动操作,以获得所需的情节。

seaborn_grid = sns.lmplot('value', 'wage', col='variable', hue='education', data=df_melt, sharex=False)
seaborn_grid.fig.set_figwidth(8)

left, bottom, width, height = seaborn_grid.fig.axes[0]._position.bounds
left2, bottom2, width2, height2 = seaborn_grid.fig.axes[1]._position.bounds
left_diff = left2 - left
seaborn_grid.fig.add_axes((left2 + left_diff, bottom, width, height))

sns.boxplot('education', 'wage', data=df_wage, ax = seaborn_grid.fig.axes[2])
ax2 = seaborn_grid.fig.axes[2]
ax2.set_yticklabels([])
ax2.set_xticklabels(ax2.get_xmajorticklabels(), rotation=30)
ax2.set_ylabel('')
ax2.set_xlabel('');

leg = seaborn_grid.fig.legends[0]
leg.set_bbox_to_anchor([0, .1, 1.5,1])

字符串
得到

DataFrames的示例数据:

df_melt = {
    'education': ['1. < HS Grad', '4. College Grad', '3. Some College', '4. College Grad', '2. HS Grad'],
    'value': [18, 24, 45, 43, 50],
    'variable': ['age', 'age', 'age', 'age', 'age'],
    'wage': [75.0431540173515, 70.47601964694451, 130.982177377461, 154.68529299563, 75.0431540173515]}

df_wage = {
    'education': ['1. < HS Grad', '4. College Grad', '3. Some College', '4. College Grad', '2. HS Grad'],
    'wage': [75.0431540173515, 70.47601964694451, 130.982177377461, 154.68529299563, 75.0431540173515]}

332nm8kg

332nm8kg1#

一种可能性是不使用lmplot(),而是直接使用regplot()regplot()在轴上绘图,作为ax=的参数传递。
您将失去根据特定变量自动分割数据集的能力,但如果您事先知道要生成的图,这应该不是问题。
大概是这样的:

import matplotlib.pyplot as plt
import seaborn as sns

fig, axs = plt.subplots(ncols=3)
sns.regplot(x='value', y='wage', data=df_melt, ax=axs[0])
sns.regplot(x='value', y='wage', data=df_melt, ax=axs[1])
sns.boxplot(x='education',y='wage', data=df_melt, ax=axs[2])

字符串

jyztefdp

jyztefdp2#

在seaborn 0.13.0版本中(这个问题发布7年后),在不影响底层图形位置的情况下向seaborn图形级对象添加子图仍然很困难。事实上,OP中显示的方法可能是最可读的方法。
话虽如此,正如Diziet Asahi所建议的那样,如果您想放弃海运FacetGrid,(例如lmplotcatplot等),并使用seaborn Axes级别的方法创建等效图形(例如,regplot而不是lmplotscatterplot + lineplot而不是relplot等)并向图中添加更多子图,如boxplot,你可以将你的数据按照你要在lmplot中用作cols kwarg的列进行分组(groupby子帧按照你要用作hue kwarg的列进行分组),并使用子帧中的数据绘制图。
作为一个例子,使用OP中的数据,我们可以如下所示,它创建了一个与lmplot * 有点等效 * 的图,但在右侧添加了箱线图:

# groupby data since `cols='variable'`
groupby_object = df_melt.groupby('variable')
# count number of groups to determine the required number of subplots
number_of_columns = groupby_object.ngroups

fig, axs = plt.subplots(1, number_of_columns+1, sharey=True)
for i, (_, g) in enumerate(groupby_object):
    # feed data from each sub-dataframe `g` to regplot
    sns.regplot(data=g, x='value', y='wage', ax=axs[i])
# plot the boxplot in the end
sns.boxplot(data=df_wage, x='education', y='wage', hue='education', ax=axs[-1])

字符串
OP中的示例使用hue= kwarg绘制'education'的不同拟合线。为此,我们可以再次通过'education'列分组子框架,并在相同的轴上绘制多个教育regplots。工作示例如下:

groupby_object = df_melt.groupby('variable')
number_of_columns = groupby_object.ngroups
fig, axs = plt.subplots(1, number_of_columns+1, figsize=(12, 5), sharey=True)
for i, (_, g) in enumerate(groupby_object):
    for label, g1 in g.groupby('education'):
        label = label if i == 0 else None
        sns.regplot(data=g1, x='value', y='wage', label=label, scatter_kws={'alpha': 0.7}, ax=axs[i])
sns.boxplot(data=df_wage, x='education', y='wage', hue='education', ax=axs[-1])
axs[-1].set(ylabel='', xlabel='')
axs[-1].tick_params(axis='x', labelrotation=30)
for ax, title in zip(axs, ['Age', 'Year', 'Education']):
    ax.set_title(title)
_ = fig.legend(bbox_to_anchor=(0.92, 0.5), loc="center left")


使用下面的示例数据集(我必须创建一个新的数据集,因为OP的示例不够丰富,无法制作适当的图形):

import numpy as np
import pandas as pd
rng = np.random.default_rng(0)
edu = rng.choice(['1. < HS Grad', '4. College Grad', '3. Some College', '4. College Grad','2. HS Grad'], size=100)
wage = rng.normal(75, 25, 100)
df_melt = pd.DataFrame({'education': edu, 'value': rng.normal(30, 20, 100), 'variable': rng.choice(['age', 'year'], 100), 'wage': wage})
df_wage = pd.DataFrame({'education': edu, 'wage': wage})


上面的代码绘制了下图:


的数据

相关问题