# NUMPY - NUMerical PYthon

NumPy (**Numerical Python**) is an open-source Python library that’s used in almost every field of science and engineering. It’s the universal standard for working with numerical data in Python, and it’s at the core of the scientific Python and PyData ecosystems.

The NumPy API is used extensively in Pandas, SciPy, Matplotlib, sci-kit-learn, sci-kit-image and most other data science and scientific Python packages.

1. It provides **ndarray**, a homogeneous n-dimensional array object, with methods to efficiently operate on it.
    
2. It adds powerful data structures to Python that guarantee efficient calculations with arrays and matrices and it supplies an enormous library of high-level mathematical functions that operate on these arrays and matrices.
    

### Installing NumPy

```python
conda install numpy
```

or

```python
pip install numpy
```

### Importing NumPy

```python
import numpy as np
```

## Difference between a Python list and a NumPy array?

NumPy gives you an enormous range of fast and efficient ways of creating arrays and manipulating numerical data inside them. While a Python list can contain different data types within a single list, all of the elements in a NumPy array should be homogeneous. The mathematical operations that are meant to be performed on arrays would be extremely inefficient if the arrays weren’t homogeneous..

1. An array consumes less memory and is convenient to use.
    

## What is an array?

An array is a central data structure of the NumPy library. An array is a grid of values and it contains information about the raw data, how to locate an element, and how to interpret an element.

### Other:

The `rank` of the array is the number of dimensions. The `shape` of the array is a tuple of integers giving the size of the array along each dimension.

```python
print(a[0])
```

## What are the attributes of an array?

An array is usually a fixed-size container of items of the same type and size. The number of dimensions and items in an array is defined by its shape. The shape of an array is a tuple of non-negative integers that specify the sizes of each dimension

**dimensions are called axes**

Array **attributes** reflect information intrinsic to the array itself. If you need to get, or even set, properties of an array without creating a new array, you can often access an array through its attributes.

## How to create a Basic array?

1. `np.array()`
    
2. `np.zeros()`
    
3. `np.ones()`
    
4. `np.empty()`
    
5. `np.arange()`
    
6. `np..linspace()`
    
7. `dtype`
    

You can also use `np.linspace()` to create an array with values that are spaced linearly in a specified interval.

`dtype=np.int64`

## Adding, removing, and sorting elements

1. `np.sort()`
    
2. `np.concatenate()`
    

In addition to sort, which returns a sorted copy of an array, you can use:

