Working with NumPy Arrays and Numerical Operations
Course: Python Training
Topic: Working with NumPy Arrays and Numerical Operations
Course Link: Python Training Course at JustAcademy
Demo Link: Register for a Course Demo
Website: Visit JustAcademy
1. Introduction to NumPy
NumPy stands for Numerical Python. It is a fundamental Python library used for numerical computing, mathematical operations, scientific calculations, data analysis, and multidimensional array processing. NumPy provides an efficient array object called ndarray, which can store elements in one or more dimensions and perform operations on entire arrays.
Python lists are useful for general-purpose programming, but NumPy arrays provide specialized functionality for numerical data. They support vectorized calculations, mathematical functions, statistical operations, matrix manipulation, broadcasting, and efficient memory usage for many numerical workloads.
NumPy is widely used in data science, machine learning, scientific research, engineering, financial analysis, image processing, and artificial intelligence.
Learning Objectives
- Understand NumPy and its importance in numerical computing.
- Install NumPy and import it into a Python program.
- Create one-dimensional, two-dimensional, and multidimensional arrays.
- Understand array dimensions, shape, size, and data types.
- Perform arithmetic and mathematical operations on arrays.
- Access array elements using indexing and slicing.
- Filter data using Boolean conditions.
- Understand broadcasting and vectorized operations.
- Perform statistical calculations and matrix operations.
- Reshape, combine, split, sort, and copy arrays.
- Apply NumPy to a practical numerical data analysis project.
2. Installing NumPy
NumPy must be installed in the Python environment before it can be imported, unless it is already included in that environment.
Install Using pip
python -m pip install numpy
To verify the installation, open a Python terminal or create a Python file and execute the following code.
import numpy as np
print(np.__version__)
The command displays the installed NumPy version. The alias np is the conventional short name used when importing NumPy.
Importing NumPy
import numpy as np
numbers = np.array([10, 20, 30, 40])
print(numbers)
Output:
[10 20 30 40]
The import numpy as np statement makes NumPy functions available through the np prefix. For example, np.array() creates an array and np.mean() calculates an arithmetic mean.
3. Understanding NumPy Arrays
A NumPy array is an object of the ndarray class. It stores elements in a structured multidimensional format and generally uses a common data type for its elements.
3.1 One-Dimensional Array
A one-dimensional array contains elements along a single axis.
import numpy as np
numbers = np.array([10, 20, 30, 40, 50])
print(numbers)
Output:
[10 20 30 40 50]
This array has five elements arranged along one dimension.
3.2 Two-Dimensional Array
A two-dimensional array contains rows and columns. It is useful for representing tables, matrices, and structured numerical data.
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(matrix)
Output:
[[1 2 3]
[4 5 6]]
This array contains two rows and three columns.
3.3 Three-Dimensional Array
A three-dimensional array can represent multiple matrices or batches of numerical data.
import numpy as np
data = np.array([
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
])
print(data)
print("Dimensions:", data.ndim)
print("Shape:", data.shape)
Output:
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
Dimensions: 3
Shape: (2, 2, 2)
The array contains two blocks, with two rows and two columns in each block.
4. Creating NumPy Arrays
NumPy provides several functions for creating arrays from existing data or generating arrays with predefined values and patterns.
4.1 Creating an Array from a List
import numpy as np
numbers = np.array([1, 2, 3, 4, 5])
print(numbers)
4.2 Creating an Array of Zeros
The np.zeros() function creates an array filled with zeros.
import numpy as np
zeros = np.zeros(5)
print(zeros)
Output:
[0. 0. 0. 0. 0.]
The default data type is generally a floating-point type. You can specify another type using the dtype parameter.
zeros = np.zeros(5, dtype=int)
print(zeros)
Output:
[0 0 0 0 0]
4.3 Creating an Array of Ones
import numpy as np
ones = np.ones((2, 3))
print(ones)
Output:
[[1. 1. 1.]
[1. 1. 1.]]
The tuple (2, 3) specifies two rows and three columns.
4.4 Creating an Array with a Range of Values
The np.arange() function creates values at regular intervals. Its stop value is normally excluded.
import numpy as np
numbers = np.arange(1, 11)
print(numbers)
Output:
[ 1 2 3 4 5 6 7 8 9 10]
You can also specify a step size.
numbers = np.arange(0, 20, 5)
print(numbers)
Output:
[ 0 5 10 15]
4.5 Creating Evenly Spaced Values with linspace()
The np.linspace() function generates a specified number of evenly spaced values between a starting point and an ending point. By default, the ending point is included.
import numpy as np
numbers = np.linspace(0, 10, 6)
print(numbers)
Output:
[ 0. 2. 4. 6. 8. 10.]
Use linspace() when the required number of samples matters more than the step size.
4.6 Creating an Identity Matrix
An identity matrix contains ones along the main diagonal and zeros elsewhere.
import numpy as np
identity = np.eye(3)
print(identity)
Output:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
4.7 Creating an Array of Random Values
NumPy's random number generator creates pseudorandom values for simulations, sampling, testing, and numerical experiments.
import numpy as np
rng = np.random.default_rng(42)
numbers = rng.integers(1, 101, size=5)
print(numbers)
The seed makes the generated sequence reproducible with the same NumPy generator behavior. The upper bound of integers() is excluded, so this example generates integers from 1 through 100.
5. Important Attributes of NumPy Arrays
NumPy arrays provide attributes that describe their structure, element count, data type, and memory layout.
| Attribute |
Description |
Example |
ndim |
Number of dimensions or axes |
array.ndim |
shape |
Size along each dimension |
array.shape |
size |
Total number of elements |
array.size |
dtype |
Data type of array elements |
array.dtype |
itemsize |
Bytes used by each element |
array.itemsize |
nbytes |
Total bytes occupied by array element data |
array.nbytes |
Example
import numpy as np
data = np.array([
[10, 20, 30],
[40, 50, 60]
], dtype=np.int32)
print("Dimensions:", data.ndim)
print("Shape:", data.shape)
print("Total elements:", data.size)
print("Data type:", data.dtype)
print("Bytes per element:", data.itemsize)
print("Element storage bytes:", data.nbytes)
Output:
Dimensions: 2
Shape: (2, 3)
Total elements: 6
Data type: int32
Bytes per element: 4
Element storage bytes: 24
The nbytes attribute reports the memory occupied by the array's element data. It does not include all Python object overhead or necessarily all memory associated with referenced objects.
6. Understanding NumPy Data Types
NumPy uses data types to determine how values are represented in memory and how arithmetic operations behave. Choosing an appropriate data type can improve memory efficiency and help prevent unexpected numerical results.
Common NumPy Data Types
| Data Type |
Description |
Example |
np.int32 |
32-bit signed integer |
np.array([10, 20], dtype=np.int32) |
np.int64 |
64-bit signed integer |
np.array([100, 200], dtype=np.int64) |
np.float32 |
32-bit floating-point number |
np.array([1.5, 2.5], dtype=np.float32) |
np.float64 |
64-bit floating-point number |
np.array([1.25, 3.75], dtype=np.float64) |
np.bool_ |
Boolean value |
np.array([True, False]) |
np.str_ |
String value |
np.array(["A", "B"]) |
Specifying a Data Type
import numpy as np
numbers = np.array([1, 2, 3, 4], dtype=np.float64)
print(numbers)
print(numbers.dtype)
Output:
[1. 2. 3. 4.]
float64
Converting an Array's Data Type
The astype() method creates an array with a converted data type.
import numpy as np
numbers = np.array([1.2, 2.8, 3.5])
integers = numbers.astype(int)
print(integers)
Output:
[1 2 3]
Converting floating-point values to integers truncates the fractional portion rather than rounding to the nearest integer. Data-type conversions may also lose precision or change value ranges, so select the target type carefully.
7. Indexing NumPy Arrays
Indexing retrieves individual elements from an array. NumPy uses zero-based indexing, which means the first element has index zero. Negative indices count backward from the end.
7.1 Accessing Elements in a One-Dimensional Array
import numpy as np
numbers = np.array([10, 20, 30, 40, 50])
print(numbers[0])
print(numbers[2])
print(numbers[-1])
Output:
10
30
50
7.2 Accessing Elements in a Two-Dimensional Array
For a two-dimensional array, use array[row_index, column_index].
import numpy as np
matrix = np.array([
[10, 20, 30],
[40, 50, 60]
])
print(matrix[0, 1])
print(matrix[1, 2])
Output:
20
60
7.3 Updating Array Elements
import numpy as np
numbers = np.array([10, 20, 30, 40])
numbers[1] = 99
print(numbers)
Output:
[10 99 30 40]
Array elements can be changed by assigning compatible values. The value must be representable by the array's data type.
8. Slicing NumPy Arrays
Slicing selects a range of array elements. The common syntax is array[start:stop:step]. The start index is included, while the stop index is excluded.
8.1 Basic Slicing
import numpy as np
numbers = np.array([10, 20, 30, 40, 50, 60])
print(numbers[1:4])
print(numbers[:3])
print(numbers[3:])
print(numbers[::2])
Output:
[20 30 40]
[10 20 30]
[40 50 60]
[10 30 50]
8.2 Reversing an Array
import numpy as np
numbers = np.array([1, 2, 3, 4, 5])
print(numbers[::-1])
Output:
[5 4 3 2 1]
8.3 Slicing a Two-Dimensional Array
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(matrix[0:2, 1:3])
Output:
[[2 3]
[5 6]]
The first slice selects rows 0 and 1. The second slice selects columns 1 and 2.
Important Note: Views and Copies
Basic slicing often returns a view of the original array rather than an independent copy. Changes made through the view can therefore change the original array.
import numpy as np
original = np.array([10, 20, 30, 40])
view = original[:2]
view[0] = 99
print(original)
Output:
[99 20 30 40]
Use copy() when you need an independent array.
original = np.array([10, 20, 30, 40])
independent = original[:2].copy()
independent[0] = 99
print(original)
print(independent)
Output:
[10 20 30 40]
[99 20]
9. Arithmetic Operations on NumPy Arrays
NumPy supports element-wise arithmetic operations. When arrays have compatible shapes, mathematical operators act on corresponding elements without requiring an explicit Python loop.
9.1 Addition
import numpy as np
a = np.array([10, 20, 30])
b = np.array([1, 2, 3])
print(a + b)
Output:
[11 22 33]
9.2 Subtraction
print(a - b)
Output:
[ 9 18 27]
9.3 Multiplication
print(a * b)
Output:
[10 40 90]
9.4 Division
print(a / b)
Output:
[10. 10. 10.]
Division of integer arrays generally produces floating-point results. Division by zero can produce warnings and values such as infinity or NaN for floating-point arrays. Validate denominators when zero is not meaningful for the calculation.
9.5 Exponentiation
numbers = np.array([2, 3, 4])
print(numbers ** 2)
Output:
[ 4 9 16]
9.6 Modulus
numbers = np.array([10, 15, 20])
print(numbers % 6)
Output:
[4 3 2]
Arithmetic Operators Summary
| Operator |
Operation |
Example |
+ |
Element-wise addition |
a + b |
- |
Element-wise subtraction |
a - b |
* |
Element-wise multiplication |
a * b |
/ |
Element-wise division |
a / b |
** |
Element-wise exponentiation |
a ** 2 |
% |
Element-wise remainder |
a % 2 |
10. Universal Functions and Mathematical Operations
NumPy provides universal functions, often called ufuncs, that operate element by element on array values. They include mathematical functions for square roots, powers, exponentials, logarithms, trigonometry, and rounding.
10.1 Square Root
import numpy as np
numbers = np.array([4, 9, 16, 25])
print(np.sqrt(numbers))
Output:
[2. 3. 4. 5.]
10.2 Exponential Values
numbers = np.array([0, 1, 2])
print(np.exp(numbers))
This calculates \(e^x\) for every element in the array.
10.3 Logarithms
numbers = np.array([1, np.e, np.e ** 2])
print(np.log(numbers))
The np.log() function calculates natural logarithms. Be aware that zero and negative inputs require special handling in real-valued logarithmic calculations.
10.4 Rounding Values
numbers = np.array([1.234, 2.678, 3.456])
print(np.round(numbers, 2))
Output:
[1.23 2.68 3.46]
10.5 Absolute Values
numbers = np.array([-10, -5, 0, 5, 10])
print(np.abs(numbers))
Output:
[10 5 0 5 10]
10.6 Trigonometric Functions
angles = np.array([0, np.pi / 2, np.pi])
print(np.sin(angles))
NumPy's trigonometric functions use radians rather than degrees.
11. Vectorization in NumPy
Vectorization means applying an operation to an entire array instead of explicitly looping through every element in Python. Many NumPy operations are implemented using optimized compiled code and can be faster and more concise than equivalent Python loops for large numerical datasets.
Example Without Vectorization
numbers = [10, 20, 30, 40]
result = []
for number in numbers:
result.append(number * 2)
print(result)
Example With Vectorization
import numpy as np
numbers = np.array([10, 20, 30, 40])
result = numbers * 2
print(result)
Output:
[20 40 60 80]
Vectorization reduces repetitive code and often improves performance. The exact speed benefit depends on the operation, data size, memory layout, and the work performed.
12. Broadcasting in NumPy
Broadcasting allows NumPy to perform operations on arrays with different but compatible shapes. It can make calculations possible without manually repeating data to create arrays of identical dimensions.
12.1 Adding a Scalar to an Array
import numpy as np
numbers = np.array([10, 20, 30])
print(numbers + 5)
Output:
[15 25 35]
The scalar value 5 is applied to every array element.
12.2 Broadcasting with Two-Dimensional Data
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
row_values = np.array([10, 20, 30])
print(matrix + row_values)
Output:
[[11 22 33]
[14 25 36]]
The row array has shape (3,), so it can be broadcast across both rows of the matrix.
Broadcasting Rules
- Compare array dimensions from right to left.
- Dimensions are compatible when their sizes are equal or one of them is 1.
- Missing dimensions are treated as if their size were 1.
- If any pair of dimensions is incompatible, NumPy raises a broadcasting-related error.
Example of Incompatible Shapes
import numpy as np
a = np.array([1, 2, 3])
b = np.array([10, 20])
# These arrays have incompatible shapes.
# result = a + b
The shapes (3,) and (2,) are incompatible for element-wise addition. Reshape or reorganize the data only if that matches the intended calculation.
13. Comparison and Boolean Operations
Comparison operators compare array elements and produce Boolean arrays containing True or False. These results are useful for filtering, validating, and classifying numerical data.
13.1 Comparison Operators
import numpy as np
numbers = np.array([10, 20, 30, 40, 50])
print(numbers > 30)
print(numbers == 20)
print(numbers <= 30)
Output:
[False False False True True]
[False True False False False]
[ True True True False False]
13.2 Boolean Indexing
Boolean indexing selects values for which a condition is true.
numbers = np.array([10, 20, 30, 40, 50])
filtered = numbers[numbers > 25]
print(filtered)
Output:
[30 40 50]
13.3 Combining Conditions
Use & for element-wise logical AND and | for element-wise logical OR. Put each comparison inside parentheses.
import numpy as np
numbers = np.array([10, 20, 30, 40, 50])
filtered = numbers[(numbers >= 20) & (numbers <= 40)]
print(filtered)
Output:
[20 30 40]
For element-wise conditions, use NumPy's logical operators rather than Python's scalar and and or operators.
13.4 Using np.where()
The np.where() function selects values based on a condition.
import numpy as np
marks = np.array([35, 75, 48, 90, 25])
result = np.where(marks >= 40, "Pass", "Fail")
print(result)
Output:
['Fail' 'Pass' 'Pass' 'Pass' 'Fail']
The function returns an array containing "Pass" where the condition is true and "Fail" otherwise.
14. Aggregation and Statistical Operations
Aggregation functions summarize numerical data by producing results such as totals, averages, minimums, maximums, and standard deviations. These operations are widely used in data analysis and reporting.
14.1 Sum
import numpy as np
numbers = np.array([10, 20, 30, 40])
print(np.sum(numbers))
Output:
100
14.2 Mean
numbers = np.array([10, 20, 30, 40])
print(np.mean(numbers))
Output:
25.0
14.3 Median
numbers = np.array([10, 20, 30, 40, 100])
print(np.median(numbers))
Output:
30.0
The median is the middle value after the data is ordered. It is often less affected by extreme values than the mean.
14.4 Minimum and Maximum
numbers = np.array([15, 8, 45, 22, 30])
print("Minimum:", np.min(numbers))
print("Maximum:", np.max(numbers))
14.5 Standard Deviation and Variance
numbers = np.array([10, 20, 30, 40, 50])
print("Variance:", np.var(numbers))
print("Standard deviation:", np.std(numbers))
By default, NumPy calculates population variance and population standard deviation using ddof=0. Use ddof=1 when calculating the common sample variance or sample standard deviation estimate.
14.6 Finding Minimum and Maximum Indices
numbers = np.array([12, 45, 7, 89, 34])
print("Minimum index:", np.argmin(numbers))
print("Maximum index:", np.argmax(numbers))
Output:
Minimum index: 2
Maximum index: 3
Common Statistical Functions
| Function |
Purpose |
np.sum() |
Calculates the sum of values |
np.mean() |
Calculates the arithmetic mean |
np.median() |
Calculates the median |
np.min() |
Finds the minimum value |
np.max() |
Finds the maximum value |
np.std() |
Calculates standard deviation |
np.var() |
Calculates variance |
np.percentile() |
Calculates a requested percentile |
np.argmax() |
Returns the index of a maximum value |
np.argmin() |
Returns the index of a minimum value |
15. Axis-Based Operations
For multidimensional arrays, the axis parameter specifies the dimension along which an operation is performed. Understanding axes is essential when calculating row-wise or column-wise summaries.
import numpy as np
data = np.array([
[10, 20, 30],
[40, 50, 60]
])
print("Total:", np.sum(data))
print("Column totals:", np.sum(data, axis=0))
print("Row totals:", np.sum(data, axis=1))
Output:
Total: 210
Column totals: [50 70 90]
Row totals: [ 60 150]
For a two-dimensional array:
axis=0 combines values down the rows, producing a result for each column.
axis=1 combines values across the columns, producing a result for each row.
The same principle applies to functions such as np.mean(), np.min(), and np.max().
16. Reshaping NumPy Arrays
Reshaping changes an array's dimensions without changing its sequence of elements. The new shape must be compatible with the total number of elements.
16.1 Using reshape()
import numpy as np
numbers = np.arange(1, 7)
matrix = numbers.reshape(2, 3)
print(matrix)
Output:
[[1 2 3]
[4 5 6]]
The original array contains six elements, and the shape (2, 3) also requires six elements.
16.2 Using -1 in reshape()
NumPy can infer one dimension when the other dimensions are provided.
numbers = np.arange(12)
matrix = numbers.reshape(3, -1)
print(matrix.shape)
Output:
(3, 4)
Only one dimension can be inferred using -1 in a reshape operation.
16.3 Flattening an Array
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
flat = matrix.flatten()
print(flat)
Output:
[1 2 3 4 5 6]
flatten() returns a copy. The ravel() method returns a flattened array and may return a view when possible.
17. Transposing and Swapping Axes
Transposing rearranges the axes of an array. For a two-dimensional matrix, it exchanges rows and columns.
import numpy as np
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(matrix.T)
Output:
[[1 4]
[2 5]
[3 6]]
The original shape is (2, 3), while the transposed shape is (3, 2). Transposition is commonly used in matrix calculations and data transformations.
18. Combining and Splitting Arrays
NumPy provides functions to join arrays horizontally or vertically and split arrays into smaller parts. The arrays must satisfy the dimensional requirements of the operation.
18.1 Concatenating Arrays
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.concatenate((a, b))
print(result)
Output:
[1 2 3 4 5 6]
18.2 Stacking Arrays Vertically
import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
result = np.vstack((a, b))
print(result)
Output:
[[1 2]
[3 4]
[5 6]
[7 8]]
18.3 Stacking Arrays Horizontally
result = np.hstack((a, b))
print(result)
Output:
[[1 2 5 6]
[3 4 7 8]]
18.4 Splitting an Array
import numpy as np
numbers = np.array([10, 20, 30, 40, 50, 60])
parts = np.split(numbers, 3)
for part in parts:
print(part)
Output:
[10 20]
[30 40]
[50 60]
np.split() requires equal division when an integer number of sections is specified. Use np.array_split() when the array cannot be divided evenly into the requested number of sections.
19. Sorting and Searching Arrays
Sorting organizes numerical values in ascending or descending order. Searching functions help locate matching values or determine where a value belongs in a sorted array.
19.1 Sorting Values
import numpy as np
numbers = np.array([40, 10, 50, 20, 30])
print(np.sort(numbers))
Output:
[10 20 30 40 50]
19.2 Sorting in Descending Order
numbers = np.array([40, 10, 50, 20, 30])
descending = np.sort(numbers)[::-1]
print(descending)
Output:
[50 40 30 20 10]
19.3 Finding Matching Positions
import numpy as np
numbers = np.array([10, 20, 30, 20, 40])
positions = np.where(numbers == 20)
print(positions[0])
Output:
[1 3]
19.4 Finding Unique Values
numbers = np.array([10, 20, 10, 30, 20, 40])
print(np.unique(numbers))
Output:
[10 20 30 40]
20. Matrix Operations and Linear Algebra
NumPy supports matrix multiplication, dot products, transposition, determinants, inverses, and other linear algebra operations through its standard functions and the numpy.linalg module.
20.1 Element-Wise Multiplication vs. Matrix Multiplication
Element-wise multiplication uses the * operator. Matrix multiplication uses the @ operator or np.matmul().
import numpy as np
a = np.array([
[1, 2],
[3, 4]
])
b = np.array([
[5, 6],
[7, 8]
])
print("Element-wise multiplication:")
print(a * b)
print("Matrix multiplication:")
print(a @ b)
Output:
Element-wise multiplication:
[[ 5 12]
[21 32]]
Matrix multiplication:
[[19 22]
[43 50]]
20.2 Dot Product
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b))
Output:
32
The dot product is calculated as 1*4 + 2*5 + 3*6 = 32.
20.3 Determinant of a Matrix
import numpy as np
matrix = np.array([
[1, 2],
[3, 4]
])
print(np.linalg.det(matrix))
The determinant of this matrix is approximately -2, subject to floating-point representation.
20.4 Solving Linear Equations
The np.linalg.solve() function solves a system of linear equations represented by a coefficient matrix and a right-hand-side vector.
import numpy as np
A = np.array([
[2, 1],
[1, 3]
])
b = np.array([5, 6])
solution = np.linalg.solve(A, b)
print(solution)
Output:
[1.8 1.4]
The solution means that \(x=1.8\) and \(y=1.4\) satisfy the equations \(2x+y=5\) and \(x+3y=6\).
21. Handling Missing and Invalid Numerical Values
Numerical datasets may contain missing or invalid values. NumPy commonly represents missing numerical values using np.nan for floating-point arrays. It also provides functions for identifying NaN values and calculating statistics while ignoring them.
21.1 Identifying NaN Values
import numpy as np
data = np.array([10.0, 20.0, np.nan, 40.0])
print(np.isnan(data))
Output:
[False False True False]
21.2 Calculating a Mean While Ignoring NaN
data = np.array([10.0, 20.0, np.nan, 40.0])
print(np.mean(data))
print(np.nanmean(data))
Output:
nan
23.333333333333332
np.mean() propagates NaN in this example, while np.nanmean() ignores NaN values. Always decide whether missing values should be ignored, replaced, or treated as errors according to the meaning of the data.
21.3 Replacing NaN Values
import numpy as np
data = np.array([10.0, 20.0, np.nan, 40.0])
cleaned = np.nan_to_num(data, nan=0.0)
print(cleaned)
Output:
[10. 20. 0. 40.]
Replacing missing values with zero is appropriate only when zero has a valid meaning for the problem. In other situations, use a suitable imputation strategy or preserve the missing values.
22. Random Sampling and Reproducibility
NumPy's random generator can produce random numbers, select samples, and shuffle data. These features are useful in simulations, statistical experiments, testing, and machine learning.
22.1 Generating Random Floating-Point Values
import numpy as np
rng = np.random.default_rng(42)
values = rng.random(5)
print(values)
The values are generated between 0 inclusive and 1 exclusive.
22.2 Random Integers
rng = np.random.default_rng(42)
values = rng.integers(1, 11, size=5)
print(values)
The integers range from 1 through 10 because the upper bound is excluded.
22.3 Shuffling an Array
import numpy as np
rng = np.random.default_rng(42)
numbers = np.array([1, 2, 3, 4, 5])
rng.shuffle(numbers)
print(numbers)
shuffle() changes the order of the array in place. Use a seeded generator when reproducibility is important, and do not use NumPy's pseudorandom generator for cryptographic secrets.
23. Saving and Loading NumPy Arrays
NumPy can save arrays to disk and load them later. This is useful for intermediate data, scientific experiments, and applications that repeatedly work with the same numerical dataset.
23.1 Saving an Array as a Binary File
import numpy as np
numbers = np.array([10, 20, 30, 40])
np.save("numbers.npy", numbers)
23.2 Loading a Saved Array
import numpy as np
loaded = np.load("numbers.npy")
print(loaded)
23.3 Saving Multiple Arrays
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.savez("arrays.npz", first=a, second=b)
with np.load("arrays.npz") as archive:
print(archive["first"])
print(archive["second"])
Load files only from trusted sources when using NumPy formats that may involve pickled Python objects. Avoid enabling pickle loading for untrusted data.
23.4 Saving Data as a Text File
import numpy as np
data = np.array([
[10, 20, 30],
[40, 50, 60]
])
np.savetxt("data.csv", data, delimiter=",", fmt="%d")
loaded = np.loadtxt("data.csv", delimiter=",")
print(loaded)
Text formats are convenient for interoperability, although binary formats may preserve numerical data more efficiently and accurately.
24. Improving Numerical Performance
NumPy is designed for efficient numerical operations, but performance depends on data size, memory usage, array shape, and the selected operations.
- Use vectorized operations: Prefer array expressions over unnecessary Python loops for numerical calculations.
- Choose suitable data types: Use an appropriate integer or floating-point type for the required precision and value range.
- Avoid unnecessary copies: Copying large arrays consumes additional memory.
- Understand views: Slicing may create a view that shares memory with the original array.
- Use broadcasting: Avoid manually repeating data when compatible shapes allow broadcasting.
- Process large datasets carefully: Avoid creating several large temporary arrays unnecessarily.
- Profile before optimizing: Measure the actual bottleneck rather than assuming a particular operation is slow.
- Validate numerical results: Floating-point calculations may contain rounding errors.
Example: Efficient Array Calculation
import numpy as np
prices = np.array([100.0, 200.0, 300.0, 400.0])
discount_rate = 0.10
final_prices = prices * (1 - discount_rate)
print(final_prices)
Output:
[ 90. 180. 270. 360.]
Every price is reduced by 10% through a single vectorized expression.
25. Common NumPy Errors and Troubleshooting
| Error or Problem |
Possible Cause |
Solution |
ModuleNotFoundError: No module named 'numpy' |
NumPy is not installed in the active Python environment |
Run python -m pip install numpy in the correct environment |
IndexError |
An index is outside the valid range |
Check array size and valid index values |
| Broadcasting error |
Array shapes are incompatible |
Inspect shape and reshape only when appropriate |
| Unexpected modification of original data |
A slice is a view sharing memory |
Use copy() when independent data is required |
| Unexpected integer result |
Integer data type or integer conversion affects the result |
Inspect dtype and use an appropriate type |
| Unexpected NaN or infinity |
Invalid numerical input or division by zero |
Validate inputs and use appropriate NaN or infinity handling |
| Reshape error |
The requested shape has an incompatible element count |
Ensure the product of dimensions equals the number of elements |
| Unexpected matrix multiplication result |
Used element-wise multiplication instead of matrix multiplication |
Use @ or np.matmul() where appropriate |
26. Practical Project: Student Marks Analysis Using NumPy
Project Objective: Create a numerical analysis program that stores student marks in a NumPy array, calculates totals and averages, identifies the highest and lowest marks, and classifies students according to a passing threshold.
Project Requirements
- Store marks in a NumPy array.
- Calculate the total and average marks.
- Find the highest and lowest marks.
- Identify the positions of the highest and lowest marks.
- Filter marks that meet the passing threshold.
- Count the number of students who pass and fail.
- Display a summary of the results.
Complete Project Code
import numpy as np
student_names = np.array([
"Aarav",
"Diya",
"Kabir",
"Meera",
"Rohan"
])
marks = np.array([78, 92, 35, 64, 48])
passing_mark = 40
total_marks = np.sum(marks)
average_marks = np.mean(marks)
highest_mark = np.max(marks)
lowest_mark = np.min(marks)
highest_index = np.argmax(marks)
lowest_index = np.argmin(marks)
passed = marks >= passing_mark
failed = marks < passing_mark
print("Student Marks Analysis")
print("----------------------")
print("Names:", student_names)
print("Marks:", marks)
print("Total marks:", total_marks)
print("Average marks:", round(average_marks, 2))
print("Highest mark:", highest_mark)
print("Highest scorer:", student_names[highest_index])
print("Lowest mark:", lowest_mark)
print("Lowest scorer:", student_names[lowest_index])
print("Students passed:", np.sum(passed))
print("Students failed:", np.sum(failed))
print("Passing marks:", marks[passed])
print("Failing marks:", marks[failed])
Expected Output
Student Marks Analysis
----------------------
Names: ['Aarav' 'Diya' 'Kabir' 'Meera' 'Rohan']
Marks: [78 92 35 64 48]
Total marks: 317
Average marks: 63.4
Highest mark: 92
Highest scorer: Diya
Lowest mark: 35
Lowest scorer: Kabir
Students passed: 4
Students failed: 1
Passing marks: [78 92 64 48]
Failing marks: [35]
Project Explanation
- Create arrays: Store student names and marks in NumPy arrays.
- Calculate totals: Use
np.sum() to find the sum of marks.
- Calculate averages: Use
np.mean() to calculate the average.
- Find extremes: Use
np.max() and np.min().
- Identify students: Use
np.argmax() and np.argmin() to locate the positions of the highest and lowest marks.
- Apply conditions: Create Boolean arrays to classify marks.
- Count results: Sum Boolean values to count students who pass or fail.
Project Extensions
- Add subject-wise marks using a two-dimensional array.
- Calculate averages for each student and each subject.
- Identify students whose marks are above the class average.
- Calculate standard deviation to examine mark variation.
- Save the analysis to a CSV file.
- Handle missing marks and validate that marks fall within the allowed range.
27. Practice Exercises
- Create a NumPy array containing the integers from 1 to 20.
- Create a 3-by-3 array containing the numbers 1 through 9.
- Generate an array of ten zeros and an array of ten ones.
- Generate 11 evenly spaced values between 0 and 100.
- Calculate the sum, mean, median, minimum, and maximum of an array.
- Multiply every element in an array by 5.
- Extract all even numbers from an array using Boolean indexing.
- Reverse a one-dimensional array using slicing.
- Reshape a 12-element array into a 3-by-4 matrix.
- Calculate row-wise and column-wise totals for a two-dimensional array.
- Combine two arrays using
np.concatenate().
- Sort an array in ascending and descending order.
- Find the indices of the smallest and largest values.
- Replace NaN values using an appropriate missing-data strategy.
- Build a marks-analysis program with pass/fail classification.
28. Interview Questions and Answers
Q1. What is NumPy?
Answer: NumPy is a Python library for numerical computing. It provides multidimensional arrays, mathematical functions, statistical operations, broadcasting, and linear algebra functionality.
Q2. What is an ndarray?
Answer: An ndarray is NumPy's main array object. It stores elements in a multidimensional structure and generally uses a common data type for its elements.
Q3. What is the difference between a Python list and a NumPy array?
Answer: Python lists can contain mixed object types and are useful for general-purpose collections. NumPy arrays are designed for numerical and multidimensional operations, support vectorized calculations, and often use memory more efficiently for homogeneous numerical data.
Q4. What is the difference between shape, size, and ndim?
Answer: shape gives the size of each dimension, size gives the total number of elements, and ndim gives the number of dimensions.
Q5. What is the difference between arange() and linspace()?
Answer: arange() generates values based on a step size and normally excludes the stop value. linspace() generates a specified number of evenly spaced values and includes the endpoint by default.
Q6. What is vectorization?
Answer: Vectorization applies operations to whole arrays instead of explicitly iterating through each element in Python. It often produces simpler and faster numerical code.
Q7. What is broadcasting?
Answer: Broadcasting allows NumPy to perform operations on arrays with compatible shapes without requiring them to have identical dimensions.
Q8. What is Boolean indexing?
Answer: Boolean indexing selects elements based on a Boolean condition. For example, numbers[numbers > 10] selects values greater than 10.
Q9. What is the difference between * and @ for arrays?
Answer: The * operator performs element-wise multiplication. The @ operator performs matrix multiplication when the operand shapes satisfy the required rules.
Q10. What is the difference between a view and a copy?
Answer: A view shares underlying data with another array, so changes may affect both. A copy has independent array data. Use copy() when modifications must not affect the original.
Q11. How do you handle missing numerical values?
Answer: Use np.isnan() to identify NaN values and functions such as np.nanmean() to calculate certain statistics while ignoring NaN. Replacing missing values should be based on the data's meaning.
Q12. What is the purpose of axis in NumPy?
Answer: The axis parameter specifies the dimension along which an operation is performed. For a two-dimensional array, axis=0 generally produces column-wise results and axis=1 produces row-wise results.
Q13. How do you reshape a NumPy array?
Answer: Use reshape() to change the dimensions while preserving the number of elements. The requested shape must be compatible with the array's total size.
Q14. How do you find the largest value in an array?
Answer: Use np.max(array) to return the largest value and np.argmax(array) to return the index of a maximum value along the selected axis.
Q15. How can you save a NumPy array?
Answer: Use np.save() to save an array in NPY format, np.savez() to save multiple arrays in an NPZ archive, and np.savetxt() to export numerical data to a text file.
29. Quick Revision Checklist
- NumPy is a fundamental library for numerical computing in Python.
np.array() creates arrays from Python sequences.
np.zeros(), np.ones(), np.arange(), and np.linspace() create arrays in different ways.
ndim, shape, size, and dtype describe an array.
- Indexing and slicing retrieve selected array elements.
- Basic slicing often returns a view; use
copy() for independent data.
- Arithmetic operators perform element-wise operations.
- Vectorization simplifies numerical calculations.
- Broadcasting allows operations on compatible array shapes.
- Boolean indexing filters values based on conditions.
- Aggregation functions calculate totals, averages, and statistical summaries.
- The axis parameter controls the dimension used by many operations.
reshape(), transpose(), and concatenation functions manipulate array structures.
- The
@ operator performs matrix multiplication.
- NumPy supports random sampling and saving or loading numerical arrays.
- Correct data types, validation, and memory management help avoid numerical errors.
30. Conclusion
NumPy arrays and numerical operations are essential tools for Python programming, data analysis, scientific computing, and machine learning. By understanding array creation, indexing, slicing, arithmetic operations, broadcasting, statistics, reshaping, and matrix calculations, developers can process numerical datasets efficiently and clearly.
Practical experience is the best way to build confidence with NumPy. Start with small arrays, verify their shapes and data types, experiment with vectorized operations, and gradually move toward multidimensional datasets and real-world analytical projects.
31. Further Learning Resources