NumPy: np.sign(), np.signbit(), np.copysign()
This article describes the following functions related to the sign of the NumPy array numpy.ndarray
.
- Get the sign of each element of the NumPy array:
np.sign()
- Basic usage
- For negative zero, infinity
inf
, and missing valuenan
- For complex numbers
- Check the sign of each element of the NumPy array:
np.signbit()
- Basic usage
- Check with comparison operators
- Count the number of elements for each sign
- For negative zero, infinity
inf
, and missing valuenan
- For complex numbers
- Replace the sign of a NumPy array with that of another array:
np.copysign()
- Basic usage
- Broadcasting
- For negative zero, infinity
inf
, and missing valuenan
- For complex numbers
If you want to use sign()
or copysign()
without NumPy, see the following article.
Get the sign of each element of the NumPy array: np.sign()
You can get the sign of each element of the NumPy array with np.sign()
.
Basic usage
Pass numpy.ndarray
to numpy.sign()
. numpy.ndarray
is returned, where negative values are -1
, positive values are 1
, and 0
is 0
.
import numpy as np
a = np.array([-100, -10, 0, 10, 100])
print(a)
# [-100 -10 0 10 100]
print(np.sign(a))
# [-1 -1 0 1 1]
print(type(np.sign(a)))
# <class 'numpy.ndarray'>
print(np.sign(a).dtype)
# int64
The data type dtype
is the same as the original numpy.ndarray
.
a_float = np.array([-1.23, 0.0, 1.23])
print(a_float)
# [-1.23 0. 1.23]
print(np.sign(a_float))
# [-1. 0. 1.]
print(np.sign(a_float).dtype)
# float64
For scalar values, a scalar value is returned. In this case, the type is the same as the original type.
print(np.sign(100))
# 1
print(type(np.sign(100)))
# <class 'numpy.int64'>
print(np.sign(-1.23))
# -1.0
print(type(np.sign(-1.23)))
# <class 'numpy.float64'>
For negative zero, infinity inf
, and missing value nan
A floating-point number float
can represent negative zeros (= -0.0
).
numpy.sign()
returns 0.0
for both positive and negative 0.0
. Its sign is returned for the infinity inf
and nan
for nan
.
a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [ 0. -0. inf -inf nan]
print(np.sign(a_special))
# [ 0. 0. 1. -1. nan]
print(np.sign(a_special).dtype)
# float64
For complex numbers
In the case of complex numbers, np.sign()
returns the sign of the real part if the real part is not 0
or the sign of the imaginary part if the real part is 0
. All returned values are complex numbers whose imaginary part is 0
.
a_complex = np.array([[10 + 10j, -10 + 10j], [10 - 10j, -10 - 10j], [10, -10], [10j, -10j], [0, np.nan], [0j, np.nan * 1j]])
print(a_complex)
# [[ 10.+10.j -10.+10.j]
# [ 10.-10.j -10.-10.j]
# [ 10. +0.j -10. +0.j]
# [ 0.+10.j -0.-10.j]
# [ 0. +0.j nan +0.j]
# [ 0. +0.j nan+nanj]]
print(np.sign(a_complex))
# [[ 1.+0.j -1.+0.j]
# [ 1.+0.j -1.+0.j]
# [ 1.+0.j -1.+0.j]
# [ 1.+0.j -1.+0.j]
# [ 0.+0.j nan+0.j]
# [ 0.+0.j nan+0.j]]
Use the real
and imag
attributes to get the sign of the real and imaginary parts.
print(a_complex.real)
# [[ 10. -10.]
# [ 10. -10.]
# [ 10. -10.]
# [ 0. -0.]
# [ 0. nan]
# [ 0. nan]]
print(np.sign(a_complex.real))
# [[ 1. -1.]
# [ 1. -1.]
# [ 1. -1.]
# [ 0. 0.]
# [ 0. nan]
# [ 0. nan]]
print(a_complex.imag)
# [[ 10. 10.]
# [-10. -10.]
# [ 0. 0.]
# [ 10. -10.]
# [ 0. 0.]
# [ 0. nan]]
print(np.sign(a_complex.imag))
# [[ 1. 1.]
# [-1. -1.]
# [ 0. 0.]
# [ 1. -1.]
# [ 0. 0.]
# [ 0. nan]]
Check the sign of each element of the NumPy array: np.signbit()
You can check the sign of each element of the NumPy array with np.signbit()
. As the name suggests, it returns the sign bit as a boolean value. Negative values are True
, 0
and positive values are False
.
You can do the same with the comparison operator, as discussed below.
Basic usage
Pass numpy.ndarray
to numpy.signbit()
. numpy.ndarray
is returned, where negative values are True
, 0
and positive values are False
.
a = np.array([-100, -10, 0, 10, 100])
print(a)
# [-100 -10 0 10 100]
print(np.signbit(a))
# [ True True False False False]
print(type(np.signbit(a)))
# <class 'numpy.ndarray'>
print(np.signbit(a).dtype)
# bool
For scalar values, a scalar value is returned.
print(np.signbit(-100))
# True
Check with comparison operators
You can do the same as np.signbit()
with comparison operators.
print(a == 0)
# [False False True False False]
print(a > 0)
# [False False False True True]
print(a >= 0)
# [False False True True True]
print(a < 0)
# [ True True False False False]
print(a <= 0)
# [ True True True False False]
Count the number of elements for each sign
You can count the number of True
by passing numpy.ndarray
of bool
to numpy.count_nonzero()
.
Therefore, by specifying the result of numpy.signbit()
to numpy.count_nonzero()
, you can count the number of True
, that is, the number of negative elements.
print(np.count_nonzero(np.signbit(a)))
# 2
You can also negate each element with ~
. This allows you to count the number of False
, that is, the number of 0
and positive values.
print(~np.signbit(a))
# [False False True True True]
print(np.count_nonzero(~np.signbit(a)))
# 3
You can do the same with comparison operators.
print(np.count_nonzero(a == 0))
# 1
print(np.count_nonzero(a < 0))
# 2
print(np.count_nonzero(a > 0))
# 2
For negative zero, infinity inf
, and missing value nan
numpy.signbit()
determines zero and infinity inf
of a floating-point number float
based on its sign. nan
is considered False
.
a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [ 0. -0. inf -inf nan]
print(np.signbit(a_special))
# [False True False True False]
The results of the comparison with 0
are as follows.
print(a_special == 0)
# [ True True False False False]
print(a_special < 0)
# [False False False True False]
#
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
# """Entry point for launching an IPython kernel.
print(a_special > 0)
# [False False True False False]
#
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in greater
# """Entry point for launching an IPython kernel.
Both positive and negative 0
are considered equivalent to 0
. nan
is considered False
for all comparison operations.
In some environments, as in the example above, a warning may be issued for comparison operations on numpy.ndarray
with nan
. Since it is not an error, the process is not terminated.
For complex numbers
numpy.signbit()
does not support complex numbers.
a_complex = np.array([3 + 4j, -3 - 4j])
print(a_complex)
# [ 3.+4.j -3.-4.j]
# print(np.signbit(a_complex))
# TypeError: ufunc 'signbit' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
For complex numbers, you can get absolute values with np.abs()
and real and imaginary parts with real
and imag
attributes.
print(np.abs(a_complex))
# [5. 5.]
print(a_complex.real)
# [ 3. -3.]
print(a_complex.imag)
# [ 4. -4.]
print(np.signbit(a_complex.real))
# [False True]
print(a_complex.real < 0)
# [False True]
Replace the sign of a NumPy array with that of another array: np.copysign()
You can replace the sign of a NumPy array with that of another array with np.copysign()
.
Basic usage
The sign of the array of the first argument is replaced by the sign of the array of the second argument. The data type of the returned array is always a floating-point number float
.
a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
b = np.arange(-5, 7).reshape(3, 4)
print(b)
# [[-5 -4 -3 -2]
# [-1 0 1 2]
# [ 3 4 5 6]]
a_copysign = np.copysign(a, b)
print(a_copysign)
# [[-0. -1. -2. -3.]
# [-4. 5. 6. 7.]
# [ 8. 9. 10. 11.]]
print(a_copysign.dtype)
# float64
You can also specify a scalar value.
print(np.copysign(10, -5))
# -10.0
print(type(np.copysign(10, -5)))
# <class 'numpy.float64'>
Broadcasting
In operations between arrays of different shapes, the shapes are aligned by broadcasting when possible.
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
b_small = np.array([-100, -100, 100, 100])
print(b_small)
# [-100 -100 100 100]
print(a + b_small)
# [[-100 -99 102 103]
# [ -96 -95 106 107]
# [ -92 -91 110 111]]
Broadcast is also done in numpy.copysign()
.
print(np.copysign(a, b_small))
# [[-0. -1. 2. 3.]
# [-4. -5. 6. 7.]
# [-8. -9. 10. 11.]]
If it cannot be broadcast, an error is raised.
b_mismatch = np.array([-100, -100, 100])
print(b_mismatch)
# [-100 -100 100]
# print(np.copysign(a, b_mismatch))
# ValueError: operands could not be broadcast together with shapes (3,4) (3,)
You can also specify a scalar value for the second argument of numpy.copysign()
.
print(np.copysign(b, -10))
# [[-5. -4. -3. -2.]
# [-1. -0. -1. -2.]
# [-3. -4. -5. -6.]]
You can also use np.abs()
, which returns an absolute value, to align the signs of all elements. The return value of numpy.copysign()
is always float
, but np.abs()
returns an array of the type corresponding to the original data type.
print(np.abs(b) * -1)
# [[-5 -4 -3 -2]
# [-1 0 -1 -2]
# [-3 -4 -5 -6]]
print(np.abs(b) * -1.0)
# [[-5. -4. -3. -2.]
# [-1. -0. -1. -2.]
# [-3. -4. -5. -6.]]
For negative zero, infinity inf
, and missing value nan
In a floating-point number float
, zero and infinity inf
are treated like any other value because it has a sign.
If the first argument is nan
, it remains nan
no matter what the second argument is.
a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [ 0. -0. inf -inf nan]
print(np.copysign(a_special, 1))
# [ 0. 0. inf inf nan]
print(np.copysign(a_special, -1))
# [ -0. -0. -inf -inf nan]
If the second argument is nan
, it is considered positive.
print(np.copysign([10, 10, 10, 10, 10], a_special))
# [ 10. -10. 10. -10. 10.]
print(np.copysign([-10, -10, -10, -10, -10], a_special))
# [ 10. -10. 10. -10. 10.]
For complex numbers
numpy.copysign()
does not support complex numbers.
a_complex = np.array([10 + 10j, -10 + 10j])
print(a_complex)
# [ 10.+10.j -10.+10.j]
# print(np.copysign(a_complex, 1))
# TypeError: ufunc 'copysign' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
# print(np.copysign([1, 1], a_complex))
# TypeError: ufunc 'copysign' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''