Python/Numpy

3. Computation on Numpy Arrays : Universal Functions

njh1008 2025. 6. 17. 09:13

Introducing Ufuncs

  • Python's default implementation does some operations very slowly.
  • For many types of operations, Numpy provides a convenient interface into just this kind of statically typed, compiled routine. This is known as a vertorized operation
  • This vectorized approach is designed to push the loop into the compiled layer that underlies Numpy, leading to much faster execution
  • Vectorized operation in Numpy are implemented via ufuncs, whose main purpose is to quickly execute repeated operations on values in Numpy arrays. Ufuncs are extremely flexible.

Exploring Ufuncs

  • Ufuncs exist in two flavors: unary ufuncs, which operate on a single input, and binary ufuncs, which operate on two inputs.

Array Arithmetic

# In[1]
x=np.arange(4)
print("x     =",x)
print("x + 5 =",x+5)
print("x - 5 =",x-5)
print("x * 2 =",x*2)
print("x / 2 =",x/2)
print("x // 2 =",x//2)
# Out[1]
x     = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [0.  0.5 1.  1.5]
x // 2 = [0 0 1 1]

# In[2]
print("-x     =",-x)
print("x ** 2 =",x**2)
print("x % 2  =",x%2)
# Out[2]
-x     = [ 0 -1 -2 -3]
x ** 2 = [0 1 4 9]
x % 2  = [0 1 0 1]

# In[3]
-(0.5*x + 1) ** 2
# Out[3]
array([-1.  , -2.25, -4.  , -6.25])

Arithmetic operators implemented in Numpy

Operator Equivalent ufunc Description
+ np.add Addition
- np.subtract Subtraction
- np.negative Unary negation
* np.multiply Multiplication
/ np.divide Division
// np.floor_divide Floor division
** np.power Exponentiation
% np.mod Modulus/remainder

Absolute Value

  • Using np.absolute or np.abs
    # In[4]
    x=np.array([-2,-1,0,1,2])
    print(abs(x))
    print(np.absolute(x))
    print(abs(x))
    # Out[4]
    [2 1 0 1 2]
    [2 1 0 1 2]
    [2 1 0 1 2]

# In[5]
x=np.array([3-4j,4-3j,2+0j,0+1j])
np.abs(x)
# Out[5]
array([5., 5., 2., 1.])

Trigonometric Functions

# In[6]
theta=np.linspace(0,np.pi,3)
print("theta      =",theta)
print("sin(theta) =",np.sin(theta))
print("cos(theta) =",np.cos(theta))
print("tan(theta) =",np.tan(theta))
# Out[6]
theta      = [0.         1.57079633 3.14159265]
sin(theta) = [0.0000000e+00 1.0000000e+00 1.2246468e-16]
cos(theta) = [ 1.000000e+00  6.123234e-17 -1.000000e+00]
tan(theta) = [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]

# In[7]
x=[-1,0,1]
print("x         =",x)
print("arcsin(x) =",np.arcsin(x))
print("arccos(x) =",np.arccos(x))
print("arctan(x) =",np.arctan(x))
# Out[7]
x         = [-1, 0, 1]
arcsin(x) = [-1.57079633  0.          1.57079633]
arccos(x) = [3.14159265 1.57079633 0.        ]
arctan(x) = [-0.78539816  0.          0.78539816]

Exponents and logarithms

# In[8]
x=[1,2,3]
print("x    =",x)
print("e^x  =",np.exp(x))
print("2^x  =",np.exp2(x))
print("3^x  =",np.power(3,x))
# Out[8]
x    = [1, 2, 3]
e^x  = [ 2.71828183  7.3890561  20.08553692]
2^x  = [2. 4. 8.]
3^x  = [ 3  9 27]

# In[9]
x=[1,2,4,10]
print("x       =",x)
print("ln(x)   =",np.log(x))
print("log2(x) =",np.log2(x))
print("log10(x)=",np.log10(x))
# Out[9]
x       = [1, 2, 4, 10]
ln(x)   = [0.         0.69314718 1.38629436 2.30258509]
log2(x) = [0.         1.         2.         3.32192809]
log10(x)= [0.         0.30103    0.60205999 1.        ]

# In[10]
x=[0,0.001,0.01,0.1]
print("exp(x) - 1 =",np.expm1(x))
print("log(1 + x) =",np.log1p(x))
# Out[10]
exp(x) - 1 = [0.         0.0010005  0.01005017 0.10517092]
log(1 + x) = [0.         0.0009995  0.00995033 0.09531018]

Specialized Ufuncs

  • Another excellent source for more specialized ufuncs is the submodule scipy.special. If you want to compute some obscure mathematical function on your data, chances are it is implemented in scipy.special.
    # In[11]
    from scipy import special

# In[12]
# Gamma function (generalized factorials) and related functions
x=[1,5,10]
print("gamma(x)     =",special.gamma(x))
print("ln|gamma(x)| =",special.gammaln(x))
print("beta(x,2)    =",special.beta(x,2))
# Out[12]
gamma(x)     = [1.0000e+00 2.4000e+01 3.6288e+05]
ln|gamma(x)| = [ 0.          3.17805383 12.80182748]
beta(x,2)    = [0.5        0.03333333 0.00909091]

# In[13]
# Error function (integral of Gaussian)
x=np.array([0,0.3,0.7,1.0])
print("erf(x)    =",special.erf(x))
print("erfc(x)   =",special.erfc(x))
print("erfinv(X) =",special.erfinv(x))
# Out[13]
erf(x)    = [0.         0.32862676 0.67780119 0.84270079]
erfc(x)   = [1.         0.67137324 0.32219881 0.15729921]
erfinv(X) = [0.         0.27246271 0.73286908        inf]

Advanced Ufuncs Features

Specifying Output

  • In large calculations, it is useful to be able to specify the array where the result of the calculation will be stored.
  • Rather than creating a temporary array, you can use this to write computation results directly to the memory location where you'd like them to be.
  • For all ufuncs, you can do this out argument of the function.
    # In[14]
    x=np.arange(5)
    y=np.empty(5)
    np.multiply(x,10,out=y)
    print(x)
    print(y)
    # Out[14]
    [0 1 2 3 4]
    [ 0. 10. 20. 30. 40.]

# In[15]
y=np.zeros(10)
np.power(2,x,out=y[::2])
print(y)
# Out[15]
[ 1.  0.  2.  0.  4.  0.  8.  0. 16.  0.]

Aggregations

  • reduce method : if we'd like to reduce an array with a particular operation.
    # In[16]
    x=np.arange(1,6)
    print(np.add.reduce(x))
    # Out[16]
    15

# In[17]
np.multiply.reduce(x)
# Out[17]
120
  • accumulate method : if we'd like to store all the intermediate results of the computation.
    # In[18]
    np.add.accumulate(x)
    # Out[18]
    array([ 1,  3,  6, 10, 15])

# In[19]
np.multiply.accumulate(x)
# Out[19]
array([  1,   2,   6,  24, 120])

Outer Products

  • outer method : any ufunc can compute the output of all pairs of two different inputs using this method.
    # In[20]
    x=np.arange(1,6)
    np.multiply.outer(x,x)
    # Out[20]
    array([[ 1,  2,  3,  4,  5],
         [ 2,  4,  6,  8, 10],
         [ 3,  6,  9, 12, 15],
         [ 4,  8, 12, 16, 20],
         [ 5, 10, 15, 20, 25]])