1

Goal: Given a seaborn catplot (kind="bar") with multiple rows, grouped bars, and a mapped stripplot, how do I add statistical annotations (p-values).

The following code from @Trenton McKinney generates my figure without statistical annotation. I would like to insert statistical annotation into this figure:

import seaborn as sns

tips = sns.load_dataset("tips")

g = sns.catplot(x="sex", y="total_bill", hue="smoker", row="time", data=tips, kind="bar", ci = "sd", 
    edgecolor="black", errcolor="black", errwidth=1.5, capsize = 0.1, height=4, aspect=.7,alpha=0.5)

g.map(sns.stripplot, 'sex', 'total_bill', 'smoker', hue_order=['Yes', 'No'], order=['Male', 'Female'],
  palette=sns.color_palette(), dodge=True, alpha=0.6, ec='k', linewidth=1)

Catplot without statistical annotation


What I tried: I tried to use statannotations.Annotator.Annotator.plot_and_annotate_facets(). However, I was not able to get it working properly.

I also tried to use statannotations.Annotator.Annotator.new_plot(). However, this just worked for barplots but not for catplots. This is the corresponding code based on @r-beginners:

import seaborn as sns
from statannotations.Annotator import Annotator
%matplotlib inline
import matplotlib.pyplot as plt

df = sns.load_dataset("tips")

x="sex"
y="total_bill"
hue="smoker"
hue_order=['Yes', 'No']

pairs = [
    (("Male", "Yes"), ("Male", "No")),
    (("Female", "Yes"), ("Female", "No"))]

ax = sns.barplot(data=df, x=x, y=y, hue=hue, hue_order=hue_order, seed=2021, ci="sd", 
    edgecolor="black", errcolor="black", errwidth=1.5, capsize = 0.1, alpha=0.5)

sns.stripplot(x=x, y=y, hue=hue, data=df, dodge=True, alpha=0.6, ax=ax)

annot = Annotator(None, pairs)

annot.new_plot(ax, pairs, plot='barplot',
           data=df, x=x, y=y, hue=hue, hue_order=hue_order, seed=2021)
annot.configure(test='Mann-Whitney', text_format='simple', loc='inside', verbose=2)
annot.apply_test().annotate()

plt.legend(loc='upper left', bbox_to_anchor=(1.03, 1), title=hue)

Statistical annotation for a barplot

Question: Does anyone know how to insert statistical annotation into a figure-level plot, preferably a catplot (kind="bar")?

Ali Yeşilkanat
  • 597
  • 1
  • 7
  • 21
Alex Schubert
  • 67
  • 2
  • 7

3 Answers3

4

Following the M. Sch.'s answer, we should subset the original data table, otherwise, the statistics will be calculated on the whole dataset:

import seaborn as sns
from statannotations.Annotator import Annotator


tips = sns.load_dataset("tips")

args = dict(x="sex", y="total_bill", hue="smoker", hue_order=["Yes","No"], order=['Male', 'Female'])

g = sns.catplot(edgecolor="black", errcolor="black", errwidth=1.5, capsize = 0.1, height=4, aspect=.7,alpha=0.5, kind="bar", ci = "sd", row="time", data=tips, **args)
g.map(sns.stripplot, args["x"], args["y"], args["hue"], hue_order=args["hue_order"], order=args["order"], palette=sns.color_palette(), dodge=True, alpha=0.6, ec='k', linewidth=1)

pairs = [
    (("Male", "Yes"), ("Male", "No")),
    (("Female", "Yes"), ("Female", "No"))
]

for name,ax in g.axes_dict.items():
# subset the table otherwise the stats were calculated on the whole dataset
        annot = Annotator(ax, pairs, **args,data=tips.loc[tips['time']==name,:]) 
        annot.configure(test='Mann-Whitney', text_format='simple', loc='inside', verbose=2)
        annot.apply_test().annotate()

Output plot: output figure

Jingxin Fu
  • 41
  • 2
3

I think you can just iterate over the axes in the FacetGrid and apply the Annotator element wise.

Here is a short example with your provided code:

import seaborn as sns
from statannotations.Annotator import Annotator
%matplotlib inline


tips = sns.load_dataset("tips")

args = dict(x="sex", y="total_bill", data=tips, hue="smoker", hue_order=["Yes","No"], order=['Male', 'Female'])

g = sns.catplot(edgecolor="black", errcolor="black", errwidth=1.5, capsize = 0.1, height=4, aspect=.7,alpha=0.5, kind="bar", ci = "sd", row="time", **args)
g.map(sns.stripplot, args["x"], args["y"], args["hue"], hue_order=args["hue_order"], order=args["order"], palette=sns.color_palette(), dodge=True, alpha=0.6, ec='k', linewidth=1)

pairs = [
    (("Male", "Yes"), ("Male", "No")),
    (("Female", "Yes"), ("Female", "No"))
]

for ax_n in g.axes:
    for ax in ax_n:
        annot = Annotator(ax, pairs, **args)
        annot.configure(test='Mann-Whitney', text_format='simple', loc='inside', verbose=2)
        annot.apply_test().annotate()

This produces the following plot:
Plot with annotations

M. Sch.
  • 95
  • 1
  • 8
1

I managed to get the Annotator.plot_and_annotate_facets() working. It is not well-documented and a bit unnatural but, for the sake of completeness, please find it below:

import seaborn as sns
from statannotations import Annotator
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

g = sns.catplot(x="sex", y="total_bill", hue="smoker", row="time", data=tips, kind="bar", ci = "sd", 
    edgecolor="black", errcolor="black", errwidth=1.5, capsize = 0.1, height=4, aspect=.7,alpha=0.5)

pairs = [
    (("Male", "Yes"), ("Male", "No")),
    (("Female", "Yes"), ("Female", "No"))
    ]

ant = Annotator(None, pairs)
kwargs = {
    'plot_params': { # this takes what normally goes into sns.barplot etc.
        'x': 'sex',
        'y': 'total_bill',
        'hue': 'smoker',
        'hue_order': ['Yes', 'No']
    },
    'annotation_func': 'apply_test', # has three options
    'configuration': {'test': 'wilcox'}, # this takes what normally goes into ant.configure
    'plot': 'boxplot'
}

g.map_dataframe(ant.plot_and_annotate_facets, **kwargs)
plt.show()

And the resulting plot is shown below:

the resulting plot

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
egaznep
  • 11
  • 1