web-dev-qa-db-fra.com

Regroupement de boîtes à moustaches dans Seaborn lorsque l'entrée est un DataFrame

J'ai l'intention de tracer plusieurs colonnes dans un pandas dataframe, tous regroupés par une autre colonne utilisant groupby à l'intérieur seaborn.boxplot. Il y a une bonne réponse ici, pour un problème similaire dans matplotlibmatplotlib: Group boxplots mais étant donné que seaborn.boxplot est livré avec l'option groupby J'ai pensé qu'il pourrait être beaucoup plus facile de le faire dans seaborn.

Ici, nous allons avec un exemple reproductible qui échoue:

import seaborn as sns
import pandas as pd
df = pd.DataFrame(
[
[2, 4, 5, 6, 1],
[4, 5, 6, 7, 2],
[5, 4, 5, 5, 1],
[10, 4, 7, 8, 2],
[9, 3, 4, 6, 2],
[3, 3, 4, 4, 1]
], columns=['a1', 'a2', 'a3', 'a4', 'b'])

#Plotting by seaborn
sns.boxplot(df[['a1','a2', 'a3', 'a4']], groupby=df.b)

Ce que j'obtiens est quelque chose qui ignore complètement l'option groupby:

Failed groupby

Alors que si je fais cela avec une colonne, cela fonctionne grâce à une autre SO question Seaborn groupby pandas Series :

sns.boxplot(df.a1, groupby=df.b)

seaborn that does not fail

Je voudrais donc rassembler toutes mes colonnes dans un même tracé (toutes les colonnes ont une échelle similaire).

ÉDITER:

La question ci-dessus SO a été modifiée et inclut maintenant une réponse "pas propre" à ce problème, mais ce serait bien si quelqu'un avait une meilleure idée de ce problème.

20
Arman

Vous pouvez utiliser directement boxplot (j'imagine que lorsque la question a été posée, ce n'était pas possible, mais avec seaborn version> 0.6 c'est le cas).

Comme expliqué par @mwaskom, vous devez "fondre" l'échantillon de données dans sa "forme longue" où chaque colonne est une variable et chaque ligne est une observation:

df_long = pd.melt(df, "b", var_name="a", value_name="c")

Ensuite, il vous suffit de tracer:

sns.boxplot(x="a", hue="b", y="c", data=df_long)

plot obtained with boxplot

1
MrT77

Comme le notent les autres réponses, la fonction boxplot se limite à tracer une seule "couche" de boîtes à moustaches, et le paramètre groupby n'a d'effet que lorsque l'entrée est une série et que vous en avez une seconde variable que vous souhaitez utiliser pour regrouper les observations dans chaque case.

Cependant, vous pouvez accomplir ce que je pense que vous espérez avec la fonction factorplot, en utilisant kind="box". Mais, vous devrez d'abord "fondre" l'exemple de trame de données dans ce qu'on appelle un format long ou "bien rangé" où chaque colonne est une variable et chaque ligne est une observation:

df_long = pd.melt(df, "b", var_name="a", value_name="c")

Il est alors très simple de tracer:

sns.factorplot("a", hue="b", y="c", data=df_long, kind="box")

enter image description here

24
mwaskom

La fonction groupby de Seaborn prend Series non DataFrames, c'est pourquoi cela ne fonctionne pas.

Pour contourner ce problème, vous pouvez procéder comme suit:

fig, ax = plt.subplots(1,2, sharey=True)
for i, grp in enumerate(df.filter(regex="a").groupby(by=df.b)):
    sns.boxplot(grp[1], ax=ax[i])

il donne : sns

Notez que df.filter(regex="a") est équivalent à df[['a1','a2', 'a3', 'a4']]

   a1  a2  a3  a4
0   2   4   5   6
1   4   5   6   7
2   5   4   5   5
3  10   4   7   8
4   9   3   4   6
5   3   3   4   4

J'espère que cela t'aides

4
jrjc

Ce n'est pas vraiment mieux que la réponse que vous avez liée, mais je pense que le moyen d'y parvenir dans seaborn est d'utiliser la fonction FacetGrid , car le paramètre groupby n'est défini que pour la série passé à la fonction boxplot.

Voici du code - le pd.melt est nécessaire parce que (pour autant que je sache) le mappage de facettes ne peut prendre que des colonnes individuelles comme paramètres, donc les données doivent être transformées en un format "long".

g = sns.FacetGrid(pd.melt(df, id_vars='b'), col='b')
g.map(sns.boxplot, 'value', 'variable')

faceted seaborn boxplot

4
chrisb