Enable Dark Mode!
universal-functions-ufuncs-in-numpy.jpg
By: Arwa VV

Universal Functions (ufuncs) in NumPy

Technical

NumPy, a fundamental library for numerical computing in Python, offers a powerful feature known as Universal Functions (ufuncs). These ufuncs are a cornerstone of NumPy, enabling efficient and flexible computations on arrays by performing element-wise operations without the need for explicit looping. Let's delve deeper into the essence of NumPy ufuncs and their significance in scientific computing. 

NumPy ufuncs are functions that operate on ndarray objects in an element-by-element fashion. They provide a way to execute mathematical, logical, and other operations on arrays efficiently. Ufuncs support a wide range of arithmetic operations such as addition, subtraction, multiplication, division, and more. NumPy ufuncs encompass an extensive collection of functions covering various mathematical operations (like trigonometric functions, exponential functions, logarithms), statistical functions (mean, standard deviation, percentile), bitwise operations, comparison operations, and more. We utilize ufuncs because, in NumPy, vectorization is implemented using ufuncs, which is significantly faster than iterating over elements.

Vectorization is the process of transforming iterative statements into a vector-based action. It is faster since modern CPUs are geared for these kinds of tasks.

For example, to add the elements of two lists using ufunc, instead of iterating over both of the lists and then finding the sum of each element, you can use NumPy ufunc called add(x, y).

import numpy as np
array1 = np.array([1, 2, 3, 4, 5])
array2 = np.array([10, 20, 30, 40, 50])
result = np.add(array1, array2)
print(result)       # Output: [11 22 33 44 55]

While NumPy provides a vast collection of built-in ufuncs, users can create custom ufuncs to perform specialized operations using frompyfunc() and vectorize().

frompyfunc() allows you to create a Universal Function (ufunc) from an existing Python function, enabling you to apply this function element-wise to NumPy arrays. 

The frompyfunc() method takes the following arguments:

function - the name of the function.

inputs - the number of input arrays.

outputs - the number of output arrays.

import numpy as np
# Define a Python function that adds two numbers
def add_numbers(x, y):
return x + y
# Create a ufunc using np.frompyfunc() from the Python function
add_ufunc = np.frompyfunc(add_numbers, 2, 1)
array1 = np.array([1, 2, 3])
array2 = np.array([4, 5, 6])
# Apply the ufunc to arrays
result = add_ufunc(array1, array2)
print(result)      # Output: [5 7 9]

In this example:

add_numbers() is a simple Python function that adds two numbers.

np.frompyfunc() takes this function, specifies the number of input arguments (2), and the number of output arguments (1).

The resulting ufunc, add_ufunc, can now be used to apply the add_numbers() function element-wise to NumPy arrays array1 and array2.

vectorize() is another method to create a custom ufunc. It's a convenient way to vectorize a Python function to apply it to NumPy arrays.

import numpy as np
# Define a Python function for a custom operation
def multiply(x, y):
    return x * y  
# Vectorize the custom Python function using np.vectorize()
custom_ufunc = np.vectorize(multiply)
# Create NumPy arrays
array_x = np.array([1, 2, 3])
array_y = np.array([4, 5, 6])
# Apply the custom ufunc to arrays
result = custom_ufunc(array_x, array_y)
print(result)       # Output: [4 10 18]

Both frompyfunc() and vectorize() allow users to create custom ufuncs. However, for better performance, native NumPy ufuncs implemented in C are preferred over custom Python-based ufuncs for computation-intensive tasks. Custom ufuncs are useful for specialized operations where vectorization is not directly available through native NumPy functions.

To check the type of a function if it is a ufunc or not:

import numpy as np
print(type(np.add))

<class 'numpy.ufunc'> will be returned if it's a ufunc.

import numpy as np
if type(np.add) == np.ufunc:
  print('add is ufunc')
else:
  print('add is not ufunc')

Output: add is ufunc

Simple Arithmetic

ufuncs in NumPy allow for performing simple arithmetic operations on arrays efficiently.

