W3docs

NumPy Tutorial

Learn NumPy from scratch: create and index arrays, reshape, broadcast, run aggregations, and perform linear algebra with clear Python examples.

NumPy (Numerical Python) is the foundational library for numerical computing in Python. It introduces the ndarray — a fast, fixed-type multi-dimensional array — and pairs it with hundreds of mathematical functions that operate on entire arrays at once. Virtually every scientific Python library (Pandas, SciPy, Matplotlib, scikit-learn) is built on top of NumPy.

This chapter covers:

  • What NumPy is and why it is faster than plain Python lists
  • Installing NumPy and the standard import convention
  • Creating arrays with np.array, np.zeros, np.ones, np.arange, and np.linspace
  • Indexing, slicing, and boolean masking
  • Reshaping and transposing
  • Broadcasting — operating on arrays with different shapes
  • Aggregation functions (sum, mean, std, min, max)
  • Element-wise math and linear algebra
  • Common utility functions (sort, unique, where, concatenate)

What is NumPy?

NumPy is an open-source Python library that provides:

  1. A powerful N-dimensional array object (ndarray).
  2. Element-wise mathematical functions (ufuncs) that apply to every element of an array in compiled C code rather than a Python loop.
  3. Linear algebra, Fourier transforms, and random-number routines.

Why NumPy is faster than Python lists

A Python list can hold items of any type, so every element stores a type tag and a pointer to the actual value. NumPy arrays store raw numeric data in a contiguous block of memory — no pointers, no type lookups. Combined with vectorised C loops (ufuncs), operations on a million-element NumPy array typically run 10–100× faster than equivalent Python for-loop code.

Installing NumPy

NumPy is included in the Anaconda distribution. To install it manually with pip:

pip install numpy

Importing NumPy

The universal convention is to import NumPy as np:

import numpy as np

Every example in this chapter assumes this import is already in scope.

Creating Arrays

From a Python list

Pass any list (or list of lists) to np.array():

python— editable, runs on the server
[1 2 3 4 5]
int64
(5,)

A 2-D array (matrix) uses a list of lists:

python— editable, runs on the server
[[1 2 3]
 [4 5 6]
 [7 8 9]]
(3, 3)

Array creation shortcuts

FunctionWhat it creates
np.zeros((2, 3))Array of 0.0 with shape (2, 3)
np.ones(4, dtype=int)Array of 1 with shape (4,)
np.eye(3)3×3 identity matrix
np.arange(start, stop, step)Like Python range(), returns array
np.linspace(start, stop, n)n evenly spaced values from start to stop
import numpy as np

print(np.zeros((2, 3)))
print(np.ones(4, dtype=int))
print(np.arange(0, 10, 2))
print(np.linspace(0, 1, 5))
[[0. 0. 0.]
 [0. 0. 0.]]
[1 1 1 1]
[0 2 4 6 8]
[0.   0.25 0.5  0.75 1.  ]

np.linspace is especially useful when you need a precise number of points — for example, when preparing an x-axis for a plot. See the Matplotlib Intro chapter for how to pair it with plotting functions.

Indexing and Slicing

1-D indexing and slicing

NumPy uses the same [start:stop:step] syntax as Python lists, but it also supports negative indices and strides.

import numpy as np

a = np.array([10, 20, 30, 40, 50])

print(a[0])      # first element
print(a[-1])     # last element
print(a[1:4])    # elements at index 1, 2, 3
print(a[::2])    # every other element
10
50
[20 30 40]
[10 30 50]

2-D indexing

For a 2-D array, provide [row, col]:

import numpy as np

b = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

print(b[1, 2])    # row 1, col 2 → 6
print(b[0, :])    # first row  → [1 2 3]
print(b[:, 1])    # second column → [2 5 8]
print(b[0:2, 1:3])  # sub-matrix
6
[1 2 3]
[2 5 8]
[[2 3]
 [5 6]]

Boolean masking

Pass a boolean array as an index to select only the elements where the condition is True:

import numpy as np

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

print(a[a > 3])        # elements greater than 3
print(a[a % 2 == 0])   # even elements
[4 5]
[2 4]

Boolean masks are the idiomatic NumPy replacement for filtered list comprehensions and are many times faster on large arrays.

Reshaping and Transposing

Reshaping

np.reshape() (or the .reshape() method) returns a view of the data with a new shape. The total number of elements must stay the same.

python— editable, runs on the server
[[1 2 3]
 [4 5 6]]

Use -1 for a dimension you want NumPy to infer automatically:

import numpy as np

a = np.arange(12)
print(a.reshape(3, -1))   # 3 rows, NumPy infers 4 columns
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Transposing

.T or np.transpose() swaps axes (rows ↔ columns for 2-D arrays):

python— editable, runs on the server
[[1 3 5]
 [2 4 6]]
(2, 3)

Adding and Removing Elements

Appending elements

np.append() returns a new flat array — it does not modify the original in-place (unlike list.append).

