Difficulty: Beginner
NumPy (Numerical Python) is the foundational library for numerical computing in Python. It provides a powerful N-dimensional array object called ndarray, which is far more efficient than Python's built-in lists for mathematical operations. The core of NumPy is implemented in C and Fortran, which means array operations execute at near-native speed rather than being interpreted line by line like pure Python code. Almost every data science and machine learning library in Python, including Pandas, Scikit-learn, and TensorFlow, is built on top of NumPy arrays.
The primary object in NumPy is the ndarray (n-dimensional array). You create arrays using np.array() by passing in a Python list or nested list. Unlike Python lists, which can hold mixed types, a NumPy array stores elements of a single data type (dtype). This homogeneity is what allows NumPy to store data in contiguous blocks of memory and perform vectorized operations efficiently. Common dtypes include int64 for integers, float64 for floating-point numbers, and bool for boolean values.
Every NumPy array has several important attributes. The shape attribute returns a tuple describing the dimensions of the array: for a 1D array of 5 elements, shape is (5,); for a 2D array with 3 rows and 4 columns, shape is (3, 4). The ndim attribute gives the number of dimensions, size gives the total number of elements, and dtype tells you the data type of the elements. Understanding these attributes is essential for debugging shape mismatches and ensuring your data pipelines work correctly.
You can also create arrays using convenience functions like np.zeros(), np.ones(), np.arange(), and np.linspace(). These are particularly useful when you need to initialize arrays of a specific shape before filling them with computed values. np.zeros((3, 4)) creates a 3x4 array of zeros, np.arange(0, 10, 2) creates an array [0, 2, 4, 6, 8] similar to Python's range(), and np.linspace(0, 1, 5) creates 5 evenly spaced values from 0 to 1 inclusive.
NumPy arrays are mutable, meaning you can change individual elements or slices after creation. However, unlike Python lists, you cannot append elements to an existing array without creating a new one. This is because the array is stored as a fixed block of memory. When you need to combine arrays, you use functions like np.concatenate(), np.vstack(), or np.hstack() which create new arrays from existing ones.
import numpy as np
# 1D array from a list
arr1 = np.array([1, 2, 3, 4, 5])
print("1D array:", arr1)
print("Type:", type(arr1))
# 2D array from nested lists
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print("2D array:")
print(arr2)
np.array() converts a Python list into a NumPy ndarray. A flat list produces a 1D array, and a list of lists produces a 2D array. The type() function confirms the object is an ndarray.
import numpy as np
arr = np.array([[10, 20, 30], [40, 50, 60]])
print("Shape:", arr.shape)
print("Dimensions:", arr.ndim)
print("Size:", arr.size)
print("Dtype:", arr.dtype)
A 2x3 array has shape (2, 3), 2 dimensions, 6 total elements, and since we passed integers, the dtype is int64 by default.
import numpy as np
# Explicit dtype
float_arr = np.array([1, 2, 3], dtype=np.float64)
print("Float array:", float_arr)
print("Dtype:", float_arr.dtype)
# Zeros and ones
zeros = np.zeros((2, 3))
print("Zeros:")
print(zeros)
ones = np.ones(4)
print("Ones:", ones)
Passing dtype=np.float64 forces integer values to be stored as floats. np.zeros() and np.ones() create arrays filled with 0.0 or 1.0 respectively, defaulting to float64 dtype.
import numpy as np
# arange: like range() but returns ndarray
arr1 = np.arange(0, 10, 2)
print("arange(0,10,2):", arr1)
# linspace: evenly spaced values
arr2 = np.linspace(0, 1, 5)
print("linspace(0,1,5):", arr2)
print("arange shape:", arr1.shape)
print("linspace shape:", arr2.shape)
np.arange() works like Python's range() but returns an ndarray. np.linspace() divides an interval into a specified number of evenly spaced points, including both endpoints.
NumPy arrays, ndarray, np.array(), dtype, shape