ICT582 Python Revision
🎯 Learning Objectives
πŸ“š Key Concepts

What is NumPy?

NumPy = Numerical Python. A third-party package for multi-dimensional array processing.

pip install numpy       # install once
import numpy as np     # convention: alias 'np'

NumPy is used alongside SciPy and Matplotlib as a MATLAB replacement. Created by Travis Oliphant in 2005 from Numeric and Numarray.

Why ndarray over list?

  • Lists are heterogeneous (different types allowed) but slow for large arrays.
  • ndarrays are homogeneous (all elements same type) and stored in one continuous memory block.
  • Processing speed can be 50Γ— faster than lists for large numerical data.
  • NumPy also provides many convenient functions for multi-dimensional array operations.

Creating ndarrays

import numpy as np

# From a list or tuple
a = np.array([1, 2, 3, 4])         # 1D
b = np.array([[1,2,3],[4,5,6]])    # 2D (2Γ—3)

# Special constructors
np.zeros((2, 4))    # 2Γ—4 filled with 0.0
np.ones((3, 2))     # 3Γ—2 filled with 1.0
np.empty((3, 5))    # 3Γ—5 with uninitialized values

# arange (like range, returns ndarray)
np.arange(5)            # [0 1 2 3 4]
np.arange(2, 8)         # [2 3 4 5 6 7]
np.arange(1, 7, 2)      # [1 3 5]
np.arange(12).reshape(3, 4)   # 3Γ—4 matrix

# linspace β€” n evenly spaced values
np.linspace(0, 3, 9)    # 9 values from 0 to 3
np.linspace(0, np.pi, 10)  # 10 values 0 to Ο€

Ndarray Properties

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

print(a.ndim)      # 3      (number of dimensions)
print(a.shape)     # (2, 3, 4) (size of each dimension)
print(a.size)      # 24    (total elements)
print(a.dtype)     # int64  (element type)
print(a.itemsize)  # 8     (bytes per element)
PropertyMeaning
ndimNumber of dimensions (axes)
shapeTuple: size along each axis
sizeTotal number of elements
dtypeData type of elements (e.g. int64, float64)
itemsizeBytes per element

Dimensions and Shapes Visualised

Codendimshapesize
np.array(3)0()1
np.array([2,1,3,4])1(4,)4
np.array([[2,1,3,4],[1,3,5,7],[4,6,1,3]])2(3,4)12

Element Type (dtype)

Ndarrays are homogeneous β€” all elements must be the same type. If you mix int and float, all become float.

a = np.array([2, 3.1, 4, 5])
print(a)        # [2.  3.1 4.  5. ]  (all float)

# Specify dtype explicitly
b = np.array([2, 4, 5, 10], dtype=np.int16)
c = np.arange(1, 5, dtype=float)   # [1. 2. 3. 4.]

Indexing Ndarrays

a = np.array([1, 2, 3, 4])
print(a[2])     # 3   (positive index)
print(a[-2])    # 3   (negative index)

# 2D indexing: [row, col]
b = np.array([[1,2,3,4],
              [5,6,7,8]])
print(b[1, 2])   # 7  (row 1, col 2)
print(b[0, -1])  # 4  (row 0, last col)

Slicing Ndarrays

Same syntax as lists: [start:stop:step]. Stop is exclusive.

a = np.array([1,2,3,4,5,6,7,8])
print(a[1:5])    # [2 3 4 5]
print(a[:5:2])   # [1 3 5]

# 2D slicing
c = np.arange(12).reshape(3, 4)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
print(c[1, :])    # [4 5 6 7]  (row 1)
print(c[:, 2])    # [2 6 10]  (col 2)
print(c[0:2, 1:3]) # [[1 2][5 6]]

Copy vs View

  • A slice of an ndarray creates a view (no copy). Modifying the slice modifies the original.
  • Use .copy() to get an independent copy.
a = np.array([1, 2, 3, 4])
b = a[1:3]       # view β€” shares memory with a
b[0] = 99
print(a)         # [1 99  3  4]  (a was also changed!)

c = a[1:3].copy()  # independent copy
c[0] = 0
print(a)         # [1 99  3  4]  (a unchanged)

Element-wise Operations

Arithmetic operations apply to every element β€” no loop needed.

a = np.array([1, 2, 3, 4])
print(a + 10)    # [11 12 13 14]
print(a * 2)     # [ 2  4  6  8]
print(a ** 2)    # [ 1  4  9 16]

b = np.array([10, 20, 30, 40])
print(a + b)     # [11 22 33 44]
print(a * b)     # [10 40 90 160]

Universal Functions (ufuncs)

NumPy provides element-wise mathematical functions called ufuncs:

a = np.array([1, 4, 9, 16])
print(np.sqrt(a))   # [1. 2. 3. 4.]
print(np.exp(a))    # e^1, e^4, ...
print(np.log(a))    # natural log
print(np.sin(a))    # sine of each
print(np.abs(a))    # absolute value

Matrix Multiplication

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

print(A * B)    # Element-wise: [[ 5 12][21 32]]
print(A @ B)    # Matrix multiplication: [[19 22][43 50]]
# Or: np.matmul(A, B)

Concatenate Arrays

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.concatenate([a, b])   # [1 2 3 4 5 6]
⚠️ Exam Focus
  1. Know why ndarrays are faster than lists: homogeneous, contiguous memory, 50Γ— speed.
  2. Know ndarray properties: ndim, shape, size, dtype, itemsize and what they return.
  3. arange vs linspace: arange = fixed step; linspace = fixed number of values.
  4. Slicing creates a view (not a copy) β€” modifying a slice modifies the original. Use .copy() for independence.
  5. Element-wise: a * b multiplies corresponding elements; a @ b is matrix multiplication.
❌ Common Mistakes
⚑ Quick Recap
← Topic 09: OOP Topic 11: Matplotlib β†’