在使用 groupby 时遍历列以生成条形图

Iterate through columns to generate barplots while using groupby

这是我的数据框:

data = {'machine': ['machine_a', 'machine_a', 'machine a', 'machine a', 'machine a', 'machine a', 'machine_b', 'machine_b', 'machine_b', 'machine_b', 'machine_b', 'machine_b', 'machine_c', 'machine_c', 'machine_c', 'machine_c', 'machine_c', 'machine_c'], 'bin': ['(0, 200]', '(200, 400]', '(400, 600]', '(600, 800]', '(800, 1000]', '(1000, 1200]', '(0, 200]', '(200, 400]', '(400, 600]', '(600, 800]', '(800, 1000]', '(1000, 1200]', '(0, 200]', '(200, 400]', '(400, 600]', '(600, 800]', '(800, 1000]', '(1000, 1200]'], 'speed': [10, 0, 20, 0, 20, 10, 5, 0, 40, 10, 20, 10, 5, 25, 0, 10, 5, 10], 'Temp': [0, 0, 0, 20, 20, 0, 35, 0, 0, 20, 0, 70, 30, 0, 0, 5, 0, 25]}
df = pd.DataFrame(data)

      machine           bin  speed  Temp
0   machine_a      (0, 200]     10     0
1   machine_a    (200, 400]      0     0
2   machine a    (400, 600]     20     0
3   machine a    (600, 800]      0    20
4   machine a   (800, 1000]     20    20
5   machine a  (1000, 1200]     10     0
6   machine_b      (0, 200]      5    35
7   machine_b    (200, 400]      0     0
8   machine_b    (400, 600]     40     0
9   machine_b    (600, 800]     10    20
10  machine_b   (800, 1000]     20     0
11  machine_b  (1000, 1200]     10    70
12  machine_c      (0, 200]      5    30
13  machine_c    (200, 400]     25     0
14  machine_c    (400, 600]      0     0
15  machine_c    (600, 800]     10     5
16  machine_c   (800, 1000]      5     0
17  machine_c  (1000, 1200]     10    25

我想为列速度和温度创建不同的条形图,其中 x 轴是 bins 列。我想为每台不同的机器执行此操作。

到目前为止,我已经创建了一个 for 循环来遍历最后两列

import seaborn as sns
import matplotlib.pyplot as plt

for column in df.columns[2:]: 
    sns.set()
    fig, ax = plt.subplots()
    sns.set(style="ticks")
    sns.barplot(x = df.bin, y=column, data=df)
    sns.despine(offset=10, trim=True) 
    fig.set_size_inches(22,14)

这将创建 2 个条形图。 1 表示速度,1 表示温度。我该怎么做才能得到 6 个条形图(每台机器 2 个)?在这种情况下,基本上我该如何使用 groupby?

完整的综合数据

import pandas as pd
import numpy as np

bins = [f'({n*200}, {(n+1)*200}]' for _ in range(50) for n in range(109)]
machines = [f'machine_{n}' for n in range(50) for _ in range(109)]
np.random.seed(365)
speed = np.random.randint(0, 40, size=len(machines))
temp = np.random.choice([0, 30, 70], size=len(machines))

df = pd.DataFrame({'machine': machines, 'bin': bins, 'speed': speed, 'Temp': temp})

df.head()
     machine          bin  speed  Temp
0  machine_0     (0, 200]     18    30
1  machine_0   (200, 400]     33    70
2  machine_0   (400, 600]     27    30
3  machine_0   (600, 800]      5    30
4  machine_0  (800, 1000]     34    30

df.tail()
         machine             bin  speed  Temp
5445  machine_49  (20800, 21000]      6     0
5446  machine_49  (21000, 21200]     20    30
5447  machine_49  (21200, 21400]     14     0
5448  machine_49  (21400, 21600]     38    30
5449  machine_49  (21600, 21800]     24    70

可以直接使用seaborn.catplot。您只需要先融化 y 列:

import seaborn as sns

sns.catplot(data=df.melt(id_vars=['machine', 'bin']),
            col='machine',
            x='bin', y='value', hue='variable',
            kind='bar'
           )

输出:

