如何在Pandas分组条形图中添加非对称误差线?

11

在遵循此问题的被接受答案后,我能够为自己的数据重复相同的结果。然而,我需要绘制非对称的误差线。

dfdict = {'ID': ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'],
      'quarter': ['2015 2Q', '2016 1Q', '2015 2Q', '2016 1Q', '2015 2Q',
                  '2016 1Q', '2015 2Q', '2016 1Q'],
      'Percent': [0.851789, 0.333333, 0.355240, 0.167224, 1.533220,
                  0.333333, 0.170358, 0.000000],
      'AgrCoullLower': [ 0.378046, 0.057962,  0.061850, -0.027515,
                         0.866025, 0.057962, -0.028012, -0.092614],
      'AgrCoullUpper': [1.776511, 1.054612, 1.123492, 0.810851,
                        2.645141, 1.054612, 0.825960, 0.541513]}
df = pd.DataFrame(dfdict)
df
  ID  quarter   Percent  AgrCoullLower  AgrCoullUpper
0  A  2015 2Q  0.851789       0.378046       1.776511
1  A  2016 1Q  0.333333       0.057962       1.054612
2  B  2015 2Q  0.355240       0.061850       1.123492
3  B  2016 1Q  0.167224      -0.027515       0.810851
4  C  2015 2Q  1.533220       0.866025       2.645141
5  C  2016 1Q  0.333333       0.057962       1.054612
6  D  2015 2Q  0.170358      -0.028012       0.825960
7  D  2016 1Q  0.000000      -0.092614       0.541513

errLo = df.pivot(index='ID', columns='quarter', values='AgrCoullLower')
errHi = df.pivot(index='ID', columns='quarter', values='AgrCoullUpper')

df.pivot(index='ID', columns='quarter', values='Percent')\
    .plot(kind='bar', yerr=errLo)

按 ID 和季度分组的百分比

由于 Matplotlib 可以使用 yerr=[ylo, yhi] 结构来表示不对称的误差条,我希望在这里做类似的事情。不幸的是,不能简单地用 yerr=[errLo, errHi] 替换,因为这些数组(形状为 (4,2))不能被简单地插入到此方式中(稍后跟踪堆栈),使用 yerr=np.column_stack((errLo, errHi)) 绘制的图形包含对称的误差条(从不使用第二个数组值)。使用 yerr=np.row_stack((errLo, errHi)),我得到

ValueError: yerr must be a scalar, the same dimensions as y, or 2xN.

有没有办法说服Pandas提供分组的,非对称误差线?

那个堆栈跟踪:

ValueError                                Traceback (most recent call last)
<ipython-input-26-336a22db15e6> in <module>()
----> 1 df.pivot(index='ID', columns='quarter', values='Percent')    .plot(kind='bar', yerr=[errLo, errHi])

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in __call__(self, x, y, kind, ax, subplots, sharex, sharey, layout, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, secondary_y, sort_columns, **kwds)
   3669                           fontsize=fontsize, colormap=colormap, table=table,
   3670                           yerr=yerr, xerr=xerr, secondary_y=secondary_y,
-> 3671                           sort_columns=sort_columns, **kwds)
   3672     __call__.__doc__ = plot_frame.__doc__
   3673 

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in plot_frame(data, x, y, kind, ax, subplots, sharex, sharey, layout, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, secondary_y, sort_columns, **kwds)
   2554                  yerr=yerr, xerr=xerr,
   2555                  secondary_y=secondary_y, sort_columns=sort_columns,
-> 2556                  **kwds)
   2557 
   2558 

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in _plot(data, x, y, subplots, ax, kind, **kwds)
   2380                             pass
   2381                 data = series
-> 2382         plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
   2383 
   2384     plot_obj.generate()

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in __init__(self, data, **kwargs)
   1843 
   1844         self.log = kwargs.pop('log',False)