import numpy as np
array_a = np.array([10, 20, 30, 38])
array_b = np.array([2, 4, 6, 8])
# Addition using the add() ufunc
addition_result = np.add(array_a, array_b)
# Subtraction using the subtract() ufunc
subtraction_result = np.subtract(array_a, array_b)
# Multiplication using the multiply() ufunc
multiplication_result = np.multiply(array_a, array_b)
# Division using the divide() ufunc
division_result = np.divide(array_a, array_b)
# Finding power using the power() ufunc
power_result = np.power(array_a, array_b)
# Finding remainder using the mod() and remainder() ufunc
mod_result = np.mod(array_a, array_b)
remainder_result = np.remainder(array_a, array_b)
# Finding both the quotient and the the mod using divmod()ufunc
quotient_result = np.divmod(array_a, array_b)
print("Array A:", array_a)
print("Array B:", array_b)
print("Addition Result:", addition_result)
print("Subtraction Result:", subtraction_result)
print("Multiplication Result:", multiplication_result)
print("Division Result:", division_result)
print("Power Result:", power_result)
print("Mod Result:", mod_result)
print("Remainder Result:", remainder_result)
print("Quotient Result:", quotient_result)

Output:

Array A: [10 20 30 38]

Array B: [2 4 6 8]

Addition Result: [12 24 36 46]

Subtraction Result: [ 8 16 24 30]

Multiplication Result: [ 20  80 180 304]

Division Result: [5.   5.   5.   4.75]

Power Result: [          100        160000     729000000 4347792138496]

Mod Result: [0 0 0 6]

Remainder Result: [0 0 0 6]

Quotient Result: (array([5, 5, 5, 4]), array([0, 0, 0, 6]))

Addition using np.add(): Adds corresponding elements of array_a and array_b.

Subtraction using np.subtract(): Subtracts corresponding elements of array_b from array_a.

Multiplication using np.multiply(): Multiplies corresponding elements of array_a and array_b.

Division using np.divide(): Divides corresponding elements of array_a by array_b.

Power using np.power(): Raises elements of array_a to the power of elements in array_b.

Modulus using np.mod() and np.remainder(): Finds the remainder after division of array_a by array_b.

Quotient and mod using np.divmod(): Returns both the quotient and the remainder of the division of array_a by array_b.

Rounding Decimals

NumPy provides a set of ufuncs for rounding decimal numbers efficiently. These ufuncs allow you to round elements within NumPy arrays to a specified number of decimal places or to the nearest whole number. The most commonly used rounding ufuncs in NumPy are:

* round()

* floor()

* ceil()

* trunc()

* fix()

round() function rounds each element of an array to the nearest integer or to a specified number of decimals if the decimals parameter is provided.

import numpy as np
decimal_array = np.array([3.1416, 2.7182, 5.98765, 9.12345])
# Round elements to the nearest integer
rounded_integers = np.round(decimal_array)
# Round elements to two decimal places
rounded_decimals = np.round(decimal_array, decimals=2)
print("Original Array:", decimal_array)
print("Rounded to Nearest Integer:", rounded_integers)
print("Rounded to Two Decimals:", rounded_decimals)

Output:

Original Array: [3.1416  2.7182  5.98765 9.12345]

Rounded to Nearest Integer: [3. 3. 6. 9.]

Rounded to Two Decimals: [3.14 2.72 5.99 9.12]

floor() rounds elements of an array to the nearest integer less than or equal to the original value.

ceil() rounds elements of an array to the nearest integer greater than or equal to the original value.

import numpy as np
decimal_array = np.array([3.1416, 2.7182, 5.98765, 9.12345])
# Round elements down to the nearest integer (floor)
floor_values = np.floor(decimal_array)
# Round elements up to the nearest integer (ceil)
ceil_values = np.ceil(decimal_array)
print("Original Array:", decimal_array)
print("Rounded Down (Floor):", floor_values)
print("Rounded Up (Ceil):", ceil_values)

Output:

Original Array: [3.1416  2.7182  5.98765 9.12345]

Rounded Down (Floor): [3. 2. 5. 9.]

Rounded Up (Ceil): [ 4.  3.  6. 10.]

trunc() and fix() function truncates each element towards zero, effectively removing the decimal part without rounding.

