Гистограмма 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?
Ответы
Ответ 1
Numpy имеет удобную функцию для решения этой np.clip
: np.clip
. Несмотря на то, как может звучать название, оно не удаляет значения, оно просто ограничивает их указанным диапазоном. В основном это делает Артем "грязным хаком", встроенным. Вы можете оставить значения такими, какие они есть, но в вызове hist
просто оберните массив np.clip
, вот так
plt.hist(np.clip(values_A, bins[0], bins[-1]), bins=bins)
Это лучше по ряду причин:
-
Это намного быстрее - по крайней мере, для большого количества элементов. Numpy делает свою работу на уровне C. Работа со списками Python (как в понимании списка Artem) имеет много накладных расходов для каждого элемента. По сути, если у вас есть возможность использовать NumPy, вы должны.
-
Вы делаете это там, где это необходимо, что снижает вероятность ошибок в вашем коде.
-
Вам не нужно держать вторую копию массива, которая уменьшает использование памяти (кроме как в пределах этой одной строки) и еще больше снижает вероятность ошибок.
-
Использование 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]()