Использование numpy.bincount с весами массива - PullRequest
5 голосов
/ 05 марта 2011

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

np.bincount([1, 1, 0],weights=np.array([1, 2, 4]))
Out: array([ 4.,  3.])

Однако я хотел бы использовать массив измерений 2 как:

np.bincount([1, 1, 0],weights=np.array([[1,1], [2,2], [4,4]]))
ValueError: object too deep for desired array

Желаемый вывод:

Out: array([[ 4.,  4.],[3., 3.]])

Лучшее объяснение после комментариев:

Я хочу суммировать каждую строку массива в соответствующий индекс.

С циклом это будет:

Bin=np.zeros(2,2)
for i in [1,1,0]:
    Bin[i]+=a[i]

a - предыдущая матрица 3x2 Есть ли эффективный способ получить этот результат?

Ответы [ 2 ]

3 голосов
/ 05 марта 2011

Согласно справочной документации:

numpy.bincount(x, weights=None, minlength=None)

весовые коэффициенты: массив_подобный, необязательный; Веса, массив той же формы, что и x.

Таким образом, вы не можете напрямую использовать bincount, если не измените x каким-либо образом.

Редактировать: Итак, я придумал немного хитрый способ сделать это, но не гарантирую производительность при работе с большими массивами.В основном я собираюсь использовать, как скудные разреженные матрицы обрабатывают повторяющиеся записи с одинаковыми индексами (они их суммируют):

 from scipy.sparse import *
 a = np.array([[1,1], [2,2], [4,4]])
 ii = np.array([1, 1, 0])

 ares = a.reshape((-1,),order='F')
 # ares == array([1, 2, 4, 1, 2, 4])

 col = np.tile(ii,(a.shape[1],))
 # col == np.array([1, 1, 0, 1, 1, 0])

 row = np.tile([0,1],(a.shape[0],1)).reshape((-1,),order='F') 
 # row == np.array([0,0,0,1,1,1]) 

 g = coo_matrix((ares,(col,row)),shape=(2,2))
 print g.todense()     

Теперь вам нужно обобщить это для ваших точных данных.Основная идея заключается в том, что вы хотите отобразить каждую точку данных на правильный элемент массива результатов, а затем позволить разреженному массиву обрабатывать суммирование повторяющихся записей.

В противном случае, я бы посмотрел на использование Cython, если вы вынужденыЧтобы решить эту проблему, используйте цикл.

Редактировать 2: Для ударов я рассчитал два разных метода:

import numpy as np
from scipy.sparse import *

def method1():
    return np.array([np.bincount(ii, r) for r in a.T]).T

def method2():
    ares = a.reshape((-1,),order='F')
    col = np.tile(ii,(a.shape[1],))
    row = np.tile(np.arange(a.shape[1]),(a.shape[0],1)).reshape((-1,),order='F') 

    return coo_matrix((ares,(col,row)),shape=(np.unique(ii).size,a.shape[1])).todense()

if __name__ == '__main__':
    from timeit import Timer

    a = np.random.randint(0,1000,(1000000,3))
    ii = np.random.randint(0,10,(a.shape[0],))

    N = 100
    t1 = Timer("method1()", "from __main__ import method1")
    t2 = Timer("method2()", "from __main__ import method2")
    print 't2/t1: %f' % (t2.timeit(N)/t1.timeit(N))

На моей машине method2 составляет около 3-5x медленнее, чем method1, в зависимости от формы входов, поэтому зацикливание не обязательно является плохим вариантом.

0 голосов
/ 01 апреля 2019

Вы должны использовать матрицу scipy csr для представления индексов, а затем скалярное произведение с вашими данными. На моем ноутбуке это в 14 раз быстрее, чем у @ JoshAdel method1 и в 54 раза быстрее, чем у @ JoshAdel method2 для больших матриц.

def method1():
    return np.array([np.bincount(ii, r) for r in a.T]).T

def method2():
    ares = a.reshape((-1,),order='F')
    col = np.tile(ii,(a.shape[1],))
    row = np.tile(np.arange(a.shape[1]),(a.shape[0],1)).reshape((-1,),order='F') 

    return coo_matrix((ares,(col,row)),shape=(ii.max()+1,a.shape[1])).todense()

def method3():
    csr = csr_matrix((np.ones(ii.shape[0]), (ii, np.arange(ii.shape[0]))))
    return csr*a

Давайте сгенерируем случайные данные и рассчитаем их время:

n = 1<<18
d = 512
ii = np.random.randint(low=1, high=1<<10, size=n)
a = np.random.randn((n, d))

%timeit method1()
# 1 loop, best of 3: 3.13 s per loop

%timeit method2()
# 1 loop, best of 3: 11.7 s per loop

%timeit method3()
# 1 loop, best of 3: 216 ms per loop

# sanity checks:
assert (method1() == method2()).all()
assert (method1() == method3()).all()

примечание: я заменил np.unique(ii).size в method2 на ii.max()+1

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...