import numpy as np
decimal_array = np.array([3.1416, -2.7182, 5.98765, -9.12345])
# Truncate decimal values towards zero
truncated_values = np.trunc(decimal_array)
print("Original Array:", decimal_array)
print("Truncated Values:", truncated_values)

Output:

Original Array: [ 3.1416  -2.7182   5.98765 -9.12345]

Truncated Values: [ 3. -2.  5. -9.]

NumPy Summations

In NumPy, you can perform summations on arrays using various functions. NumPy provides the numpy.sum() function to calculate the sum of array elements along a specified axis or of the entire array. 

Difference between summation and addition:

Addition is done between two numbers or operands whereas summation happens over n elements.

1. Summing all elements in an array:

import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
# Calculating the sum of all elements in the array
total_sum = np.sum(arr)
print(total_sum)      # Output: 21

2. Cumulative sum:

Cumulative sum refers to the running total of a sequence of numbers or elements in which each element is added to the sum of the preceding elements. It creates a new array or sequence where each element represents the sum of all elements preceding it, including itself.

import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# Calculating the cumulative sum of elements
cumulative_sum = np.cumsum(arr)
print(cumulative_sum)    # Output: [ 1  3  6 10 15]

3. Summing elements along a specific axis:

import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
# Calculating the sum along rows (axis=0)
row_sum = np.sum(arr, axis=0)
print(row_sum)        # Output: [5 7 9]
# Calculating the sum along columns (axis=1)
col_sum = np.sum(arr, axis=1)
print(col_sum)        # Output: [ 6 15]

Numpy Products

NumPy has ufuncs that allow for the computation of products of array elements. Some of the key product-related ufuncs in NumPy include:

1. Products:

prod() function computes the product of array elements over a specified axis or        the entire array if no axis is specified.

import numpy as np
arr = np.array([1, 2, 3, 4])
x = np.prod(arr)
print(x)           # Output: 24    
This returns the product of all elements, ie, 1 * 2 * 3 * 4 = 24.
import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([5, 6, 7, 8])
x = np.prod([arr1, arr2])
print(x)           # Output: 40320

This calculates the product considering the entire list as a single element.

Therefore, the result x would be the product of all elements in the list [arr1, arr2] as if it were a single element, resulting in x = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 = 40320.

2. Product Over an Axis:

import numpy as np
arr = np.array([[1, 2], [3, 4]])
# Compute the product along rows (axis=0)
product_along_rows = np.prod(arr, axis=0)
print(product_along_rows)   # Output: [3 8]
# Compute the product along columns (axis=1)
product_along_cols = np.prod(arr, axis=1)
print(product_along_cols)   # Output: [ 2 12]

3. Cumulative Product:

Cumulative product represents a series of successive results obtained by multiplying the elements within a given sequence together.

import numpy as np
arr = np.array([5, 6, 7, 8])
cum_prod = np.cumprod(arr)
print(cum_prod)  # Output: [   5   30  210 1680]

NumPy Differences

diff() function in NumPy computes the differences between consecutive elements along a specified axis.

import numpy as np
arr = np.array([1, 3, 6, 10, 15])
diff_arr = np.diff(arr)
print(diff_arr)        # Output: [2 3 4 5]

NumPy Logs

NumPy provides several ufuncs for computing logarithms, allowing users to perform logarithmic operations on arrays efficiently. The main logarithmic ufuncs in NumPy include log(), log10() and log2().

log() function computes the natural logarithm (base e) of each element in the array.

import numpy as np
array = np.array([1, 2, 3, 4, 5])
logarithm_values = np.log(array)
print(logarithm_values)

Output: [0.         0.69314718 1.09861229 1.38629436 1.60943791]

log10() function computes the base-10 logarithm of each element in the array.

import numpy as np
array = np.array([1, 10, 100, 1000])
log10_values = np.log10(array)
print(log10_values)        # Output: [0. 1. 2. 3.]

log2() function computes the base-2 logarithm of each element in the array.

import numpy as np
array = np.array([1, 2, 4, 8, 16])
log2_values = np.log2(array)
print(log2_values)         # Output: [0. 1. 2. 3. 4.]

