#python #numpy
#python #numpy
Вопрос:
Вопрос:
У меня есть набор данных, подобный следующему:
import numpy as np
x = np.arange(0,10000,0.5)
y = np.arange(x.size)/x.size
При построении графика в пространстве log-log это выглядит следующим образом:
import matplotlib.pyplot as plt
plt.loglog(x, y)
plt.show()
Очевидно, что на этом графике журнала много избыточной информации.
Мне не нужно 10000 точек, чтобы представить эту тенденцию.
Мой вопрос заключается в следующем: как я могу объединить эти данные, чтобы они отображали одинаковое количество точек в каждом порядке величины логарифмической шкалы? При каждом порядке величины я хотел бы получить около десяти баллов. Следовательно, мне нужно выделить ‘x’ с экспоненциально растущим размером ячейки, а затем взять среднее значение всех элементов y
, соответствующих каждой ячейке.
Попытка:
Сначала я генерирую ячейки, для которых хочу использовать x
.
# need a nicer way to do this.
# what if I want more than 10 bins per order of magnitude?
bins = 10**np.arange(1,int(round(np.log10(x.max()))))
bins = np.unique((bins.reshape(-1,1)*np.arange(0,11)).flatten())
#array([ 0, 10, 20, 30, 40, 50, 60, 70, 80,
# 90, 100, 200, 300, 400, 500, 600, 700, 800,
# 900, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000,
# 9000, 10000])
Во-вторых, я нахожу индекс ячейки, которому соответствует каждый элемент x:
digits = np.digitize(x, bins)
Теперь часть, с которой мне действительно нужна помощь. Я хочу взять среднее значение каждого элемента в y
, соответствующего каждой ячейке, а затем построить график этих средних значений в зависимости от средних точек ячейки:
# need a nicer way to do this.. is there an np.searchsorted() solution?
# this way is quick and dirty, but it does not scale with acceptable speed
averages = []
for d in np.unique(digits):
mask = digits==d
y_mean = np.mean(y[mask])
averages.append(y_mean)
del mask, y_mean, d
# now plot the averages within each bin against the center of each bin
plt.loglog((bins[1:] bins[:-1])/2.0, averages)
plt.show()
Краткие сведения:
Есть ли более плавный способ сделать это? Как я могу сгенерировать произвольные n
точки на порядок величины вместо 10?
Комментарии:
1. Просто любопытно, почему разбиение их на ячейки приводит к меньшему потреблению ресурсов, чем их фактическое нанесение на график?
2. Привет @Anoop — это потому, что в этом примере
len(averages)
равно 28, аlen(y)
равно 10000, поэтому на первом графике 10000 точек, в то время как на втором меньше 30. В моем приложении наборы данных на самом деле содержат около 10 миллионов точек.
Ответ №1:
Я отвечу на два из ваших нескольких вопросов: Как альтернативно создать ячейки и сгенерировать произвольные n
точки на порядок величины вместо 10?
Вы можете использовать np.logspace
и np.outer
для создания своих ячеек для любого произвольного n
значения следующим образом. База по умолчанию в logspace
равна 10. Он генерирует логарифмически разнесенные точки, аналогичные linspace
который генерирует линейно разнесенную сетку.
Для n=10
n = 10
bins = np.unique(np.outer(np.logspace(0, 3, 4), np.arange(0, n 1)))
# array([0.e 00, 1.e 00, 2.e 00, 3.e 00, 4.e 00, 5.e 00, 6.e 00, 7.e 00,
# 8.e 00, 9.e 00, 1.e 01, 2.e 01, 3.e 01, 4.e 01, 5.e 01, 6.e 01,
# 7.e 01, 8.e 01, 9.e 01, 1.e 02, 2.e 02, 3.e 02, 4.e 02, 5.e 02,
# 6.e 02, 7.e 02, 8.e 02, 9.e 02, 1.e 03, 2.e 03, 3.e 03, 4.e 03,
# 5.e 03, 6.e 03, 7.e 03, 8.e 03, 9.e 03, 1.e 04])
Для n=20
n = 20
bins = np.unique(np.outer(np.logspace(0, 3, 4), np.arange(0, n 1)))
# array([0.0e 00, 1.0e 00, 2.0e 00, 3.0e 00, 4.0e 00, 5.0e 00, 6.0e 00, 7.0e 00, 8.0e 00, 9.0e 00, 1.0e 01, 1.1e 01, 1.2e 01, 1.3e 01, 1.4e 01, 1.5e 01, 1.6e 01, 1.7e 01, 1.8e 01, 1.9e 01, 2.0e 01, 3.0e 01, 4.0e 01, 5.0e 01, 6.0e 01, 7.0e 01, 8.0e 01, 9.0e 01, 1.0e 02, 1.1e 02, 1.2e 02, 1.3e 02, 1.4e 02, 1.5e 02, 1.6e 02, 1.7e 02, 1.8e 02, 1.9e 02, 2.0e 02, 3.0e 02, 4.0e 02, 5.0e 02, 6.0e 02, 7.0e 02, 8.0e 02, 9.0e 02, 1.0e 03, 1.1e 03, 1.2e 03, 1.3e 03, 1.4e 03, 1.5e 03, 1.6e 03, 1.7e 03, 1.8e 03, 1.9e 03, 2.0e 03, 3.0e 03, 4.0e 03, 5.0e 03, 6.0e 03, 7.0e 03, 8.0e 03, 9.0e 03, 1.0e 04, 1.1e 04, 1.2e 04, 1.3e 04, 1.4e 04, 1.5e 04, 1.6e 04, 1.7e 04, 1.8e 04, 1.9e 04, 2.0e 04])
Редактировать
Если вы хотите 0, 10, 20, 30...90, 100, 200, 300...
, вы можете сделать следующее
n = 10
bins = np.unique(np.outer(np.logspace(1, 3, 3), np.arange(0, n 1)))
# array([ 0., 10., 20., 30., 40., 50., 60., 70.,
# 80., 90., 100., 200., 300., 400., 500., 600.,
# 700., 800., 900., 1000., 2000., 3000., 4000., 5000.,
# 6000., 7000., 8000., 9000., 10000.])
Комментарии:
1. что, если я хочу, чтобы самая маленькая ячейка находилась между
0
и10
? Я понимаю, что не могу объединить свои данные в пределах этого диапазона, не уничтожив их. Таким образом,n=10
последовательность должна быть0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, ...
2.
bins = np.unique(np.outer(np.logspace(1, 3, 3), np.arange(0, n 2)))
?3. @kevinkayaks: Вы были близки к этому. Но вам просто нужно использовать
n 1
. Вы использовали(1,3,3)
правильно. Проверьте мою правку4. Спасибо! это очень помогает. Теперь я буду работать над усреднением всех
y
, соответствующих каждому компоненту bin5. @kevinkayaks: В вашем подходе вам не нужно,
flatten
потому чтоnp.unique
уже есть плоский одномерный массив.