Enable Dark Mode!
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 nparray1 = 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 numbersdef add_numbers(x, y):return x + y# Create a ufunc using np.frompyfunc() from the Python functionadd_ufunc = np.frompyfunc(add_numbers, 2, 1)array1 = np.array([1, 2, 3])array2 = np.array([4, 5, 6])# Apply the ufunc to arraysresult = add_ufunc(array1, array2)print(result)      # Output: [5 7 9]`

In this example:

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 operationdef multiply(x, y):    return x * y  # Vectorize the custom Python function using np.vectorize()custom_ufunc = np.vectorize(multiply)# Create NumPy arraysarray_x = np.array([1, 2, 3])array_y = np.array([4, 5, 6])# Apply the custom ufunc to arraysresult = 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 npprint(type(np.add))`

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

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

### Simple Arithmetic

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

`import numpy as nparray_a = np.array([10, 20, 30, 38])array_b = np.array([2, 4, 6, 8])# Addition using the add() ufuncaddition_result = np.add(array_a, array_b)# Subtraction using the subtract() ufuncsubtraction_result = np.subtract(array_a, array_b)# Multiplication using the multiply() ufuncmultiplication_result = np.multiply(array_a, array_b)# Division using the divide() ufuncdivision_result = np.divide(array_a, array_b)# Finding power using the power() ufuncpower_result = np.power(array_a, array_b)# Finding remainder using the mod() and remainder() ufuncmod_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()ufuncquotient_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]))

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 npdecimal_array = np.array([3.1416, 2.7182, 5.98765, 9.12345])# Round elements to the nearest integerrounded_integers = np.round(decimal_array)# Round elements to two decimal placesrounded_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 npdecimal_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 npdecimal_array = np.array([3.1416, -2.7182, 5.98765, -9.12345])# Truncate decimal values towards zerotruncated_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.

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

1. Summing all elements in an array:

`import numpy as nparr = np.array([[1, 2, 3], [4, 5, 6]])# Calculating the sum of all elements in the arraytotal_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 nparr = np.array([1, 2, 3, 4, 5])# Calculating the cumulative sum of elementscumulative_sum = np.cumsum(arr)print(cumulative_sum)    # Output: [ 1  3  6 10 15]`

3. Summing elements along a specific axis:

`import numpy as nparr = 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 nparr = 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 nparr1 = 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 nparr = 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 nparr = 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 nparr = 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 nparray = 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 nparray = 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 nparray = 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 logimport numpy as npnplog = 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 npn1 = 6n2 = 8lcm = np.lcm(n1, n2)print(lcm)      # Output: 24`

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

`import numpy as nparr = 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 npn1 = 6n2 = 9gcd = 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 nparr = 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 nparr = 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 nparray = 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 nparray = 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 npdegrees = 90radians = 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 npradians = np.pi / 2  # 90 degrees in radiansdegrees = 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 npsine_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 npcosine_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 nptangent_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 npbase = 3perp = 4hypot = 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 nparr = 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 nparr1 = 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 nparr1 = 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 nparr1 = 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 nparr1 = 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 nparr1 = 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 npx = np.array([0.5, 1.0, 2.0])# Hyperbolic sinesinh_vals = np.sinh(x)print("Hyperbolic sine:", sinh_vals)# Hyperbolic cosinecosh_vals = np.cosh(x)print("Hyperbolic cosine:", cosh_vals)# Hyperbolic tangenttanh_vals = np.tanh(x)print("Hyperbolic tangent:", tanh_vals)# Inverse hyperbolic sinearcsinh_vals = np.arcsinh(1.0)print("Inverse hyperbolic sine:", arcsinh_vals)# Inverse hyperbolic cosinearccosh_vals = np.arccosh(1.0)print("Inverse hyperbolic cosine:", arccosh_vals)# Inverse hyperbolic tangentarctanh_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.

### Recent Posts

#### How to Connect Devices and Kitchen Printers in Odoo 17 POS

Calicut

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

London

Cybrosys Limited
Alpha House,
100 Borough High Street, London,
SE1 1LB, United Kingdom

Kochi

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

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,