Numpy как исключить массив из векторизации?

У меня есть функция:

      import numpy as np

t = np.array([['t1', 0],['t2',0],['t3',1],['t4',1]])
i = np.array(['t3', 't4'])

def myfunc(d, x): 
    return d[:,1][np.where(d[:,0] == x)]

vfunc = np.vectorize(myfunc, excluded=['d'])
vfunc(d=t,x=i)

Ожидаемый результат: array(['1', '1'], dtype='<U2')

Выдает ошибку: ValueError: setting an array element with a sequence

Я не понимаю, почему это не работает после аргумента исключения в документации: https://numpy.org/doc/stable/reference/generated/numpy.vectorize.html

3 ответа

Как сказал hpaulj Знай, проверяй, не угадывай .

Это было немного сложно определить, но вот оно.

Документы говорят By default, pyfunc is assumed to take scalars as input and output.

Чтобы удалить эту ошибку, добавьте аргумент otypes, вы не получите точного вывода, но numpy может по крайней мере правильно определить, что ему нужно вернуть

      vfunc = np.vectorize(myfunc, excluded=['d'], otypes='O')
      signature : string, optional

    Generalized universal function signature, e.g., (m,n),(n)->(m) for
 vectorized matrix-vector multiplication. If provided, pyfunc will be called
 with (and expected to return) arrays with shapes given by the size of
 corresponding core dimensions. By default, pyfunc is assumed to take scalars
 as input and output.

Ниже приводится подробная причина предлагаемого решения.

Ваша функция не возвращает скалярный вывод, она возвращает векторный вывод для каждого входного скаляра. В то время как входные данные являются скалярами (t3,) и (t4,), фактические выходные данные являются векторными. array(['1'], dtype='<U2'), array(['1'], dtype='<U2')] соответственно.

Поскольку вы не указываете подпись, по умолчанию numpy считает, что выходные данные скалярны, и пытается поместить их в массив numpy с dtypes как dtype ввода (но фактический dtype - это объект), пытаясь создать вектор.

Это ошибка ValueError: setting an array element with a sequence, потому что ваш вывод ufunc является вектором для каждого скаляра ввода, а не скаляром, а также не определена сигнатура (причина, по которой numpy предполагает, что вывод является скалярным).

Также посмотрите пример, похожий на ваш, в документах по векторизации numpy, numpy использует скалярный тип возврата, а не вектор

      def mypolyval(p, x):

    _p = list(p)

    res = _p.pop(0)

    while _p:

        res = res*x + _p.pop(0)

    return res

vpolyval = np.vectorize(mypolyval, excluded=['p'])

vpolyval(p=[1, 2, 3], x=[0, 1])
array([3, 6])

Я прихожу к такому выводу через отладку, так как у меня недостаточно опыта использования векторизации.

При отладке в function_base.py в строке L2257 накапливаются выходы, которые имеют значение array([array(['1'], dtype='<U2'), array(['1'], dtype='<U2')], dtype=object). Затем на L2260 numpy пытается преобразовать их в требуемый dtype, но терпит неудачу, потому что он предполагал последовательность скаляров, но получил последовательность последовательности.

Просто поставьте точку останова в vscode и попытайтесь увидеть выходные данные переменных, которые вы можете понять.

"векторизует" в том смысле, что позволяет передавать массив (ы) функции, которая в противном случае работает только со скалярными значениями. Но его бывает сложно использовать правильно, и это не улучшает производительность. Он не компилирует вашу функцию, поэтому низкоуровневые концепции «векторизации» не применяются.


      In [1]: t = np.array([['t1', 0],['t2',0],['t3',1],['t4',1]])
   ...: i = np.array(['t3', 't4'])
   ...: 
In [2]: def myfunc(d, x):
   ...:     return d[:,1][np.where(d[:,0] == x)]
   ...: 
   ...: vfunc = np.vectorize(myfunc, excluded=['d'])

Ваша проблема - с полным отслеживанием

      In [3]: vfunc(d=t,x=i)
Traceback (most recent call last):
  File "<ipython-input-3-ea7904300378>", line 1, in <module>
    vfunc(d=t,x=i)
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 2163, in __call__
    return self._vectorize_call(func=func, args=vargs)
  File "/usr/local/lib/python3.8/dist-packages/numpy/lib/function_base.py", line 2249, in _vectorize_call
    res = asanyarray(outputs, dtype=otypes[0])
ValueError: setting an array element with a sequence

Что производит ваша функция? массивы!

      In [4]: myfunc(t,i[0])
Out[4]: array(['1'], dtype='<U21')
In [5]: myfunc(t,i[1])
Out[5]: array(['1'], dtype='<U21')

