Search Knowledge

© 2026 LIBREUNI PROJECT

NumPy: The Foundation of Numerical Computing

The Importance of NumPy

NumPy (Numerical Python) is the fundamental package for scientific computing in Python. It provides a high-performance multidimensional array object and tools for working with these arrays. If you are doing any form of data science or scientific computing in Python, NumPy is the engine under the hood.

Why not use Python Lists?

Python lists are incredibly flexible—they can hold elements of different types, and they grow dynamically. However, this flexibility comes at a significant performance cost.

The Problem with Python Lists:

  1. Memory Overhead: Each element in a Python list is a full-fledged object. A list of integers doesn’t just store the numbers; it stores pointers to integer objects, which each contain type information and reference counts.
  2. Lack of Locality: Because list elements are pointers, they can be scattered across memory. This prevents hardware-level optimizations like CPU caching and pre-fetching.
  3. Looping Speed: Iterating over a Python list in a loop is slow because the interpreter must check the type and perform dispatching for every single operation.

The NumPy Solution: ndarray

The core of NumPy is the ndarray (n-dimensional array) object.

  • Contiguous Memory: ndarrays store data in a single, contiguous block of memory.
  • Homogeneous Types: Every element in a NumPy array must be of the same type (e.g., all 64-bit floats).
  • Vectorized Operations: Operations on NumPy arrays are performed by compiled C/Fortran code, which can process entire arrays at once without Python loop overhead.

Creating Arrays

NumPy provides multiple ways to initialize arrays.

python
1 
2import numpy as np
3 
4# From a list
5a = np.array([1, 2, 3, 4, 5])
6print("From list:", a)
7 
8# Filled with zeros or ones
9zeros = np.zeros((3, 3))
10ones = np.ones((2, 4))
11print("Zeros:\n", zeros)
12 
13# Ranges
14arange = np.arange(0, 10, 2)
15linspace = np.linspace(0, 1, 5) # 5 points between 0 and 1
16print("Arange:", arange)
17print("Linspace:", linspace)
18 
19# Random numbers
20rand = np.random.rand(2, 2)
21print("Random:\n", rand)
22 

Array Attributes

Every ndarray has properties that describe its shape, size, and data type.

  • shape: A tuple indicating the size of each dimension (e.g., (rows, cols)).
  • ndim: The number of dimensions (axes).
  • size: The total number of elements.
  • dtype: The data type of the elements (e.g., int32, float64).
python
1 
2import numpy as np
3 
4arr = np.array([[1, 2, 3], [4, 5, 6]])
5 
6print(f"Shape: {arr.shape}")
7print(f"Dimensions: {arr.ndim}")
8print(f"Size: {arr.size}")
9print(f"Data Type: {arr.dtype}")
10 

Vectorization: The Secret Sauce

Vectorization is the process of replacing explicit Python loops with array expressions. This is the primary way to achieve high performance in NumPy.

Consider adding two large vectors:

python
1 
2import numpy as np
3import time
4 
5size = 1000000
6a = list(range(size))
7b = list(range(size))
8 
9# Python Loop approach
10start = time.time()
11c = [a[i] + b[i] for i in range(size)]
12end = time.time()
13print(f"Python list addition time: {end - start:.4f}s")
14 
15# NumPy approach
16na = np.arange(size)
17nb = np.arange(size)
18start = time.time()
19nc = na + nb
20end = time.time()
21print(f"NumPy vector addition time: {end - start:.4f}s")
22 

As you can see, the NumPy version is orders of magnitude faster. It’s not just “shorter code”—it’s fundamentally different execution.

Memory Layout and Slicing

NumPy slicing is unique because it creates views of the data rather than copies. This is extremely efficient for large datasets but requires caution: changing a slice changes the original array.

python
1 
2import numpy as np
3 
4arr = np.array([0, 1, 2, 3, 4, 5])
5s = arr[1:4]
6s[0] = 99
7 
8print("Original array after modifying slice:", arr)
9 

In the next module, we will explore advanced indexing and multi-dimensional array manipulations.