# Universal Functions (ufunc) in NumPy

Ufuncs, short for Universal Functions, are special functions in NumPy designed to work on arrays element by element. They let you perform operations across entire arrays without needing loops, making your code much faster and more readable.

## Arithmetic Operations

First, let’s break down some common arithmetic ufuncs. With these functions, you can perform standard arithmetic operations like addition, subtraction, multiplication, and division on arrays.

### np.add()

The `np.add()`

function performs element-wise addition between two arrays, returning a new array containing the sum of corresponding elements.

**Example:**

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("Array 1:", array1) print("Array 2:", array2) print("Result of Element-wise Addition:", result)

#### Output:

```
Array 1: [1 2 3 4 5]
Array 2: [10 20 30 40 50]
Result of Element-wise Addition: [11 22 33 44 55]
```

### np.subtract()

The `np.subtract()`

takes two arrays and subtracts each element of the second array from the corresponding element of the first, giving you a new array with the differences.

**Example:**

result = np.subtract(array1, array2) print("Result of Element-wise Addition:", result)

#### Output:

`Result of Element-wise Addition: [ -9 -18 -27 -36 -45]`

### np.multiply()

The `np.multiply()`

is used for element-wise multiplication, where each element of one array is multiplied by the corresponding element of another, producing a new array.

**Example:**

result = np.multiply(array1, array2) print("Result of Element-wise Addition:", result)

#### Output:

`Result of Element-wise Addition: [ 10 40 90 160 250]`

### np.divide()

Divide elements in one array by the elements in another array using `np.divide()`

. It’s perfect for doing element-wise division and getting a new array with the results.

**Example:**

result = np.divide(array1, array2) print("Result of Element-wise Addition:", result)

#### Output:

`Result of Element-wise Addition: [0.1 0.1 0.1 0.1 0.1]`

### np.power()

The `numpy.power()`

raises elements of an array to specified powers, giving back a new array with the results of exponentiation.

**Example:**

exponents = np.array([2, 3, 2, 1, 2]) result = np.power(array1, exponents) print("Result of Element-wise Addition:", result)

#### Output:

`Result of Element-wise Addition: [ 1 8 9 4 25]`

## Aggregation and Reduction Functions

Aggregation and reduction operations in NumPy help condense large datasets into meaningful insights. They help in calculating totals, finding extremes, and understanding cumulative changes in data.

### np.sum()

The `np.sum()`

function adds up all the elements in an array, giving you the total sum of the data.

**Example:**

import numpy as np array = np.array([1, 2, 3, 4, 5]) total_sum = np.sum(array) print("Array:", array) print("Sum of Array Elements:", total_sum)

#### Output:

```
Array: [1 2 3 4 5]
Sum of Array Elements: 15
```

### np.min() and np.max()

These functions find the smallest and largest values in an array, respectively, helping you identify boundaries or extremes.

**Example:**

minimum = np.min(array) maximum = np.max(array) print("Minimum:",minimum) print("Maximum:",maximum)

#### Output:

```
Minimum: 1
Maximum: 5
```

### np.prod()

You can use `np.prod()`

function to multiply all the elements in an array together, useful for calculating cumulative products.

**Example:**

product = np.prod(array) print(product)

#### Output:

`120`

### np.cumsum()

With this function, you get the cumulative sum of elements in an array, showing how values accumulate over time or across categories.

**Example:**

cumulative_sum = np.cumsum(array) print(cumulative_sum)

#### Output:

`[ 1 3 6 10 15]`

### np.cumprod()

Similar to cumulative sums, cumulative products (`np.cumprod()`

) show how values multiply together as you progress through the array, indicating growth or decline trends.

**Example:**

cumulative_product = np.cumprod(array) print(cumulative_product)

#### Output:

`[ 1 2 6 24 120]`

## Other Mathematical Functions

Now, let’s explore some other NumPy’s math functions that provide efficient ways to perform common mathematical operations on arrays

### np.sqrt()

The `np.sqrt()`

function computes the square root of each element in an array, returning a new array with the square root values. It’s particularly useful for tasks involving distances, magnitudes, or scaling operations.

**Example:**

import numpy as np # Sample array arr = np.array([40, 9, 30, 45, 144]) # Compute square roots square_roots = np.sqrt(arr) print("Square Roots:", square_roots)

#### Output:

`Square Roots: [ 6.32455532 3. 5.47722558 6.70820393 12. ]`

### np.abs()

With the** **`numpy.abs()`

, you get the absolute value of each number in an array, meaning it turns all negative values positive. It’s handy for distance calculations or removing negativity from data.

**Example:**

import numpy as np # Sample array arr = np.array([-4, 9, -2, 0, 5]) # Compute absolute values absolute_values = np.abs(arr) print("Absolute Values:", absolute_values)

#### Output:

`Absolute Values: [4 9 2 0 5]`

### np.sign()

The `np.sign()`

function tells you whether each number in an array is negative, positive, or zero. It’s useful for tasks like figuring out directions or categorizing data based on its positivity or negativity.

**Example:**

import numpy as np # Sample array arr = np.array([-4, 9, -2, 0, 5]) # Determine signs signs = np.sign(arr) print("Signs:", signs)

