Как сделать сгруппированную гистограмму с последовательными и хорошо выровненными ячейками? - PullRequest
0 голосов
/ 10 июля 2020

Я хотел бы создать гистограмму данных, хранящихся в pandas DataFrame, где гистограмма разделена на две группы в соответствии с другим столбцом в этом фрейме данных (назовем его столбцом target, который может быть 1 или 0). У меня проблемы с правильным выравниванием ящиков для обеих групп.

Это то, что у меня есть:

def fun_histByTarget(df, cols, target):
    target = df[target]
    if isinstance(cols, str):
        cols = [cols]
    fig = plt.figure(figsize=(18, 5 * ((len(cols) + 1) // 2)), dpi= 80)
    for i in range(len(cols)):
        sp = fig.add_subplot((len(cols) + 1) // 2, 2, i + 1)
        col = df[cols[i]].copy()
        sp.hist(col[target==0], color='red',  alpha=.3, label='target = 0', align='left')
        sp.hist(col[target==1], color='blue', alpha=.3, label='target = 1', align='left')
        sp.legend()
        sp.set_title(cols[i])

Это результат:

fun_histByTarget(test, 'integer_col', 'target')

результат_1

Я пробовал вручную добавлять корзины с помощью

bins = np.linspace(col.values.min(), col.values.max(), 10)

, но это не помогает. Результирующие интервалы выбраны очень странно, так что некоторые столбцы гистограммы полностью попадают между двумя целочисленными значениями, даже если все данные целочисленные. Вероятно, это потому, что я жестко запрограммировал 10 ящиков. Но автоматически выбрать правильное количество ящиков очень сложно. Есть ли лучший способ сделать это?

1 Ответ

1 голос
/ 10 июля 2020

Чтобы получить одинаковые интервалы гистограммы для обоих, достаточно использовать параметр bins= с точно такими же границами. Так что непонятно, почему ваш тест не работает. (Трудно сказать, не видя точный используемый код.)

Кроме того, имя столбца integer_col указывает на то, что столбец содержит только целые числа. Гистограммы в основном предназначены для работы с непрерывными данными. Если у вас есть только целые числа и вы создаете границы ячеек как np.linspace(1, 7, 10), будет 9 ячеек со странными границами в [1.0, 1.667, 2.333, 3.0, 3.667, 4.333, 5.0, 5.667, 6.333, 7.0]. Таким образом, целочисленное значение 1 попадет в первую ячейку, значение 2 во вторую, значение 3 либо в третью, либо в четвертую (в зависимости от ошибок округления с плавающей запятой), ... Более удобный выбор ячеек будет на 0.5, 1.5, 2.5, ... как в коде ниже. (Я также изменил align='left' на значение по умолчанию align='mid', чтобы полосы располагались на том же месте, что и их соответствующие значения.)

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

def fun_histByTarget(df, cols, target):
    target = df[target]
    if isinstance(cols, str):
        cols = [cols]
    fig = plt.figure(figsize=(18, 5 * ((len(cols) + 1) // 2)), dpi=80)
    for i in range(len(cols)):
        ax = fig.add_subplot((len(cols) + 1) // 2, 2, i + 1)
        col = df[cols[i]]
        bins = np.arange(col.min() - 0.5, col.max() + 0.5001, (col.max() - col.max()) // 20 + 1)
        ax.hist(col[target == 0], bins=bins, color='red', alpha=.3, label='target = 0', align='mid')
        ax.hist(col[target == 1], bins=bins, color='blue', alpha=.3, label='target = 1', align='mid')
        ax.legend()
        ax.set_title(cols[i])

target = np.random.randint(0, 2, 100)
integer_col = np.where(target == 0, np.random.randint(1, 7, target.size), np.random.randint(1, 6, target.size))
test = pd.DataFrame({'integer_col': integer_col, 'target': target})
fun_histByTarget(test, 'integer_col', 'target')
plt.show()

итоговый сюжет

Если вы хотите избежать перекрытия полос, диаграмма имеет гораздо больше вариантов, но вам нужно будет вычислить количество на отдельном этапе (например, используя np.hist или через pd.cut) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...