Numpy передает входной массив как аргумент " out " в ufunc

обычно безопасно ли предоставлять входной массив в качестве необязательного аргумента out для ufunc в numpy, если тип правильный? Например, я проверил, что работает следующее:

>>> import numpy as np
>>> arr = np.array([1.2, 3.4, 4.5])
>>> np.floor(arr, arr)
array([ 1.,  3.,  4.])

тип массива должен быть либо совместим, либо идентичен выходу (который является float для numpy.floor()), или это происходит:

>>> arr2 = np.array([1, 3, 4], dtype = np.uint8)
>>> np.floor(arr2, arr2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ufunc 'floor' output (typecode 'e') could not be coerced to provided output parameter (typecode 'B') according to the casting rule ''same_kind''

Итак, учитывая, что массив надлежащего типа, как правило, безопасно применять ufuncs на месте? Или floor() исключительный случай? Этот документация не дает ясности, и не делают следующие два потока, которые имеют касательное отношение к вопросу:

  1. Numpy изменить массив на месте?
  2. Numpy Ceil и пол" из " аргумент

EDIT:

как догадка первого порядка, я бы предположил, что это часто, но не всегда безопасно, на основе учебника в http://docs.scipy.org/doc/numpy/user/c-info.ufunc-tutorial.html. Как представляется, нет никаких ограничений на использование выходного массива в качестве временного держателя для промежуточных результатов во время вычисления. Хотя что-то вроде floor() и ciel() не может требовать временного хранения, более сложные функции могут. При этом вся существующая библиотека может быть написана с учетом этого.

1 ответов


на out параметром функции numpy является массив, в котором записывается результат. Главное преимущество использования out избегает выделения новой памяти там, где это не требуется.

безопасно ли использовать запись вывода функции в том же массиве, что и вход? Общего ответа нет, это зависит от того, что делает функция.

два примера:

вот два примера ufunc-подобных функции:

In [1]: def plus_one(x, out=None):
   ...:     if out is None:
   ...:         out = np.zeros_like(x)
   ...: 
   ...:     for i in range(x.size):
   ...:         out[i] = x[i] + 1
   ...:     return out
   ...: 

In [2]: x = np.arange(5)

In [3]: x
Out[3]: array([0, 1, 2, 3, 4])

In [4]: y = plus_one(x)

In [5]: y
Out[5]: array([1, 2, 3, 4, 5])

In [6]: z = plus_one(x, x)

In [7]: z
Out[7]: array([1, 2, 3, 4, 5])

функции shift_one:

In [11]: def shift_one(x, out=None):
    ...:     if out is None:
    ...:         out = np.zeros_like(x)
    ...: 
    ...:     n = x.size
    ...:     for i in range(n):
    ...:         out[(i+1) % n] = x[i]
    ...:     return out
    ...: 

In [12]: x = np.arange(5)

In [13]: x
Out[13]: array([0, 1, 2, 3, 4])

In [14]: y = shift_one(x)

In [15]: y
Out[15]: array([4, 0, 1, 2, 3])

In [16]: z = shift_one(x, x)

In [17]: z
Out[17]: array([0, 0, 0, 0, 0])

функции plus_one нет проблем: ожидаемый результат получается, когда параметры x и out являются одним и тем же массивом. Но функция shift_one дает удивительный результат, когда параметры x и out являются одним и тем же массивом, потому что массив

Обсуждение

функции вида out[i] := some_operation(x[i]), например plus_one выше но также пол функций, ceil, грех, cos, tan, журнал, автомобилей и т. д., Насколько я знаю это безопасное для записи результата на вход с помощью параметра out.

также безопасное для функций, принимающих два входных параметра вида " out[i]: = some_operation(x[i], y[i]), таких как функция numpy add, multiply, subtract.

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

In [18]: a = np.arange(4).reshape((2,2))

In [19]: a
Out[19]: 
array([[0, 1],
       [2, 3]])

In [20]: b = (np.arange(4) % 2).reshape((2,2))

In [21]: b
Out[21]: 
array([[0, 1],
       [0, 1]], dtype=int32)

In [22]: c = np.dot(a, b)

In [23]: c
Out[23]: 
array([[0, 1],
       [0, 5]])

In [24]: d = np.dot(a, b, out=a)

In [25]: d
Out[25]: 
array([[0, 1],
       [0, 3]])

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