#### Output:

`Signs: [-1 1 -1 0 1]`

## Trigonometric and Hyperbolic Functions

NumPy’s trigonometric and hyperbolic functions are invaluable for various scientific and engineering applications, enabling computations involving angles, distances, and oscillations.

### np.deg2rad() and np.rad2deg()

The `np.deg2rad()`

function converts angles from degrees to radians. It takes an angle in degrees as input and returns the equivalent angle in radians. It’s handy for working with trigonometric functions like sine and cosine, which typically use radians.

Conversely, `np.rad2deg()`

converts angles from radians to degrees. This function is useful for presenting angles in a more familiar format or when dealing with systems that use degrees.

**Example:**

import numpy as np degrees = np.array([0, 30, 45, 60, 90]) radians = np.deg2rad(degrees) degrees_again = np.rad2deg(radians) print("Radians:", radians) print("Degrees_again:", degrees_again)

#### Output:

```
Radians: [0. 0.52359878 0.78539816 1.04719755 1.57079633]
Degrees_again: [ 0. 30. 45. 60. 90.]
```

### numpy.sin(), numpy.cos(), and numpy.tan()

These functions calculate the sine, cosine, and tangent of angles, respectively.

Sine helps understand the relationship between angles and the lengths of sides in right triangles.

Cosine helps determine the ratio of lengths of sides in right triangles.

Tangent helps find the ratio of the opposite side to the adjacent side in right triangles.

**Example:**

angles = np.array([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2]) sine_values = np.sin(angles) cosine_values = np.cos(angles) tangent_values = np.tan(angles) print("sine values:", sine_values) print("cosine values:", cosine_values) print("tangent values:", tangent_values)

#### Output:

```
sine values: [0. 0.5 0.70710678 0.8660254 1. ]
cosine values: [1.00000000e+00 8.66025404e-01 7.07106781e-01 5.00000000e-01
6.12323400e-17]
tangent values: [0.00000000e+00 5.77350269e-01 1.00000000e+00 1.73205081e+00
1.63312394e+16]
```

### numpy.arcsin(), numpy.arccos(), and numpy.arctan()

These functions perform the inverse operations of sine, cosine, and tangent, returning angles in radians.

**Example:**

opposite_side_lengths = np.array([0, 1, np.sqrt(2)/2, np.sqrt(3)/2, 1]) inverse_sines = np.arcsin(opposite_side_lengths) inverse_cosines = np.arccos(opposite_side_lengths) inverse_tangents = np.arctan(opposite_side_lengths) print("inverse_sines:", inverse_sines) print("inverse_cosines:", inverse_cosines) print("inverse_tangents:", inverse_tangents)

#### Output:

```
inverse_sines: [0. 1.57079633 0.78539816 1.04719755 1.57079633]
inverse_cosines: [1.57079633 0. 0.78539816 0.52359878 0. ]
inverse_tangents: [0. 0.78539816 0.61547971 0.71372438 0.78539816]
```

### numpy.sinh(), numpy.cosh(), and numpy.tanh()

Hyperbolic functions are mathematical functions that resemble trigonometric functions but operate on the hyperbola rather than the circle.

The `np.sinh()`

calculates the hyperbolic sine of values. It’s useful for modeling exponential growth or decay processes.

The `numpy.cosh()`

computes the hyperbolic cosine of values. This is relevant in fields like civil engineering and electrical engineering.

The `np.tanh()`

determines the hyperbolic tangent of values. This is useful in representing processes with limitations or logistic growth patterns, common in biology and neural networks.

**Example:**

numbers = np.array([0, 1, 2, 3, 4]) hyperbolic_sines = np.sinh(numbers) hyperbolic_cosines = np.cosh(numbers) hyperbolic_tangents = np.tanh(numbers) print("hyperbolic_sines:", hyperbolic_sines) print("hyperbolic_cosines:", hyperbolic_cosines) print("hyperbolic_tangents:", hyperbolic_tangents)

#### Output:

```
hyperbolic_sines: [ 0. 1.17520119 3.62686041 10.01787493 27.2899172 ]
hyperbolic_cosines: [ 1. 1.54308063 3.76219569 10.067662 27.30823284]
hyperbolic_tangents: [0. 0.76159416 0.96402758 0.99505475 0.9993293 ]
```

## Exponential and Logarithmic Functions

Exponential and logarithmic functions are essential in various scientific and mathematical contexts, aiding in modeling growth, decay, and relationships between variables.

### np.exp()

The `np.exp()`

function calculates the exponential value of each number in an array, raising the mathematical constant e to the power of the input values. It’s helpful for modeling processes with exponential growth or decay.

**Example:**

import numpy as np numbers = np.array([1, 2, 3, 4, 5]) exponential_values = np.exp(numbers) print(exponential_values)

#### Output :

`[ 2.71828183 7.3890561 20.08553692 54.59815003 148.4131591 ]`

### np.log()

The `np.log()`

computes the natural logarithm (base e) of each number in an array. It returns an array of natural logarithm values, providing a way to reverse exponentiation and retrieve the original input values. It’s commonly used in probability, signal processing, and finance.