python— editable, runs on the server
[1 2 3 4 5 6]

For repeated appends inside a loop, building a Python list and converting it once with np.array() at the end is far more efficient than calling np.append() repeatedly.

Removing elements

np.delete(arr, indices) returns a new array with the specified indices removed:

python— editable, runs on the server
[1 2 5]

Concatenating arrays

np.concatenate() joins two or more arrays along an existing axis:

import numpy as np

a = np.array([1, 2])
b = np.array([3, 4])
print(np.concatenate([a, b]))
[1 2 3 4]

Broadcasting

Broadcasting is NumPy's rule for applying operations between arrays of different shapes — without copying data. The classic example is adding a scalar to an array:

import numpy as np

a = np.array([1, 2, 3])
print(a + 10)    # 10 is broadcast across all elements
[11 12 13]

A more powerful case: adding a 1-D array to each row of a 2-D array:

import numpy as np

matrix = np.array([[1, 2, 3],
                   [4, 5, 6]])
row    = np.array([10, 20, 30])

print(matrix + row)
[[11 22 33]
 [14 25 36]]

NumPy compares shapes from the right: (2, 3) + (3,) is valid because the trailing dimensions match; row is conceptually stretched into a (2, 3) array without any memory allocation.

Mathematical Operations

Element-wise arithmetic

All the standard operators (+, -, *, /, **) work element-wise on arrays of the same shape. The equivalent named functions (np.add, np.subtract, np.multiply, np.divide) can be useful when passing operations as arguments.

python— editable, runs on the server
[5 7 9]
[ 4 10 18]
[1 4 9]

Universal math functions (ufuncs)

NumPy provides vectorised versions of all standard math functions:

import numpy as np

a = np.array([0, 1, 4, 9, 16], dtype=float)

print(np.sqrt(a))
print(np.log(np.array([1, np.e, np.e**2])))   # natural log
print(np.sin(np.array([0, np.pi/2, np.pi])))
[0. 1. 2. 3. 4.]
[0. 1. 2.]
[ 0.000e+00  1.000e+00 -8.742e-08]

The tiny value near zero for sin(π) is normal floating-point rounding — np.pi is an approximation of π.

Aggregation Functions

Aggregation functions reduce an array (or one axis of it) to a single value:

import numpy as np

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

print(np.sum(a))    # 15
print(np.mean(a))   # 3.0
print(np.std(a))    # 1.4142135623730951
print(np.min(a))    # 1
print(np.max(a))    # 5
15
3.0
1.4142135623730951
1
5

For 2-D arrays, pass axis=0 to aggregate along columns or axis=1 to aggregate along rows:

import numpy as np

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

print(np.sum(m, axis=0))   # column totals: [5 7 9]
print(np.sum(m, axis=1))   # row totals:    [6 15]
[5 7 9]
[ 6 15]

Linear Algebra

NumPy's np.dot() computes the dot product of two 1-D vectors or the matrix product of two 2-D arrays. For matrix multiplication the @ operator (Python 3.5+) is the modern shorthand.

import numpy as np

a = np.array([1, 2])
b = np.array([3, 4])
print(np.dot(a, b))     # 1*3 + 2*4 = 11

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A @ B)            # matrix product
11
[[19 22]
 [43 50]]

np.linalg contains more advanced operations:

FunctionPurpose
np.linalg.det(A)Determinant
np.linalg.inv(A)Matrix inverse
np.linalg.eig(A)Eigenvalues and eigenvectors
np.linalg.solve(A, b)Solve the linear system A·x = b

Utility Functions

Sorting

import numpy as np

a = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print(np.sort(a))         # returns a sorted copy
print(np.argsort(a))      # indices that would sort the array
[1 1 2 3 4 5 6 9]
[1 3 6 0 2 4 7 5]

Unique values

import numpy as np

a = np.array([1, 2, 2, 3, 3, 3])
print(np.unique(a))
[1 2 3]

Conditional selection with np.where

np.where(condition, x, y) returns x where the condition is True and y elsewhere:

import numpy as np

a = np.array([1, 2, 3, 4, 5])
print(np.where(a > 2, a, 0))
[0 0 3 4 5]

NumPy with Matplotlib

NumPy and Matplotlib are designed to work together. np.linspace generates the x values; NumPy ufuncs compute y; Matplotlib plots the result:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 200)
plt.plot(x, np.sin(x), label='sin')
plt.plot(x, np.cos(x), label='cos')
plt.legend()
plt.title('Sine and Cosine')
plt.show()

This example requires Matplotlib to be installed (pip install matplotlib) and a display or an Agg backend for headless environments. See the Matplotlib Intro chapter for a detailed walkthrough.

What to Learn Next

  • SciPy Tutorial — higher-level scientific computing (integration, optimization, signal processing) built on NumPy arrays.
  • Matplotlib Intro — visualise NumPy arrays as line charts, scatter plots, histograms, and more.
  • Matplotlib Plotting — fine-tune figure layout, axes, and styles.
Was this page helpful?