Learning Numpy

## Advantages of using NumPy over Lists

1. No need for for-loops, which saves on coding time.

Python list:

```
for i in range(len(my_list)):
    my_list[i] *= 3

```

NumPy array:

```
my_array *= 3

```

2. Single type for each field to avoid type checking.

3. Uses less memory. No pointers. Contiguous blocks.

## Creating NumPy Arrays

To create a basic array you can perform the following using the array function:

```
import numpy as np
a = np.array([2, 3, 4])

```

When 'a' is printed it will print:

```
[2, 3, 4]

```

We can also create an array using the arange  function which uses the following syntax:

```
np.arange(<start>, <end>, <step>)

```

So for example if we built an array like this:

```
b = np.arange(1, 12, 2)

```

it would print the following:

```
[ 1  3  5  7  9 11]

```

Another way of creating an array is using the linspace (linear space) function. It uses the following syntax:

```
np.linspace(<start>, <end>, <no. of elements>)

```

This is used to create a floating point array. So for example:

```
c = np.linspace(1, 12, 6)

```

Would create an array that looks like the following:

```
[ 1.   3.2  5.4  7.6  9.8 12. ]

```

If you have a regular Python list that you want to convert into a Numpy array:

```
list = [1, 2, 3, 4, 5]
numpyList = np.array(list)

```

## Reshape

If we want to reshape our array into a two dimensional array we can use the reshape function:

```
<array> = <array>.reshape(<rows>, <columns>)

```

For example:

```
c = np.linspace(1, 12, 6)
c = c.reshape(3, 2)
print(c)

```

This code would print:

```
[[ 1.   3.2]
 [ 5.4  7.6]
 [ 9.8 12.​ ]]​​​​​​

```

## Size

We can get the size of the array, which is the number of elements in the array with the size variable.
So for the above array we made (c) we could use:

```
print(c.size)

```

and it would return the number 6.

## Shape

we can get the shape of the array (number of rows and columns using the shape variable:

```
print(c.shape)

```

This will return (3, 2). Three rows and two columns.

## Data Type

Using the dtype variable, we can find out the data type used in the array:

```
print(c.dtype)

```

This will print 'float64'.
You can also pass dtype as a parameter when creating arrays, so you can set the desired data type.
For example :

```
c = np.array([2, 3, 4], dtype=np.int16)

```

## Item Size

The item variable will tell you how much memory in bytes each element of the array takes up each.

## Creating Multidimensional Arrays

You can create multidimensional arrays using something like the following syntax:

```
d = np.array([(1.5, 4, 5), (7, 9, 12)])

```

Note the rounded braces and a comma used to create each set, rather than just the square braces used for creating one dimensional arrays.

## Performing on Elements in the Array

As shown in the first section, there is no need for for-loops to check comparisons of each of the elements in a numpy array. So for example, say we want to check whether each of the elements of our above array is greater than 5 :

```
print(d > 5)

```

Just a few characters! And the output would look like this:

```
[[False  False  False]
 [ True   True   True]]

```

As another example, let's say that we want to multiply every element in the array by 4 :

```
d *= 4
print(d)

```

Just like that we would get this output :

```
[[ 6. 16. 20.]
 [28. 36. 48.]]

```

How cool is that! No iterating through all the elements!.

## Zeros & Ones

Say you wanted to create an array of zeros, we can do that with the following syntax :

```
<array> = np.zeros((<rows>, <columns>))

```

This will be defaulted to floating point values (float64).

You can also create an array of ones using the following syntax :

```
<array> = np.ones((<rows>, <columns>))

```

The above syntax examples are for if you want to create multidimensional arrays, but if you want to create a single dimensional array you can simply use :

```
# Create an array of 5 zeros.
<array> = np.zeros(5)

```

## Random

NumPy has a random function so you can create an array of random values.

```

# An array of 5 random integers from 0 - 10.
f = np.random.randint(0, 10, 5)

```

## Mathematical Funcitons

Sum : Find the sum of all the numbers in the array.

```
f.sum()

```

Min & Max : Get the smallest or largest value in the array.

```
f.min()
f.max()

```

Mean : Get the mean value of all the numbers.

```
f.mean()

```

Variance :

```
f.var()

```

Standard Deviation:

```
f.std()

```

You can also select axis to perform the mathematical operation on. E.g :

```
# Find the sum of the top row of the array.
f.sum(axis=1)

```

so if you had an array that looked like this :

```
[[3, 8],
 [4, 5],
 [3, 8]]

print(<array>.sum(axis=1)

```

You would get the following output :

```
[11, 9, 11]

```

because 3 + 8 = 11, 4 + 5 = 9, and 3 + 8 = 11.

The horizontal axis = 1 and the vertical axis = 0.

## Importing Text Files

Numpy has the ability to import a text file with a single line of code. For example :

```
data = np.loadtxt("data.txt", dtype=np.uint8, delimiter = ",", skiprows=1)

```

This is good, quick and easy way of importing well formatted text.

## Shuffle

We can shuffle an array using the following :

```
np.random.shuffle(<array>)

```

We dont need to use the equals sign here because it will shuffle your array **in place**.

## Choice

This will pick a random value from our array every time we run it :

```
np.random.choice(<array>)

```

## Accessing Elements in an Array

For example, num\_array[0, 0] is referencing the first row and first column in an array.
e.g, If you have an array that looks like :

```
num_array([[4, 3, 6],
           [1, 9, 8]])

```

Then you could access the elements in that array in the following way :

```
num_array[0, 0]   # 4
num_array[1, 2]   # 8

```

Or to access a slice of the array :

```
num_array[0:2, 0:2]   # Get elements 4, 3 and 1, 9.

```

## Matrix Multiplication

### Order of operations

Here is an example of how to multiply a 2x4 and 4x3 matrix:

### Numpy Example

Here is an example of using Numpy to multiply two matrices containing 60,000 and 80,000 integers:

```
import numpy as np
import time

 **SYNTAX:**
Create : np.arange(<start>, <stop>)
Reshape : np.reshape(x, y)
Multiply : np.matmul(array_1, array_2)

tick = time.time()
# Reshape gives us an easy way to dictate what shape we want our array (matrix) to be.
np_arr_1 = np.arange(0, 60000).reshape(300, 200)
np_arr_2 = np.arange(0, 80000).reshape(200, 400)

# matmul does the heavy lifting of multiplying where we would have needed a triple nested loop otherwise without numpy.
my_result_arr = np.matmul(np_arr_1, np_arr_2)
time_to_completion = time.time() - tick

print("execution time with NumPy: ", time_to_completion)

```

## References

Python: NumPy | Numerical Python Arrays Tutorial
Numpy Guide for People in a Hurry

Numpy - Python made efficient

Writing beautiful code with NumPy