**Example:**

natural_logarithms = np.log(numbers) print(natural_logarithms)

#### Output:

`[0. 0.69314718 1.09861229 1.38629436 1.60943791]`

### numpy.log10()

The `np.log10()`

calculates the base-10 logarithm of each element in an array. Use it when you want to understand data in a more human-friendly way, especially when dealing with large or small values, such as in astronomy, seismology, or data analysis.

**Example:**

base_10_logarithms = np.log10(numbers) print(base_10_logarithms)

#### Output:

`[0. 0.30103 0.47712125 0.60205999 0.69897 ]`

## Rounding Functions

Rounding functions are crucial for adjusting numerical values to the nearest whole number or a specified precision level, facilitating data presentation, analysis, and formatting.

### np.round()

The `np.round()`

function rounds each element in an array to the nearest integer. It follows the standard rounding rule, where values with a decimal part of 0.5 or higher are rounded up, while those with a decimal part less than 0.5 are rounded down.

**Example:**

import numpy as np numbers = np.array([1.23, 2.58, 3.94, 4.27, 5.68]) rounded_integers = np.round(numbers) rounded_decimals = np.round(numbers, decimals=1) print(rounded_integers) print(rounded_decimals)

#### Output:

```
[1. 3. 4. 4. 6.]
[1.2 2.6 3.9 4.3 5.7]
```

### np.floor()

The `np.floor()`

rounds each element in an array down to the nearest integer. This function is useful for converting real numbers to integers while ensuring that the result is not greater than the original value.

**Example:**

floor_values = np.floor(numbers) print(floor_values)

#### Output:

`[1. 2. 3. 4. 5.]`

### np.ceil()

`np.ceil()`

rounds each element in an array up to the nearest integer.This function is handy for situations where you need to ensure that the rounded value is not less than the original value.

**Example:**

ceil_values = np.ceil(numbers) print(ceil_values)

#### Output:

`[2. 3. 4. 5. 6.]`

## Statistical Functions

Statistical functions play a crucial role in summarizing data, extracting key insights, and understanding the distribution and variability of values within arrays.

### np.mean()

The `np.mean()`

function computes the average value of elements in an array, providing a measure of central tendency.

**Example:**

import numpy as np data = np.array([12, 15, 18, 21, 24]) mean_value = np.mean(data)

#### Output:

`18.0`

### np.median()

The `np.median()`

calculates the middle value of elements in an array. If there’s an even number of values, it cleverly calculates the average of the two middle ones.

**Example:**

median_value = np.median(data) print(median_value)

#### Output:

`18.0`

### np.std()

The `np.std()`

function tells us how much the numbers in our array spread out from the average. If the spread is small, most numbers are close to the average. If it’s large, numbers vary a lot from the average.

**Example:**

std_deviation = np.std(data) print(std_deviation)

#### Output:

`4.242640687119285`

### np.var()

`np.var()`

measures how much the numbers in our array vary from the average, but it does this by looking at the squared differences from the average. It quantifies the degree of dispersion or spread in the data.

**Example:**

variance = np.var(data) print(variance)

#### Output:

`18.0`

### np.percentile()

`np.percentile()`

helps us understand how our data is spread out by telling us the value below which a certain percentage of our data falls. For example, the 75th percentile is the value below which 75% of our data lies.

**Example:**

percentile_75 = np.percentile(data, 75) # 75th percentile print(percentile_75)

#### Output:

`21.0`

## Set Functions in NumPy

NumPy, also has functions to create and work with sets. The `np.unique()`

function in NumPy allows us to create a set of unique elements from an array. It returns the sorted unique elements of an array, eliminating duplicates.

**Example:**

import numpy as np data = np.array([2, 3, 2, 1, 5, 4, 5]) unique_elements = np.unique(data)

#### Output:

`[1 2 3 4 5]`

### Performing Set Operations

NumPy provides several functions for performing set operations similar to those in mathematics.

`np.union1d()`

**:**Combines two sets, removing duplicates, and gives you a new sorted set with all the unique values from both.`np.intersect1d()`

**:**Finds the common items between two sets and gives you a new set with just those items.`np.setdiff1d()`

**:**Calculates the set difference between two arrays, returning the sorted values that are in the first array but not in the second array.`np.setxor1d()`

**:**Shows you the items that are unique to each set. It’s like combining the results of`setdiff1d()`

on both sets.

**Example:**

set_A = np.array([1, 2, 3, 4, 5]) set_B = np.array([3, 4, 5, 6, 7]) union_result = np.union1d(set_A, set_B) intersection_result = np.intersect1d(set_A, set_B) difference_result = np.setdiff1d(set_A, set_B) symmetric_difference_result = np.setxor1d(set_A, set_B) print("union_result:", union_result) print("intersection_result:", intersection_result) print("difference_result:", difference_result) print("symmetric_difference_result:", symmetric_difference_result)

#### Output:

```
union_result: [1 2 3 4 5 6 7]
intersection_result: [3 4 5]
difference_result: [1 2]
symmetric_difference_result: [1 2 6 7]
```