### Do the Numpy Dance, Is your Chance to do the Nump!

Sir Numpy says:

The Numpy Dance is your chance to do the nump
Do the Numpy Nump, come on and do the Numpty Nump
Do the Numpy Nump, just watch me do the Numpty Nump
Do ya know what I'm doin’, doin’ the Numpty Nump
Do the Numpy Nump, do the Numpty Nump

### Start at the beginning: `NumPy`

The first library that we will investigate is `numpy`

. Simply put, `numpy`

allows us to represent
mathematical objects like arrays and matricies of different datatypes and performs operations on those objects thereby
easing the burden of writing the tedious code within your applications to do the mundane representations and operations.

Diving right in, let's look at some examples of types of objects we can create and how we can operate on them using the
library.

#### creation of arrays

The most basic thing we can do is create a one-dimensional array (also known as a vector):

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

Here we've created a row vector containing for elements. Numpy objects are typed and implicitly, we've created an `int64`

type by
passing in the integers. We can verify the type of our array:

```
>>> v.dtype
dtype('int64')
```

We can also verify the dimensions of our array (i.e. the **shape**):

Rather than implicitly typing our array object, we can explicitly specify it when we create or array:

```
v = np.array([1,2,3,4], dtype=float)
```

We can also turn a list into an array by **shaping** it from an array **range**:

```
>>> v = np.arange(16).reshape(4,4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
```

`arange`

works semantically like our built-in `range`

function, creating an array ranging over the given value. So long as the reshaping
integers, `n`

and `m`

are factors of the `len(arange(a,b,c))`

, then we can `reshape`

our range into an `n`

by `m`

array. For example:

```
>>> v = np.arange(0,32,2).reshape(4,4)
array([[ 0, 2, 4, 6],
[ 8, 10, 12, 14],
[16, 18, 20, 22],
[24, 26, 28, 30]])
```

We can even create *complex-valued* arrays:

```
>>> c = np.array( [ [1+2.j,2+1.j], [3+5.j,4+1.j] ], dtype=complex )
array([[ 1.+2.j, 2.+1.j],
[ 3.+5.j, 4.+1.j]])
```

#### operations on arrays

So you created an array, now what? We can perform operations on them that are familair, like addition subtraction, multiplication,
and division resulting in the expected semantics for arrays. For example:

```
# given two arrays, a and b, and vector c
a = np.array([[1,2],[3,4]])
b = np.array([[4,3],[2,1]])
c = np.array([0.5,2])
# add two arrays
>>> a + b
array([[5, 5],
[5, 5]])
# multiply two arrays
>>> a * b
array([[4, 6],
[6, 4]])
# inverse
>>> 1/a
array([[ 1. , 0.5 ],
[ 0.33333333, 0.25 ]])
# sin of all values in array a
>>> np.sin(a)
array([[ 0.84147098, 0.90929743],
[ 0.14112001, -0.7568025 ]])
```

There are nearly 600 functions in the `numpy`

library - this is a brief sample of only the basic ones. Check out the documentation
to learn more. To give you a sense of how extensive the library is, here are the top-level categories of functions:

- Array creation routines
- Array manipulation routines
- Binary operations
- String operations
- C-Types Foreign Function Interface (numpy.ctypeslib)
- Datetime Support Functions
- Data type routines
- Optionally Scipy-accelerated routines (numpy.dual)
- Mathematical functions with automatic domain (numpy.emath)
- Discrete Fourier Transform (numpy.fft)
- Financial functions
- Functional programming
- NumPy-specific help functions
- Indexing routines
- Input and output
- Linear algebra (numpy.linalg)
- Logic functions
- Masked array operations
- Mathematical functions
- Matrix library (numpy.matlib)
- Miscellaneous routines
- Random sampling (numpy.random)
- Set routines
- Sorting, searching, and counting
- Statistics

Go experiment with the library and get more familiar with function of interest to you. Now that we have a basic undertanding, let's
move on to visualizing data representations and transformations within `numpy`

objects.