2013-07-31 9 views
4

У меня есть два буферизированных массива numpy (a и b). Мне нужно найти, сколько их элементов равны. В настоящее время я делаю len(a) - (a^b).sum(), но операция xor создает совершенно новый массив numpy, как я понимаю. Как эффективно реализовать это желаемое поведение без создания ненужного временного массива?Вычислить сумму результатов оператора без выделения ненужного массива

Я пробовал использовать numexpr, но я не могу заставить его работать правильно. Он не поддерживает идею, что True равен 1, а False - 0, поэтому я должен использовать ne.evaluate("sum(where(a==b, 1, 0))"), что занимает примерно в два раза больше.

Редактировать: Я забыл упомянуть, что один из этих массивов на самом деле представляет собой представление другого массива другого размера, и оба массива должны считаться неизменяемыми. Оба массива 2-мерные и имеют размер около 25x40.

Да, это узкое место моей программы и стоит оптимизировать.

+0

Мне кажется необычным, что это ваше узкое место, а не то, что генерирует входные данные для эту часть вашего кода. Откуда берутся 'a' и' b'? – user2357112

+0

На это приходится примерно 30% времени выполнения моей общей программы. Я ищу буквы в изображении. У меня 62 небольших изображения, и я проверяю, сколько пикселей у них общее с большим изображением в нескольких местах. – Ponkadoodle

ответ

2

На моей машине это быстрее:

(a == b).sum() 

Если вы не хотите использовать какую-либо дополнительную память, чем я хотел бы предложить использовать Numba. Я не слишком хорошо знаком с этим, но это работает хорошо. Я столкнулся с некоторыми проблемами с получением Cython для получения логического массива NumPy.

from numba import autojit 
def pysumeq(a, b): 
    tot = 0 
    for i in xrange(a.shape[0]): 
     for j in xrange(a.shape[1]): 
      if a[i,j] == b[i,j]: 
       tot += 1 
    return tot 
# make numba version 
nbsumeq = autojit(pysumeq) 
A = (rand(10,10)<.5) 
B = (rand(10,10)<.5) 
# do a simple dry run to get it to compile 
# for this specific use case 
nbsumeq(A, B) 

Если у вас нет Numba, я предложил бы использовать ответ на @ user2357112

Edit: Просто есть Cython версия работает, вот .pyx файл. Я бы пошел с этим.

from numpy cimport ndarray as ar 
cimport numpy as np 
cimport cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
def cysumeq(ar[np.uint8_t,ndim=2,cast=True] a, ar[np.uint8_t,ndim=2,cast=True] b): 
    cdef int i, j, h=a.shape[0], w=a.shape[1], tot=0 
    for i in xrange(h): 
     for j in xrange(w): 
      if a[i,j] == b[i,j]: 
       tot += 1 
    return tot 
+0

Спасибо за первое предложение - я предположил, что оператор равенства вернет одно логическое значение, а не применяется к каждому элементу. Numba выглядит как боль, которую нужно настроить, чтобы установить пользовательский RTL-LLVM и все, что я бы предпочел не заниматься. – Ponkadoodle

+0

Да, это сложно настроить, если вы уже не используете дистрибутив Anaconda Python. Я бы использовал Cython, но я не мог заставить его хорошо работать с буферизированными массивами NumPy. – IanH

+0

Хорошо, я просто вычислил способ сделать это с Cython и обновил ответ. – IanH

1

Для начала вы можете пропустить затем сделать шаг * B:

>>> a 
array([ True, False, True, False, True], dtype=bool) 
>>> b 
array([False, True, True, False, True], dtype=bool) 
>>> np.sum(~(a^b)) 
3 

Если вы не против уничтожения массива а или б, я не уверен, что вы получите быстрее, чем это:

>>> a^=b #In place xor operator 
>>> np.sum(~a) 
3 
+2

'~ a' создает промежуточный массив,' np.bitwise_not (a, out = a) 'не должен. Но если массивы длинны, умножение двух скаляров, вероятно, будет быстрее, чем отрицание массива. – Jaime

+0

Стоит также отметить, что умножение на самом деле в любом случае предварительно вычислено. Эта часть была псевдокодом. – Ponkadoodle

1

Если проблема распределения и открепление, сохранить единый массив выходной и сказать NumPy поместить результаты там каждый раз, когда:

out = np.empty_like(a) # Allocate this outside a loop and use it every iteration 
num_eq = np.equal(a, b, out).sum() 

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

+0

Вы имеете в виду 'np.logical_xor'? – Daniel

+0

Nope. Он хочет знать, сколько элементов равно. Более просто использовать функцию, которая проверяет, равны ли вещи. Тем не менее, я перепутал пространство имен в исходной версии. – user2357112

+0

Я, возможно, сойду с ума, но я не могу найти модуль 'logic' для numpy. – Daniel

0

Улучшения при ответе IanH, это также можно получить доступ к основному массиву C в Numpy массива изнутри Cython путем подачи mode="c" в ndarray.

from numpy cimport ndarray as ar 
cimport numpy as np 
cimport cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
cdef int cy_sum_eq(ar[np.uint8_t,ndim=2,cast=True,mode="c"] a, ar[np.uint8_t,ndim=2,cast=True,mode="c"] b): 
    cdef int i, j, h=a.shape[0], w=a.shape[1], tot=0 
    cdef np.uint8_t* adata = &a[0, 0] 
    cdef np.uint8_t* bdata = &b[0, 0] 
    for i in xrange(h): 
     for j in xrange(w): 
      if adata[j] == bdata[j]: 
       tot += 1 
     adata += w 
     bdata += w 
    return tot 

Это примерно 40% быстрее на моей машине, чем Cython версии IanH, и я обнаружил, что переставлять содержимое цикла, кажется, не делают большой разницы в этот момент, вероятно, из-за оптимизации компилятора.На этом этапе можно было бы связать с функцией C, оптимизированной с помощью SSE, и таким образом выполнить эту операцию и пройти adata и bdata как uint8_t* s

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

  • Нет связанных вопросов^_^