# Introduction to NumPy–Part 1

I’d like to at this time clarify that the various contributors to this blog are probably going to end up making both a personal this is interesting blog, as well as a blog containing updates for Valkyrie Robotics, and a place to rapidly provide conceptual overviews.

Today, however, I’d like to give an overview of NumPy, which, according to their website, is “the fundamental package for scientific computing with Python.” More bluntly, it’s a package for multi-dimenensional arrays and matrices as well as operations on those datatypes.

If you need to do Linear Algebra in Python, it will probably be in the form of NumPy matrices.

In this post, I expect an understanding of Python 3, as this is not a Python tutorial.

import numpy as np


We, of course, start out with the numpy include. The convention is to include using import numpy as np rather than doing from numpy import *

### NumPy ndarray: Multidimensional arrays

Arrays enable you to perform mathmatical operations on blocks of data as if they were a scalar.

# arange works similarly to Python's range function
arr0 = np.arange(1, 10, 2)
arr0

Out[2]:

array([1, 3, 5, 7, 9])

# create a 2D list in native Python
data = [[1, 2, 3, 4],
[5, 6, 7, 9]]

# initialize a ndarray to that list
arr1 = np.array(data)

data2 = [6.8, 3, 0.5, 7]

arr2 = np.array(data2)

arr1

Out[4]:

array([[1, 2, 3, 4],
[5, 6, 7, 9]])

arr1.ndim

Out[5]:

2

arr1.shape

Out[6]:

(2, 4)


np.array infers a datatype about the objects it contains. This is stored in a special object. For example:

arr1.dtype

Out[7]:

dtype('int64')

arr2.dtype

Out[8]:

dtype('float64')


We can also use other functions to create new arrays, such as zeros, ones, or empty. The first two will create arrays filled with zero or one respecitively, whereas the latter creates empty arrays. These empty arrays are not necessarily zeroes.

np.zeros(10)

Out[9]:

array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

np.ones((3, 6))

Out[10]:

array([[ 1.,  1.,  1.,  1.,  1.,  1.],
[ 1.,  1.,  1.,  1.,  1.,  1.],
[ 1.,  1.,  1.,  1.,  1.,  1.]])

np.empty((1, 4, 9))

Out[11]:

array([[[  6.91354255e-310,   1.09642495e-316,   2.82737564e-317,
2.47032823e-323,   0.00000000e+000,   0.00000000e+000,
0.00000000e+000,   6.91354255e-310,   0.00000000e+000],
[  0.00000000e+000,  -2.77323087e-027,   0.00000000e+000,
0.00000000e+000,  -8.12748304e-298,   0.00000000e+000,
0.00000000e+000,   4.66932976e-088,   0.00000000e+000],
[  0.00000000e+000,   6.91350373e-310,   0.00000000e+000,
0.00000000e+000,   6.86025377e-057,   0.00000000e+000,
0.00000000e+000,   5.14400001e+271,   0.00000000e+000],
[  0.00000000e+000,  -4.82088475e+232,   9.53546696e-322,
1.27837628e-316,   1.09643681e-316,   0.00000000e+000,
0.00000000e+000,  -8.65683232e-271,   6.91354050e-310]]])


Here are some of the ways to construct arrays:

Function Description
asarray Convert input to array if input is not already an array.
ones, ones_like Create an array filled with ones of a given shape. ones_like uses the shape of an already existing array.
zeros, zeros_like Create an array filled with zeroes of a given shape. zeros_like uses the shape of an already existing array.
empty, empty_like Create an array of the given shape but do not fill it with values; just allocate memory.
eye, identity Construct an N x N identity matrix.

#### Data Types for Arrays

Dtypes usually map directly to the underlying level of code, which makes it easy for NumPy to quickly read and write data. Feel free to google all the different possibilities.

arr = np.array([1, 2, 16, 42, 54])

arr.dtype

Out[12]:

dtype('int64')

floats = arr.astype(np.float64)
floats.dtype

Out[13]:

dtype('float64')


This will cast the internal objects to the new type, and will raise a TypeError if you screw it up. For arrays, unlike matrices, arithmetic operations simply apply it to each element, like a for loop would do.

arr = np.array([[1., 2., 3.], [4., 5., 6.]])

arr

Out[14]:

array([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])

# Not matrix multiplication
arr * arr

Out[15]:

array([[  1.,   4.,   9.],
[ 16.,  25.,  36.]])

arr - arr

Out[16]:

array([[ 0.,  0.,  0.],
[ 0.,  0.,  0.]])


In terms of NumPy array indexing, 1D arrays work similarly to lists in native Python:

arr = np.arange(10)

arr

Out[17]:

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr[5]

Out[18]:

5

arr[5:8]

Out[19]:

array([5, 6, 7])

arr[5:8] = 12

arr

Out[21]:

array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])


As you can see, slices are simply views of the entire array. As a result, they will make any changes made to themselves onto the actual array. No data is actually copied.

arr_slice = arr[5:8]

arr_slice[1] = 12345

arr

Out[22]:

array([    0,     1,     2,     3,     4,    12, 12345,    12,     8,     9])

arr_slice[:] = 64

arr

Out[23]:

array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])


For higher dimensional arrays, you slice using smaller arrays. For instance, to slice a 2D array, you use 1D arrays.

# Create an array using arange and then reshape to 2D
arr2d = np.arange(1, 10).reshape((3, 3))

arr2d

Out[24]:

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])


Sadly, this works the opposite of matrices. So arr2d[0, 1] would give you 2, not 4, and arr2d[2, 1] would give you 8.

arr3d = np.arange(1, 13).reshape((2, 2, 3))
arr3d

Out[25]:

array([[[ 1,  2,  3],
[ 4,  5,  6]],

[[ 7,  8,  9],
[10, 11, 12]]])

# Gives us a 2D array
arr3d[0]

Out[26]:

array([[1, 2, 3],
[4, 5, 6]])

old = arr3d[0].copy()

arr3d[0] = 42

arr3d

Out[27]:

array([[[42, 42, 42],
[42, 42, 42]],

[[ 7,  8,  9],
[10, 11, 12]]])

arr3d[0] = old
arr3d

Out[28]:

array([[[ 1,  2,  3],
[ 4,  5,  6]],

[[ 7,  8,  9],
[10, 11, 12]]])


Another way to think of it: arr3d[1, 0] will give you all arrays which start with (1, 0) giving us a 1D array.

In the next post, I’ll talk about more fancy indexing and mathematical operations, and then about NumPy matrices.

### Written by Lee Mracek

"We shouldn't use math to optimize this." — no one ever
Contact him at lee@valkyrierobotics.com.

Updated