或者将变量作为行:

import seaborn as sns

sns.catplot(data=df.melt(id_vars=['machine', 'bin']),
            col='machine',
            row='variable',
            x='bin', y='value',
            kind='bar',
            color='k',
            )

输出:

或者拆分变量:

import seaborn as sns

sns.catplot(data=df.melt(id_vars=['machine', 'bin']),
            hue='machine',
            x='bin', y='value', col='variable',
            kind='bar'
           )

这是一个使用 groupby 的版本:

df2 = df.melt(id_vars=['machine', 'bin'])

COLS = df2['machine'].nunique()
ROWS = df2['variable'].nunique()

fig, axes = plt.subplots(ncols=COLS,
                         nrows=ROWS,
                        )
i = 0
for group, d in df2.groupby(['machine', 'variable']):
    ax = axes[i%ROWS][i//ROWS]
    ax.bar(d['bin'], d['value'])
    if not i//ROWS:
        ax.set_ylabel(group[1])
    if i%ROWS == ROWS-1:
        ax.set_xlabel(group[0])
    i+=1

输出:

  • 鉴于评论中描述的现有数据和更新的合成数据集。
  • 使用seaborn.barplotseabornmatplotlib
  • 的高级 API
import matplotlib.pyplot as plt
import seaborn as sns

# melt the dataframe into a long form and group by the machine
dfmg = df.melt(id_vars=['machine', 'bin']).groupby('machine')

选项 1

  • 为每台机器创建一个单独的绘图,与机器名称一起保存
    • 这节省了 50 个图,每台机器 1 个
for mach, data in dfmg:
    plt.figure(figsize=(20, 5))
    sns.barplot(data=data, x='bin', y='value', hue='variable')
    plt.xticks(rotation=90, ha='center')
    plt.title(mach)
    plt.legend(loc='upper left')
    plt.tight_layout()
    plt.savefig(f'{mach}.png')
    plt.show()

  • 'Temp''speed'
  • 的单独图
for mach, data in dfmg:
    fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(20, 5), sharex=True)
    
    # select data
    temp = data[data.variable.eq('Temp')]
    speed = data[data.variable.eq('speed')]
    
    # plot data
    sns.barplot(data=temp, x='bin', y='value', ax=ax1)  # ax1.bar(data=temp, x='bin', y='value') - without seaborn
    sns.barplot(data=speed, x='bin', y='value', ax=ax2)  # ax2.bar(data=speed, x='bin', y='value') - without seaborn
    
    ax1.set(xlabel=None, ylabel='Temp')
    ax1.tick_params(bottom=False)
    ax2.set(ylabel='Speed')
    ax2.set_xticklabels(ax2.get_xticklabels(), rotation=90, ha='center')  # ax2.tick_params('x', rotation=90) - without seaborn
    
    fig.suptitle(mach)
    fig.tight_layout()
    fig.savefig(f'{mach}.png')
    plt.show()

选项 2

  • 或者,创建单个图形将图形上的所有图
    • 所有 50 台机器都节省了 1 个数字
  • 本节图中每台机器的绘图,看起来与之前的绘图相同,具有橙色和蓝色色调。
fig, axes = plt.subplots(nrows=len(df.machine.unique()), ncols=1, figsize=(20, 250))
for i, (mach, data) in enumerate(df.melt(id_vars=['machine', 'bin']).groupby('machine')):
    sns.barplot(data=data, x='bin', y='value', hue='variable', ax=axes[i])
    axes[i].set_xticklabels(axes[i].get_xticklabels(), rotation=90, ha='center')
    axes[i].set(title=mach)
    axes[i].legend(loc='upper left')
fig.tight_layout()
fig.savefig('machines_all.png')
plt.show()
  • seaborn.catplot 可以用一条线创建等效的单图级图
import seaborn as sns

# melt the dataframe into a long form
dfm = df.melt(id_vars=['machine', 'bin'])

# plot
p = sns.catplot(data=dfm, col='machine', x='bin', y='value', hue='variable', kind='bar', col_wrap=1, height=4, aspect=5)
p.set_xticklabels(rotation=90) 
p.savefig("facet_plot.png")