NumPy: np.sign(), np.signbit(), np.copysign()

Posted: | Tags: Python, NumPy

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 value nan
    • 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 value nan
    • 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 value nan
    • 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''

Related Categories

Related Articles