1. Introduction
Approach for Learning NumPy
- Focus on Capabilities over Memorisation: Avoid attempting to mug up or memorise specific syntaxes. Instead, understand what operations and structures are supported, and refer to the official documentation when writing code.
- Active Parallel Coding: Rather than passive watching, open tutorials and official documentation side-by-side with your environment. Write the code yourself to build confidence and muscle memory.
- Project-Driven Learning: The best way to internalise syntaxes is to work on end-to-end, hands-on projects where you write the same NumPy commands repeatedly.
Installing and Running NumPy
- Installation: In your terminal or a Jupyter notebook cell, install the package using
pip: - Importing: It is standard practice to import NumPy under the alias
npto keep code clean and standardised: - Interactive Environments: Using interactive environments like Jupyter notebooks allows you to execute blocks of code on-the-fly and check array shapes and dimensions immediately.
2. NumPy Fundamentals
Why NumPy Arrays? (Need and Internals)
At the core of the NumPy library is the NDArray (N-Dimensional Array) object. Standard Python lists have significant performance bottlenecks when handling large-scale data:- Homogeneity vs. Heterogeneity: Standard Python lists can contain heterogeneous data types, while NDArrays are homogeneous—all elements must share the exact same data type (e.g., all integers, all floats, or all strings).
- The Cost of Looping in Python: For large-scale math on millions of elements, Python lists require explicit, interpreted loops. This incurs heavy overhead because Python continually interprets the code and manipulates individual Python objects in memory.
- The C-Speed Under the Hood: NumPy operations are executed speedily at near-C speed. This is because NumPy relies on optimized, precompiled C code under the hood, saving the interpreter’s overhead and managing elements in contiguous memory blocks. It gives you the “best of both worlds”: the code simplicity of Python and the execution speed of C.
Vectorization
Vectorization is the absence of any explicit looping or indexing in your Python code.- Instead of looping over elements manually (
for x in array), you write standard mathematical notations (e.g.,C = A * B). - It produces concise, highly readable, and pythonic code with fewer lines and fewer bugs.
- Looping is implicitly offloaded to precompiled C code, making operations incredibly efficient.
Overview of Broadcasting
Broadcasting describes the implicit element-by-element behaviour of array operations.- By default, when an NDArray is involved, operations (arithmetic, logical, bitwise, or functional) happen element-wise.
- If you operate on two arrays of different shapes, NumPy evaluates if their dimensions are compatible.
- If they are compatible, NumPy expands the smaller array under the hood (without making actual copies in memory) until its shape matches the larger array, allowing the element-wise operation to proceed unambiguously. If they are incompatible, a
ValueErroris raised.
3. Creating Arrays
Basic Array Creation
To instantiate a standard array from a Python list or iterable, usenp.array():
Zeros Array (np.zeros)
Creates an array filled entirely with zeros.
- Usage: Pass the desired shape as a single integer or a tuple.
- Note: Do not pass multiple dimensions as flat arguments (e.g.,
np.zeros(2, 3)), as NumPy will attempt to interpret the second argument as a data type. Always wrap multi-dimensional shapes in an outer tuple.
Ones Array (np.ones)
Creates an array filled entirely with ones, acting similarly to np.zeros.
Empty Array (np.empty)
Creates an uninitialised array of a specified shape.
- How it works: Instead of setting elements to zero or one,
np.emptysimply allocates the memory block and leaves whatever garbage values were already present in those memory addresses. - Why use it: It is faster than
np.zeros,np.ones, ornp.randombecause it avoids the overhead of value initialisation. It is highly useful when you plan to immediately overwrite every element in the array anyway.
Range Creation (np.arange)
Generates sequences of numbers over a range.
- Syntax:
np.arange([start], stop, [step]) - Default values:
startdefaults to0, andstepdefaults to1. - Inclusivity: The
stopvalue is not inclusive.
Linearly Spaced Arrays (np.linspace)
Generates a specified number of evenly spaced values over a specified interval.
- Key Difference from
arange: Instead of specifying a step size, you specify the exact number of elements you want. - Defaults: Returns elements as
float64by default. You can explicitly override this using thedtypeparameter.
Random Number Generation (np.random)
Modern NumPy uses a default generator object for producing random numbers.
- To use it, instantiate the generator first using
np.random.default_rng(). - Generate random integers within a range using the
.integers(low, high, size)method.
4. Array Properties
Knowing your array properties is essential for debugging structural errors and matching dimensional shapes.
Mental Model for Multi-Dimensional Layouts: In mathematical notation, we access a 2D matrix by row index first and column index second. For higher dimensions, a helpful mental model is that the column index comes last, and the row index is second-to-last.
5. Indexing and Slicing
Accessing Elements
NumPy is zero-indexed. You access elements using square brackets.- Positive Indexing:
a[0]accesses the first element. - Negative Indexing:
a[-1]accesses the last element, anda[-2]accesses the second-to-last element.
Modifying Elements
You can mutate array elements in place by assigning a value directly to a targeted index.Basic Slicing
Extracts sections of an array using[start:stop:step] notation.
- Slices include the
startindex but exclude thestopindex.
2D Array Slicing
For a two-dimensional array, the syntax isarr[row_slice, column_slice]. You separate row and column instructions with a comma:
Conditional (Boolean) Slicing
This is a powerful filtering technique where elements are selected based on logical conditions.- Generating a Boolean Mask: Running an operator like
a < 6returns an array ofTrue/Falseflags corresponding to whether each element meets the condition. - Filtering: Passing this mask back into the array returns a flat, new array containing only the elements where the mask is
True.
- Multiple Conditions: You can combine multiple logical conditions. You must wrap each condition in round brackets and use bitwise operators (
&for AND,|for OR).
- Retrieving Indices of Matches (
np.nonzero): If you want to find the indices where the condition is met instead of the values themselves, usenp.nonzero(condition). It returns a tuple of arrays (one for each dimension) containing coordinates of matching elements.
6. Array Manipulation
Reshaping Arrays (reshape)
Changes the shape structure of an array without modifying its underlying data.
- Sizing Constraint: The total size (number of elements) in the reshaped array must exactly match the original array size, otherwise NumPy throws a
ValueError.
- Row-Major vs. Column-Major Order (
order): You can control how elements are read/placed in memory during a reshape:order='C'(default, C-like order): Fills elements row-by-row.order='F'(Fortran-like order): Fills elements column-by-column.
Flattening Arrays (flatten, ravel)
Converts a multi-dimensional array into a flat 1D array.
ravel(Shallow/View): Returns a flattened view of the original array. Changes made to the ravelled array will directly mutate the original parent array. It is highly memory-efficient because no copy of the underlying data is made.flatten(Deep Copy): Returns a completely new 1D copy of the array. Modifying the flattened array will not affect the parent array.
Transposing Arrays (transpose, .T)
Transposes the matrix by swapping row indices with column indices.
- Access via
.Tor the.transpose()method. - Transposing twice returns the original array structure. It does not modify the original parent array in place.
Reversing/Flipping Arrays (flip, slicing)
Reverses the order of elements along axes.
np.flip(arr): Flips the elements across all axes.- Axis-Specific Flipping:
np.flip(arr, axis=0)reverses vertically along the rows.np.flip(arr, axis=1)reverses horizontally along the columns.
- Sub-array Flipping: You can flip targeted portions. For example,
np.flip(arr[1])reverses only the second row, whilenp.flip(arr[:, 1])reverses only the second column.
7. Combining and Splitting Arrays
Concatenation (concatenate)
Combines multiple arrays along a specified axis.
- By default,
axis=0is used, which vertically stacks them. - Constraint: All arrays must have matching dimensions except along the concatenation axis, or NumPy will raise an error.
Vertical Stacking (vstack)
Stacks arrays on top of each other (vertically along axis=0).
Horizontal Stacking (hstack)
Stacks arrays adjacent to each other side-by-side (horizontally along axis=1).
Horizontal Splitting (hsplit)
Splits an array horizontally along columns.
- Split into Equal Sections: Pass an integer specifying the number of equal sub-arrays.
- Split at Specific Coordinates: Pass a list of column indices where cuts should happen.
8. Sorting and Copying
Sorting Arrays (sort, argsort, partition)
np.sort: Returns a sorted copy of the array.np.argsort: Returns the indices that would sort the array, allowing you to perform indirect sorting.np.partition: Partitions an array around a specified indexk. All elements smaller than the element atkare shuffled to the left, and all larger elements are moved to the right. The elements on either side are not guaranteed to be sorted. This is highly useful for top-k selection algorithms.
Views vs. Copies
Understanding memory management in NumPy is critical to preventing unintended mutations.- Views (Shallow Copies): To save memory and execution overhead, basic operations like slicing and indexing return views rather than copies. If you modify a slice, the changes propagate to the original array.
- Deep Copies: If you need a completely isolated array, call the
.copy()method explicitly to allocate separate memory.
9. Aggregate Functions
Aggregations can collapse an entire array or be calculated along specific dimensions using theaxis parameter.
axis=0(Rows Axis): Computes operations vertically down columns.axis=1(Columns Axis): Computes operations horizontally across rows.
10. Mathematical Vector Operations
What are Vector Operations?
Vector operations in NumPy allow mathematical operations to be performed on entire arrays at once, instead of processing one element at a time using loops. This concept is known as vectorization. NumPy internally uses highly optimized C code, making these operations much faster and more efficient than traditional Python loops.Key Idea: Perform operations on the whole array with a single statement.
Example 1: Vector Addition
Example 2: Vector Subtraction
Example 3: Vector Multiplication
Example 4: Vector Division
Example 5: Scalar Operations
A scalar is a single numeric value. NumPy automatically applies the scalar to every element of the array.Example 6: Power Operation
Example 7: Square Root
Example 8: Trigonometric Functions
Why Vectorization?
Without NumPy
Using NumPy
- No explicit loops
- Less code
- Faster execution
- Better readability
- Optimized memory usage
Real-World Example
Suppose an employee receives a ₹5,000 salary increment.11. Broadcasting
Concept of Broadcasting
Broadcasting is a powerful feature of NumPy that allows arithmetic operations between arrays of different shapes, without explicitly copying data. Instead of creating a larger array, NumPy virtually expands the smaller array to match the larger one whenever possible.Key Idea: Automatically expand smaller arrays so mathematical operations become possible.
Broadcasting Rules
NumPy compares array dimensions from right to left. Two dimensions are compatible if:- They are equal.
- One of them is 1.
Example 1: Scalar Broadcasting
Example 2: Matrix + Scalar
Example 3: Broadcasting a Row Vector
Example 4: Broadcasting a Column Vector
Example 5: Student Marks
Suppose every student receives 5 bonus marks in every subject.Example 6: Image Brightness
Example 7: Temperature Conversion
Convert Celsius temperatures into Fahrenheit. Formula:Example 8: Broadcasting Failure
Broadcasting Compatibility
Real-World Analogy
Imagine a teacher announces:“Everyone gets 5 grace marks.”Instead of giving each student the marks individually, the same bonus is automatically applied to every student. Broadcasting works in exactly the same way—NumPy automatically applies smaller arrays wherever they fit.
Key Points
- Broadcasting enables operations on arrays of different shapes.
- Smaller arrays are virtually expanded without copying data.
- Shapes must satisfy broadcasting rules.
- Broadcasting improves both memory efficiency and performance.
- It is one of NumPy’s most powerful features for scientific computing and machine learning.
12. Broadcasting and Vectorization - How They Work Together
Students often think that Broadcasting and Vectorization are the same. In reality, they are two different concepts that work together to perform efficient mathematical operations on NumPy arrays.Step 1: Broadcasting (Shape Compatibility)
Broadcasting is the process of making arrays with different shapes compatible for arithmetic operations. NumPy logically expands the smaller array to match the shape of the larger array without actually copying the data into memory.Purpose: Make the array shapes compatible.
Step 2: Vectorization (Element-wise Computation)
Once the shapes become compatible, NumPy performs the mathematical operation on all elements simultaneously without using explicit Python loops.Purpose: Perform fast element-wise operations.
Example
Step 1: Broadcasting
The shapes of the arrays areImportant: NumPy does not actually create this expanded array in memory. This is only a conceptual view to understand broadcasting.
Step 2: Vectorization
After broadcasting, NumPy performs the addition on all corresponding elements simultaneously.Visual Representation
Real-World Analogy
Imagine a classroom with two rows of students. The teacher announces:“Each student receives 5 bonus marks.”Instead of writing the value 5 for every student individually, the same value is automatically applied to every student in each row.
- Broadcasting is like extending the single bonus value wherever it is needed.
- Vectorization is like adding those bonus marks to every student’s score simultaneously.
Broadcasting vs Vectorization
Key Takeaways
- Broadcasting and Vectorization are not the same.
- Broadcasting prepares arrays by making their shapes compatible.
- Vectorization performs the mathematical operation on the compatible arrays.
- Broadcasting does not create extra copies of the data; the expansion is only logical.
- Together, broadcasting and vectorization make NumPy fast, memory-efficient, and ideal for scientific computing, data analysis, and machine learning.
Easy to Remember:
Broadcasting prepares the arrays → Vectorization performs the computation.
Practice and Next Steps
Before moving to the next section, make sure to practice your NumPy skills using the interactive notebook:NumPy Practice Exercise
Practice your skills using the interactive notebook.💻 VS Code | 🚀 Colab | 📥 Download
Next Library: Pandas
Learn data manipulation and analysis using Pandas DataFrames and Series.