To find a log at any base,  NumPy does not have a function to take log at any base, so we can utilize the frompyfunc() function in conjunction with the built-in math.log() function to handle multiple input parameters and determine the output accordingly.

from math import log
import numpy as np
nplog = np.frompyfunc(log, 2, 1)
print(nplog(100, 15))       # Output: 1.7005483074552052

NumPy LCM Lowest Common Multiple

The Lowest Common Multiple is the smallest positive integer that is a common multiple of two numbers.

import numpy as np
n1 = 6
n2 = 8
lcm = np.lcm(n1, n2)
print(lcm)      # Output: 24

To find LCM in arrays, we can use the reduce() method.

import numpy as np
arr = np.array([3, 6, 9])
lcm = np.lcm.reduce(arr)
print(lcm)        # Output: 18

Here, output is 18 ie, the lowest common multiple of all three numbers (3*6=18, 6*3=18 and 9*2=18).

NumPy GCD Greatest Common Denominator

GCD, or "Greatest Common Divisor," also known as the greatest common factor or highest common factor, represents the largest positive integer that can evenly divide two or more integers without resulting in a remainder.

import numpy as np
n1 = 6
n2 = 9
gcd = np.gcd(n1, n2)
print(gcd)       # Output: 3

To find the GCD of all values in an array, the reduce() method can be used.

import numpy as np
arr = np.array([20, 8, 32, 36, 16])
gcd = np.gcd.reduce(arr)
print(gcd)       # Output: 4

NumPy Trigonometric Functions

In NumPy, trigonometric functions are available as universal functions (ufuncs) that operate element-wise on an array. 

1. np.sin() computes the sine of each element in the array.

import numpy as np
arr = np.array([np.pi/2, np.pi/3, np.pi/4, np.pi/5])
x = np.sin(arr)
print(x)   # Output: [1.         0.8660254  0.70710678 0.58778525]

2. np.cos() computes the cosine of each element in the array.

import numpy as np
array = np.array([0, np.pi/2, np.pi])
cos = np.cos(array)
print(cos)   # Output: [ 1.000000e+00  6.123234e-17 -1.000000e+00]

3. np.tan() computes the tangent of each element in the array.

import numpy as np
array = np.array([0, np.pi/4, np.pi/2])
tan = np.tan(array)
print(tan)  # Output: [ 0.00000000e+00  1.00000000e+00  1.63312394e+16]

4. np.deg2rad() converts angles from degrees to radians. It takes an input angle in degrees and returns the equivalent angle in radians.

import numpy as np
degrees = 90
radians = np.deg2rad(degrees)
print(radians)  # Output: 1.5707963267948966

5. np.rad2deg() is used to convert angles from radians to degrees. It takes an input angle in radians and returns the equivalent angle in degrees.

import numpy as np
radians = np.pi / 2  # 90 degrees in radians
degrees = np.rad2deg(radians)
print(degrees)  # Output: 90.0

6. np.arcsin() computes the inverse sine (also known as arcsine) of the elements in an array. It takes the values within the range [-1, 1] and returns the corresponding angles in radians.

import numpy as np
sine_values = np.array([0, 0.5, 1])
arcsine_result = np.arcsin(sine_values)
print(arcsine_result)  # Output: [0.         0.52359878 1.57079633]

7. np.arccos() computes the inverse cosine (arccosine) of the elements in an array

import numpy as np
cosine_values = np.array([1, 0.5, 0])
arccosine_result = np.arccos(cosine_values)
print(arccosine_result)  # Output: [0.         1.04719755 1.57079633]

8. np.arctan() computes the inverse tangent (arctangent) of the elements in an array. 

import numpy as np
tangent_values = np.array([0, 1, np.sqrt(3)])
arctan_result = np.arctan(tangent_values)
print(arctan_result)  # Output: [0.         0.78539816 1.04719755]

9. np.hypot() calculates the hypotenuse given the lengths of the two perpendicular sides of a right-angled triangle.

import numpy as np
base = 3
perp = 4
hypot = np.hypot(base, perp)
print(hypot)      # Output: 5.0

NumPy Set Operations

In NumPy, set operations can be performed on arrays to compute various set operations such as union, intersection, difference, symmetric difference, etc., between arrays. NumPy provides several functions to perform these set operations.

