У меня есть два источника данных, к которым я могу присоединиться по полю, и хочу обобщить их в виде диаграммы:

Данные

Два столбца общих данных DataFrames A:

ROWS = 1000
df = pd.DataFrame.from_dict({'A': np.arange(ROWS),
                             'B': np.random.randint(0, 60, size=ROWS),
                             'C': np.random.randint(0, 100, size=ROWS)})
df.head()
   A   B   C
0  0  10  11
1  1   7  64
2  2  22  12
3  3   1  67
4  4  34  57

И other, к которому я присоединился как таковой:

other = pd.DataFrame.from_dict({'A': np.arange(ROWS),
                                'D': np.random.choice(['One', 'Two'], ROWS)})
other.set_index('A', inplace=True)
df = df.join(other, on=['A'], rsuffix='_right')
df.head()
   A   B   C    D
0  0  10  11  One
1  1   7  64  Two
2  2  22  12  One
3  3   1  67  Two
4  4  34  57  One

Вопрос

Правильный способ получить столбчатую диаграмму с количеством:

  • C является GTE50 и D является одним
  • C GTE50 и D два
  • C является LT50 и D является одним
  • C - это LT50, а D - это два

Сгруппированы по B, объединены в 0, 1-10, 11-20, 21-30, 21-40, 41+.

3
Leonel Galán 2 Май 2019 в 19:10

3 ответа

Лучший ответ

IIUC, это может быть значительно упрощено до одной группы, используя преимущества clip и np.ceil для формирования ваших групп. Один стэк с двумя уровнями дает нам B-группировку в качестве нашей оси X со столбиками для каждой комбинации D-C:

Если вам нужны более приятные метки, вы можете отобразить значения groupby:

(df.groupby(['D', 
             df.C.ge(50).map({True: 'GE50', False: 'LT50'}),
             np.ceil(df.B.clip(lower=0, upper=41)/10).map({0: '0', 1: '1-10', 2: '11-20', 3: '21-30', 4: '31-40', 5: '41+'})
            ])
     .size().unstack([0,1]).plot.bar())

enter image description here


Также это эквивалентно группе B на:

pd.cut(df['B'],
       bins=[-np.inf, 1, 11, 21, 31, 41, np.inf],
       right=False,
       labels=['0', '1-10', '11-20', '21-30', '31-40', '41+'])
6
ALollz 2 Май 2019 в 16:36

Пробовал другой способ сделать это.

df['Bins'] = np.where(df['B'].isin([0]), '0',
            np.where(df['B'].isin(range(1,11)), '1-10',
            np.where(df['B'].isin(range(11,21)), '11-20',
            np.where(df['B'].isin(range(21,31)), '21-30',
            np.where(df['B'].isin(range(31,40)), '31-40','41+')
            ))))

df['Class_type'] = np.where(((df['C']>50) & (df['D']== 'One') ), 'C is GTE50 and D is One',
            np.where(((df['C']>50) & (df['D']== 'Two')) , 'C is GTE50 and D is Two',
            np.where(((df['C']<50) & (df['D']== 'One') ), 'C is LT50 and D is One',
                     'C is LT50 and D is Two')
            ))


df.groupby(['Bins', 'Class_type'])['C'].sum().unstack().plot(kind='bar')
plt.show()

#### Output ####

enter image description here

ПРЕДУПРЕЖДЕНИЕ. Не уверен, насколько оптимальным является решение. Кроме того, оно потребляет дополнительное пространство, поэтому его сложность может возрасти.

1
Preetham 2 Май 2019 в 18:10

Numpy

Используя числовые массивы для подсчета, создайте DataFrame для построения графика

labels = np.array(['0', '1-10', '11-20', '21-30', '31-40', '41+'])
ge_lbl = np.array(['GE50', 'LT50'])

u, d = np.unique(df.D.values, return_inverse=True)
bins = np.array([1, 11, 21, 31, 41]).searchsorted(df.B)
ltge = (df.C.values >= 50).astype(int)

shape = (len(u), len(labels), len(ge_lbl))
out = np.zeros(shape, int)
np.add.at(out, (d, bins, ltge), 1)

pd.concat({
    d_: pd.DataFrame(o, labels, ge_lbl)
    for d_, o in zip(u, out)
}, names=['Cx', 'D'], axis=1).plot.bar()

enter image description here

1
piRSquared 2 Май 2019 в 17:35