Применение функции к массиву с использованием Numpy, когда функция содержит условие - PullRequest
0 голосов
/ 03 декабря 2018

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

pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1

Теперь, если я запускаю:

result = fun(pts, pts)

, я получаю ошибку

ValueError: Истинное значениемассив с более чем одним элементом является неоднозначным.Используйте a.any () или a.all ()

, поднятые в строке if x > y.Мой неэффективный обходной путь, который дает правильный результат, но слишком медленный, таков:

result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])

Каков наилучший способ добиться этого более хорошим (и, что более важно, более быстрым) способом?

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

pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1

Теперь, если я запускаю:

result = fun(pts, pts)

, я получаю ошибку

ValueError: Истинное значениемассив с более чем одним элементом является неоднозначным.Используйте a.any () или a.all ()

, поднятые в строке if x > y.Мой неэффективный обходной путь, который дает правильный результат, но слишком медленный, таков:

result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])

Каков наилучший способ добиться этого более хорошим (и, что более важно, более быстрым) способом?

EDIT : использование

def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
x = np.array(range(10))
y = np.array(range(10))
xv,yv = np.meshgrid(x,y)
result = fun(xv, yv)  

все еще вызывает то же самое ValueError.

Ответы [ 3 ]

0 голосов
/ 03 декабря 2018
In [253]: x = np.random.randint(0,10,5)
In [254]: y = np.random.randint(0,10,5)
In [255]: x
Out[255]: array([3, 2, 2, 2, 5])
In [256]: y
Out[256]: array([2, 6, 7, 6, 5])
In [257]: x>y
Out[257]: array([ True, False, False, False, False])
In [258]: np.where(x>y,0,1)
Out[258]: array([0, 1, 1, 1, 1])

Для декартового сравнения с этими двумя 1d массивами измените один, чтобы он мог использовать broadcasting:

In [259]: x[:,None]>y
Out[259]: 
array([[ True, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [ True, False, False, False, False]])
In [260]: np.where(x[:,None]>y,0,1)
Out[260]: 
array([[0, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 1, 1, 1, 1]])

Ваша функция с if работает только для скалярных входов.Если заданы массивы, a>b создает логический массив, который нельзя использовать в операторе if.Ваша итерация работает, потому что она передает скалярные значения.Для некоторых сложных функций это лучшее, что вы можете сделать (np.vectorize может сделать итерацию проще, но не быстрее).

Мой ответ - посмотреть на сравнение массивов и получить из этого ответ.В этом случае аргумент 3 where отлично справляется с отображением логического массива в желаемую 1/0.Существуют и другие способы сделать это отображение.

Ваш двойной цикл требует дополнительного уровня кодирования, транслируемого None.

0 голосов
/ 04 декабря 2018

Для более сложного примера, или если массивы, с которыми вы имеете дело, немного больше, или если вы можете записать в уже выделенный массив, вы можете рассмотреть Numba.

Пример

import numba as nb
import numpy as np

@nb.njit()
def fun(x, y):
  if x > y:
    return 0
  else:
    return 1

@nb.njit(parallel=False)
#@nb.njit(parallel=True)
def loop(x,y):
  result=np.empty((x.shape[0],y.shape[0]),dtype=np.int32)
  for i in nb.prange(x.shape[0]):
    for j in range(y.shape[0]):
      result[i,j] = fun(x[i], y[j])
  return result

@nb.njit(parallel=False)
def loop_preallocated(x,y,result):
  for i in nb.prange(x.shape[0]):
    for j in range(y.shape[0]):
      result[i,j] = fun(x[i], y[j])
  return result

Время

x = np.array(range(1000))
y = np.array(range(1000))

#Compilation overhead of the first call is neglected

res=np.where(x[:,None]>y,0,1) -> 2.46ms
loop(single_threaded)         -> 1.23ms
loop(parallel)                -> 1.0ms
loop(single_threaded)*        -> 0.27ms
loop(parallel)*               -> 0.058ms

* Может быть под влиянием кэша.Протестируйте на собственных примерах.

0 голосов
/ 03 декабря 2018

Ошибка довольно явная - предположим, у вас есть

x = np.array([1,2])
y = np.array([2,1])

, такой, что

(x>y) == np.array([0,1])

, что должно быть результатом вашего оператора if np.array([0,1])?это правда или ложь?numpy говорит вам, что это неоднозначно.Использование

(x>y).all()

или

(x>y).any()

является явным, и, таким образом, numpy предлагает вам решения - либо любая пара ячеек удовлетворяет условию, либо все они - и однозначная истиназначение.Вы должны определить для себя, что именно вы имели в виду под вектор x больше, чем вектор y .

Решение numpy для работы на всех парах x и y, таких какx[i]>y[j] должен использовать сетку сетки для генерации всех пар:

>>> import numpy as np
>>> x=np.array(range(10))
>>> y=np.array(range(10))
>>> xv,yv=np.meshgrid(x,y)
>>> xv[xv>yv]
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8,
       9, 4, 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 6, 7, 8, 9, 7, 8, 9, 8, 9, 9])
>>> yv[xv>yv]
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
       2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8])

либо отправьте xv и yv на fun, либо создайте сетку в функции, в зависимости от того, что делает большесмысл.Это генерирует все пары xi,yj, такие что xi>yj.Если вы хотите фактические индексы, просто верните xv>yv, где каждая ячейка ij соответствует x[i] и y[j].В вашем случае:

def fun(x, y):
    xv,yv=np.meshgrid(x,y)
    return xv>yv

вернет матрицу, где fun(x,y)[i][j] равно True, если x[i]>y[j], или False, в противном случае.В качестве альтернативы

return  np.where(xv>yv)

вернет кортеж из двух массивов пар индексов, так что

for i,j in fun(x,y):

также будет гарантировать x[i]>y[j].

...