【问题标题】:Indicating the statistically significant difference in bar graph在条形图中表示统计上的显着差异
【发布时间】:2012-07-16 02:35:36
【问题描述】:

我用一个条形图来表示每组的数据。其中一些条形图彼此显着不同。如何指示条形图中的显着差异?

import numpy as np
import matplotlib.pyplot as plt
menMeans   = (5, 15, 30, 40)
menStd     = (2, 3, 4, 5)
ind = np.arange(4)    # the x locations for the groups
width=0.35
p1 = plt.bar(ind, menMeans, width=width, color='r', yerr=menStd)
plt.xticks(ind+width/2., ('A', 'B', 'C', 'D') )

我的目标是

【问题讨论】:

  • 是否只有局部相邻的比较?也就是说,您是否只想显示(A,B) (B,C) (C,D)而不是(A,C)之间的区别?
  • 不,我想对所有可能的配对进行比较。
  • 可能很难在图表上显示这一点,尤其是在有大量项目的情况下。如果您有 N=10,则项目有 45 种不同的成对比较!似乎您可以在矩阵上显示成对的 p 值。这行得通吗?
  • 你只是想实现附加的情节,还是你真的想要@Hooked 建议的矩阵?
  • 大多数时候不需要比较所有可能的配对。与上述情况一样,比较 (A,C) 或 (A,D) 或 (B,D) 不会给出任何新信息。所以理想情况下,我想比较选定的对,比如说在一种情况下它可以是(A,B),(B,C)和(C,D)(如上所述),如果它可以是(A,B) ,(A,C) 和 (A,D)。

标签: python matplotlib figure


【解决方案1】:

我在这里做了几件事,我建议在处理复杂的情节时做这些事情。将自定义格式提取到字典中,当您想要更改参数时,它使生活变得简单 - 您可以将此字典传递给多个绘图。我还为 annotate 的 itervalues 编写了一个自定义函数,作为奖励,如果你真的想要,它可以在 (A,C) 之间进行注释(但我坚持我的评论,这不是正确的视觉方法)。一旦数据发生变化,它可能需要进行一些调整,但这应该会让你走上正确的轨道。

import numpy as np
import matplotlib.pyplot as plt
menMeans   = (5, 15, 30, 40)
menStd     = (2, 3, 4, 5)
ind  = np.arange(4)    # the x locations for the groups
width= 0.7
labels = ('A', 'B', 'C', 'D')

# Pull the formatting out here
bar_kwargs = {'width':width,'color':'y','linewidth':2,'zorder':5}
err_kwargs = {'zorder':0,'fmt':None,'linewidth':2,'ecolor':'k'}  #for matplotlib >= v1.4 use 'fmt':'none' instead

fig, ax = plt.subplots()
ax.p1 = plt.bar(ind, menMeans, **bar_kwargs)
ax.errs = plt.errorbar(ind, menMeans, yerr=menStd, **err_kwargs)


# Custom function to draw the diff bars

def label_diff(i,j,text,X,Y):
    x = (X[i]+X[j])/2
    y = 1.1*max(Y[i], Y[j])
    dx = abs(X[i]-X[j])

    props = {'connectionstyle':'bar','arrowstyle':'-',\
                 'shrinkA':20,'shrinkB':20,'linewidth':2}
    ax.annotate(text, xy=(X[i],y+7), zorder=10)
    ax.annotate('', xy=(X[i],y), xytext=(X[j],y), arrowprops=props)

# Call the function
label_diff(0,1,'p=0.0370',ind,menMeans)
label_diff(1,2,'p<0.0001',ind,menMeans)
label_diff(2,3,'p=0.0025',ind,menMeans)


plt.ylim(ymax=60)
plt.xticks(ind, labels, color='k')
plt.show()

【讨论】:

  • 非常感谢。信息量很大。我只是将 ax.annotate(text, xy=(X[i],y+7), zorder=10) 更改为 ax.annotate(text, xy=(x,y+7), zorder=10) 以使 p 值居中。
  • @imsc 这就是我一开始使用的,但那是文本块左侧的位置——而不是文本块的中心。对我来说,这似乎与那个位置有点偏离中心。无论哪种方式,我希望你看看你可以如何调整!
  • 哦,是的,我还把ha='center'放在annotate中。
【解决方案2】:

上面的答案启发我自己写了一个小而灵活的函数:

def barplot_annotate_brackets(num1, num2, data, center, height, yerr=None, dh=.05, barh=.05, fs=None, maxasterix=None):
    """ 
    Annotate barplot with p-values.

    :param num1: number of left bar to put bracket over
    :param num2: number of right bar to put bracket over
    :param data: string to write or number for generating asterixes
    :param center: centers of all bars (like plt.bar() input)
    :param height: heights of all bars (like plt.bar() input)
    :param yerr: yerrs of all bars (like plt.bar() input)
    :param dh: height offset over bar / bar + yerr in axes coordinates (0 to 1)
    :param barh: bar height in axes coordinates (0 to 1)
    :param fs: font size
    :param maxasterix: maximum number of asterixes to write (for very small p-values)
    """

    if type(data) is str:
        text = data
    else:
        # * is p < 0.05
        # ** is p < 0.005
        # *** is p < 0.0005
        # etc.
        text = ''
        p = .05

        while data < p:
            text += '*'
            p /= 10.

            if maxasterix and len(text) == maxasterix:
                break

        if len(text) == 0:
            text = 'n. s.'

    lx, ly = center[num1], height[num1]
    rx, ry = center[num2], height[num2]

    if yerr:
        ly += yerr[num1]
        ry += yerr[num2]

    ax_y0, ax_y1 = plt.gca().get_ylim()
    dh *= (ax_y1 - ax_y0)
    barh *= (ax_y1 - ax_y0)

    y = max(ly, ry) + dh

    barx = [lx, lx, rx, rx]
    bary = [y, y+barh, y+barh, y]
    mid = ((lx+rx)/2, y+barh)

    plt.plot(barx, bary, c='black')

    kwargs = dict(ha='center', va='bottom')
    if fs is not None:
        kwargs['fontsize'] = fs

    plt.text(*mid, text, **kwargs)

这让我可以相对简单地获得一些不错的注释,例如:

heights = [1.8, 2, 3]
bars = np.arange(len(heights))

plt.figure()
plt.bar(bars, heights, align='center')
plt.ylim(0, 5)
barplot_annotate_brackets(0, 1, .1, bars, heights)
barplot_annotate_brackets(1, 2, .001, bars, heights)
barplot_annotate_brackets(0, 2, 'p < 0.0075', bars, heights, dh=.2)

【讨论】:

    【解决方案3】:

    如果你正在使用 matplotlib 并寻求箱线图注释,请使用我的代码作为函数:

    统计注释

    def AnnoMe(x1, x2, ARRAY, TXT):
        y, h, col = max(max(ARRAY[x1-1]),max(ARRAY[x2-1])) + 2, 2, 'k'
        plt.plot([x1, x1, x2, x2], [y, y+h, y+h, y], lw=1.5, c=col)
        plt.text((x1+x2)*.5, y+h, TXT, ha='center', va='bottom', color=col)
    

    其中“x1”和“x2”是您要比较的两列,“ARRAY”是用于说明箱线图的列表列表。而且,“TXT”是您的文本,例如 p 值或字符串格式的显着/不显着。

    因此,调用它:

    AnnoMe(1, 2, MyArray, "p-value=0.02")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-04
      • 1970-01-01
      • 1970-01-01
      • 2020-07-04
      • 2020-10-20
      • 1970-01-01
      • 1970-01-01
      • 2021-04-09
      相关资源
      最近更新 更多