Подтвердить что ты не робот

Гистограмма Matplotlib с бункером для сбора больших значений

У меня есть массив со значениями, и я хочу создать его гистограмму. Меня в основном интересуют низкие номера, и я хочу собрать каждое число выше 300 в одном ящике. Этот бункер должен иметь ту же ширину, что и все другие (одинаково широкие) бункеры. Как я могу это сделать?

Примечание: этот вопрос связан с этим вопросом: Определение ширины буфера/оси x в гистограмме Matplotlib

Это то, что я пробовал до сих пор:

import matplotlib.pyplot as plt
import numpy as np

def plot_histogram_01():
    np.random.seed(1)
    values_A = np.random.choice(np.arange(600), size=200, replace=True).tolist()
    values_B = np.random.choice(np.arange(600), size=200, replace=True).tolist()

    bins = [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 600]

    fig, ax = plt.subplots(figsize=(9, 5))
    _, bins, patches = plt.hist([values_A, values_B], normed=1,  # normed is deprecated and will be replaced by density
                                bins=bins,
                                color=['#3782CC', '#AFD5FA'],
                                label=['A', 'B'])

    xlabels = np.array(bins[1:], dtype='|S4')
    xlabels[-1] = '300+'

    N_labels = len(xlabels)
    plt.xlim([0, 600])
    plt.xticks(25 * np.arange(N_labels) + 12.5)
    ax.set_xticklabels(xlabels)

    plt.yticks([])
    plt.title('')
    plt.setp(patches, linewidth=0)
    plt.legend()

    fig.tight_layout()
    plt.savefig('my_plot_01.png')
    plt.close()

Это результат, который выглядит не очень хорошо: enter image description here

Затем я изменил строку с помощью xlim:

plt.xlim([0, 325])

Со следующим результатом: enter image description here

Он выглядит более или менее, как я хочу, но последний бит не отображается сейчас. Какой трюк мне не хватает, чтобы визуализировать этот последний бит с шириной 25?

4b9b3361

Ответ 1

Numpy имеет удобную функцию для решения этой np.clip: np.clip. Несмотря на то, как может звучать название, оно не удаляет значения, оно просто ограничивает их указанным диапазоном. В основном это делает Артем "грязным хаком", встроенным. Вы можете оставить значения такими, какие они есть, но в вызове hist просто оберните массив np.clip, вот так

plt.hist(np.clip(values_A, bins[0], bins[-1]), bins=bins)

Это лучше по ряду причин:

  1. Это намного быстрее - по крайней мере, для большого количества элементов. Numpy делает свою работу на уровне C. Работа со списками Python (как в понимании списка Artem) имеет много накладных расходов для каждого элемента. По сути, если у вас есть возможность использовать NumPy, вы должны.

  2. Вы делаете это там, где это необходимо, что снижает вероятность ошибок в вашем коде.

  3. Вам не нужно держать вторую копию массива, которая уменьшает использование памяти (кроме как в пределах этой одной строки) и еще больше снижает вероятность ошибок.

  4. Использование bins[0], bins[-1] вместо жесткого кодирования значений снижает вероятность повторения ошибок, поскольку вы можете изменить бины только там, где были определены bins; вам не нужно помнить, чтобы изменить их в вызове clip или где-либо еще.

Итак, чтобы сложить все вместе, как в ОП:

import matplotlib.pyplot as plt
import numpy as np

def plot_histogram_01():
    np.random.seed(1)
    values_A = np.random.choice(np.arange(600), size=200, replace=True)
    values_B = np.random.choice(np.arange(600), size=200, replace=True)

    bins = np.arange(0,350,25)

    fig, ax = plt.subplots(figsize=(9, 5))
    _, bins, patches = plt.hist([np.clip(values_A, bins[0], bins[-1]),
                                 np.clip(values_B, bins[0], bins[-1])],
                                # normed=1,  # normed is deprecated; replace with density
                                density=True,
                                bins=bins, color=['#3782CC', '#AFD5FA'], label=['A', 'B'])

    xlabels = bins[1:].astype(str)
    xlabels[-1] += '+'

    N_labels = len(xlabels)
    plt.xlim([0, 325])
    plt.xticks(25 * np.arange(N_labels) + 12.5)
    ax.set_xticklabels(xlabels)

    plt.yticks([])
    plt.title('')
    plt.setp(patches, linewidth=0)
    plt.legend(loc='upper left')

    fig.tight_layout()
plot_histogram_01()

result of code above

Ответ 2

Извините, я не знаком с matplotlib. Поэтому у меня есть грязный хак для вас. Я просто поместил все значения, которые больше 300 в одном бункере, и изменил размер бункера.

Корень проблемы заключается в том, что matplotlib пытается поместить все бункеры в график. В R я конвертировал свои ящики в факторную переменную, поэтому они не рассматриваются как реальные числа.

import matplotlib.pyplot as plt
import numpy as np

def plot_histogram_01():
    np.random.seed(1)
    values_A = np.random.choice(np.arange(600), size=200, replace=True).tolist()
    values_B = np.random.choice(np.arange(600), size=200, replace=True).tolist()
    values_A_to_plot = [301 if i > 300 else i for i in values_A]
    values_B_to_plot = [301 if i > 300 else i for i in values_B]

    bins = [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325]

    fig, ax = plt.subplots(figsize=(9, 5))
    _, bins, patches = plt.hist([values_A_to_plot, values_B_to_plot], normed=1,  # normed is deprecated and will be replaced by density
                                bins=bins,
                                color=['#3782CC', '#AFD5FA'],
                                label=['A', 'B'])

    xlabels = np.array(bins[1:], dtype='|S4')
    xlabels[-1] = '300+'

    N_labels = len(xlabels)

    plt.xticks(25 * np.arange(N_labels) + 12.5)
    ax.set_xticklabels(xlabels)

    plt.yticks([])
    plt.title('')
    plt.setp(patches, linewidth=0)
    plt.legend()

    fig.tight_layout()
    plt.savefig('my_plot_01.png')
    plt.close()

plot_histogram_01()

enter image description here