NumPy (Numerical Python) is the foundation of the scientific Python ecosystem. While Python lists are great, they aren't designed for heavy mathematical lifting. NumPy introduces the ndarray (n-dimensional array), which is faster, uses less memory, and allows for complex mathematical operations over entire datasets without writing loops.
The core of NumPy is the array. Unlike a Python list, every element in a NumPy array must be of the same data type.
import numpy as np
# Creating a 1D array (Vector)
arr_1d = np.array([1, 2, 3, 4, 5])
type(arr_1d) # numpy.ndarray
arr_1d.dtype # dtype('int64')
arr_1d.size # 5
arr_1d.ndim # 1 (number of dimensions)
arr_1d.shape # (5,) a tuple representing the size in each dimension
b = np.array([3.1, 2.057, 5.2, 213.009])
b.dtype # dtype('float64')
c = np.array([0, 1, 2, 3, 4])
c[0] = 100 # changes index 0 to 100
# Creating a 2D array (Matrix)
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(f"Dimensions: {arr_2d.ndim}")
print(f"Shape: {arr_2d.shape}") # Returns (rows, columns)
You don't always have to type out your data. NumPy has built-in functions to generate arrays quickly.
One of the best things about NumPy is vectorization. You can perform operations on the entire array at once without a for loop.
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
# Element-wise addition
print(a + b) # Output: [11, 22, 33]
# Scalar multiplication
print(a * 2) # Output: [2, 4, 6]
# Array multiplication with scalar
c = np.array([1, 2])
d = 2 x c # np array -> [2, 4]
# Universal Functions (ufuncs)
print(np.sqrt(a))
print(np.sin(a))
With two vectors:
u = np.array([1, 0])
v = np.array([0, 1])
z = u + v
we can see the result visually when we add the "x" values together and the "y" values together.
Similarly we can do subtraction, multiplication and division. The
In NumPy, the Hadamard product (also known as element-wise multiplication) is computed using the multiplication operator or a specific functional call. It can be done by:
Asterisk Operator (*): The most common way to perform element-wise multiplication on NumPy arrays (ndarrays).
numpy.multiply(a, b): A functional equivalent to the * operator. It is useful when you need to specify additional parameters like an output array (out) or a mask (where)
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Method 1: Using the * operator
result_op = A * B
# Method 2: Using np.multiply()
result_func = np.multiply(A, B)
# Both yield:
# [[ 5, 12],
# [21, 32]]
We can apply a universal function to a Numpy array, such as the average or mean:
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
Slicing in NumPy is similar to Python lists but extends to multiple dimensions.
data = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
# Get a specific element [row, column]
print(data[0, 1]) # 20
# Slice rows and columns [row_start:row_stop, col_start:col_stop]
print(data[0:2, 1:3])
# Returns:
# [[20, 30],
# [50, 60]]
Sometimes you need to change the "view" of your data without changing the data itself.
arr = np.arange(12) # [0, 1, ..., 11]
# Reshape to 3 rows, 4 columns
reshaped = arr.reshape(3, 4)
# Transpose (flip axes)
transposed = reshaped.T
Speed: NumPy operations are implemented in C, making them significantly faster than Python loops.
Broadcasting: NumPy can perform operations between arrays of different shapes (under certain rules).
Memory: It uses contiguous memory blocks, which is much more efficient for your RAM.