Давайте попробуем еще раз, на этот раз отобразив значения, переданные в функцию, и настройку. Поскольку вы читаете достаточно, чтобы использовать excludeвы, должно быть, тоже встречали параметр. Эта проблема часто вызывает проблемы у спрашивающих SO. (На основании пробного расчета ваш vectorize установил otypes к str, в результате чего ValueError)

      In [6]: def myfunc(d, x):
   ...:     print(d,x)
   ...:     return d[:,1][np.where(d[:,0] == x)]
   ...: 
   ...: vfunc = np.vectorize(myfunc, excluded=['d'], otypes=['O'])
In [7]: vfunc(d=t,x=i)
[['t1' '0']
 ['t2' '0']
 ['t3' '1']
 ['t4' '1']] t3
[['t1' '0']
 ['t2' '0']
 ['t3' '1']
 ['t4' '1']] t4
Out[7]: 
array([array(['1'], dtype='<U21'), array(['1'], dtype='<U21')],
      dtype=object)

In [8]: np.hstack(_)
Out[8]: array(['1', '1'], dtype='<U21')

excluded действительно работал, весь dпроходил каждый раз. Альтернативой было бы определение функции для использования глобального массива, а не ожидания ее в качестве аргумента.

      def myfunc(x):
   print(x)
   return t[:,1][np.where(t[:,0] == x)]

Давайте немного обобщим аргументы

      In [12]: t = np.array([['t1', 0],['t2',0],['t3',1],['t4',1],['t4',10]])
    ...: i = np.array(['t3', 't4','t5'])
    ...: 
    ...: 
In [13]: vfunc(d=t,x=i)
[['t1' '0']
 ['t2' '0']
 ['t3' '1']
...
Out[13]: 
array([array(['1'], dtype='<U21'), array(['1', '10'], dtype='<U21'),
       array([], dtype='<U21')], dtype=object)

в результате получается 3 массива разного размера. hstack все равно будет работать.

Но с одним 1d аргументом было бы так же легко и быстрее использовать:

      In [14]: np.array([myfunc(t,j) for j in i])
[['t1' '0']
 ['t2' '0']
 ['t3' '1']
 ....
<ipython-input-14-207a42bdb1fb>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  np.array([myfunc(t,j) for j in i])
Out[14]: 
array([array(['1'], dtype='<U21'), array(['1', '10'], dtype='<U21'),
       array([], dtype='<U21')], dtype=object)

векторизация

Это может иметь много значений. simd vectorizationдля меня в новинку, но ведь я уже много лет не занимался программированием на низком уровне («С»). никоим образом не компилирует и не изменяет вашу функцию. У него есть четкий отказ от ответственности (я полагаю, он может быть выделен жирным шрифтом и в начале документов). Он предназначен в первую очередь для функций, которые принимают скалярные значения и возвращают такие же простые результаты, и наиболее полезен, когда он принимает несколько аргументов и для которых вы хотите воспользоваться преимуществами broadcasting.

      The vectorized function evaluates `pyfunc` over successive tuples
of the input arrays like the python map function, except it uses the
broadcasting rules of numpy.

Неофициально (и без документального обоснования) мы говорим о numpy«векторизация» как способ значительного увеличения производительности. На самом деле это означает перемещение итераций с уровня Python (циклы for, понимание списков) в скомпилированные методы numpy. Это разница между интерпретатором и компилятором. np.vectorize, несмотря на название, этого не делает.

Способ использования numpy-методов:

      In [17]: t[:,0]==i[:,None]
Out[17]: 
array([[False, False,  True, False, False],
       [False, False, False,  True,  True],
       [False, False, False, False, False]])

Добавляя измерение к, мы можем протестировать t[:,0] против всех iценности сразу. Применение where чтобы получить индексы:

      In [19]: np.where(t[:,0]==i[:,None])
Out[19]: (array([0, 1, 1]), array([2, 3, 4]))

и используя это для индексации t:

      In [20]: t[_[1],1]
Out[20]: array(['1', '1', '10'], dtype='<U21')

Или мы могли бы использовать логическую маску в Out [17] строка за строкой:

      In [21]: [t[j,1] for j in _17]
Out[21]: 
[array(['1'], dtype='<U21'),
 array(['1', '10'], dtype='<U21'),
 array([], dtype='<U21')]

С возможным сочетанием размеров массивов это сложно сделать без какой-либо итерации на уровне Python. Быстрые numpy-операции работают с многомерными массивами, а не с «рваными».

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

Я сам имел дело с этой проблемой, и переключение аргумента на ключевое слово мгновенно устранило ее.

Другие вопросы по тегам