* `[argsort](<`[`https://numpy.org/doc/stable/reference/generated/numpy.argsort.html#numpy.argsort`](https://numpy.org/doc/stable/reference/generated/numpy.argsort.html#numpy.argsort)`>)`, which is an indirect sort along a specified axis,
    
* `[lexsort](<`[`https://numpy.org/doc/stable/reference/generated/numpy.lexsort.html#numpy.lexsort`](https://numpy.org/doc/stable/reference/generated/numpy.lexsort.html#numpy.lexsort)`>)`, which is an indirect stable sort on multiple keys,
    
* `[searchsorted](<`[`https://numpy.org/doc/stable/reference/generated/numpy.searchsorted.html#numpy.searchsorted`](https://numpy.org/doc/stable/reference/generated/numpy.searchsorted.html#numpy.searchsorted)`>)`, which will find elements in a sorted array, and
    
* `[partition](<`[`https://numpy.org/doc/stable/reference/generated/numpy.partition.html#numpy.partition`](https://numpy.org/doc/stable/reference/generated/numpy.partition.html#numpy.partition)`>)`, which is a partial sort.
    

## How to know the shape and size of an array?

1. `ndarray.ndim`
    
2. `ndarray.size`
    
3. `nadarray.shape`
    

`ndarray.ndim` will tell you the number of axes, or dimensions, of the array.

`ndarray.size` will tell you the total number of elements of the array. This is the *product* of the elements of the array’s shape.

`ndarray.shape` will display a tuple of integers that indicate the number of elements stored along each dimension of the array. If, for example, you have a 2-D array with 2 rows and 3 columns, the shape of your array is `(2, 3)`.

## Can you reshape an array?

1. `arr.reshape()`
    

Using `arr.reshape()` will give a new shape to an array without changing the data. Just remember that when you use the reshape method, the array you want to produce needs to have the same number of elements as the original array. If you start with an array with 12 elements, you’ll need to make sure that your new array also has a total of 12 elements.

`a` is the array to be reshaped.

`newshape` is the new shape you want. You can specify an integer or a tuple of integers. If you specify an integer, the result will be an array of that length. The shape should be compatible with the original shape.

`order:` `C` means to read/write the elements using C-like index order,`F` means to read/write the elements using Fortran-like index order, `A` means to read/write the elements in Fortran-like index order if a is Fortran contiguous in memory, C-like order otherwise. (This is an optional parameter and doesn’t need to be specified.)

&lt;aside&gt; 💡 Read more about internal organization of NumPy arrays here.

&lt;/aside&gt;

## How to convert a 1D array into a 2D array (how to add a new axis to an array)

1. `np.newaxis`
    
2. `np.expand_dims`
    

Using `np.newaxis` will increase the dimensions of your array by one dimension when used once. This means that a **1D** array will become a **2D** array, a **2D** array will become a **3D** array, and so on.

## Indexing and Slicing

```python
data = np.array([1, 2, 3])

data[1]
2

data[0:2]
array([1, 2])

data[1:]
array([2, 3])

data[-2:]
array([2, 3])
```

![Screenshot 2023-06-17 213228.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d57804c3-18f7-46dc-aba6-ca8f9f02768c/Screenshot_2023-06-17_213228.png align="left")

## How to create an array from existing data?

1. slicing
    
2. indexing
    
3. `np.vstack()`
    
4. `np.hsplit()`
    
5. `.view()`
    
6. `copy()`
    

&lt;aside&gt; 💡 I have left it for later reading

&lt;/aside&gt;

# Basic array operations

1. addition
    
2. subtraction
    
3. multiplication
    
4. division
    
5. `.sum( )`
    
6. `b.sum(axis=0`
    

## Broadcasting

NumPy understands that the multiplication should happen with each cell. That concept is called **broadcasting**. Broadcasting is a mechanism that allows NumPy to perform operations on arrays of different shapes. The dimensions of your array must be compatible, for example, when the dimensions of both arrays are equal or when one of them is 1. If the dimensions are not compatible, you will get a `ValueError`.

## More array operations

1. `max( )`
    
2. `min( )`
    
3. `sum( )`
    
4. `mean( )`
    
5. product == `prod( )`
    
6. standard deviation = = `std( )`
    

Creating matrices

Be aware that when NumPy prints N-dimensional arrays, the last axis is looped over the fastest while the first axis is the slowest. For instance:

![Screenshot 2023-06-17 215551.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/57a99102-c34f-4f49-b104-7c435fa5b8ba/Screenshot_2023-06-17_215551.png align="left")

![Screenshot 2023-06-17 215612.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/90ac0832-8c1f-4310-801c-70c90c5536bd/Screenshot_2023-06-17_215612.png align="left")

![Screenshot 2023-06-17 215659.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f415d8db-fe32-4d51-a2b1-1d04c08021d5/Screenshot_2023-06-17_215659.png align="left")

![Screenshot 2023-06-17 215817.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cbeeb031-1a91-472e-a7f4-16307290c0a6/Screenshot_2023-06-17_215817.png align="left")

## Generating random numbers

&lt;aside&gt; 💡 Something wrong

&lt;/aside&gt;

## How to get unique items and counts?

```python
unique_values = np.unique(a)

```

```python
print(unique_values)
[11 12 13 14 15 16 17 18 19 20]
```

```python
unique_values, indices_list = np.unique(a, return_index=True)
```

```python
print(indices_list)
[ 0  2  3  4  5  6  7 12 13 14]
```

```python
unique_values, occurrence_count = np.unique(a, return_counts=True)

```

```python
print(occurrence_count)
[3 2 2 2 1 1 1 1 1 1]
```

## Transposing and reshaping a matrix

1. `arr.reshape( )`
    
2. `arr.transpose( )`
    
3. `arr.T`
    
    ![Screenshot 2023-06-17 220937.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6e843d86-0f5f-4fe3-b905-cbc9a673e843/Screenshot_2023-06-17_220937.png align="left")
    

![Screenshot 2023-06-17 221024.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/40b3130d-1d71-48f8-aa66-c1df4b36d3ea/Screenshot_2023-06-17_221024.png align="left")

## How to reverse an array?

1. `np.flip( )`
    

You can also reverse the contents of only one column or row. For example, you can reverse the contents of the row at index position 1 (the second row):

```python
arr_2d[1] = np.flip(arr_2d[1])
```

```python
print(arr_2d)
[[ 1  2  3  4]
 [ 8  7  6  5]
 [ 9 10 11 12]]
```

You can also reverse the column at index position 1 (the second column):

```plaintext
arr_2d[:,1] = np.flip(arr_2d[:,1])
```

```plaintext
print(arr_2d)
[[ 1 10  3  4]
 [ 8  7  6  5]
 [ 9  2 11 12]]
```

## Reshaping and flattening multidimensional arrays

There are two popular ways to flatten an array :

1. `flatten( )`
    
2. `ravel( )`
    

The primary difference between the two is that the new array created using `ravel()` is actually a reference to the parent array (i.e., a “view”). This means that any changes to the new array will affect the parent array as well. Since `ravel` does not create a copy, it’s memory efficient.

## How to access the docstring for more information

1. `help( )`
    
2. `?`
    
3. `??`
    

When it comes to the data science ecosystem, Python and NumPy are built with the user in mind. One of the best examples of this is the built-in access to documentation. Every object contains the reference to a string, which is known as the **docstring**.

## Working with mathematical formulas?

The ease of implementing mathematical formulas that work on arrays is one of the things that make NumPy so widely used in the scientific Python community.

![Screenshot 2023-06-17 223301.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6330b0db-8b15-40fc-aa43-67749e8b9223/Screenshot_2023-06-17_223301.png align="left")

What makes this work so well is that `predictions` and `labels` can contain one or a thousand values. They only need to be the same size.

We can visualize like this:

![Screenshot 2023-06-17 223340.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a85d2beb-82a0-4317-b311-2267c7dac2f9/Screenshot_2023-06-17_223340.png align="left")

## How to save and load NumPy objects?

1. [`np.save`](http://np.save)
    
2. `np.savez`
    
3. `np.savetxt`
    
4. `np.load`
    
5. `np.loadtxt`
    

You will, at some point, want to save your arrays to disk and load them back without having to re-run the code. Fortunately, there are several ways to save and load objects with NumPy. The ndarray objects can be saved to and loaded from the disk files with `loadtxt` and `savetxt` functions that handle normal text files, `load` and `save` functions that handle NumPy binary files with a **.npy** file extension, and a `savez` function that handles NumPy files with a **.npz** file extension.

If you want to store a single ndarray object, store it as a .npy file using [`np.save`](http://np.save). If you want to store more than one ndarray object in a single file, save it as a .npz file using `np.savez`. You can also save several arrays into a single file in compressed npz format with `[savez_compressed](<`[`https://numpy.org/doc/stable/reference/generated/numpy.savez_compressed.html#numpy.savez_compressed`](https://numpy.org/doc/stable/reference/generated/numpy.savez_compressed.html#numpy.savez_compressed)`>)`.

## Importing and exporting a CSV

It’s simple to read in a CSV that contains existing information. The best and easiest way to do this is to use [Pandas](https://pandas.pydata.org/).

`import pandas as pd`

&lt;aside&gt; 💡 `a = np.array([2, 1, 5, 7, 4, 6, 8, 14, 10, 9, 18, 20, 22])`

&lt;/aside&gt;

```plaintext
import matplotlib.pyplot as plt

# If you're using Jupyter Notebook, you may also want to run the following
# line of code to display your code in the notebook:

%matplotlib inline
```

```plaintext
plt.plot(a)
# If you are running from a command line, you may need to do this:
# >>> plt.show()
```

## Introduction

There are 6 general mechanisms for creating arrays:

1. **Conversion from other Python structures (i.e. lists and tuples)**
    
2. **Intrinsic NumPy array creation functions (e.g. arange, ones, zeros, etc.)**
    
    1. `np.arange( )`
        
    2. `np.linspace( )`
        
    3. `np.eye(3) #for 2d array creation functions`
        
    4. `np.diag( )`
        
    5. `np.vander([1,2,3,4], 2)`
        
    6. `np.vander((1, 2, 3, 4), 4)`
        
    7. `np.zeros( )`
        
    8. `np.ones( )`
        
    9. Random generator:
        
    
    ```plaintext
    from numpy.random import default_rng
    ```
    
    ```plaintext
    default_rng(42).random((2,3))
    ```
    
    j. `np.indices((3,3))`
    
3. **Replicating, joining, or mutating existing arrays**
    
4. **Reading arrays from disk, either from standard or custom formats**
    
5. **Creating arrays from raw bytes through the use of strings or buffers**
    
6. **Use of special library functions (e.g., random)**
    

# **Indexing on** `ndarrays`

`[ndarrays](<`[`https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)`>)` can be indexed using the standard Python `x[obj]` syntax, where *x* is the array and *obj* the selection.

# Slicing

Basic slicing extends Python’s basic concept of slicing to N dimensions. Basic slicing occurs when *obj* is a `[slice](<`[`https://docs.python.org/3/library/functions.html#slice`](https://docs.python.org/3/library/functions.html#slice)`>)` object (constructed by `start:stop:step` notation inside of brackets), an integer, or a tuple of slice objects and integers. `[Ellipsis](<`[`https://docs.python.org/3/library/constants.html#Ellipsis>)`and](https://docs.python.org/3/library/constants.html#Ellipsis>)and) `[newaxis](<`[`https://numpy.org/doc/stable/reference/constants.html#numpy.newaxis`](https://numpy.org/doc/stable/reference/constants.html#numpy.newaxis)`>)` objects can be interspersed with these as well.

All arrays generated by basic slicing are always [views](https://numpy.org/doc/stable/glossary.html#term-view) of the original array.

The basic slice syntax is `i:j:k` where *i* is the starting index, *j* is the stopping index, and *k* is the step ().

Negative *i* and *j* are interpreted as *n + i* and *n + j* where *n* is the number of elements in the corresponding dimension. Negative *k* makes stepping go towards smaller indices.
