Oboustranný seskupeny barplots s Python seaborn

0

Otázka

Snažím se nakreslit oboustranný graf podobný populační pyramida je vysvětleno zde a zde. Problém je, že mám kategorické proměnné (muži/ženy), že chci do skupiny společně:

import pandas as pd
import seaborn as sns

# data
data = {'species': ['X', 'X', 'Y', 'Y', 'Z', 'Z', 'X', 'X', 'Y', 'Y', 'Z', 'Z'],
        'sex': ['male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female'], 
        'mass (g)': [4000, 3500, 3800, 3200, 5500, 4900, 2500, 2100, 2400, 2000, 4200, 3800],
        'age': ['adult', 'adult', 'adult', 'adult', 'adult', 'adult', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile']}
df = pd.DataFrame(data)

# convert juvenile mass to negative
df.loc[df.age.eq('juvenile'), 'mass (g)'] = df['mass (g)'].mul(-1)

# plot
sns.set_theme(style="whitegrid")
fig, ax = plt.subplots(figsize=(10,5))
sns.barplot(data=df, x='mass (g)', y='species', hue='sex', ci=False, orient='horizontal', dodge=True)
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
plt.show()

Na obrázku níže je to, co jsem usilovat. Různé barevné pruhy jsou pro mužské/ženské pohlaví. Různých druhů X, Y, Z jsou v samostatných kategorické skupiny. Mříže na pravé straně obrázek show hmotnost dospělých pro každý druh.

Jsem načrtl v červené pruhy na levé straně ukázat hmotnosti nedospělých jedinců pro každý druh. Jak tohle mám sakra vykreslit? Nemůžu nic najít užitečné v seaborn docs nebo TAK.

enter image description here

matplotlib pandas plot python
2021-11-23 21:44:10
3

Nejlepší odpověď

2

Zkusit něco jako tohle:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# data
data = {'species': ['X', 'X', 'Y', 'Y', 'Z', 'Z', 'X', 'X', 'Y', 'Y', 'Z', 'Z'],
        'sex': ['male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female'], 
        'mass (g)': [4000, 3500, 3800, 3200, 5500, 4900, 2500, 2100, 2400, 2000, 4200, 3800],
        'age': ['adult', 'adult', 'adult', 'adult', 'adult', 'adult', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile']}
df = pd.DataFrame(data)

# convert juvenile mass to negative
df.loc[df.age.eq('juvenile'), 'mass (g)'] = df['mass (g)'].mul(-1)

# plot
sns.set_theme(style="whitegrid")
fig, ax = plt.subplots(figsize=(10,5))
df_reshape = df.set_index(['species','sex','age']).unstack(['age','sex'])['mass (g)']
df_reshape.loc[:, 'adult'].plot.barh(ax=ax)
df_reshape.loc[:, 'juvenile'].plot.barh(legend=False, ax=ax)
plt.show()

Výstup:

enter image description here


import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# data
data = {'species': ['X', 'X', 'Y', 'Y', 'Z', 'Z', 'X', 'X', 'Y', 'Y', 'Z', 'Z'],
        'sex': ['male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female'], 
        'mass (g)': [4000, 3500, 3800, 3200, 5500, 4900, 2500, 2100, 2400, 2000, 4200, 3800],
        'age': ['adult', 'adult', 'adult', 'adult', 'adult', 'adult', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile']}
df = pd.DataFrame(data)

# convert juvenile mass to negative
df.loc[df.age.eq('juvenile'), 'mass (g)'] = df['mass (g)'].mul(-1)

# plot
sns.set_theme(style="whitegrid")
fig, ax = plt.subplots(figsize=(10,5))
df_reshape = df.set_index(['species','sex','age']).unstack(['age','sex'])['mass (g)']
df_reshape.loc[:, ['adult']].plot.barh(ax=ax, edgecolor='k')
df_reshape.loc[:, ['juvenile']].plot.barh(ax=ax, label='Juvenile', color=['navy','red'], alpha=.6, edgecolor='k', hatch='/')
plt.show()

Výstup:

enter image description here

2021-11-23 22:18:43
1

Pokud budete kombinovat kladné a záporné hodnoty, ve výchozím nastavení seaborn je barplot bude průměr z nich.

Můžete kreslit dva barplots zády k sobě a zpátečky levá:

from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd

data = {'species': ['X', 'X', 'Y', 'Y', 'Z', 'Z', 'X', 'X', 'Y', 'Y', 'Z', 'Z'],
        'sex': ['male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female'],
        'mass (g)': [4000, 3500, 3800, 3200, 5500, 4900, 2500, 2100, 2400, 2000, 4200, 3800],
        'age': ['adult', 'adult', 'adult', 'adult', 'adult', 'adult', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile']}
df = pd.DataFrame(data)
df['sex'] = pd.Categorical(df['sex'])  # make hue column categorical, forcing a fixed order

sns.set_theme(style='whitegrid')
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 5), sharey=True, gridspec_kw={'wspace': 0})


# draw adult subplot at the right
sns.barplot(data=df[df['age'] == 'adult'], x='mass (g)', y='species', hue='sex',
            ci=False, orient='horizontal', dodge=True, ax=ax2)
ax2.yaxis.set_label_position('right')
ax2.tick_params(axis='y', labelright=True, right=True)
ax2.set_title('  '+'adult', loc='left')
ax2.legend_.remove()  # remove the legend; the legend will be in ax1

# draw juvenile subplot at the left
sns.barplot(data=df[df['age'] == 'juvenile'], x='mass (g)', y='species', hue='sex',
            ci=False, orient='horizontal', dodge=True, ax=ax1)

# optionally use the same scale left and right
xmax = max(ax1.get_xlim()[1], ax2.get_xlim()[1])
ax1.set_xlim(xmax=xmax)
ax2.set_xlim(xmax=xmax)

ax1.invert_xaxis()  # reverse the direction
ax1.tick_params(labelleft=False, left=False)
ax1.set_ylabel('')
ax1.set_title('juvenile'+'  ', loc='right')

plt.tight_layout()
plt.show()

two sns.barplots, back to back

Zajímavostí seaborn je barplots je, že to bude také dělat práci v průměru z hodnot uvedených v datovém s sebou pro každého jednotlivce (a výpočet intervalu spolehlivosti).

2021-11-23 22:11:31

Na nevýhodou tohoto přístupu je, že mříže na dva handside nejsou vázány na stejné měřítko, což může být zavádějící.
Quang Hoang

@johanC z nějakého důvodu, kód, pod # optionally use the same scale left and right nemá žádný vliv (pro můj aktuální kód, není MWE v mém příspěvku). tj. mříže na levé a pravé straně grafu nejsou správně zarovnány
Medulla Oblongata

díky, to je to, co jsem udělal, ale pořád je to nevyrovnané
Medulla Oblongata

ano, jsem v podstatě zkopírovat svůj kód a používá vlastní datový soubor pro to - všechny příkazy jsou beze změny. Vypadá to, že mříže na levé straně grafu mají větší šířku, než na pravé straně - můžete vymáhat bar šířka pro oba ax1 a ax2?
Medulla Oblongata

Můžete upravit vaši otázku a přidat obrázek, který pozemku? Možná tam jsou více odstín hodnot, jsou k dispozici v jedné vs. druhé sady? Možná, druhy sloupec by také měla jednoznačně být kategorický? (df['species'] = pd.Categorical(df['species']))
JohanC

Mohl bych psát samostatnou otázkou pro to, díky za vaši pomoc.
Medulla Oblongata
1

Jen jsem použil `pivot pro tvar data správně

import pandas as pd
import seaborn as sns

# data
data = {'species': ['X', 'X', 'Y', 'Y', 'Z', 'Z', 'X', 'X', 'Y', 'Y', 'Z', 'Z'],
        'sex': ['male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female', 'male', 'female'], 
        'mass (g)': [4000, 3500, 3800, 3200, 5500, 4900, 2500, 2100, 2400, 2000, 4200, 3800],
        'age': ['adult', 'adult', 'adult', 'adult', 'adult', 'adult', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile', 'juvenile']}
df = pd.DataFrame(data)

# convert juvenile mass to negative
df.loc[df.age.eq('juvenile'), 'mass (g)'] = df['mass (g)'].mul(-1)

# pivot data
df=df.pivot(columns=['age'], index=['species', 'sex'], values=['mass (g)']).reset_index()
df = df.set_index(['species', 'sex'])['mass (g)'].reset_index()

# plot
sns.set_theme(style="whitegrid")
fig, ax = plt.subplots(figsize=(10,5))
sns.barplot(data=df, x='adult', y='species', hue='sex', ci=False, orient='horizontal', dodge=True)
sns.barplot(data=df, x='juvenile', y='species', hue='sex', ci=False, orient='horizontal', dodge=True)

enter image description here

2021-11-24 00:32:50

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................