Seaborn:使用不对称的自定义错误条按组制作条形图

Seaborn: Making barplot by group with asymmetrical custom error bars

我有一个 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 中的每个变量获取单独的子条形图。我正在尝试构建我需要的最终图,如下所示。

这是我目前所拥有的。

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 作为误差线合并到因子图上?

在我们开始之前,让我提一下 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 的解决方案开始,但使用 pandas 绘图包装器(原因稍后会清楚)。

没有使用 Seaborn

Pandas 允许使用每列属于或构成一个组的数据框来绘制分组条形图。 因此,要采取的步骤是

  1. 根据不同的数量创建多个子图variables
  2. groupby 日期框架 variables
  3. 为每个组创建一个透视数据框,其中 grp1 的值作为列,m 的值作为值。对两个错误列执行相同的操作。
  4. 应用
  5. 中的解决方案

代码将如下所示:

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 
    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()

使用 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 图上。此概念显示在 中。但是,由于 seaborn.barplot 多次调用 plt.bar,每个 hue 一次,每次调用的错误都是相同的(或者它们的维度不匹配)。

因此,我看到的唯一选择是使用 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 
    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()