векторизованный подход к биннингу с numpy / scipy в Python - PullRequest
5 голосов
/ 03 мая 2010

Я складываю 2d-массив (x по y) в Python в ячейки его значения x (заданного в «bin»), используя np.digitize:

elements_to_bins = digitize(vals, bins)

где "vals" - это двумерный массив, т. Е .:

 vals = array([[1, v1], [2, v2], ...]). 

elements_to_bins просто говорит, в какую корзину попадает каждый элемент. Затем я хочу получить список, длина которого равна количеству бинов в «корзинах», и каждый элемент возвращает у-размерность «vals», которая попадает в этот лот. Я делаю это прямо сейчас:

points_by_bins = []
for curr_bin in range(min(elements_to_bins), max(elements_to_bins) + 1):
    curr_indx = where(elements_to_bins == curr_bin)[0]
    curr_bin_vals = vals[:, curr_indx]
    points_by_bins.append(curr_bin_vals)

Есть ли более элегантный / простой способ сделать это? Все, что мне нужно, - это список списков значений y, которые попадают в каждую ячейку.

спасибо.

Ответы [ 3 ]

3 голосов
/ 03 мая 2010

Если я правильно понимаю ваш вопрос:

vals = array([[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]])  # Example

(x, y) = vals.T  # Shortcut
bin_limits = range(min(x)+1, max(x)+2)  # Other limits could be chosen
points_by_bin = [ [] for _ in bin_limits ]  # Final result
for (bin_num, y_value) in zip(searchsorted(bin_limits, x, "right"), y):  # digitize() finds the correct bin number
    points_by_bin[bin_num].append(y_value)

print points_by_bin  # [[10, 11], [20, 21, 22]]

Быстрая работа массива Numpy searchsorted() используется для максимальной эффективности. Затем значения добавляются одно за другим (поскольку конечный результат не является прямоугольным массивом, Numpy не может сильно помочь в этом). Это решение должно быть быстрее, чем несколько вызовов where() в цикле, что заставляет Numpy многократно перечитывать один и тот же массив.

1 голос
/ 31 мая 2015

Это вернет структуру данных, аналогичную IDL HISTOGRAM Reverse_Indices:

ovec = np.argsort(vals)
ivec = np.searchsorted(vals, bin_limits, sorter=ovec)

Тогда список элементов, попадающих в корзину #i, равен

ovec[ ivec[i] : ivec[i+1] ]

(мои быстрые временные тесты говорят, что это в 5 раз быстрее, чем алгоритм EOL, поскольку он не мешает создавать списки разных размеров)

0 голосов
/ 04 мая 2010

Являются ли ключи bin целыми числами, а не binning, как в вашем примере? Тогда вы могли бы просто сделать это, без NumPy:

from collections import defaultdict
bins = defaultdict(list)  # or [ [] ...] as in EOL

vals = [[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]]  # nparray.tolist()
for nbin, val in vals:
    bins[nbin].append(val)

print "bins:", bins
# defaultdict(<type 'list'>, {1: [10, 11], 2: [20, 21, 22]})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...