Seaborn Factorplot 生成实际绘图下方多余的空白图。

17

大家好,

我正在尝试使用子图函数和Seaborn库绘制两个Factorplots。我已经能够使用以下代码单独绘制两个图。然而,seaborn会在实际图像下面生成额外的图像(请参考下面的图片)。是否有一种方法可以避免seaborn生成额外的空白图像?我尝试使用plt.close来清除图像,但不幸的是它只关闭了1个图像。
此外,我正在尝试将图例移动到图像外并在图像旁显示图例。有没有简便的方法可以实现?我尝试了seaborn包中可用的legend_out,但没有起作用。

我的代码:

f,axes=plt.subplots(1,2,figsize=(8,4))

sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=n, size=4, aspect=2,ax=axes[0])

sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=low_pickups, size=4, aspect=2,ax=axes[1])
plt.close(2)
plt.show()

上述代码的输出:

这里输入图片描述

注意: 我是python新手,请在您的代码中提供解释。

Dataframe的Dput

#n dataframe
{'borough': {0: 'Bronx', 1: 'Brooklyn', 2: 'EWR', 3: 'Manhattan', 4: 'Queens', 5: 'Staten Island', 6: 'Unknown'}, 'pickups': {0: 50.66705042597283, 1: 534.4312687082662, 2: 0.02417683628827999, 3: 2387.253281142068, 4: 309.35482385447847, 5: 1.6018880957863229, 6: 2.0571804140650674}}
#low_pickups dataframe
{'borough': {2: 'EWR', 5: 'Staten Island', 6: 'Unknown'}, 'pickups': {2: 0.02417683628827999, 5: 1.6018880957863229, 6: 2.0571804140650674}}
3个回答

36

请注意,最近版本的 Seaborn 中将factorplot称为'catplot'

catplotfactorplot是图级别函数。这意味着它们应该在图的级别上工作,而不是在轴的级别上工作。

您的代码中发生了什么:

f,axes=plt.subplots(1,2,figsize=(8,4))
  • 这将创建“图1”。
sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=n, size=4, aspect=2,ax=axes[0])
  • 这会创建 'Figure 2',但不是在 Figure 2 上绘制,而是告诉 seaborn 在 Figure 1axes [0] 上进行绘制,因此 Figure 2 保持为空。
sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=low_pickups, size=4, aspect=2,ax=axes[1])
  • 现在这又创建了一个图形:图3,同样地,你要告诉seaborn从图1axes[1]上绘制。
plt.close(2)
  • 在这里,您关闭了由seaborn创建的空的Figure 2

所以现在你只剩下Figure 1,它有两个轴,你通过factorplot调用“注入”进去,还有一个仍然是空的Figure 3,它是由第二个factorplot调用创建的,但是从来没有显示过任何内容 :(。

plt.show()
  • 现在你可以看到图1有两个轴,而图3是一个空图。

    这是在终端运行时的情况,在笔记本中,您可能只会看到两张图像一个在另一个下方,看起来像一个有3个轴的图像。


如何解决:

你有两个选项:

1. 快速解决:

plt.show()之前简单地关闭图3

f,axes=plt.subplots(1,2,figsize=(8,4))

sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=n, size=4, aspect=2,ax=axes[0])

sns.factorplot(x="borough", y="pickups", hue="borough", kind='bar', data=low_pickups, size=4, aspect=2,ax=axes[1])
plt.close(2)
plt.close(3)
plt.show()

基本上,您正在使用来自“Figure 1”的“自定义”轴来短路创建图形和绘制轴的factorplot部分。 也许这不是factorplot的设计初衷,但是如果它能工作,那就行了...而且确实如此。

2. 正确的方法:

让图形级别函数完成其工作并创建自己的图形。您需要做的是指定您希望作为列显示的变量。

由于似乎您有两个数据框,nlow_pickups,因此您应该首先创建一个包含名为cat的列的单个数据框,其中cat要么是n,要么是low_pickups

# assuming n and low_pickups are a pandas.DataFrame:
# first add the 'cat' column for both
n['cat'] = 'n'
low_pickups['cat'] = 'low_pickups'
# now create a new dataframe that is a combination of both
comb_df = n.append(low_pickups)

现在,您可以通过单个调用sns.catplot(或在您的情况下是sns.factorplot)并使用变量cat作为列来创建您的图形:

sns.catplot(x="borough", y="pickups", col='cat', hue="borough", kind='bar', sharey=False, data=comb_df, size=4, aspect=1)
plt.legend()
plt.show()

注意: 默认情况下sharey=False是必须的,因为如果它为true,则第二个面板中的值将比第一个面板中的值小得多,基本上看不到。

然后版本2给出: 输入图像描述

您可能仍需要一些样式,但我会把这个留给您;)

希望这有所帮助!


2
看到plt.close(2)让我挑起了眉毛 :) - LoneWanderer
@LoneWanderer 我也是 :). 我认为 OP 的代码来自一个例子,其中有人最初只调用了 factorplot 一次,而 plt.close(2) 只是为了关闭此调用创建的图形。我建议的“快速解决方案”只是将此策略适应于当前情况的一种方法。 - j-i-l
现在如何更改图形的大小? - haneulkim
@lmbloo 在创建 catplot 时可以设置 heightaspect。有关详细信息,请参见 此 SO 回答 - j-i-l

2
我猜这是因为FactorPlot本身使用了subplot。
编辑于2019年3月10日18:43 GMT:从seaborn的categorical.py源代码{{link1}}可以确认:catplot(和factorplot)使用了matplotlib的subplot。@Jojo的答案完美地解释了发生了什么。
def catplot(x=None, y=None, hue=None, data=None, row=None, col=None,
            col_wrap=None, estimator=np.mean, ci=95, n_boot=1000,
            units=None, order=None, hue_order=None, row_order=None,
            col_order=None, kind="strip", height=5, aspect=1,
            orient=None, color=None, palette=None,
            legend=True, legend_out=True, sharex=True, sharey=True,
margin_titles=False, facet_kws=None, **kwargs):
    ... # bunch of code
    g = FacetGrid(**facet_kws) # uses subplots

