Hi Enthusiastic Learners! In this article we learn about many Universal Functions or Built-In functions of NumPy. Universal Functions plays a very crucial role in getting best performance out of NumPy and if you want to know advantages and effects on performance while using Universal Functions (UFuncs), you should go through our article Why use Universal Functions or Built in Functions in NumPy ?

And to learn basics of NumPy you can go through these 2 detailed articles, they will help you get a good understanding of creating & traversing NumPy arrays.

In this article we will be covering following topics:

- Arithmetic Universal Functions
- Trigonometric Universal Functions
- Exponent & Logarithmic Universal Functions

## Arithmetic Universal Functions¶

The biggest advantage of using UFuncs for Arithmetic Operations is that they all look same as that of our standard Mathematical operators, that is, you can simply use ‘`+`

‘, ‘`-`

‘, ‘`/`

‘ & ‘`*`

‘ for their Mathematical meaningful operations – Addition, Subtraction, Division & Multiplication respectively.

Let’s create an Array and try out these operations — just remember one thing when we are adding or subtracting a scalar value to or from an Array it will implement to all elements of that array.

```
import numpy as np
# Our Base Array
x = np.arange(1, 20, 3)
x
```

```
print("x + 4 = " + str(x + 4))
print("x - 4 = " + str(x - 4))
print("x / 4 = " + str(x / 4))
print("x * 4 = " + str(x * 4))
```

Note: One interesting thing to note here is that NumPy automatically selects that which data-type to choose after any operation. In above example, when we divided integer values of array we got Float or Decimal values in output. Thus, it automatically chooses the higher data-set.

You can also perform following operations:

- Negate all values of an array
- Finding modulus of all values of array (remainder of values)
- Finding power of all numbers

```
print("Negate all values of array")
print ("-x \t= " + str(-x))
print("\nModulus of all numbers with 4")
print("x % 4 \t= " +str(x % 4))
print("\nCalculating power of all number with 3")
print("x ** 3 \t=" + str(x ** 3))
```

Corresponding to above Mathematical operators we also have standard NumPy functions which are internally called whenever an operator is used. List of these functions is as follows:

- ”
”`+`

`np.add`

- ”
”`-`

`np.subtract`

- ”
”`/`

`np.divide`

- ”
”`*`

`np.multiply`

- ”
”`-val`

`np.negative`

- ”
”`%`

`np. mod`

- ”
`*`

”`*`

`np.pow`

## Trigonometric Universal Functions¶

We can could use trigonometric functions to find both standard results as well as inverse trigonometric results too.

Let’s begin with creating an array of different angles.

```
angle = np.arange(0,15, 4)
angle
```

```
print("tan(angle) = " + str(np.tan(angle)))
print("\nsin(angle) = " + str(np.sin(angle)))
print("\ncos(angle) = " + str(np.cos(angle)))
```

Let’s see **Inverse Trigonometric Functions** too.

First create an array on which we will be applying inverse trigonometric functions to get corresponding angles.

```
values = [0, 1, -1]
values
```

```
print("arctan(values) = " + str(np.arctan(values)))
print("\narcsin(values) = " + str(np.arcsin(values)))
print("\narccos(values) = " + str(np.arccos(values)))
```

NumPy also provides us with a set of **Hyperbolic Trigonometric functions**.

Here is an example for them.

```
print("tanh(angle) = " + str(np.tanh(angle)))
print("\nsinh(angle) = " + str(np.sinh(angle)))
print("\ncosh(angle) = " + str(np.cosh(angle)))
```

One thing to note here is that for getting Hyperbolic functions all you had to do was add an ‘h’ at end of each function. So, its very easy to remember.

And similarly, you can check Inverse Hyperbolic Functions — add ‘arc’ before function name and ‘h’ at the end. That is, **arc**tan**h**(), **arc**sin**h**() and **arc**cos**h**().

It make things easy to remember.

## Exponent & Logarithmic Universal Functions¶

Following is the list of **Exponential Functions**

**exp(x)**— e^x**expm1(x)**— e^x — Used when ‘x’ is very small, it provides more accuracy in comparison to exp(), however it is a little bit slower. So, try using it only when you have very small values.**exp2()**— 2^x — Used only when calculating power of scalar value ‘2’**power(n,x)**— n^x — Any number raise to power ‘x’

Let’s see them in action.

```
# base array
x = np.arange(1, 8, 2)
x
```

```
print("-- e^x --")
print(np.exp(x))
print("\n-- 2^x --")
print(np.exp2(x))
print("\n-- 5^x --")
print(np.power(5, x))
```

#### exp(x) VS expm1(x) — which is better for precision¶

Begin with creating an array of small values.

```
x_small = np.array([0.01, 0.001, 0.0001, 0.00001])
x_small
```

```
print("EXP() -- Standard Function")
print(np.exp(x_small))
print("\nEXPM1() -- High Precision")
print(np.expm1(x_small))
```

As you can see we get more precision while using `expm1()`

.

Following is the list of **Logarithmic Functions**

**log(x)**— Natural Log**log1p(x)**— Natural Log with high precision. Use it when value of ‘x’ is very small.**log2(x)**— Log with Base 2**log10(x)**— Log with Base 10

Let’s see how they work.

```
# base array
x = np.arange(1, 8, 2)
x
```

```
print("-- log(x) --")
print(np.log(x))
print("\n-- log2(x) --")
print(np.log2(x))
print("\n-- log10(x) --")
print(np.log10(x))
```

#### log(x) VS logp(x) — which is better for precision¶

Begin with creating an array of small values.

```
print("LOG() -- Standard Log Function")
print(np.exp(x_small))
print("\nLOG() -- High Precision")
print(np.expm1(x_small))
```

From results it is clear that for very small number we get higher precision when we use **logp()** function.

In our next tutorial we will be learning Aggregation Functions in depth, as we have covered only very few over here. There are a lot more functions that we need to explore yet.

So stay tuned & Keep Learning!!

And don’t forget to check our YouTube Channel **ML For Analytics**.

You can also follow us on **Facebook!!**