2015-09-03 2 views
4

У меня есть массив массивов данных формы (N, 20, 20), где N - некоторое очень большое число. Я хочу получить количество уникальных значений в каждом из 20-сегментных массивов 20x20. с петлей, которая была бы:vectorize numpy unique для subarrays

values = [] 
for i in data: 
    values.append(len(np.unique(i))) 

Как я мог векторизации этот цикл? скорость - это проблема.

Если я попробую np.unique (данные), я получаю уникальные значения для всего массива данных, а не отдельные блоки 20x20, так что это не то, что мне нужно.

+0

Рассматривали ли вы запись функции Fortran для этого и обертывание ее с помощью f2py? В подпрограмме fortran вы можете легко распараллелить с помощью OpenMP. Я часто прибегаю к этому подходу, когда мне нужно ускорить цикл с интенсивным вычислением. – deepak

+1

Другим подходом может быть использование numba http://numba.pydata.org. У этого есть декоратор векторизации, который, я считаю, может быть применим к этому случаю.Я не эксперт в numba, поэтому вы можете взглянуть на него. – deepak

+0

Спасибо deepak. Я не знаю Fortran, я бы предпочел использовать cython, я должен использовать другой язык. Я могу изучить, как использовать numba. – martinako

ответ

3

Во-первых, вы можете работать с data.reshape(N,-1), так как вас интересует сортировка последних 2-х измерений.

простой способ, чтобы получить число уникальных значений для каждой строки сбрасывать каждую строку в набор, и пусть это сделать сортировку:

[len(set(i)) for i in data.reshape(data.shape[0],-1)] 

Но это итерация, через вероятно, быстрый один ,

Проблема с «векторизации» заключается в том, что набор или список уникальных значений в каждой строке будет отличаться по длине. «строки с различной длиной» - это красный флаг, когда речь идет о «векторизации». У вас больше нет «прямоугольной» компоновки данных, которая делает возможным большинство векторизации.

Вы можете сортировать каждую строку:

np.sort(data.reshape(N,-1)) 

array([[1, 2, 2, 3, 3, 5, 5, 5, 6, 6], 
     [1, 1, 1, 2, 2, 2, 3, 3, 5, 7], 
     [0, 0, 2, 3, 4, 4, 4, 5, 5, 9], 
     [2, 2, 3, 3, 4, 4, 5, 7, 8, 9], 
     [0, 2, 2, 2, 2, 5, 5, 5, 7, 9]]) 

Но как можно определить уникальные значения в каждой строке без перебора? Подсчет числа ненулевых различий может просто сделать трюк:

In [530]: data=np.random.randint(10,size=(5,10)) 

In [531]: [len(set(i)) for i in data.reshape(data.shape[0],-1)] 
Out[531]: [7, 6, 6, 8, 6] 

In [532]: sdata=np.sort(data,axis=1) 
In [533]: (np.diff(sdata)>0).sum(axis=1)+1    
Out[533]: array([7, 6, 6, 8, 6]) 

Я собирался добавить предупреждение о поплавках, но если np.unique работает для ваших данных, мой подход должен работать так же хорошо.


[(np.bincount(i)>0).sum() for i in data] 

Это итеративный решение, которое явно быстрее, чем мой len(set(i)) версии, и конкурентоспособна с diff...sort.

В [585]: data.shape Out [585]: (10000, 400)

In [586]: timeit [(np.bincount(i)>0).sum() for i in data] 
1 loops, best of 3: 248 ms per loop 

In [587]: %%timeit          
sdata=np.sort(data,axis=1) 
(np.diff(sdata)>0).sum(axis=1)+1 
    .....: 
1 loops, best of 3: 280 ms per loop 

Я только что нашел более быстрый способ использовать bincount, np.count_nonzero

In [715]: timeit np.array([np.count_nonzero(np.bincount(i)) for i in data]) 
10 loops, best of 3: 59.6 ms per loop 

Я был удивленно улучшил скорость. Но затем я вспомнил, что count_nonzero используется в других функциях (например, np.nonzero), чтобы выделить пространство для их результатов возврата. Поэтому имеет смысл, что эта функция будет закодирована для максимальной скорости. (Это не помогает в случае diff...sort, потому что он не принимает параметр оси).

+0

Спасибо. Он работает! Хотя я надеялся заставить его работать быстрее. По моим данным занимает около 7 с, и я хотел бы сделать это менее чем за 1 секунду. Если нет другого ответа с более быстрым методом, я соглашусь с вами. – martinako

+0

Итак, «векторизовать» - это действительно код для «самого быстрого»? :) 'np.sort' занимает около 3/4 времени; часть 'diff' только 1/4. 'sort' by row занимает столько же времени, сколько и сортировка всего сплющенного массива. – hpaulj

+0

Я нашел версию «bincount», которая немного быстрее, но она повторяется по строке. – hpaulj

Смежные вопросы