1. Introduction: Why NumPy Is Essential for Data Science & AI
In previous modules, we explored the foundations of data science and introduced the primary tools in the Python data ecosystem. While Python provides built-in lists for storing collections of items, standard lists become noticeably slow and memory-inefficient when performing intensive mathematical operations on large volumes of data.
Scientific computing and machine learning libraries require performing massive numbers of mathematical operations on arrays and matrices. To handle this scale, Python relies on NumPy (Numerical Python), an optimized library designed specifically for fast, memory-efficient array manipulation.
- Python standard list:
for x in list: ... — element-by-element iteration (high overhead, slower execution).
- NumPy ndarray:
array + 5 — vectorized manipulation (executes in parallel at C speed).
2. The Core Data Structure: The ndarray
At the heart of NumPy is its core data structure: the ndarray (n-dimensional array).
- N-dimensional support: An ndarray can store data across any number of dimensions—including 1D (vectors), 2D (matrices/tables), and 3D or higher-dimensional tensors.
- Vectorized manipulation: Instead of using manual loops that iterate through each item one by one, NumPy treats the array as a single vector and executes mathematical operations across the entire vector in parallel. This vectorized approach makes array computations blazing fast and forms the computational foundation for modern machine learning frameworks.
3. Setting Up the Environment in Jupyter Notebook
Step 1: Installing and Verifying NumPy
Before using NumPy, verify that it is installed in your Python environment. In a Jupyter Notebook cell, you can install or check NumPy by prefixing the pip command with an exclamation mark (!):
# Installing or verifying NumPy inside a Jupyter Notebook cell
!pip install numpy
When run, Python confirms whether NumPy is already installed and displays the active version (such as version 2.3.5).
Step 2: Importing NumPy with the Industry Standard Alias
It has become a standard convention across the software and data science industry to import NumPy using the alias np:
# Standard industry import convention
import numpy as np
import time # Standard library used for benchmarking performance
4. Creating and Inspecting a 2D Array
Creating an ndarray
To create an ndarray, pass a nested list of values into the np.array() constructor. In this example, we define a 2D matrix with two rows and three columns:
import numpy as np
# Creating a 2-row, 3-column NumPy array
arr = np.array([
[1, 2, 3], # Row 1
[4, 5, 6] # Row 2
])
print("Array Contents:\n", arr)
Key Array Properties: shape, ndim, and dtype
NumPy provides three essential attributes to inspect the structure and data types of an array:
# 1. Inspecting dimensions in terms of (rows, columns)
print("Shape:", arr.shape)
# 2. Inspecting the number of dimensions
print("Number of Dimensions (ndim):", arr.ndim)
# 3. Inspecting the data type of the elements
print("Data Type (dtype):", arr.dtype)
Output analysis:
Shape: (2, 3)
Number of Dimensions (ndim): 2
Data Type (dtype): int64
arr.shape: Returns (2, 3), confirming that the array consists of 2 rows and 3 columns.
arr.ndim: Returns 2, indicating that the array is two-dimensional. (Real-world client datasets often arrive with multiple dimensions.)
arr.dtype: Returns int64, meaning each element is stored as a 64-bit integer. This indicates that a 64-bit Python build is running on a 64-bit operating system.
5. Performance Benchmark: Standard Python List vs. NumPy ndarray
To verify the performance advantage of NumPy, we compare the execution time of performing an arithmetic operation (+ 5) on 1 million elements using a standard Python list versus a NumPy array.
Approach 1: Using a Standard Python List and a for Loop
import time
# Creating a standard Python list with 1,000,000 numbers
huge_list = list(range(1000000))
result_list = []
# Starting timer
start_time = time.time()
# Iterating and appending +5 to each element
for item in huge_list:
result_list.append(item + 5)
end_time = time.time()
print(f"Standard List Time: {end_time - start_time:.4f} seconds")
Measured duration: Approximately 0.1177 seconds.
Approach 2: Using a NumPy ndarray with Vectorization
# Creating a NumPy ndarray with 1,000,000 numbers
huge_array = np.arange(1000000)
# Starting timer
start_time = time.time()
# Vectorized operation: applied across the entire array in parallel without a loop
result_array = huge_array + 5
end_time = time.time()
print(f"NumPy ndarray Time: {end_time - start_time:.4f} seconds")
Measured duration: Approximately 0.0028 seconds.
Why NumPy Wins
Do not write manual for loops over NumPy arrays when applying arithmetic operations, because a loop forces Python to process each element individually. Delegating the calculation directly to the ndarray (huge_array + 5) allows NumPy's underlying C engine to execute the operation across all elements in parallel via vectorization. This produces a dramatic performance speedup, making NumPy the clear choice whenever handling large datasets.