使用不对称自定义误差条按组制作seaborn.barplot

4

我有一个Pandas数据框,其中有几个类别分组列如下。

gr1 grp2 variables  lb     m       ub
 A   A1      V1     1.00   1.50    2.5           
 A   A2      V2     1.50   2.50    3.5         
 B   A1      V1     3.50   14.50   30.5           
 B   A2      V2     0.25   0.75    1.0

我正在尝试使用FacetGridvariables中的每个变量获取单独的子条形图。我正在尝试构建我需要的最终图,它看起来像下面这样。

Plot with FacetGrid and Multiple Categorical Variables

这是我目前为止的成果。

g = sns.FacetGrid(df, col="variables", hue="grp1")
g.map(sns.barplot, 'grp2', 'm', order=times)

但不幸的是,这会将我所有的数据点堆叠在一起。

我应该如何使用Seaborn来解决这个问题?

更新:以下代码基本上实现了我想要的功能,但目前没有显示yerr

g = sns.factorplot(x="Grp2", y="m", hue="Grp1", col="variables", data=df, kind="bar", size=4, aspect=.7, sharey=False)

我该如何将lbub作为误差线加入到factorplot中呢?

1个回答

9
在开始之前,我需要提醒一下,matplotlib要求错误相对于数据而非绝对边界。因此,我们需要修改数据框以考虑这一点,通过减去相应的列来实现。
u = u"""grp1 grp2 variables  lb     m       ub
A   A1      V1     1.00   1.50    2.5           
A   A2      V2     1.50   2.50    3.5         
B   A1      V1     7.50   14.50   20.5           
B   A2      V2     0.25   0.75    1.0
A   A2      V1     1.00   6.50    8.5           
A   A1      V2     1.50   3.50    6.5         
B   A2      V1     3.50   4.50   15.5           
B   A1      V2     8.25   12.75  13.9"""

import io
import pandas as pd

df = pd.read_csv(io.StringIO(u), delim_whitespace=True)
# errors must be relative to data (not absolute bounds)
df["lb"] = df["m"]-df["lb"]
df["ub"] = df["ub"]-df["m"]

现在有两种解决方案,本质上是相同的。让我们从不使用 seaborn 的解决方案开始(原因稍后会变得清晰)。

不使用 Seaborn

Pandas 可以通过使用每列属于或构成一个组的数据框来绘制分组条形图。需要执行以下步骤:

  1. 根据不同的 variables 创建多个子图。
  2. 通过 variables 对数据框进行分组。
  3. 对于每个组,创建一个透视数据框,其中包含 grp1 的值作为列,m 作为值。对于两个误差列也做同样处理。
  4. 应用 How add asymmetric errorbars to Pandas grouped barplot? 中的解决方案。

代码如下:

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

df = pd.read_csv(io.StringIO(u), delim_whitespace=True)
# errors must be relative to data (not absolute bounds)
df["lb"] = df["m"]-df["lb"]
df["ub"] = df["ub"]-df["m"]

def func(x,y,h,lb,ub, **kwargs):
    data = kwargs.pop("data")
    # from https://dev59.com/sFsV5IYBdhLWcg3wpQJ8#37139647
    errLo = data.pivot(index=x, columns=h, values=lb)
    errHi = data.pivot(index=x, columns=h, values=ub)
    err = []
    for col in errLo:
        err.append([errLo[col].values, errHi[col].values])
    err = np.abs(err)
    p = data.pivot(index=x, columns=h, values=y)
    p.plot(kind='bar',yerr=err,ax=plt.gca(), **kwargs)

fig, axes = plt.subplots(ncols=len(df.variables.unique()))
for ax, (name, group) in zip(axes,df.groupby("variables")):
    plt.sca(ax)
    func("grp2", "m", "grp1", "lb", "ub", data=group, color=["limegreen", "indigo"])
    plt.title(name)

plt.show()

enter image description here

使用Seaborn

Seaborn的factorplot不允许自定义误差条。因此,需要使用FaceGrid方法。为了避免条形图叠加,需要将hue参数放在map调用中。因此,以下代码等效于问题中的sns.factorplot调用。

g = sns.FacetGrid(data=df, col="variables", size=4, aspect=.7 ) 
g.map(sns.barplot, "grp2", "m", "grp1", order=["A1","A2"] )

现在的问题是,我们无法从外部获取误差条,更重要的是,我们无法为分组条形图提供误差给seaborn.barplot。对于非分组条形图,可以通过yerr参数提供误差,该参数传递给matplotlib的plt.bar绘图函数。这个概念在this question中展示。然而,由于seaborn.barplot多次调用plt.bar,每次调用都会有相同的误差(或它们的维度不匹配)。
因此,我唯一看到的选择是使用FacetGrid,并将与上面使用的完全相同的函数映射到它上面。这种方法使得使用seaborn变得过时,但为了完整起见,这里是FacetGrid的解决方案。
import io
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

df = pd.read_csv(io.StringIO(u), delim_whitespace=True)
# errors must be relative to data (not absolute bounds)
df["lb"] = df["m"]-df["lb"]
df["ub"] = df["ub"]-df["m"]

def func(x,y,h,lb,ub, **kwargs):
    data = kwargs.pop("data")
    # from https://dev59.com/sFsV5IYBdhLWcg3wpQJ8#37139647
    errLo = data.pivot(index=x, columns=h, values=lb)
    errHi = data.pivot(index=x, columns=h, values=ub)
    err = []
    for col in errLo:
        err.append([errLo[col].values, errHi[col].values])
    err = np.abs(err)
    p = data.pivot(index=x, columns=h, values=y)
    p.plot(kind='bar',yerr=err,ax=plt.gca(), **kwargs)

g = sns.FacetGrid(df, col="variables", size=4, aspect=.7,  ) 
g.map_dataframe(func, "grp2", "m", "grp1", "lb", "ub" , color=["limegreen", "indigo"]) 
g.add_legend()

plt.show()

enter image description here


非常感谢您详细的回复。我已经根据我的确切用例稍微修改了代码,现在它完美地工作了。 - Black

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