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()
исключительный случай? Этот документация не дает ясности, и не делают следующие два потока, которые имеют касательное отношение к вопросу:
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]])
последнее замечание: если реализация многопоточна, результат небезопасной функции может быть даже недетерминированным, поскольку зависит от порядка, в котором обрабатываются элементы массива.