Если я правильно понял ваш вопрос сейчас, у вас есть такой фрейм данных:
N = 100
M = 5
df = pd.DataFrame()
for i in range(1,M+1):
df[f'NH{i:02d}'] = np.random.normal(loc=i, size=(N,))
df[f'NH{i:02d}cat'] = np.random.choice(['NacZ','NacX'], size=(N,))
print(df.head())
output:
NH01 NH01cat NH02 NH02cat NH03 NH03cat NH04 NH04cat \
0 0.231058 NacZ 1.872279 NacZ 4.048766 NacX 3.869479 NacZ
1 0.062530 NacX 1.210339 NacZ 3.374466 NacZ 2.827855 NacX
2 1.146168 NacX 0.752690 NacZ 3.948877 NacZ 4.320780 NacZ
3 0.266700 NacZ 0.874896 NacX 1.529101 NacX 3.448940 NacZ
4 1.620292 NacX 0.689638 NacX 2.778528 NacX 4.590301 NacZ
NH05 NH05cat
0 3.757337 NacX
1 4.552330 NacZ
2 5.188367 NacX
3 5.067367 NacZ
4 4.108142 NacZ
, который вы хотели бы построить с помощью вложенных в оттенки ящиков.
Для этого вам необходимо преобразовать ваши данные из «широких» в «длинные». Возможно, есть более эффективный способ сделать это (возможно, отдельный вопрос для pandas expert), но вы можете использовать pd.wide_to_long()
, если вы слегка измените имена столбцов:
import re
df2 = df.copy()
df2.columns = [re.sub('NH([0-9]{2})cat','cat-NH\\1',c) for c in df2.columns]
df2.columns = [re.sub('^NH([0-9]{2})$','val-NH\\1',c) for c in df2.columns]
df2['id'] = df.index
df2 = pd.wide_to_long(df2, ['val','cat'], j='NH', i='id', sep='-', suffix='NH\\d+')
df2 = df2.reset_index()
теперь df2 выглядит как это:
id NH val cat
0 0 NH01 0.231058 NacZ
1 1 NH01 0.062530 NacX
2 2 NH01 1.146168 NacX
3 3 NH01 0.266700 NacZ
4 4 NH01 1.620292 NacX
, который вы можете построить:
sns.boxplot(y="val",x="NH",hue='cat', data=df2)