并且包含FacetGrid定义的axisgrid.py源代码:

class FacetGrid(Grid):
    def __init(...):
        ... # bunch of code
        # Build the subplot keyword dictionary
        subplot_kws = {} if subplot_kws is None else subplot_kws.copy()
        gridspec_kws = {} if gridspec_kws is None else gridspec_kws.copy()
        # bunch of code
        fig, axes = plt.subplots(nrow, ncol, **kwargs)

所以,是的,你在不知不觉中创建了很多子图,并且用`ax = ...`参数搞乱了它们。 @Jojo是正确的。
以下是一些其他选项:
选项1 输入图像描述 选项2 输入图像描述 请注意,factorplot在更高版本的seaborn中已被弃用。
import pandas as pd
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt

print(pd.__version__)
print(sns.__version__)
print(matplotlib.__version__)

# n dataframe
n = pd.DataFrame(
    {'borough': {0: 'Bronx', 1: 'Brooklyn', 2: 'EWR', 3: 'Manhattan', 4: 'Queens', 5: 'Staten Island', 6: 'Unknown'},
     'kind': {0: 'n', 1: 'n', 2: 'n', 3: 'n', 4: 'n', 5: 'n', 6: 'n'},
     'pickups': {0: 50.66705042597283, 1: 534.4312687082662, 2: 0.02417683628827999, 3: 2387.253281142068,
                 4: 309.35482385447847, 5: 1.6018880957863229, 6: 2.0571804140650674}})
# low_pickups dataframe
low_pickups = pd.DataFrame({'borough': {2: 'EWR', 5: 'Staten Island', 6: 'Unknown'},
                            'kind': {0: 'low_pickups', 1: 'low_pickups', 2: 'low_pickups', 3: 'low_pickups',
                                     4: 'low_pickups', 5: 'low_pickups', 6: 'low_pickups'},
                            'pickups': {2: 0.02417683628827999, 5: 1.6018880957863229, 6: 2.0571804140650674}})

new_df = n.append(low_pickups).dropna()

print(n)
print('--------------')
print(low_pickups)
print('--------------')
print(new_df)

g = sns.FacetGrid(data=new_df, col="kind", hue='kind', sharey=False)
g.map(sns.barplot, "borough", "pickups", order=sorted(new_df['borough'].unique()))
plt.show()

控制台输出:

0.24.1
0.9.0
3.0.2
         borough kind      pickups
0          Bronx    n    50.667050
1       Brooklyn    n   534.431269
2            EWR    n     0.024177
3      Manhattan    n  2387.253281
4         Queens    n   309.354824
5  Staten Island    n     1.601888
6        Unknown    n     2.057180
--------------
         borough         kind   pickups
0            NaN  low_pickups       NaN
1            NaN  low_pickups       NaN
2            EWR  low_pickups  0.024177
3            NaN  low_pickups       NaN
4            NaN  low_pickups       NaN
5  Staten Island  low_pickups  1.601888
6        Unknown  low_pickups  2.057180
--------------
         borough         kind      pickups
0          Bronx            n    50.667050
1       Brooklyn            n   534.431269
2            EWR            n     0.024177
3      Manhattan            n  2387.253281
4         Queens            n   309.354824
5  Staten Island            n     1.601888
6        Unknown            n     2.057180
2            EWR  low_pickups     0.024177
5  Staten Island  low_pickups     1.601888
6        Unknown  low_pickups     2.057180

或者尝试这个:
g = sns.barplot(data=new_df, x="kind", y="pickups", hue='borough')#, order=sorted(new_df['borough'].unique()))
g.set_yscale('log')

我必须使用对数刻度,因为数据值在一个巨大的范围内分布得非常广泛。您可以考虑使用分类(请参见pandas的cut函数)。
编辑于2019年3月10日18:43 GMT:正如@Jojo在他的答案中所述,最后一个选项确实是:
sns.catplot(data=new_df, x="borough", y="pickups", col='kind', hue='borough', sharey=False, kind='bar')

没有时间完成研究,所以所有的功劳都归他!

0

我来到这里是因为我遇到了与sns.catplot绘图下方空白的问题完全相同。@jojo的答案非常好,详细地解释了一切。作为一个新手,我无法改进它,除了一个点。快速解决方案,即关闭sns.catplot()创建的图形,最简单的实现方法是在sns.catplot()之后直接调用plt.close(plt.gcf())。这样,您不必考虑要关闭的图形的数字索引。

至少对我来说,以下内容可以正常工作,并创建了我所需的14个不同的catplots,排列得很好,5x3。没有空白图,也不在其末尾。

cols = [ "Year Built", "Year Remod/Add", "Bsmt Full Bath", "Bsmt Half Bath",
    "Full Bath", "Half Bath", "Bedroom AbvGr", "Kitchen AbvGr", "TotRms AbvGrd",
    "Fireplaces", "Garage Yr Blt", "Garage Cars", "Mo Sold", "Yr Sold" ] # len 14
fig, axs = plt.subplots(5, 3, figsize = (18, 24))
for i, ax in enumerate(axs.flatten()):
    if i < len(cols):
        sns.catplot(x = cols[i], y = "SalePrice", data = df, ax = ax, kind = "boxen")
        plt.close(plt.gcf()) # close current figure just created by catplot
    else:
        ax.set_axis_off() # hide empty subplot at end of grid

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