2010-05-02 1 views
5

Я биннинг в 2d массива (х на у) в Python в закрома его значения х (в данном «закромах»), используя np.digitize:Векторизованный подход к биннингу с Numpy/SciPy в Python

elements_to_bins = digitize(vals, bins) 

где "Vals" является 2d массив, то есть:

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

elements_to_bins просто говорит, что бен каждый элемент попадает. То, что я тогда хочу сделать, это получить список, длина которого - количество ящиков в «бункерах», и каждый элемент возвращает y-меру «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, которые попадают в каждый бит.

спасибо.

+0

Если один из ответов решил вашу проблему, отметьте его как принятый (зеленая галочка)! :) – EOL

ответ

3

Если я правильно понимаю ваш вопрос: быстрая операция searchsorted() массива

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 в использовании для достижения максимальной эффективности. Значения затем добавляются один за другим (так как конечный результат не является прямоугольной матрицей, для этого Nump не может много помочь). Это решение должно быть быстрее, чем несколько вызовов where() в цикле, которые вынуждают Numpy повторно читать то же множество.

+1

numpy.searchsorted следует предпочесть оцифровать по соображениям производительности: https://github.com/numpy/numpy/issues/2656 – Alleo

+0

@Alleo: Очень хорошая точка (для текущей реализации 'digitize()'). Я обновил ответ. – EOL

0

Являются ли ключи бина только целыми числами, без биннинга, как в вашем примере? Тогда вы могли бы просто сделать это без 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]}) 
+0

+1: это выглядит хорошо для меня, кроме, может быть, из-за того, что пустые ячейки не содержат пустых списков (которые могут быть исправлены с помощью defaultdict). Однако, может быть, у оригинального плаката есть более общие бункеры? – EOL

1

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

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

Затем список элементов, которые попадают в бен #i является

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

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