-> 1845         MPLPlot.__init__(self, data, **kwargs)
   1846 
   1847         if self.stacked or self.subplots:

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in __init__(self, data, kind, by, subplots, sharex, sharey, use_index, figsize, grid, legend, rot, ax, fig, title, xlim, ylim, xticks, yticks, sort_columns, fontsize, secondary_y, colormap, table, layout, **kwds)
    904         self.errors = {}
    905         for kw, err in zip(['xerr', 'yerr'], [xerr, yerr]):
--> 906             self.errors[kw] = self._parse_errorbars(kw, err)
    907 
    908         if not isinstance(secondary_y, (bool, tuple, list, np.ndarray, Index)):

/usr/local/lib/python3.4/dist-packages/pandas/tools/plotting.py in _parse_errorbars(self, label, err)
   1423             else:
   1424                 # raw error values
-> 1425                 err = np.atleast_2d(err)
   1426 
   1427             err_shape = err.shape

/usr/local/lib/python3.4/dist-packages/numpy/core/shape_base.py in atleast_2d(*arys)
     98     res = []
     99     for ary in arys:
--> 100         ary = asanyarray(ary)
    101         if len(ary.shape) == 0:
    102             result = ary.reshape(1, 1)

/usr/local/lib/python3.4/dist-packages/numpy/core/numeric.py in asanyarray(a, dtype, order)
    523 
    524     """
--> 525     return array(a, dtype, copy=False, order=order, subok=True)
    526 
    527 def ascontiguousarray(a, dtype=None):

ValueError: cannot copy sequence with size 4 to array axis with dimension 2
1个回答

10
经过一些尝试和错误,我弄清楚了。就像你所提到的那样:

matplotlib允许使用yerr=[ylo,yhi]构造来实现不对称的误差条。

但事实证明,由于你有两组柱状图(即“2015 2Q”和“2016 1Q”),matplotlib期望的形状是(2,2,4),或者:[组数]x2x[每组的柱状图数]。
这是我的代码,从你代码中errLo和errHi的定义之后开始:
err = []
for col in errLo:  # Iterate over bar groups (represented as columns)
    err.append([errLo[col].values, errHi[col].values])
err = np.abs(err)  # Absolute error values (you had some negatives)
pprint(err)
print 'Shape:', np.shape(err)

df.pivot(index='ID', columns='quarter', values='Percent').plot(kind='bar', yerr=err)
plt.show()

输出:

array([[[ 0.378046,  0.06185 ,  0.866025,  0.028012],
        [ 1.776511,  1.123492,  2.645141,  0.82596 ]],

       [[ 0.057962,  0.027515,  0.057962,  0.092614],
        [ 1.054612,  0.810851,  1.054612,  0.541513]]])
Shape: (2L, 2L, 4L)

enter image description here


很好的清晰解决方案,不需要回归到matplotlib原语。谢谢!但是我希望Pandas开发人员在未来的版本中能够融入更透明的方法。 - user3897315
当你说“matplotlib期望的是(2, 2, 4),或:[组数] x 2 x [每组的柱形图数量]”时,x语法是否以相反的顺序阅读形状? 就像(a,b,c)写成c x b x a一样?如果不是,我认为“或”后面的部分是反向的,因为(2,2,4)应该是(2(每组的柱形图数),2(用于非对称误差条),4(组))。 - BeeOnRope
1
@BeeOnRope,这是很久以前的事情了,现在阅读起来确实有点令人困惑。我不认为它是按相反的顺序编写的,只是定义有点模糊。对于这个图形,'quarter'是按组分类的类别,所以我的意思是有两个组-蓝色和绿色,A/B/C/D是每个组的四个条形图。你的直觉把A/B/C/D当作组,把蓝色/绿色当作条形图,所以这对你来说似乎是相反的。我肯定可以更好地表达答案。 - Shovalt
1
@Shovalt - 不用担心,因为这个答案对我帮助很大。我的评论只是为了未来的用户。确认一下,正确的形状是(a,2,b),其中a是不同系列/列的数量(在本例中为2,2015年第二季度和2016年第一季度),而b是行数(在本例中为4,A,B,C,D)。 - BeeOnRope

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