9.8. Operators¶
The first kind of math numpy runs on an
ndarray is the standard Python
operators. Arithmetic, comparison, and bit-wise
operators all work element-wise: each operator walks
the array (or both arrays) once from start to finish
inside a single library call, much faster than the
equivalent Python for loop.
9.8.1. Arithmetic¶
+, -, *, /, //, %, ** all
work between two arrays of compatible shape, or between
an array and a scalar:
a = np.array([1, 2, 3, 4], dtype=np.float)
b = np.array([10, 20, 30, 40], dtype=np.float)
print(a + b) # array([11.0, 22.0, 33.0, 44.0])
print(a * 2) # array([2.0, 4.0, 6.0, 8.0])
print(b - a) # array([9.0, 18.0, 27.0, 36.0])
print(b / a) # array([10.0, 10.0, 10.0, 10.0])
The result dtype follows the upcasting rules described on Dtypes. Integer arrays wrap on overflow; cast to a wider dtype before the operation when that matters.
The matrix-multiplication operator @ is not
implemented. Use dot() for matrix /
vector products (see Linear algebra).
9.8.1.1. In-place forms¶
Every arithmetic operator has an in-place form – +=,
-=, *=, /=, %=, **=. The in-place
form writes through the existing buffer instead of
allocating a temporary:
b = b + 1 # allocates a temporary the size of b
b += 1 # no temporary
On a microcontroller the second form is essentially mandatory for any hot loop; Performance covers the broader pattern.
9.8.2. Bitwise¶
The bit-wise operators &, |, ^ work
element-wise on integer arrays. Applied to a float
or complex array they raise TypeError:
a = np.array([0b1100, 0b1010], dtype=np.uint8)
b = np.array([0b1010, 0b1100], dtype=np.uint8)
print(a & b) # array([8, 8], dtype=uint8)
print(a | b) # array([14, 14], dtype=uint8)
print(a ^ b) # array([6, 6], dtype=uint8)
The unary ~ performs bit-wise NOT on an integer
array.
The shift operators << and >> are not wired
up at the Python operator level. The function forms
left_shift() and
right_shift() work:
np.left_shift(a, 2)
np.right_shift(b, 1)
9.8.3. Comparison¶
==, !=, <, <=, >, >= all return a
bool ndarray of the broadcast
shape:
a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)
print(a < 3)
# array([True, True, False, False, False], dtype=bool)
The boolean result is exactly what indexing and Selection and rearrangement consume.
9.8.3.1. The side rule¶
The ndarray must be on the
left of a relational operator when comparing to a
scalar. a > 2 works; 2 < a raises TypeError.
For the symmetric form, use the function names:
np.greater(5, a) # 5 > a, element-wise
np.less(5, a) # 5 < a, element-wise
np.equal(5, a) # 5 == a, element-wise
np.not_equal(5, a) # 5 != a, element-wise
The function form is also the right choice for portable code targeting environments where the in-line operators are not overloaded (CircuitPython, for example).
9.8.4. Unary operators¶
+a– returns a copy of the array.-a– negation. On unsigned dtypes the values wrap modulo \(2^N\), the same way the binary operators do.abs(a)– element-wise absolute value. On unsigned dtypes returns a copy without computation.~a– bit-wise inversion (integer arrays only).len(a)– returns the length of the first axis, matching the Python sequence convention.
9.8.5. What is missing¶
The right-hand-side operators for the comparison and
some of the bit-wise operations are not implemented in
the same way the arithmetic operators are. Use the
function forms (above) when an ndarray would land on
the right.
For the complete list of supported operators and the upcasting they follow, see numpy — numpy-compatible array operations.