import numpy as np
arr = np.array([1, 1, 1, 2, 3, 4, 5, 5, 6, 7])
x = np.unique(arr)
print(x)

1. unique() is used to find unique elements from any array. It's important to note that the unique() function operates solely on 1-D arrays to identify distinct elements.

2. union1d() computes the union of two arrays, returning a sorted array of unique elements that are present in either of the input arrays.

import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([3, 4, 5, 6])
newarr = np.union1d(arr1, arr2)
print(newarr)

2. np.intersect1d() computes the intersection of two arrays, returning a sorted array of unique elements that are present in both input arrays.

import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([3, 4, 5, 6])
intersect = np.intersect1d(arr1, arr2)
print(intersect)  # Output: [3 4]

4. np.setdiff1d() computes the set difference between two arrays, returning a sorted array of unique elements present in the first array but not in the second array.

import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([3, 4, 5, 6])
intersect = np.intersect1d(arr1, arr2)
print(intersect)  # Output: [3 4]

5. np.setxor1d() computes the symmetric difference between two arrays, returning a sorted array of unique elements that are present in either of the input arrays, but not in both.

import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([3, 4, 5, 6])
symmetric_diff = np.setxor1d(arr1, arr2)
print(symmetric_diff)  # Output: [1 2 5 6]

6. np.setdiff1d() computes the set difference between two arrays, returning a sorted array of unique elements present in the first array but not in the second array.

import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([3, 4, 5, 6])
diff = np.setdiff1d(arr1, arr2)
print(diff)  # Output: [1 2]

NumPy Hyperbolic Functions

Hyperbolic functions are analogs of trigonometric functions for hyperbolas, just as sine and cosine are for circles.

In NumPy, you can find the following hyperbolic functions:

1. numpy.sinh(x) computes the hyperbolic sine of the elements of the input array.

2. numpy.cosh(x) computes the hyperbolic cosine of the elements of the input array.

3. numpy.tanh(x) computes the hyperbolic tangent of the elements of the input array.

4. numpy.arcsinh(x) computes the inverse hyperbolic sine (or arcsine) of the elements of the input array.

5. numpy.arccosh(x) computes the inverse hyperbolic cosine (or arccosine) of the elements of the input array.

6. numpy.arctanh(x) computes the inverse hyperbolic tangent (or arctangent) of the elements of the input array.

import numpy as np
x = np.array([0.5, 1.0, 2.0])
# Hyperbolic sine
sinh_vals = np.sinh(x)
print("Hyperbolic sine:", sinh_vals)
# Hyperbolic cosine
cosh_vals = np.cosh(x)
print("Hyperbolic cosine:", cosh_vals)
# Hyperbolic tangent
tanh_vals = np.tanh(x)
print("Hyperbolic tangent:", tanh_vals)
# Inverse hyperbolic sine
arcsinh_vals = np.arcsinh(1.0)
print("Inverse hyperbolic sine:", arcsinh_vals)
# Inverse hyperbolic cosine
arccosh_vals = np.arccosh(1.0)
print("Inverse hyperbolic cosine:", arccosh_vals)
# Inverse hyperbolic tangent
arctanh_vals = np.arctanh([0.1, 0.2, 0.5])
print("Inverse hyperbolic tangent:", arctanh_vals)

Output:

Hyperbolic sine: [0.52109531 1.17520119 3.62686041]

Hyperbolic cosine: [1.12762597 1.54308063 3.76219569]

Hyperbolic tangent: [0.46211716 0.76159416 0.96402758]

Inverse hyperbolic sine: 0.881373587019543

Inverse hyperbolic cosine: 0.0

Inverse hyperbolic tangent: [0.10033535 0.20273255 0.54930614]

These are some of the universal functions in NumPy. In conclusion, NumPy universal functions (ufuncs) play a pivotal role in numerical computations by providing efficient element-wise operations on arrays. Moreover, NumPy's seamless integration with other functionalities, such as random number generation (NumPy Random), enhances its versatility, making it an indispensable tool for Python programmers and data scientists alike. If you want to know more about the Numpy and NumPy installation process, refer to our previous blog about Numpy. 


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message