Python numerical computing reference
NumPy Cheat Sheet
Quickly find NumPy syntax for creating arrays, indexing and slicing, reshaping data, broadcasting values, performing calculations, generating random numbers, and working with linear algebra.
- Copy-ready Python
- Practical array examples
- Beginner to advanced
- Printable reference
import numpy as np
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
totals = sales.sum(axis=1)
# Array result
[450 340]
Practical syntax for arrays, broadcasting, vectorized calculations, statistics, random values, and linear algebra.
Find NumPy syntax
Search the NumPy Cheat Sheet
Search by function, array operation, data type, calculation, shape, axis, or common NumPy task.
Search all numbered NumPy sections.
Try a broader term such as
array, shape,
math, or indexing.
Essential syntax at a glance
NumPy Quick Reference
Use this compact reference to create arrays, inspect their structure, select values, reshape data, calculate summaries, and combine arrays.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Import NumPy |
import numpy as np
|
Imports NumPy with its standard alias. | |
| Create an array |
arr = np.array([10, 20, 30])
|
Creates a one-dimensional array. | |
| Create zeros |
np.zeros((2, 3))
|
Creates a 2 × 3 array filled with zeros. | |
| Create a sequence |
np.arange(0, 10, 2)
|
Returns even values from 0 through 8. | |
| Inspect dimensions |
arr.shape
|
Returns the size of every array axis. | |
| Inspect data type |
arr.dtype
|
Returns the array’s element data type. | |
| Reshape an array |
arr.reshape(2, 3)
|
Reorganizes six values into two rows. | |
| Filter values |
arr[arr > 0]
|
Returns values greater than zero. | |
| Calculate column means |
arr.mean(axis=0)
|
Calculates one mean for each column. | |
| Combine arrays |
np.concatenate([a, b], axis=0)
|
Joins compatible arrays along their rows. |
Create, inspect, and reshape an array
import numpy as np
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
print(sales.shape)
print(sales.ndim)
print(sales.size)
print(sales.dtype)
high_sales = sales[sales >= 140]
column_totals = sales.sum(axis=0)
row_averages = sales.mean(axis=1)
print(high_sales)
print(column_totals)
print(row_averages)
In a two-dimensional array, axis=0 calculates down the
rows and returns one result per column. axis=1 calculates
across the columns and returns one result per row.
Set up your environment
Install & Import NumPy
Install NumPy with pip or conda, import it using the standard
np alias, and verify which version is available in your
Python environment.
| Task | Command | Purpose | Copy |
|---|---|---|---|
| Install with pip |
python -m pip install numpy
|
Installs NumPy from the Python Package Index. | |
| Upgrade with pip |
python -m pip install --upgrade numpy
|
Upgrades NumPy in the active Python environment. | |
| Install with conda |
conda install numpy
|
Installs NumPy from the configured conda channels. | |
| Install from conda-forge |
conda install -c conda-forge numpy
|
Installs NumPy from the community-maintained channel. | |
| Import NumPy |
import numpy as np
|
Imports NumPy using its conventional short alias. | |
| Check the version |
print(np.__version__)
|
Displays the imported NumPy version. | |
| Show build details |
np.show_config()
|
Shows build, compiler, and linked-library information. |
Create an isolated environment
A virtual environment keeps NumPy and other project dependencies separate from packages installed for unrelated projects.
python3 -m venv numpy-env
source numpy-env/bin/activate
python -m pip install --upgrade pip
python -m pip install numpy
py -m venv numpy-env
.\numpy-env\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install numpy
Verify the installation
import numpy as np
values = np.array([10, 20, 30])
print("NumPy version:", np.__version__)
print("Array:", values)
print("Total:", values.sum())
NumPy version: 2.5.0
Array: [10 20 30]
Total: 60
If Python cannot import NumPy after installation, confirm that your terminal, code editor, and notebook are using the same Python environment in which NumPy was installed.
Prefer one package manager inside each environment. Mixing pip and conda packages unnecessarily can produce dependency conflicts that are difficult to diagnose.
Build arrays efficiently
Array Creation
Create NumPy arrays from Python sequences, generate evenly spaced values, initialize arrays with predefined values, and construct common matrix patterns.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| From a Python list |
np.array([10, 20, 30])
|
Creates a one-dimensional array. | |
| Create a 2D array |
np.array([[1, 2], [3, 4]])
|
Creates an array with two rows and two columns. | |
| Specify a data type |
np.array([1, 2, 3], dtype=np.float64)
|
Creates an array containing 64-bit floating-point values. | |
| Convert array-like data |
np.asarray(values)
|
Converts compatible input without copying when possible. | |
| Fill with zeros |
np.zeros((2, 3), dtype=int)
|
Creates a 2 × 3 integer array filled with zeros. | |
| Fill with ones |
np.ones((3, 2), dtype=float)
|
Creates a 3 × 2 floating-point array filled with ones. | |
| Fill with one value |
np.full((2, 4), 7)
|
Creates a 2 × 4 array filled with the value 7. | |
| Integer sequence |
np.arange(0, 12, 2)
|
Returns values from 0 through 10 in steps of 2. | |
| Evenly spaced values |
np.linspace(0, 1, 5)
|
Creates five evenly spaced values including both endpoints. | |
| Identity matrix |
np.eye(3)
|
Creates a 3 × 3 matrix with ones on the diagonal. |
Create arrays from Python sequences
Pass a list or nested lists to np.array(). Nested lists must
have compatible lengths to form a regular multidimensional numeric array.
import numpy as np
prices = np.array(
[19.99, 24.50, 18.75, 32.00],
dtype=np.float64
)
print(prices)
print(prices.shape)
print(prices.dtype)
sales = np.array([
[120, 150, 180],
[90, 110, 140],
[200, 175, 225]
])
print(sales)
print(sales.shape)
# Shape: 3 rows × 3 columns
Initialize arrays with predefined values
zeros = np.zeros(
(2, 3),
dtype=np.int64
)
ones = np.ones(
(2, 3),
dtype=np.float64
)
discounts = np.full(
(2, 3),
0.15
)
template = np.array([
[4, 8, 12],
[16, 20, 24]
])
empty_results = np.zeros_like(
template
)
weights = np.full_like(
template,
2
)
Generate numeric sequences
# Use a defined step size
even_numbers = np.arange(
start=0,
stop=12,
step=2
)
# Use a defined number of values
percentages = np.linspace(
start=0,
stop=1,
num=5
)
print(even_numbers)
print(percentages)
[ 0 2 4 6 8 10]
[0. 0.25 0.5 0.75 1. ]
Create common matrix patterns
identity = np.eye(
3,
dtype=np.int64
)
print(identity)
diagonal = np.diag(
[10, 20, 30]
)
print(diagonal)
Use np.arange() when the step size matters. Use
np.linspace() when the number of evenly spaced values
matters, especially when working with floating-point ranges.
np.empty() allocates an array without initializing its
entries. The array may contain arbitrary values already present in
memory, so assign every element before using the result.
Understand array structure
Array Attributes
Inspect an array’s dimensions, shape, element count, data type, and memory usage before transforming or calculating with its values.
| Attribute | Example | Returns | Copy |
|---|---|---|---|
shape |
arr.shape
|
A tuple containing the length of each axis. | |
ndim |
arr.ndim
|
The number of array dimensions or axes. | |
size |
arr.size
|
The total number of elements in the array. | |
dtype |
arr.dtype
|
The data type shared by the array elements. | |
itemsize |
arr.itemsize
|
The number of bytes used by one element. | |
nbytes |
arr.nbytes
|
The total bytes consumed by the array elements. | |
strides |
arr.strides
|
Bytes to move in memory when advancing along each axis. | |
flags |
arr.flags
|
Information about memory layout and write access. |
Inspect an array
The following array contains two rows and three columns. Its
shape is therefore (2, 3), while its total
size is six.
import numpy as np
sales = np.array([
[120, 150, 180],
[90, 110, 140]
], dtype=np.int64)
print("Shape:", sales.shape)
print("Dimensions:", sales.ndim)
print("Elements:", sales.size)
print("Data type:", sales.dtype)
print("Bytes per element:", sales.itemsize)
print("Total bytes:", sales.nbytes)
Shape: (2, 3)
Dimensions: 2
Elements: 6
Data type: int64
Bytes per element: 8
Total bytes: 48
Understand shape and axes
one_dimensional = np.array(
[10, 20, 30, 40]
)
print(one_dimensional.shape)
print(one_dimensional.ndim)
print(one_dimensional.size)
# (4,)
# 1
# 4
three_dimensional = np.zeros(
(2, 3, 4)
)
print(three_dimensional.shape)
print(three_dimensional.ndim)
print(three_dimensional.size)
# (2, 3, 4)
# 3
# 24
A one-dimensional array with four elements has the shape
(4,), not (4). The comma identifies it as a
one-item tuple.
Check memory usage
small = np.array(
[1, 2, 3, 4],
dtype=np.int8
)
large = np.array(
[1, 2, 3, 4],
dtype=np.int64
)
print(small.nbytes)
print(large.nbytes)
# 4
# 32
expected_shape = (2, 3)
if sales.shape != expected_shape:
raise ValueError(
"Unexpected array shape: "
f"{sales.shape}"
)
print("Shape is valid")
Checking shape and dtype early can prevent
broadcasting errors, incorrect axis calculations, and unexpected type
conversions later in a data pipeline.
arr.nbytes reports the memory occupied by the array’s
elements. It does not include all Python object overhead or memory owned
by objects referenced from an object-dtype array.
Control precision and memory
Data Types
Choose an appropriate NumPy data type to control numeric precision, memory usage, valid value ranges, and how operations interpret the elements in an array.
| Data type | Example | Typical use | Copy |
|---|---|---|---|
int8 |
np.array([1, 2, 3], dtype=np.int8)
|
Small signed integers from −128 through 127. | |
int64 |
np.array([1, 2, 3], dtype=np.int64)
|
Large signed whole numbers. | |
uint8 |
np.array([0, 128, 255], dtype=np.uint8)
|
Small non-negative integers, commonly used for image data. | |
float32 |
np.array([1.5, 2.5], dtype=np.float32)
|
Lower-memory floating-point values with reduced precision. | |
float64 |
np.array([1.5, 2.5], dtype=np.float64)
|
Standard double-precision floating-point values. | |
bool |
np.array([True, False], dtype=np.bool_)
|
Boolean values used in masks and logical operations. | |
complex128 |
np.array([1 + 2j], dtype=np.complex128)
|
Double-precision complex numbers. | |
datetime64 |
np.array(["2026-09-29"], dtype="datetime64[D]")
|
Calendar dates stored at daily precision. | |
timedelta64 |
np.array([1, 7, 30], dtype="timedelta64[D]")
|
Durations stored in days. | |
Unicode |
np.array(["North", "South"], dtype="U10")
|
Fixed-width Unicode strings of up to ten characters. |
Specify a data type during creation
Pass the dtype argument when creating an array if the required
precision or storage format is already known.
import numpy as np
ratings = np.array(
[1, 3, 4, 5, 2],
dtype=np.int8
)
print(ratings.dtype)
print(ratings.itemsize)
print(ratings.nbytes)
# int8
# 1
# 5
prices = np.array(
[19.99, 24.50, 32.00],
dtype=np.float64
)
print(prices.dtype)
print(prices.itemsize)
print(prices.nbytes)
# float64
# 8
# 24
Convert an existing array
Use astype() to convert an array to another data type. The
method normally returns a new array containing the converted values.
numeric_text = np.array(
["10", "20", "30"]
)
integers = numeric_text.astype(
np.int64
)
measurements = np.array([
12.8,
17.4,
21.9
])
whole_numbers = measurements.astype(
np.int64
)
print(integers)
print(whole_numbers)
[10 20 30]
[12 17 21]
Converting floating-point values to an integer dtype discards the
fractional part. Use np.round(), np.floor(),
or np.ceil() first when a specific rounding rule is needed.
Inspect numeric limits
limits = np.iinfo(
np.int16
)
print(limits.min)
print(limits.max)
# -32768
# 32767
limits = np.finfo(
np.float32
)
print(limits.min)
print(limits.max)
print(limits.eps)
Determine the resulting data type
integer_values = np.array(
[1, 2, 3],
dtype=np.int32
)
decimal_values = np.array(
[1.5, 2.5, 3.5],
dtype=np.float64
)
common_type = np.result_type(
integer_values,
decimal_values
)
print(common_type)
# float64
Smaller data types can reduce memory use significantly, but the selected type must still cover the complete range and precision required by the data.
Fixed-width integer types cannot represent values outside their supported
range. Check limits with np.iinfo() before converting or
calculating with potentially large values.
Select values and subarrays
Indexing & Slicing
Access individual elements, select ranges, extract rows and columns, and work across multiple dimensions with NumPy’s indexing syntax.
| Task | NumPy syntax | Selection | Copy |
|---|---|---|---|
| First element |
arr[0]
|
Selects the element at index 0. | |
| Last element |
arr[-1]
|
Selects the final element. | |
| Range of values |
arr[1:4]
|
Selects indexes 1, 2, and 3. | |
| Every second value |
arr[::2]
|
Selects every second element from the beginning. | |
| Reverse an array |
arr[::-1]
|
Returns the elements in reverse order. | |
| One 2D element |
matrix[1, 2]
|
Selects row 1, column 2. | |
| One row |
matrix[1, :]
|
Selects every column from row 1. | |
| One column |
matrix[:, 1]
|
Selects column 1 from every row. | |
| Rectangular section |
matrix[0:2, 1:3]
|
Selects rows 0–1 and columns 1–2. | |
| Last position of every axis |
cube[..., -1]
|
Selects the final value along the last axis. |
Index and slice a one-dimensional array
NumPy uses zero-based indexing. The stop position in a slice is excluded,
following the same start:stop:step convention as standard
Python sequences.
import numpy as np
values = np.array([
10, 20, 30, 40, 50, 60
])
first = values[0]
last = values[-1]
middle = values[1:5]
every_second = values[::2]
reversed_values = values[::-1]
print(first)
print(last)
print(middle)
print(every_second)
print(reversed_values)
10
60
[20 30 40 50]
[10 30 50]
[60 50 40 30 20 10]
Select rows and columns
sales = np.array([
[120, 150, 180],
[90, 110, 140],
[200, 175, 225]
])
second_row = sales[1, :]
first_two_rows = sales[:2, :]
print(second_row)
print(first_two_rows)
first_column = sales[:, 0]
last_two_columns = sales[
:,
1:
]
center_values = sales[
0:2,
1:3
]
print(first_column)
print(last_two_columns)
print(center_values)
Select individual coordinates
Provide one index for each dimension to retrieve a single scalar value from a multidimensional array.
sales = np.array([
[120, 150, 180],
[90, 110, 140],
[200, 175, 225]
])
top_left = sales[0, 0]
center = sales[1, 1]
bottom_right = sales[-1, -1]
print(top_left)
print(center)
print(bottom_right)
# 120
# 110
# 225
Use ellipsis with higher-dimensional arrays
cube = np.arange(24).reshape(
2, 3, 4
)
first_last_axis = cube[..., 0]
last_last_axis = cube[..., -1]
print(first_last_axis)
print(last_last_axis)
values = np.array([
10, 20, 30
])
column = values[:, np.newaxis]
row = values[np.newaxis, :]
print(column.shape)
print(row.shape)
# (3, 1)
# (1, 3)
Create an independent sliced copy
original = np.array([
10, 20, 30, 40, 50
])
independent = original[1:4].copy()
independent[0] = 999
print(original)
print(independent)
# [10 20 30 40 50]
# [999 30 40]
A basic slice can share memory with its original array. Modifying the
slice may therefore modify the source array. Call .copy()
when an independent result is required.
Prefer matrix[row, column] over chained indexing such as
matrix[row][column]. The comma form is clearer and directly
expresses the intended coordinates.
Filter arrays with conditions
Boolean Masking
Create boolean conditions to filter values, combine multiple rules, replace matching elements, and locate positions that satisfy a condition.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Filter greater values |
arr[arr > 10]
|
Returns values greater than 10. | |
| Values in a range |
arr[(arr >= 10) & (arr <= 50)]
|
Returns values from 10 through 50. | |
| Either condition |
arr[(arr < 0) | (arr > 100)]
|
Returns values outside the interval from 0 to 100. | |
| Invert a mask |
arr[~mask]
|
Returns values where the existing mask is false. | |
| Match allowed values |
np.isin(arr, [10, 20, 30])
|
Creates a mask for values found in the supplied list. | |
| Replace conditionally |
np.where(arr >= 50, arr, 0)
|
Keeps values of at least 50 and replaces the rest with zero. | |
| Find matching indexes |
np.flatnonzero(arr > 10)
|
Returns flat indexes where the condition is true. | |
| Count matches |
np.count_nonzero(arr > 10)
|
Counts elements that satisfy the condition. |
Create and apply a boolean mask
Comparing an array with a value produces a boolean array of the same shape. Use that boolean array inside brackets to return only matching elements.
import numpy as np
sales = np.array([
120, 85, 210, 95, 175, 60
])
high_sales_mask = sales >= 150
high_sales = sales[high_sales_mask]
print(high_sales_mask)
print(high_sales)
[False False True False True False]
[210 175]
Combine multiple conditions
values = np.array([
5, 15, 25, 45, 75, 110
])
in_range = values[
(values >= 20)
& (values <= 80)
]
print(in_range)
# [25 45 75]
values = np.array([
-15, 10, 35, 70, 125
])
outside_range = values[
(values < 0)
| (values > 100)
]
print(outside_range)
# [-15 125]
Use & for AND, | for OR, and
~ for NOT. Do not use Python’s and,
or, or not with complete NumPy arrays.
Place every comparison inside parentheses.
Filter rows in a two-dimensional array
Build the mask from one column and apply it to the complete array to keep every column from the matching rows.
# Columns: product_id, units, revenue
records = np.array([
[101, 5, 120],
[102, 8, 240],
[103, 3, 75],
[104, 9, 310]
])
revenue_mask = records[:, 2] >= 200
high_revenue_rows = records[revenue_mask]
print(high_revenue_rows)
[[102 8 240]
[104 9 310]]
Match a collection of allowed values
product_ids = np.array([
101, 102, 103, 104, 105
])
featured_ids = np.array([
102, 104
])
featured_mask = np.isin(
product_ids,
featured_ids
)
featured = product_ids[
featured_mask
]
print(featured)
# [102 104]
blocked_ids = np.array([
101, 105
])
allowed = product_ids[
~np.isin(
product_ids,
blocked_ids
)
]
print(allowed)
# [102 103 104]
Replace values conditionally
balances = np.array([
150, -25, 80, -10, 220
])
clean_balances = balances.copy()
clean_balances[
clean_balances < 0
] = 0
print(clean_balances)
# [150 0 80 0 220]
scores = np.array([
45, 72, 88, 59, 91
])
labels = np.where(
scores >= 60,
"Pass",
"Fail"
)
print(labels)
# ['Fail' 'Pass' 'Pass' 'Fail' 'Pass']
Locate and count matches
temperatures = np.array([
18, 24, 31, 27, 35, 21
])
hot_mask = temperatures >= 30
matching_indexes = np.flatnonzero(
hot_mask
)
number_of_matches = np.count_nonzero(
hot_mask
)
print(matching_indexes)
print(number_of_matches)
# [2 4]
# 2
Give complex conditions a descriptive variable name when the same mask is used for filtering, counting, replacement, or validation. This makes the code easier to read and avoids repeating the condition.
Change array dimensions
Reshape & Flatten
Reorganize array dimensions without changing the underlying values, convert multidimensional arrays into one dimension, and add or remove axes when preparing data for calculations.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Reshape to rows |
arr.reshape(2, 3)
|
Reorganizes six elements into two rows and three columns. | |
| Infer one dimension |
arr.reshape(2, -1)
|
Calculates the second dimension automatically. | |
| Flatten when possible |
arr.ravel()
|
Returns a flattened view when possible. | |
| Flatten to a copy |
arr.flatten()
|
Returns an independent one-dimensional copy. | |
| Flatten with reshape |
arr.reshape(-1)
|
Returns a one-dimensional representation of all elements. | |
| Remove size-one axes |
np.squeeze(arr)
|
Removes axes whose length is one. | |
| Add an axis |
np.expand_dims(arr, axis=0)
|
Adds a new axis at the specified position. | |
| Create a column array |
arr[:, np.newaxis]
|
Changes a shape of (n,) into (n, 1). |
Reshape a one-dimensional array
A reshape must preserve the total number of elements. An array containing
12 values can become (3, 4), (4, 3), or
(2, 2, 3), but not (5, 3).
import numpy as np
values = np.arange(1, 13)
matrix = values.reshape(
3, 4
)
print(values.shape)
print(matrix.shape)
print(matrix)
(12,)
(3, 4)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
Infer a dimension with −1
Use -1 once in a requested shape to let NumPy calculate that
dimension from the number of available elements.
values = np.arange(12)
two_rows = values.reshape(
2, -1
)
print(two_rows.shape)
print(two_rows)
# Shape: (2, 6)
values = np.arange(12)
four_columns = values.reshape(
-1, 4
)
print(four_columns.shape)
print(four_columns)
# Shape: (3, 4)
A shape such as reshape(-1, -1) is ambiguous and raises an
error. Specify every dimension except one.
Compare ravel() and flatten()
matrix = np.array([
[10, 20],
[30, 40]
])
flat_view = matrix.ravel()
flat_view[0] = 999
print(matrix)
# The source may change because
# ravel can return a view.
matrix = np.array([
[10, 20],
[30, 40]
])
flat_copy = matrix.flatten()
flat_copy[0] = 999
print(matrix)
# The source remains unchanged
# because flatten returns a copy.
Add a new axis
A one-dimensional array has the shape (n,). Add an axis when
an operation requires a row with shape (1, n) or a column
with shape (n, 1).
values = np.array([
10, 20, 30
])
row = values[
np.newaxis,
:
]
print(row)
print(row.shape)
# [[10 20 30]]
# (1, 3)
values = np.array([
10, 20, 30
])
column = values[
:,
np.newaxis
]
print(column)
print(column.shape)
# [[10]
# [20]
# [30]]
# (3, 1)
Add and remove size-one dimensions
values = np.array([
10, 20, 30
])
expanded = np.expand_dims(
values,
axis=(0, 2)
)
restored = np.squeeze(
expanded
)
print(values.shape)
print(expanded.shape)
print(restored.shape)
# (3,)
# (1, 3, 1)
# (3,)
An axis with two or more elements cannot be removed with
np.squeeze(). Specify the axis argument when
only a particular size-one dimension should be removed.
Print or validate arr.shape after reshaping data for machine
learning, broadcasting, or matrix calculations. Arrays can contain the
correct values while still having an incompatible arrangement.
Reorder array dimensions
Transpose & Axes
Swap rows and columns, reorder dimensions, move individual axes, and understand how axis numbers control calculations in multidimensional arrays.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Transpose a matrix |
matrix.T
|
Swaps the rows and columns of a 2D array. | |
| Transpose with a function |
np.transpose(matrix)
|
Reverses the order of the array’s axes by default. | |
| Reorder all axes |
np.transpose(arr, (1, 2, 0))
|
Reorders a 3D array according to the supplied axis order. | |
| Swap two axes |
np.swapaxes(arr, 0, 2)
|
Exchanges axis 0 with axis 2. | |
| Move one axis |
np.moveaxis(arr, 0, -1)
|
Moves the first axis to the final position. | |
| Sum down rows |
matrix.sum(axis=0)
|
Returns one total for each column. | |
| Sum across columns |
matrix.sum(axis=1)
|
Returns one total for each row. | |
| Keep reduced axes |
matrix.sum(axis=1, keepdims=True)
|
Retains the reduced axis with a length of one. |
Transpose a two-dimensional array
Transposing a matrix exchanges its row and column axes. An array with the
shape (2, 3) becomes an array with the shape
(3, 2).
import numpy as np
matrix = np.array([
[10, 20, 30],
[40, 50, 60]
])
transposed = matrix.T
print("Original shape:", matrix.shape)
print("Transposed shape:", transposed.shape)
print(transposed)
Original shape: (2, 3)
Transposed shape: (3, 2)
[[10 40]
[20 50]
[30 60]]
A one-dimensional array has only one axis, so arr.T keeps
the shape (n,). Add an axis first when a row or column
matrix is required.
Convert a 1D array into a row or column
values = np.array([
10, 20, 30
])
row = values.reshape(
1, -1
)
print(row)
print(row.shape)
# [[10 20 30]]
# (1, 3)
values = np.array([
10, 20, 30
])
column = values.reshape(
-1, 1
)
print(column)
print(column.shape)
# [[10]
# [20]
# [30]]
# (3, 1)
Understand axis 0 and axis 1
The selected axis is the dimension that an operation reduces. For a 2D
array, reducing axis=0 produces one result per column, while
reducing axis=1 produces one result per row.
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
column_totals = sales.sum(
axis=0
)
row_totals = sales.sum(
axis=1
)
print("Column totals:", column_totals)
print("Row totals:", row_totals)
Column totals: [210 260 320]
Row totals: [450 340]
Preserve dimensions with keepdims
Use keepdims=True when the result must retain a size-one axis
for later broadcasting or shape-compatible calculations.
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
without_keepdims = sales.sum(
axis=1
)
with_keepdims = sales.sum(
axis=1,
keepdims=True
)
print(without_keepdims.shape)
print(with_keepdims.shape)
print(with_keepdims)
# (2,)
# (2, 1)
# [[450]
# [340]]
Reorder axes in a 3D array
data = np.zeros(
(2, 3, 4)
)
reordered = np.transpose(
data,
axes=(1, 2, 0)
)
print(data.shape)
print(reordered.shape)
# (2, 3, 4)
# (3, 4, 2)
data = np.zeros(
(2, 3, 4)
)
moved = np.moveaxis(
data,
0,
-1
)
print(data.shape)
print(moved.shape)
# (2, 3, 4)
# (3, 4, 2)
Swap two selected axes
data = np.zeros(
(2, 3, 4)
)
swapped = np.swapaxes(
data,
0,
2
)
print(data.shape)
print(swapped.shape)
# (2, 3, 4)
# (4, 3, 2)
For a shape such as (2, 3, 4), axis 0 has length 2,
axis 1 has length 3, and axis 2 has length 4. Negative axis numbers
count from the end, so axis=-1 refers to the final axis.
Transpose and axis-moving operations commonly return views rather than
independent copies. Use .copy() when later modifications
must not affect the original array.
Calculate across compatible shapes
Broadcasting
Combine arrays with different but compatible shapes without manually repeating their values. Broadcasting enables concise, vectorized calculations while avoiding unnecessary copies.
| Operation | Shapes | Broadcast result | Compatible? |
|---|---|---|---|
| Array and scalar | (3, 4) and () |
(3, 4) |
Yes |
| Matrix and row | (3, 4) and (4,) |
(3, 4) |
Yes |
| Matrix and column | (3, 4) and (3, 1) |
(3, 4) |
Yes |
| Outer-style combination | (3, 1) and (1, 4) |
(3, 4) |
Yes |
| Matching dimensions | (2, 3, 4) and (3, 4) |
(2, 3, 4) |
Yes |
| Conflicting dimensions | (3, 4) and (3,) |
No valid result | No |
NumPy compares shapes from right to left. Two dimensions are compatible when they are equal or when either dimension has a length of one. Missing leading dimensions are treated as having length one.
Broadcast a scalar
A scalar has no array axes and can be combined with every element of an array.
import numpy as np
prices = np.array([
100.0, 150.0, 200.0
])
tax_rate = 1.25
prices_with_tax = prices * tax_rate
print(prices_with_tax)
# [125. 187.5 250. ]
Broadcast a row across a matrix
A one-dimensional array with shape (3,) aligns with the final
dimension of a matrix whose shape is (2, 3).
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
column_adjustments = np.array([
10, 20, 30
])
adjusted_sales = (
sales + column_adjustments
)
print(adjusted_sales)
[[130 170 210]
[100 130 170]]
Broadcast a column across a matrix
Reshape row-specific values to (n, 1) so they align with each
row and broadcast across all columns.
sales = np.array([
[120, 150, 180],
[90, 110, 140]
])
row_multipliers = np.array([
1.10,
0.90
])[:, np.newaxis]
adjusted_sales = (
sales * row_multipliers
)
print(row_multipliers.shape)
print(adjusted_sales)
(2, 1)
[[132. 165. 198.]
[ 81. 99. 126.]]
Combine a column and a row
Combining shapes (3, 1) and (1, 4) produces a
result with shape (3, 4).
rows = np.array([
10, 20, 30
])[:, np.newaxis]
columns = np.array([
1, 2, 3, 4
])[np.newaxis, :]
result = rows + columns
print(result.shape)
print(result)
(3, 4)
[[11 12 13 14]
[21 22 23 24]
[31 32 33 34]]
Center data by column
Aggregations can retain reduced axes with keepdims=True,
creating shapes that broadcast cleanly back across the original data.
measurements = np.array([
[10.0, 20.0, 30.0],
[14.0, 24.0, 34.0],
[18.0, 28.0, 38.0]
])
column_means = measurements.mean(
axis=0,
keepdims=True
)
centered = (
measurements - column_means
)
print(column_means.shape)
print(centered)
(1, 3)
[[-4. -4. -4.]
[ 0. 0. 0.]
[ 4. 4. 4.]]
Inspect a broadcasted shape
result_shape = np.broadcast_shapes(
(5, 1, 4),
(1, 3, 1)
)
print(result_shape)
# (5, 3, 4)
row = np.array([
10, 20, 30
])
expanded = np.broadcast_to(
row,
shape=(4, 3)
)
print(expanded)
np.broadcast_to() returns a broadcasted view and should not
be treated as independently allocated writable data. Call
.copy() if the expanded result must be modified.
Diagnose incompatible shapes
matrix = np.ones(
(3, 4)
)
row_values = np.array([
10, 20, 30
])
# This would fail:
# matrix + row_values
column_values = row_values[
:,
np.newaxis
]
result = matrix + column_values
print(column_values.shape)
print(result.shape)
When broadcasting fails, write both shapes down and compare their
dimensions from right to left. Add a size-one axis with
np.newaxis, reshape(), or
expand_dims() when the intended alignment is clear.
Replace loops with array operations
Vectorized Math
Perform arithmetic on complete arrays instead of processing one value at a time. Vectorized expressions are usually shorter, clearer, and faster than equivalent Python loops.
| Operation | NumPy expression | Calculation | Copy |
|---|---|---|---|
| Addition |
a + b
|
Adds corresponding elements. | |
| Subtraction |
a - b
|
Subtracts corresponding elements. | |
| Element-wise multiplication |
a * b
|
Multiplies corresponding elements. | |
| True division |
a / b
|
Divides corresponding elements and returns decimal values. | |
| Floor division |
a // b
|
Divides and rounds the result toward negative infinity. | |
| Powers |
a ** 2
|
Raises every element to the second power. | |
| Remainder |
a % b
|
Returns the element-wise division remainder. | |
| Matrix multiplication |
a @ b
|
Performs a matrix product rather than element-wise multiplication. |
Perform element-wise arithmetic
Arrays with equal or broadcast-compatible shapes can be combined directly with standard arithmetic operators.
import numpy as np
a = np.array([
10, 20, 30, 40
])
b = np.array([
2, 4, 5, 8
])
print("Add:", a + b)
print("Subtract:", a - b)
print("Multiply:", a * b)
print("Divide:", a / b)
print("Power:", a ** 2)
Add: [12 24 35 48]
Subtract: [ 8 16 25 32]
Multiply: [ 20 80 150 320]
Divide: [5. 5. 6. 5.]
Power: [ 100 400 900 1600]
Replace a Python loop
prices = [
100.0,
150.0,
200.0
]
discounted = []
for price in prices:
discounted.append(
price * 0.85
)
print(discounted)
prices = np.array([
100.0,
150.0,
200.0
])
discounted = (
prices * 0.85
)
print(discounted)
# [ 85. 127.5 170. ]
Calculate revenue and profit
Real calculations can combine several element-wise operations in one readable expression.
units = np.array([
12, 8, 15, 10
])
prices = np.array([
25.0, 40.0, 18.0, 32.0
])
unit_costs = np.array([
14.0, 22.0, 10.0, 20.0
])
revenue = units * prices
total_cost = units * unit_costs
profit = revenue - total_cost
print("Revenue:", revenue)
print("Profit:", profit)
print("Total profit:", profit.sum())
Revenue: [300. 320. 270. 320.]
Profit: [132. 144. 120. 120.]
Total profit: 516.0
Calculate percentage change
monthly_sales = np.array([
100.0, 120.0, 90.0, 135.0
])
previous = monthly_sales[:-1]
current = monthly_sales[1:]
percentage_change = (
(current - previous)
/ previous
* 100
)
print(
np.round(
percentage_change,
2
)
)
[ 20. -25. 50.]
Normalize values to a 0–1 range
values = np.array([
20.0, 35.0, 50.0, 80.0
])
minimum = values.min()
maximum = values.max()
normalized = (
(values - minimum)
/ (maximum - minimum)
)
print(normalized)
[0. 0.25 0.5 1. ]
Min-max normalization divides by maximum - minimum. If
every value is identical, that difference is zero. Handle constant
arrays before performing the division.
Divide safely
Use the where parameter of np.divide() when a
denominator can contain zeros.
numerator = np.array([
10.0, 20.0, 30.0
])
denominator = np.array([
2.0, 0.0, 5.0
])
result = np.full(
numerator.shape,
np.nan,
dtype=np.float64
)
np.divide(
numerator,
denominator,
out=result,
where=denominator != 0
)
print(result)
# [ 5. nan 6.]
Element-wise multiplication versus matrix multiplication
a = np.array([
[1, 2],
[3, 4]
])
b = np.array([
[5, 6],
[7, 8]
])
result = a * b
print(result)
# [[ 5 12]
# [21 32]]
a = np.array([
[1, 2],
[3, 4]
])
b = np.array([
[5, 6],
[7, 8]
])
result = a @ b
print(result)
# [[19 22]
# [43 50]]
Build calculations from NumPy operators, broadcasting, aggregations, and universal functions whenever possible. Use Python loops only when the operation cannot be expressed clearly with array operations.
Apply optimized element-wise functions
Universal Functions
Use NumPy universal functions, commonly called ufuncs, to perform fast element-wise calculations with support for broadcasting, conditional execution, output arrays, reductions, and accumulation.
| Task | NumPy syntax | Operation | Copy |
|---|---|---|---|
| Absolute values |
np.abs(arr)
|
Returns the magnitude of every value. | |
| Square root |
np.sqrt(arr)
|
Calculates the non-negative square root of each value. | |
| Exponential |
np.exp(arr)
|
Calculates e raised to every value. |
|
| Natural logarithm |
np.log(arr)
|
Calculates the natural logarithm of positive values. | |
| Sine |
np.sin(arr)
|
Calculates the sine of values expressed in radians. | |
| Round down |
np.floor(arr)
|
Rounds each value toward negative infinity. | |
| Round up |
np.ceil(arr)
|
Rounds each value toward positive infinity. | |
| Element-wise minimum |
np.minimum(a, b)
|
Returns the smaller value at each corresponding position. | |
| Element-wise maximum |
np.maximum(a, b)
|
Returns the larger value at each corresponding position. | |
| Check finite values |
np.isfinite(arr)
|
Identifies values that are neither infinity nor NaN. |
Apply functions element by element
A ufunc accepts an array and applies the same compiled operation to each element, returning an array with a broadcast-compatible shape.
import numpy as np
values = np.array([
1.0, 4.0, 9.0, 16.0
])
roots = np.sqrt(values)
squares = np.square(values)
logs = np.log(values)
print("Roots:", roots)
print("Squares:", squares)
print("Logs:", np.round(logs, 3))
Roots: [1. 2. 3. 4.]
Squares: [ 1. 16. 81. 256.]
Logs: [0. 1.386 2.197 2.773]
Work with angles
NumPy trigonometric functions expect angles in radians. Convert degree
values with np.deg2rad() before calculating sine, cosine, or
tangent.
degrees = np.array([
0, 30, 45, 60, 90
])
radians = np.deg2rad(
degrees
)
sine_values = np.sin(
radians
)
print(
np.round(
sine_values,
3
)
)
[0. 0.5 0.707 0.866 1. ]
Compare values element by element
available = np.array([
12, 8, 20, 5
])
requested = np.array([
10, 15, 6, 8
])
fulfilled = np.minimum(
available,
requested
)
print(fulfilled)
# [10 8 6 5]
scores = np.array([
42, 68, 91, 55
])
adjusted = np.maximum(
scores,
60
)
print(adjusted)
# [60 68 91 60]
Write results into an existing array
Many ufuncs support an out argument. It can reduce temporary
allocations when an appropriately shaped output array already exists.
values = np.array([
1.0, 4.0, 9.0, 16.0
])
result = np.empty_like(
values
)
np.sqrt(
values,
out=result
)
print(result)
# [1. 2. 3. 4.]
Apply a ufunc conditionally
Combine out and where when a function should run
only for elements that meet a condition.
values = np.array([
16.0, -4.0, 25.0, -9.0
])
result = np.full(
values.shape,
np.nan,
dtype=np.float64
)
np.sqrt(
values,
out=result,
where=values >= 0
)
print(result)
# [ 4. nan 5. nan]
Reduce and accumulate
values = np.array([
5, 10, 15, 20
])
total = np.add.reduce(
values
)
product = np.multiply.reduce(
values
)
print(total)
print(product)
# 50
# 15000
values = np.array([
5, 10, 15, 20
])
running_total = np.add.accumulate(
values
)
running_product = np.multiply.accumulate(
values
)
print(running_total)
print(running_product)
Create every pairwise combination
rows = np.array([
1, 2, 3
])
columns = np.array([
10, 20, 30, 40
])
table = np.multiply.outer(
rows,
columns
)
print(table)
[[ 10 20 30 40]
[ 20 40 60 80]
[ 30 60 90 120]]
Functions such as np.sqrt() and np.log() can
produce nan, negative infinity, or warnings when inputs fall
outside their valid real-number domain. Validate or mask inputs before
applying the function.
The where, out, reduce,
accumulate, and outer features can express
many operations without explicit Python iteration.
Summarize numeric arrays
Aggregations & Statistics
Calculate totals, averages, ranges, percentiles, variation, cumulative values, and summary statistics across complete arrays or selected axes.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Total |
np.sum(arr)
|
Adds all values in the array. | |
| Arithmetic mean |
np.mean(arr)
|
Calculates the arithmetic average. | |
| Weighted average |
np.average(arr, weights=weights)
|
Calculates an average using supplied weights. | |
| Median |
np.median(arr)
|
Returns the middle value of the sorted data. | |
| Minimum and maximum |
np.min(arr), np.max(arr)
|
Returns the smallest and largest values. | |
| Standard deviation |
np.std(arr, ddof=1)
|
Calculates sample standard deviation. | |
| Variance |
np.var(arr, ddof=1)
|
Calculates sample variance. | |
| Percentile |
np.percentile(arr, [25, 50, 75])
|
Returns the first, second, and third quartiles. | |
| Index of maximum |
np.argmax(arr)
|
Returns the flat index of the first maximum value. | |
| Cumulative total |
np.cumsum(arr)
|
Returns a running total across the values. |
Calculate a statistical summary
import numpy as np
sales = np.array([
120, 150, 180, 90,
110, 140, 200, 175
])
print("Count:", sales.size)
print("Total:", sales.sum())
print("Mean:", sales.mean())
print("Median:", np.median(sales))
print("Minimum:", sales.min())
print("Maximum:", sales.max())
print("Range:", np.ptp(sales))
print("Standard deviation:", sales.std())
Count: 8
Total: 1165
Mean: 145.625
Median: 145.0
Minimum: 90
Maximum: 200
Range: 110
Standard deviation: 35.34946003682704
Aggregate by axis
For a two-dimensional array, axis=0 returns one result per
column and axis=1 returns one result per row.
sales = np.array([
[120, 150, 180],
[90, 110, 140],
[200, 175, 225]
])
column_totals = sales.sum(
axis=0
)
column_means = sales.mean(
axis=0
)
print(column_totals)
print(column_means)
row_totals = sales.sum(
axis=1
)
row_means = sales.mean(
axis=1
)
print(row_totals)
print(row_means)
# [450 340 600]
# [150. 113.33333333 200. ]
Calculate percentiles and quartiles
values = np.array([
12, 18, 21, 25,
30, 34, 40, 52
])
quartiles = np.percentile(
values,
[25, 50, 75]
)
q1, median, q3 = quartiles
interquartile_range = q3 - q1
print("Quartiles:", quartiles)
print("IQR:", interquartile_range)
Population versus sample variation
values = np.array([
10, 15, 20, 25, 30
])
population_std = np.std(
values,
ddof=0
)
population_var = np.var(
values,
ddof=0
)
print(population_std)
print(population_var)
values = np.array([
10, 15, 20, 25, 30
])
sample_std = np.std(
values,
ddof=1
)
sample_var = np.var(
values,
ddof=1
)
print(sample_std)
print(sample_var)
NumPy uses ddof=0 by default, which calculates population
variance or standard deviation. A common sample estimate uses
ddof=1.
Find extreme values and their positions
temperatures = np.array([
18, 24, 31, 27, 35, 21
])
minimum_index = np.argmin(
temperatures
)
maximum_index = np.argmax(
temperatures
)
print(
minimum_index,
temperatures[minimum_index]
)
print(
maximum_index,
temperatures[maximum_index]
)
0 18
4 35
Calculate cumulative values
daily_sales = np.array([
100, 125, 90, 140
])
running_sales = np.cumsum(
daily_sales
)
print(running_sales)
# [100 225 315 455]
growth_factors = np.array([
1.05, 1.02, 0.98, 1.04
])
cumulative_growth = np.cumprod(
growth_factors
)
print(
np.round(
cumulative_growth,
4
)
)
Ignore missing numeric values
measurements = np.array([
12.5,
np.nan,
18.0,
21.5,
np.nan
])
print("Count:", np.count_nonzero(
~np.isnan(measurements)
))
print("Total:", np.nansum(
measurements
))
print("Mean:", np.nanmean(
measurements
))
print("Maximum:", np.nanmax(
measurements
))
Functions such as np.mean() and np.sum()
normally return nan when the selected data contains a NaN.
Use the corresponding NaN-aware function only when ignoring missing
values is appropriate for the calculation.
For multidimensional data, explicitly setting axis makes
the intended calculation clearer and helps prevent accidentally
aggregating the entire array.
Order values and locate matches
Sorting & Searching
Sort arrays, return ordering indexes, find insertion positions, retrieve top or bottom values efficiently, and locate elements that satisfy a condition.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Sort ascending |
np.sort(arr)
|
Returns a sorted copy in ascending order. | |
| Sort descending |
np.sort(arr)[::-1]
|
Returns a one-dimensional array in descending order. | |
| Return sort indexes |
np.argsort(arr)
|
Returns indexes that would sort the array. | |
| Find smallest values |
np.partition(arr, 3)[:3]
|
Places the three smallest values in the first positions. | |
| Find largest indexes |
np.argpartition(arr, -3)[-3:]
|
Returns indexes for three large values without a full sort. | |
| Find insertion position |
np.searchsorted(arr, 25)
|
Returns where 25 belongs in a sorted array. | |
| Locate matches |
np.flatnonzero(arr > 10)
|
Returns flat indexes where the condition is true. | |
| Unique values and counts |
np.unique(arr, return_counts=True)
|
Returns distinct values and their frequencies. |
Sort values
np.sort() returns a sorted copy. The original array remains
unchanged unless its own sort() method is called.
import numpy as np
values = np.array([
45, 12, 80, 25, 60
])
ascending = np.sort(
values
)
descending = ascending[::-1]
print(values)
print(ascending)
print(descending)
values = np.array([
45, 12, 80, 25, 60
])
values.sort()
print(values)
# [12 25 45 60 80]
Use np.sort(arr) when the original ordering must remain
available. Use arr.sort() only when modifying the existing
array is intentional.
Sort rows or columns
matrix = np.array([
[30, 10, 20],
[60, 50, 40]
])
sort_each_row = np.sort(
matrix,
axis=1
)
sort_each_column = np.sort(
matrix,
axis=0
)
sort_all_values = np.sort(
matrix,
axis=None
)
print(sort_each_row)
print(sort_each_column)
print(sort_all_values)
Reorder related arrays with argsort()
np.argsort() returns ordering indexes rather than sorted
values. Apply those indexes to other arrays to keep related data aligned.
products = np.array([
"Keyboard",
"Mouse",
"Monitor",
"Webcam"
])
sales = np.array([
120, 180, 95, 150
])
descending_order = np.argsort(
sales
)[::-1]
ranked_products = products[
descending_order
]
ranked_sales = sales[
descending_order
]
print(ranked_products)
print(ranked_sales)
['Mouse' 'Webcam' 'Keyboard' 'Monitor']
[180 150 120 95]
Find the largest values efficiently
Use argpartition() when only the largest or smallest
k elements are needed. It avoids fully sorting every value.
scores = np.array([
72, 91, 65, 88,
95, 79, 84
])
top_count = 3
top_indexes = np.argpartition(
scores,
-top_count
)[-top_count:]
top_indexes = top_indexes[
np.argsort(
scores[top_indexes]
)[::-1]
]
print(top_indexes)
print(scores[top_indexes])
[4 1 3]
[95 91 88]
Partitioning places the selected partition element in its sorted position and divides smaller and larger elements around it. Sort the selected subset separately when its final order matters.
Find insertion positions
np.searchsorted() uses a sorted array and returns positions
where new values can be inserted while preserving the order.
sorted_values = np.array([
10, 20, 30, 40, 50
])
position = np.searchsorted(
sorted_values,
35
)
print(position)
# 3
sorted_values = np.array([
10, 20, 30, 40, 50
])
positions = np.searchsorted(
sorted_values,
[5, 25, 55]
)
print(positions)
# [0 2 5]
Locate values that meet a condition
matrix = np.array([
[10, 45, 20],
[60, 25, 80]
])
row_indexes, column_indexes = np.where(
matrix >= 50
)
coordinates = np.column_stack((
row_indexes,
column_indexes
))
print(coordinates)
print(matrix[row_indexes, column_indexes])
[[1 0]
[1 2]]
[60 80]
Find unique values and frequencies
categories = np.array([
"A", "B", "A", "C",
"B", "A", "C", "C"
])
unique_values, counts = np.unique(
categories,
return_counts=True
)
for value, count in zip(
unique_values,
counts
):
print(value, count)
A 3
B 2
C 3
Use sort() for ordered values, argsort() for
ordering indexes, partition() for efficient top or bottom
subsets, and searchsorted() for insertion positions in
already sorted data.
Combine and divide arrays
Joining & Splitting Arrays
Combine compatible arrays along existing or new axes, stack rows and columns, and divide arrays into equal or uneven sections.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Join an existing axis |
np.concatenate([a, b], axis=0)
|
Joins compatible arrays along axis 0. | |
| Create a new axis |
np.stack([a, b], axis=0)
|
Stacks equal-shaped arrays along a new axis. | |
| Stack vertically |
np.vstack([a, b])
|
Places arrays above and below one another. | |
| Stack horizontally |
np.hstack([a, b])
|
Places arrays beside one another. | |
| Create columns |
np.column_stack([a, b])
|
Combines one-dimensional arrays as columns. | |
| Equal sections |
np.split(arr, 3)
|
Divides an array into three equal sections. | |
| Uneven sections |
np.array_split(arr, 3)
|
Divides an array even when equal sections are impossible. | |
| Split columns |
np.hsplit(matrix, 2)
|
Divides a matrix into two column groups. | |
| Split rows |
np.vsplit(matrix, 2)
|
Divides a matrix into two row groups. |
Concatenate along an existing axis
np.concatenate() joins arrays along an axis that already
exists. Every other dimension must have a compatible length.
import numpy as np
first = np.array([
[1, 2],
[3, 4]
])
second = np.array([
[5, 6]
])
combined = np.concatenate(
[first, second],
axis=0
)
print(combined)
print(combined.shape)
left = np.array([
[1, 2],
[3, 4]
])
right = np.array([
[5],
[6]
])
combined = np.concatenate(
[left, right],
axis=1
)
print(combined)
print(combined.shape)
[[1 2 5]
[3 4 6]]
(2, 3)
Stack along a new axis
Unlike concatenation, np.stack() adds a new axis. Every input
array must have the same shape.
january = np.array([
[120, 150],
[90, 110]
])
february = np.array([
[130, 160],
[95, 125]
])
monthly_sales = np.stack(
[january, february],
axis=0
)
print(monthly_sales.shape)
print(monthly_sales)
(2, 2, 2)
[[[120 150]
[ 90 110]]
[[130 160]
[ 95 125]]]
Compare concatenate() and stack()
a = np.array([
1, 2, 3
])
b = np.array([
4, 5, 6
])
concatenated = np.concatenate([
a, b
])
stacked_rows = np.stack(
[a, b],
axis=0
)
stacked_columns = np.stack(
[a, b],
axis=1
)
print(concatenated.shape)
print(stacked_rows.shape)
print(stacked_columns.shape)
# (6,)
# (2, 3)
# (3, 2)
Build rows and columns
north = np.array([
120, 150, 180
])
south = np.array([
90, 110, 140
])
regional_sales = np.vstack([
north,
south
])
print(regional_sales)
print(regional_sales.shape)
product_ids = np.array([
101, 102, 103
])
sales = np.array([
120, 180, 95
])
records = np.column_stack([
product_ids,
sales
])
print(records)
print(records.shape)
Split into equal sections
values = np.arange(
1,
13
)
batches = np.split(
values,
3
)
for batch in batches:
print(batch)
[1 2 3 4]
[5 6 7 8]
[ 9 10 11 12]
When an integer number of sections is supplied, np.split()
raises an error if the selected axis cannot be divided evenly. Use
np.array_split() when uneven sections are acceptable.
Split into uneven sections
values = np.arange(
1,
11
)
groups = np.array_split(
values,
3
)
for group in groups:
print(group)
[1 2 3 4]
[5 6 7]
[ 8 9 10]
Split at specific indexes
values = np.array([
10, 20, 30, 40,
50, 60, 70, 80
])
first, middle, last = np.split(
values,
[2, 6]
)
print(first)
print(middle)
print(last)
[10 20]
[30 40 50 60]
[70 80]
Split rows and columns
matrix = np.arange(
1,
17
).reshape(4, 4)
top, bottom = np.vsplit(
matrix,
2
)
print(top)
print(bottom)
matrix = np.arange(
1,
17
).reshape(4, 4)
left, right = np.hsplit(
matrix,
2
)
print(left)
print(right)
Print or validate the input shapes when a join fails. For
concatenate(), dimensions other than the joining axis must
match. For stack(), every input array must have the same
shape.
Control shared array memory
Copies & Views
Understand when arrays share the same underlying data, when NumPy creates independent copies, and how modifying one array can affect another.
| Task | NumPy syntax | Behavior | Copy |
|---|---|---|---|
| Create an explicit view |
view = arr.view()
|
Creates a new array object that shares the same data. | |
| Create an independent copy |
copy = arr.copy()
|
Creates a new array with independent data storage. | |
| Check shared memory |
np.shares_memory(a, b)
|
Checks whether two arrays definitely share memory. | |
| Fast possible-sharing check |
np.may_share_memory(a, b)
|
Conservatively checks whether memory might be shared. | |
| Inspect the owner |
arr.base
|
References another object when the array is backed by its data. | |
| Check data ownership |
arr.flags["OWNDATA"]
|
Reports whether the array owns its underlying memory. | |
| Independent slice |
subset = arr[1:4].copy()
|
Copies the selected values into independent storage. | |
| Copy with np.copy() |
copy = np.copy(arr)
|
Returns a copied array equivalent to an explicit array copy. |
Assignment does not copy an array
Assigning an array to another variable creates an additional reference to the same array object. Changes made through either variable are visible through both.
import numpy as np
original = np.array([
10, 20, 30
])
alias = original
alias[0] = 999
print(original)
print(alias)
print(alias is original)
[999 20 30]
[999 20 30]
True
Basic slicing usually creates a view
A basic slice commonly creates a new array object that accesses part of the original array’s data. Modifying the view can therefore modify the source.
original = np.array([
10, 20, 30, 40, 50
])
subset = original[1:4]
subset[0] = 999
print(original)
print(subset)
print(
np.shares_memory(
original,
subset
)
)
[ 10 999 30 40 50]
[999 30 40]
True
Treat a basic slice as shared data unless an independent copy is explicitly created. This behavior is efficient, but it can cause unexpected changes when the relationship is overlooked.
Create an independent copy
original = np.array([
10, 20, 30, 40, 50
])
subset = original[1:4].copy()
subset[0] = 999
print(original)
print(subset)
print(
np.shares_memory(
original,
subset
)
)
[10 20 30 40 50]
[999 30 40]
False
Advanced indexing creates a copy
Selecting values with an integer index array or boolean mask creates a copied result rather than a basic shared-memory slice.
original = np.array([
10, 20, 30, 40
])
selected = original[
[0, 2]
]
selected[0] = 999
print(original)
print(selected)
# Original is unchanged.
original = np.array([
10, 20, 30, 40
])
selected = original[
original >= 30
]
selected[0] = 999
print(original)
print(selected)
# Original is unchanged.
Check whether memory is shared
original = np.arange(10)
view = original[2:7]
copy = original[2:7].copy()
print(
np.shares_memory(
original,
view
)
)
print(
np.shares_memory(
original,
copy
)
)
print(view.flags["OWNDATA"])
print(copy.flags["OWNDATA"])
True
False
False
True
Reshape without assuming memory behavior
Reshaping often returns a view when the requested layout is compatible, but a copy may be required for some memory arrangements. Check sharing when your program depends on that distinction.
original = np.arange(12)
reshaped = original.reshape(
3, 4
)
print(
np.shares_memory(
original,
reshaped
)
)
reshaped[0, 0] = 999
print(original[0])
View versus copy summary
| Operation | Typical behavior | Important note |
|---|---|---|
| Variable assignment | Same object | No new array object or data buffer is created. |
| Basic slicing | View | The result commonly shares memory with the source. |
| Integer array indexing | Copy | The selected result has independent data. |
| Boolean indexing | Copy | The filtered result has independent data. |
view() |
View | Creates another array object using shared data. |
copy() |
Copy | Creates independent array data. |
reshape() |
Often a view | A copy may be necessary for incompatible memory layouts. |
transpose() |
Usually a view | The result commonly has a different stride arrangement. |
Views avoid allocating and duplicating data, making them useful for
performance and memory efficiency. Use .copy() at the point
where independent modification or long-term isolation is required.
Detect and handle unavailable data
Missing Values
Detect NaN and infinity, calculate summaries that ignore missing numeric values, replace invalid entries, and work with missing dates or masked observations.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Detect NaN |
np.isnan(arr)
|
Returns a boolean mask identifying NaN values. | |
| Detect finite values |
np.isfinite(arr)
|
Identifies values that are neither NaN nor infinity. | |
| Detect infinity |
np.isinf(arr)
|
Identifies positive and negative infinity. | |
| Remove NaN values |
arr[~np.isnan(arr)]
|
Returns a one-dimensional array containing non-NaN values. | |
| Mean without NaN |
np.nanmean(arr)
|
Calculates the mean while ignoring NaN values. | |
| Total without NaN |
np.nansum(arr)
|
Calculates the total while treating NaN as zero. | |
| Replace invalid values |
np.nan_to_num(arr, nan=0.0)
|
Returns an array with NaN replaced by zero. | |
| Count NaN values |
np.count_nonzero(np.isnan(arr))
|
Returns the number of NaN entries. | |
| Detect missing dates |
np.isnat(dates)
|
Identifies missing datetime or timedelta values. |
Detect NaN and infinity
Use np.isnan() for NaN specifically and
np.isfinite() when only ordinary finite numeric values should
be considered valid.
import numpy as np
values = np.array([
10.0,
np.nan,
25.0,
np.inf,
-np.inf
])
nan_mask = np.isnan(values)
infinity_mask = np.isinf(values)
finite_mask = np.isfinite(values)
print("NaN:", nan_mask)
print("Infinity:", infinity_mask)
print("Finite:", finite_mask)
NaN: [False True False False False]
Infinity: [False False False True True]
Finite: [ True False True False False]
Filter valid values
measurements = np.array([
12.5,
np.nan,
18.0,
np.inf,
21.5,
-np.inf
])
valid_measurements = measurements[
np.isfinite(measurements)
]
print(valid_measurements)
# [12.5 18. 21.5]
Calculate NaN-aware statistics
NumPy provides NaN-aware versions of common aggregations. These functions ignore NaN values but do not automatically ignore positive or negative infinity.
measurements = np.array([
12.5,
np.nan,
18.0,
21.5,
np.nan
])
print("Total:", np.nansum(
measurements
))
print("Mean:", np.nanmean(
measurements
))
print("Median:", np.nanmedian(
measurements
))
print("Minimum:", np.nanmin(
measurements
))
print("Maximum:", np.nanmax(
measurements
))
Total: 52.0
Mean: 17.333333333333332
Median: 18.0
Minimum: 12.5
Maximum: 21.5
Use NaN-aware aggregations only when excluding missing observations is statistically appropriate. Always track how many valid values contribute to the result.
Count valid and missing values
values = np.array([
10.0,
np.nan,
20.0,
np.nan,
30.0
])
missing_count = np.count_nonzero(
np.isnan(values)
)
valid_count = np.count_nonzero(
~np.isnan(values)
)
completion_rate = (
valid_count
/ values.size
* 100
)
print("Missing:", missing_count)
print("Valid:", valid_count)
print("Complete:", completion_rate)
Missing: 2
Valid: 3
Complete: 60.0
Replace NaN and infinity
np.nan_to_num() can replace NaN, positive infinity, and
negative infinity with separately chosen values.
values = np.array([
10.0,
np.nan,
np.inf,
-np.inf
])
cleaned = np.nan_to_num(
values,
nan=0.0,
posinf=999.0,
neginf=-999.0
)
print(cleaned)
# [ 10. 0. 999. -999.]
Replacing missing data with zero can materially alter totals, averages, and models. Choose a replacement strategy based on what the missing observation represents, not merely what is convenient.
Fill NaN with a calculated value
values = np.array([
10.0,
np.nan,
20.0,
30.0,
np.nan
])
fill_value = np.nanmedian(
values
)
filled = np.where(
np.isnan(values),
fill_value,
values
)
print("Median:", fill_value)
print("Filled:", filled)
Median: 20.0
Filled: [10. 20. 20. 30. 20.]
Handle missing values in integer data
Standard integer arrays cannot store np.nan. Use a separate
boolean mask, a floating-point dtype, or a masked array when integer
observations can be missing.
values = np.array([
10, 20, -1, 40
])
missing_mask = values == -1
masked_values = np.ma.array(
values,
mask=missing_mask
)
print(masked_values)
print(masked_values.mean())
[10 20 -- 40]
23.333333333333332
Detect missing dates
dates = np.array([
"2026-01-15",
"NaT",
"2026-03-20"
], dtype="datetime64[D]")
missing_dates = np.isnat(
dates
)
valid_dates = dates[
~missing_dates
]
print(missing_dates)
print(valid_dates)
[False True False]
['2026-01-15' '2026-03-20']
Store the original missing-value mask before filtering, filling, or replacing values. It preserves information about which observations were originally unavailable and supports later validation or reporting.
Generate samples and simulations
Random Numbers
Create a random number generator, draw values from common distributions, sample from arrays, shuffle observations, and use a seed when repeatable results are required.
| Task | Generator syntax | Result | Copy |
|---|---|---|---|
| Create a generator |
rng = np.random.default_rng()
|
Creates a generator with an automatically selected seed. | |
| Repeatable generator |
rng = np.random.default_rng(42)
|
Creates a generator initialized with seed 42. | |
| Random integers |
rng.integers(1, 7, size=10)
|
Draws ten integers from 1 through 6. | |
| Random decimals |
rng.random((2, 3))
|
Creates a 2 × 3 array in the interval [0, 1). | |
| Uniform distribution |
rng.uniform(10, 20, size=5)
|
Draws five decimal values uniformly between 10 and 20. | |
| Normal distribution |
rng.normal(100, 15, size=1000)
|
Draws 1,000 values with mean 100 and standard deviation 15. | |
| Random sample |
rng.choice(arr, size=3, replace=False)
|
Selects three different values from an array. | |
| Return shuffled copy |
rng.permutation(arr)
|
Returns the values in random order without changing the source. | |
| Shuffle in place |
rng.shuffle(arr)
|
Randomly reorders the existing array. |
Create a random number generator
Create one generator and reuse it throughout a related workflow. Pass an integer seed when the same sequence must be reproduced during testing, teaching, or debugging.
import numpy as np
rng = np.random.default_rng()
values = rng.random(
size=5
)
print(values)
import numpy as np
rng = np.random.default_rng(
seed=42
)
values = rng.random(
size=5
)
print(values)
NumPy generators produce pseudo-random values for statistics, simulations, and sampling. Do not use them for passwords, authentication tokens, encryption keys, or other security-sensitive values.
Generate random integers
By default, the lower boundary is included and the upper boundary is
excluded. For example, rng.integers(1, 7) can return integers
from 1 through 6.
rng = np.random.default_rng(
seed=42
)
dice_rolls = rng.integers(
low=1,
high=7,
size=12
)
print(dice_rolls)
print("Average:", dice_rolls.mean())
Generate random arrays
rng = np.random.default_rng(
seed=42
)
matrix = rng.random(
size=(3, 4)
)
print(matrix)
print(matrix.shape)
rng = np.random.default_rng(
seed=42
)
scores = rng.integers(
low=0,
high=101,
size=(3, 5)
)
print(scores)
print(scores.shape)
Draw from common distributions
rng = np.random.default_rng(
seed=42
)
prices = rng.uniform(
low=10.0,
high=50.0,
size=1000
)
print(prices.mean())
print(prices.min())
print(prices.max())
rng = np.random.default_rng(
seed=42
)
heights = rng.normal(
loc=170.0,
scale=8.0,
size=1000
)
print(heights.mean())
print(heights.std())
Sample with or without replacement
rng = np.random.default_rng(
seed=42
)
products = np.array([
"Keyboard",
"Mouse",
"Monitor",
"Webcam",
"Headset"
])
sample = rng.choice(
products,
size=3,
replace=False
)
print(sample)
rng = np.random.default_rng(
seed=42
)
choices = np.array([
"A", "B", "C"
])
sample = rng.choice(
choices,
size=10,
replace=True
)
print(sample)
Use weighted probabilities
rng = np.random.default_rng(
seed=42
)
categories = np.array([
"Low",
"Medium",
"High"
])
probabilities = np.array([
0.50,
0.35,
0.15
])
sample = rng.choice(
categories,
size=100,
replace=True,
p=probabilities
)
labels, counts = np.unique(
sample,
return_counts=True
)
print(labels)
print(counts)
Values passed through p must be non-negative and sum to one.
Normalize calculated weights before sampling when floating-point
arithmetic may have changed their total.
Shuffle or return a permutation
rng = np.random.default_rng(
seed=42
)
original = np.array([
1, 2, 3, 4, 5
])
shuffled_copy = rng.permutation(
original
)
print(original)
print(shuffled_copy)
rng = np.random.default_rng(
seed=42
)
values = np.array([
1, 2, 3, 4, 5
])
rng.shuffle(
values
)
print(values)
rng.shuffle() reorders the supplied array in place and
returns None. Use rng.permutation() when the
original order must remain unchanged.
Simulate repeated events
rng = np.random.default_rng(
seed=42
)
conversions = rng.binomial(
n=100,
p=0.08,
size=1000
)
print(
"Average conversions:",
conversions.mean()
)
print(
"Maximum conversions:",
conversions.max()
)
Accept an rng parameter in simulation and sampling
functions instead of creating hidden global random state. This makes
tests and repeated analyses easier to control.
Calculate with vectors and matrices
Linear Algebra
Multiply matrices, solve linear systems, calculate vector norms,
inspect determinants and rank, and perform eigenvalue or singular value
decompositions with numpy.linalg.
| Task | NumPy syntax | Result | Copy |
|---|---|---|---|
| Matrix product |
a @ b
|
Multiplies matrices using rows and columns. | |
| Dot product |
np.dot(a, b)
|
Calculates a vector dot product or compatible array product. | |
| Solve a system |
np.linalg.solve(a, b)
|
Solves a square, full-rank linear system. | |
| Least-squares solution |
np.linalg.lstsq(a, b, rcond=None)
|
Returns a solution that minimizes squared residuals. | |
| Matrix inverse |
np.linalg.inv(a)
|
Calculates the inverse of an invertible square matrix. | |
| Determinant |
np.linalg.det(a)
|
Calculates the determinant of a square matrix. | |
| Matrix rank |
np.linalg.matrix_rank(a)
|
Returns the number of linearly independent dimensions. | |
| Vector norm |
np.linalg.norm(vector)
|
Returns the Euclidean length by default. | |
| Eigenvalues and vectors |
np.linalg.eig(a)
|
Calculates eigenvalues and right eigenvectors. | |
| Singular value decomposition |
np.linalg.svd(a)
|
Factorizes a matrix into singular vectors and values. |
Multiply vectors and matrices
Use * for element-wise multiplication and @ for
matrix multiplication. The inner dimensions must be compatible for a
matrix product.
import numpy as np
a = np.array([
1, 2, 3
])
b = np.array([
4, 5, 6
])
dot_product = a @ b
print(dot_product)
# 32
a = np.array([
[1, 2],
[3, 4]
])
b = np.array([
[5, 6],
[7, 8]
])
product = a @ b
print(product)
# [[19 22]
# [43 50]]
Solve a linear system
The equation A @ x = b can be solved directly with
np.linalg.solve() when A is square and has full
rank.
# 3x + y = 9
# x + 2y = 8
coefficients = np.array([
[3.0, 1.0],
[1.0, 2.0]
])
results = np.array([
9.0, 8.0
])
solution = np.linalg.solve(
coefficients,
results
)
print(solution)
print(coefficients @ solution)
[2. 3.]
[9. 8.]
To solve A @ x = b, use np.linalg.solve(A, b)
instead of calculating np.linalg.inv(A) @ b. Direct solving
is generally clearer and more numerically appropriate.
Fit a least-squares line
Least squares finds the coefficients that minimize the squared difference between observed values and a linear model.
x = np.array([
1.0, 2.0, 3.0, 4.0
])
y = np.array([
2.2, 3.9, 6.1, 7.8
])
design_matrix = np.column_stack([
x,
np.ones_like(x)
])
coefficients, residuals, rank, singular_values = (
np.linalg.lstsq(
design_matrix,
y,
rcond=None
)
)
slope, intercept = coefficients
predicted = slope * x + intercept
print("Slope:", slope)
print("Intercept:", intercept)
print("Predicted:", predicted)
Calculate vector norms
vector = np.array([
3.0, 4.0
])
length = np.linalg.norm(
vector
)
print(length)
# 5.0
vector = np.array([
3.0, 4.0
])
length = np.linalg.norm(
vector
)
unit_vector = (
vector / length
)
print(unit_vector)
print(np.linalg.norm(unit_vector))
# [0.6 0.8]
# 1.0
Inspect determinant and rank
matrix = np.array([
[4.0, 2.0],
[1.0, 3.0]
])
determinant = np.linalg.det(
matrix
)
rank = np.linalg.matrix_rank(
matrix
)
trace = np.trace(
matrix
)
print("Determinant:", determinant)
print("Rank:", rank)
print("Trace:", trace)
Determinant: 10.000000000000002
Rank: 2
Trace: 7.0
A mathematically exact determinant of 10 may appear as
10.000000000000002. Use np.isclose() or
np.allclose() instead of exact equality for floating-point
comparisons.
Calculate eigenvalues and eigenvectors
Use np.linalg.eigh() for real symmetric or complex Hermitian
matrices. It takes advantage of their structure and returns ordered real
eigenvalues for real symmetric input.
matrix = np.array([
[2.0, 1.0],
[1.0, 2.0]
])
eigenvalues, eigenvectors = np.linalg.eigh(
matrix
)
print("Eigenvalues:")
print(eigenvalues)
print("Eigenvectors:")
print(eigenvectors)
print(
np.allclose(
matrix @ eigenvectors[:, 0],
eigenvalues[0] * eigenvectors[:, 0]
)
)
Perform singular value decomposition
matrix = np.array([
[3.0, 1.0],
[1.0, 3.0]
])
u, singular_values, vh = np.linalg.svd(
matrix,
full_matrices=False
)
reconstructed = (
u
@ np.diag(singular_values)
@ vh
)
print("Singular values:")
print(singular_values)
print("Reconstruction matches:")
print(
np.allclose(
matrix,
reconstructed
)
)
Handle linear algebra errors
singular_matrix = np.array([
[1.0, 2.0],
[2.0, 4.0]
])
results = np.array([
3.0, 6.0
])
try:
solution = np.linalg.solve(
singular_matrix,
results
)
except np.linalg.LinAlgError as error:
print(
"Unable to solve:",
error
)
A matrix can be technically invertible but still produce unstable
results when it is poorly conditioned. Use
np.linalg.cond() when numerical sensitivity matters.
Save and load array data
File I/O
Store arrays in NumPy’s binary formats, bundle multiple arrays in an archive, exchange numeric data through text or CSV files, and load large arrays with memory mapping.
| Task | NumPy syntax | Format or behavior | Copy |
|---|---|---|---|
| Save one array |
np.save("data.npy", arr)
|
Writes one array in NumPy’s binary NPY format. | |
| Load one array |
arr = np.load("data.npy")
|
Loads an array from an NPY file. | |
| Save multiple arrays |
np.savez("data.npz", sales=sales, costs=costs)
|
Stores named arrays in one uncompressed NPZ archive. | |
| Compress multiple arrays |
np.savez_compressed("data.npz", sales=sales)
|
Stores named arrays in a compressed NPZ archive. | |
| Export numeric CSV |
np.savetxt("data.csv", arr, delimiter=",")
|
Writes numeric array data to a text file. | |
| Load numeric CSV |
np.loadtxt("data.csv", delimiter=",")
|
Loads consistently formatted numeric text data. | |
| Load missing values |
np.genfromtxt("data.csv", delimiter=",")
|
Loads text data with support for missing values. | |
| Memory-map an array |
np.load("data.npy", mmap_mode="r")
|
Reads array data from disk without loading it all immediately. |
Save and load one array
The NPY format preserves the array’s shape and data type without requiring additional parsing or conversion.
import numpy as np
sales = np.array([
[120, 150, 180],
[90, 110, 140]
], dtype=np.int64)
np.save(
"sales.npy",
sales
)
loaded_sales = np.load(
"sales.npy",
allow_pickle=False
)
print(loaded_sales)
print(loaded_sales.shape)
print(loaded_sales.dtype)
NPY is a strong default when the file will be read by NumPy again. It preserves array metadata and avoids the precision and parsing issues that can occur with text formats.
Save multiple named arrays
NPZ archives store several NPY arrays in one file. Naming the arrays makes them easier to retrieve than relying on automatically generated keys.
sales = np.array([
120, 180, 95
])
costs = np.array([
70, 110, 60
])
np.savez_compressed(
"financial_data.npz",
sales=sales,
costs=costs
)
with np.load(
"financial_data.npz",
allow_pickle=False
) as archive:
loaded_sales = archive["sales"]
loaded_costs = archive["costs"]
profit = loaded_sales - loaded_costs
print(profit)
Export a numeric CSV file
Use np.savetxt() for simple numeric text files. Set a format
explicitly when decimal precision or integer output matters.
records = np.array([
[101, 120.50, 70.25],
[102, 180.00, 110.00],
[103, 95.75, 60.50]
])
np.savetxt(
"financial_data.csv",
records,
delimiter=",",
header="product_id,sales,cost",
comments="",
fmt=["%d", "%.2f", "%.2f"]
)
Load a consistently formatted CSV
data = np.loadtxt(
"financial_data.csv",
delimiter=",",
skiprows=1,
usecols=(1, 2)
)
sales = data[:, 0]
costs = data[:, 1]
profit = sales - costs
print(data)
print(profit)
np.loadtxt() works best when every selected row has the
same number of columns and every selected value can be converted to the
requested data type.
Load CSV data with missing values
data = np.genfromtxt(
"measurements.csv",
delimiter=",",
skip_header=1,
missing_values="",
filling_values=np.nan,
dtype=np.float64
)
print(data)
column_means = np.nanmean(
data,
axis=0
)
print(column_means)
Load named CSV columns
Set names=True to build a structured array whose fields use
the names from the first row.
records = np.genfromtxt(
"financial_data.csv",
delimiter=",",
names=True,
dtype=None,
encoding="utf-8"
)
sales = records["sales"]
costs = records["cost"]
profit = sales - costs
print(profit)
Memory-map a large array
Memory mapping allows portions of a large NPY file to be accessed without immediately loading the complete array into memory.
large_array = np.load(
"large_dataset.npy",
mmap_mode="r",
allow_pickle=False
)
first_rows = large_array[
:100
]
print(large_array.shape)
print(first_rows.mean(axis=0))
Object arrays may require pickle support, but loading pickled data from
an untrusted source can execute malicious code. Keep
allow_pickle=False unless the file is trusted and object
loading is genuinely required.
Choose a file format
| Format | Best for | Main tradeoff |
|---|---|---|
| NPY | One NumPy array with preserved dtype and shape | Not designed for manual reading in a text editor |
| NPZ | Several named NumPy arrays in one archive | Primarily intended for NumPy-compatible workflows |
| Compressed NPZ | Reducing storage for compressible array data | Requires additional compression and decompression work |
| CSV or text | Interchange with spreadsheets and other applications | May lose dtype information and numeric precision |
Check the loaded array’s shape, dtype, finite
values, and expected column count before using it in later calculations.
Combine techniques in real workflows
Practical NumPy Examples
Apply array creation, indexing, broadcasting, aggregation, sorting, statistics, and vectorized calculations to realistic data-analysis tasks.
| Example | Main techniques | Useful for |
|---|---|---|
| Sales analysis | Axis aggregation, argmax, indexing | Summarizing regional and monthly performance |
| Percentage growth | Slicing, vectorized arithmetic, safe division | Comparing consecutive periods |
| Moving average | Sliding windows, mean aggregation | Smoothing time-series data |
| Feature standardization | Broadcasting, mean, standard deviation | Preparing numeric columns for analysis |
| Outlier detection | Percentiles, boolean masking | Identifying unusually low or high values |
| Weighted ranking | Broadcasting, weighted sum, argsort | Ranking alternatives across several criteria |
Analyze sales by region and month
Treat rows as regions and columns as months. Aggregate along the correct axis to calculate totals and identify the strongest performers.
import numpy as np
regions = np.array([
"North",
"South",
"West"
])
months = np.array([
"January",
"February",
"March",
"April"
])
sales = np.array([
[120, 150, 180, 170],
[90, 110, 140, 155],
[200, 175, 225, 240]
])
regional_totals = sales.sum(
axis=1
)
monthly_totals = sales.sum(
axis=0
)
best_region_index = np.argmax(
regional_totals
)
best_month_index = np.argmax(
monthly_totals
)
print(
"Regional totals:",
dict(zip(regions, regional_totals))
)
print(
"Best region:",
regions[best_region_index]
)
print(
"Best month:",
months[best_month_index]
)
Calculate period-over-period growth
Align the previous and current periods with slicing, then calculate every percentage change in one vectorized expression.
monthly_sales = np.array([
100.0,
120.0,
90.0,
135.0,
150.0
])
previous = monthly_sales[:-1]
current = monthly_sales[1:]
growth = np.full(
previous.shape,
np.nan,
dtype=np.float64
)
np.divide(
current - previous,
previous,
out=growth,
where=previous != 0
)
growth_percentage = (
growth * 100
)
print(
np.round(
growth_percentage,
2
)
)
[ 20. -25. 50. 11.11]
Calculate a moving average
A moving average summarizes overlapping windows and can make short-term variation easier to interpret.
from numpy.lib.stride_tricks import (
sliding_window_view
)
daily_sales = np.array([
100.0,
120.0,
90.0,
135.0,
150.0,
145.0,
160.0
])
windows = sliding_window_view(
daily_sales,
window_shape=3
)
moving_average = windows.mean(
axis=1
)
print(
np.round(
moving_average,
2
)
)
[103.33 115. 125. 143.33 151.67]
With n observations and a window length of
w, a standard sliding-window calculation returns
n - w + 1 averages.
Standardize numeric features
Z-score standardization subtracts each column’s mean and divides by its standard deviation. Broadcasting applies the column statistics to every row.
features = np.array([
[25.0, 50000.0, 2.0],
[40.0, 75000.0, 5.0],
[35.0, 62000.0, 3.0],
[50.0, 90000.0, 8.0]
])
column_means = features.mean(
axis=0,
keepdims=True
)
column_std = features.std(
axis=0,
keepdims=True
)
safe_std = np.where(
column_std == 0,
1.0,
column_std
)
standardized = (
features - column_means
) / safe_std
print(
np.round(
standardized,
3
)
)
A column containing one repeated value has a standard deviation of zero. Dividing by zero produces invalid values, so detect constant columns before standardizing.
Detect outliers with the IQR method
The interquartile range method marks values below
Q1 - 1.5 × IQR or above Q3 + 1.5 × IQR.
values = np.array([
12, 14, 15, 16,
17, 18, 20, 21,
22, 85
])
q1, q3 = np.percentile(
values,
[25, 75]
)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outlier_mask = (
(values < lower_bound)
| (values > upper_bound)
)
outliers = values[
outlier_mask
]
clean_values = values[
~outlier_mask
]
print("Bounds:", lower_bound, upper_bound)
print("Outliers:", outliers)
print("Clean values:", clean_values)
Investigate unusual observations before removing them. An extreme value may represent a data-entry problem, a rare valid event, or an important signal.
Calculate and rank weighted scores
Multiply each criterion by its weight, sum across columns, and use
argsort() to return the ranking.
products = np.array([
"Product A",
"Product B",
"Product C",
"Product D"
])
# Columns: quality, value, reliability
scores = np.array([
[8.5, 7.0, 9.0],
[7.5, 9.0, 8.0],
[9.0, 6.5, 8.5],
[8.0, 8.0, 7.5]
])
weights = np.array([
0.45,
0.30,
0.25
])
weighted_scores = (
scores * weights
).sum(axis=1)
ranking = np.argsort(
weighted_scores
)[::-1]
for position, index in enumerate(
ranking,
start=1
):
print(
position,
products[index],
round(weighted_scores[index], 2)
)
Validate data before analysis
data = np.array([
[12.5, 18.0, 21.5],
[15.0, 20.0, 24.0]
])
expected_columns = 3
if data.ndim != 2:
raise ValueError(
"Expected a two-dimensional array"
)
if data.shape[1] != expected_columns:
raise ValueError(
"Unexpected number of columns"
)
if not np.issubdtype(
data.dtype,
np.number
):
raise TypeError(
"Expected numeric data"
)
if not np.all(
np.isfinite(data)
):
raise ValueError(
"Data contains NaN or infinity"
)
print("Data is valid")
Inspect shapes and data types, validate invalid values, calculate intermediate results, and confirm the final output. NumPy expressions can remain concise without hiding the logic of the analysis.
Write efficient and reliable array code
Performance & Best Practices
Prefer vectorized operations, choose appropriate data types, minimize unnecessary allocations, validate array shapes, and measure real workloads before optimizing.
| Goal | Prefer | Avoid when possible |
|---|---|---|
| Element-wise calculations | Array operators and ufuncs | Python loops over individual elements |
| Repeated output | Preallocate the final array | Growing an array during every iteration |
| Compatible shapes | Broadcasting | Manually repeating or tiling values |
| Memory efficiency | The smallest safe numeric dtype | Using maximum precision without a reason |
| Temporary results | out and in-place operations when safe |
Creating unnecessary intermediate arrays |
| Subarrays | Views when shared data is acceptable | Copying every slice automatically |
| Performance decisions | Benchmarking representative data | Optimizing from assumptions alone |
| Correct calculations | Explicit axes and shape validation | Relying on implicit dimensional assumptions |
Prefer vectorized operations
values = [
10.0,
20.0,
30.0,
40.0
]
result = []
for value in values:
result.append(
value * 1.25 + 10
)
print(result)
import numpy as np
values = np.array([
10.0,
20.0,
30.0,
40.0
])
result = (
values * 1.25 + 10
)
print(result)
Build calculations from array operators, broadcasting, ufuncs, and
aggregations. Avoid wrapping ordinary Python functions with
np.vectorize() for performance—the wrapper primarily offers
convenience and does not automatically create compiled array code.
Preallocate output arrays
NumPy arrays have a fixed size. Repeatedly appending creates new arrays and copies existing data. Allocate the known final shape once when iteration is unavoidable.
values = np.array([
10.0, 20.0, 30.0, 40.0
])
result = np.empty(
values.shape,
dtype=np.float64
)
for index, value in enumerate(values):
result[index] = (
value * 1.25 + 10
)
print(result)
Use broadcasting instead of repeated data
matrix = np.ones(
(1000, 3)
)
adjustments = np.array([
10.0, 20.0, 30.0
])
repeated = np.tile(
adjustments,
(1000, 1)
)
result = matrix + repeated
matrix = np.ones(
(1000, 3)
)
adjustments = np.array([
10.0, 20.0, 30.0
])
result = (
matrix + adjustments
)
Choose an appropriate data type
Smaller data types can reduce memory use and improve cache efficiency, but they must still represent the complete required value range and precision.
count = 1_000_000
values_64 = np.ones(
count,
dtype=np.float64
)
values_32 = np.ones(
count,
dtype=np.float32
)
print(
"float64 MB:",
values_64.nbytes / 1_000_000
)
print(
"float32 MB:",
values_32.nbytes / 1_000_000
)
float64 MB: 8.0
float32 MB: 4.0
Reuse an output array
Universal functions support an out argument that can reduce
temporary allocations when a correctly shaped output array is available.
values = np.array([
1.0, 4.0, 9.0, 16.0
])
result = np.empty_like(
values
)
np.sqrt(
values,
out=result
)
print(result)
# [1. 2. 3. 4.]
Modify in place only when it is safe
values = np.array([
10.0, 20.0, 30.0
])
scaled = values * 2.0
print(values)
print(scaled)
values = np.array([
10.0, 20.0, 30.0
])
values *= 2.0
print(values)
# [20. 40. 60.]
An array may share memory with slices or views elsewhere in the program. Confirm ownership and intended side effects before modifying values in place.
Work with contiguous arrays when required
Transposed and sliced arrays can have non-contiguous memory layouts. Some external libraries and low-level interfaces require contiguous data.
matrix = np.arange(
12
).reshape(3, 4)
transposed = matrix.T
contiguous = np.ascontiguousarray(
transposed
)
print(
transposed.flags["C_CONTIGUOUS"]
)
print(
contiguous.flags["C_CONTIGUOUS"]
)
Many NumPy operations handle non-contiguous arrays correctly. Create a contiguous copy only when a specific operation, external API, or measured workload benefits from it.
Process large data in chunks
values = np.arange(
1_000_000,
dtype=np.float64
)
chunk_size = 100_000
total = 0.0
for start in range(
0,
values.size,
chunk_size
):
stop = start + chunk_size
chunk = values[start:stop]
total += chunk.sum()
print(total)
Benchmark representative code
import timeit
import numpy as np
values = np.arange(
1_000_000,
dtype=np.float64
)
elapsed = timeit.timeit(
"np.sqrt(values)",
globals={
"np": np,
"values": values
},
number=100
)
print(
"Average seconds:",
elapsed / 100
)
Measure the complete operation with realistic shapes and data types. Keep the clearest correct implementation unless an alternative provides a meaningful improvement for the actual workload.
NumPy best-practice checklist
| Check | Why it matters |
|---|---|
Validate shape and ndim |
Prevents incorrect axes and accidental broadcasting. |
Inspect dtype |
Prevents overflow, truncation, and unnecessary memory use. |
| Check NaN and infinity | Prevents invalid values from silently propagating. |
| Use explicit axis arguments | Makes multidimensional aggregations easier to verify. |
| Know whether data is shared | Prevents unintended modification through views. |
Use allclose() for floats |
Avoids unreliable exact floating-point comparisons. |
| Seed simulations when appropriate | Makes tests and debugging reproducible. |
| Profile representative workloads | Directs optimization toward real bottlenecks. |
Diagnose common array problems
Errors & Troubleshooting
Identify shape mismatches, invalid axes, indexing mistakes, unsafe conversions, floating-point warnings, singular matrices, and installation problems.
| Error or warning | Typical cause | Recommended check |
|---|---|---|
| Operands could not be broadcast | Array dimensions are incompatible. | Compare shapes from right to left. |
| Cannot reshape array | The requested shape has a different element count. | Compare arr.size with the requested dimensions. |
| Index is out of bounds | An index is outside the valid range for an axis. | Inspect arr.shape before indexing. |
| Too many indices | The code uses more indexes than the array has dimensions. | Inspect arr.ndim. |
| AxisError | The requested axis does not exist. | Use an axis from 0 through arr.ndim - 1. |
| UFuncTypeError | An operation cannot safely write its result into the current dtype. | Convert the array to a compatible dtype first. |
| Invalid value encountered | The calculation produced NaN or used an invalid numeric domain. | Check inputs with isfinite() and validate the domain. |
| Divide by zero encountered | A denominator contains zero. | Use a mask or the where argument. |
| Singular matrix | A linear system does not have a unique solution. | Check matrix rank or use least squares when appropriate. |
| ModuleNotFoundError | NumPy is missing from the active Python environment. | Verify the interpreter and install NumPy into that environment. |
Diagnose broadcasting errors
Broadcasting compares dimensions from right to left. Dimensions are compatible when they are equal or when either dimension has a length of one.
import numpy as np
matrix = np.ones(
(3, 4)
)
values = np.array([
10, 20, 30
])
print(matrix.shape)
print(values.shape)
# matrix + values would fail:
# (3, 4) and (3,) are incompatible.
matrix = np.ones(
(3, 4)
)
values = np.array([
10, 20, 30
])
column_values = values[
:,
np.newaxis
]
result = matrix + column_values
print(column_values.shape)
print(result.shape)
Validate a reshape
values = np.arange(12)
requested_shape = (
3, 4
)
requested_size = np.prod(
requested_shape
)
if requested_size != values.size:
raise ValueError(
"Requested shape does not "
"match the array size"
)
reshaped = values.reshape(
requested_shape
)
print(reshaped)
When one dimension can be calculated from the total element count, use
-1, such as arr.reshape(3, -1). Only one
dimension can be inferred.
Prevent indexing errors
values = np.array([
10, 20, 30
])
index = 3
if not -values.size <= index < values.size:
raise IndexError(
f"Index {index} is outside "
f"an array of size {values.size}"
)
selected = values[index]
data = np.array([
10, 20, 30
])
if data.ndim != 2:
raise ValueError(
"Expected a two-dimensional "
f"array, received {data.ndim}D"
)
first_column = data[:, 0]
Catch an invalid axis
values = np.array([
10, 20, 30
])
try:
totals = values.sum(
axis=1
)
except np.exceptions.AxisError as error:
print(
"Invalid axis:",
error
)
print("Dimensions:", values.ndim)
print("Shape:", values.shape)
Fix data type and casting errors
An in-place calculation must write its result back into the existing array’s dtype. Convert the array first when the result requires a different type.
values = np.array([
10, 20, 30
])
# This can raise a casting error:
# values /= 4
print(values.dtype)
# int64 on many systems
values = np.array([
10, 20, 30
])
decimal_values = values.astype(
np.float64
)
decimal_values /= 4
print(decimal_values)
print(decimal_values.dtype)
Turn floating-point warnings into errors
Use np.errstate() to control numeric warnings inside a limited
block without changing behavior throughout the entire application.
numerator = np.array([
10.0, 20.0, 30.0
])
denominator = np.array([
2.0, 0.0, 5.0
])
try:
with np.errstate(
divide="raise",
invalid="raise",
over="raise"
):
result = (
numerator / denominator
)
except FloatingPointError as error:
print(
"Numeric calculation failed:",
error
)
Handle a singular matrix
coefficients = np.array([
[1.0, 2.0],
[2.0, 4.0]
])
results = np.array([
3.0, 6.0
])
try:
solution = np.linalg.solve(
coefficients,
results
)
except np.linalg.LinAlgError:
solution, residuals, rank, singular_values = (
np.linalg.lstsq(
coefficients,
results,
rcond=None
)
)
print("Solution:", solution)
print("Rank:", rank)
A least-squares result is not the same as a unique exact solution. Confirm that returning an approximate or minimum-norm solution is valid for the problem before using it as a fallback.
Diagnose installation and import problems
python --version
python -m pip --version
python -m pip show numpy
python -c "import numpy as np; print(np.__version__)"
python -c "import numpy as np; print(np.__file__)"
A successful installation does not help if the terminal, notebook, or editor is using a different Python environment. Compare the interpreter and package paths when NumPy imports in one place but fails in another.
Debugging checklist
| Check | Example | What it reveals |
|---|---|---|
| Shape | print(arr.shape) |
Dimension lengths and broadcasting compatibility |
| Dimensions | print(arr.ndim) |
How many axes can be indexed or reduced |
| Data type | print(arr.dtype) |
Precision, value range, and casting behavior |
| Element count | print(arr.size) |
Whether a requested reshape is possible |
| Finite values | np.all(np.isfinite(arr)) |
Whether NaN or infinity is present |
| Shared memory | np.shares_memory(a, b) |
Whether modifying one array may affect another |
| Value range | arr.min(), arr.max() |
Unexpected extremes or potential overflow |
| Close equality | np.allclose(actual, expected) |
Floating-point agreement within a tolerance |
Many NumPy bugs become clear after checking shape,
ndim, dtype, and finite values. Start with
array structure before printing a large dataset.
Frequently asked questions
NumPy FAQ
Find concise answers to common questions about NumPy arrays, performance, dimensions, broadcasting, data types, missing values, random numbers, and matrix operations.
What is NumPy used for?
NumPy is used for fast numerical computing with multidimensional arrays. It provides tools for array creation, filtering, broadcasting, statistics, simulations, linear algebra, and file input and output.
What is a NumPy ndarray?
An ndarray is NumPy’s main array object. It stores
values in one or more dimensions and normally uses one shared data
type for every element.
arr = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(type(arr))
print(arr.shape)
How is a NumPy array different from a Python list?
Python lists can contain mixed object types and are designed for general-purpose collections. NumPy arrays usually contain one data type and support efficient vectorized numerical operations, multidimensional indexing, broadcasting, and compact memory storage.
When should I use NumPy instead of pandas?
Use NumPy for dense numerical arrays, matrix calculations, simulations, scientific computing, and operations where labeled rows or columns are unnecessary. Use pandas when tabular data benefits from column names, indexes, mixed data types, joins, grouped operations, or missing-data workflows.
What do axis=0 and axis=1 mean?
The selected axis is the dimension that an operation reduces. In a
two-dimensional array, axis=0 calculates down the rows
and returns one result per column. axis=1 calculates
across the columns and returns one result per row.
column_totals = arr.sum(axis=0)
row_totals = arr.sum(axis=1)
What is NumPy broadcasting?
Broadcasting allows NumPy to calculate with arrays whose shapes are different but compatible. Shapes are compared from right to left, and dimensions are compatible when they are equal or when either dimension has a length of one.
matrix = np.ones((3, 4))
row = np.array([10, 20, 30, 40])
result = matrix + row
What is the difference between a copy and a view?
A view accesses data owned by another array, so modifying the view may modify the original data. A copy has independent storage and can be changed without affecting the source array.
shared = arr[1:4]
independent = arr[1:4].copy()
How do I change a NumPy array’s data type?
Use astype() to create an array with another data type.
Check whether the conversion can lose fractional values, precision,
or values outside the target type’s supported range.
decimal_values = np.array([
1.5, 2.5, 3.5
])
integer_values = decimal_values.astype(
np.int64
)
How does NumPy represent missing values?
Floating-point arrays commonly use np.nan for missing
numeric values. Datetime arrays use NaT. Standard
integer arrays cannot store NaN, so they require a separate mask, a
floating-point conversion, or a masked array.
How do I ignore NaN values in calculations?
Use NaN-aware functions such as np.nanmean(),
np.nansum(), np.nanmedian(), and
np.nanmax(). Only ignore missing observations when doing
so is valid for the analysis.
average = np.nanmean(values)
total = np.nansum(values)
How do I generate repeatable random numbers?
Create a generator with a fixed seed and reuse that generator throughout the workflow. This makes simulations and tests repeatable within the same intended environment.
rng = np.random.default_rng(
seed=42
)
values = rng.random(
size=5
)
What is the difference between * and @ in NumPy?
The * operator performs element-wise multiplication.
The @ operator performs a matrix product using rows and
columns.
element_wise = a * b
matrix_product = a @ b
Why does a NumPy reshape operation fail?
Reshaping fails when the requested dimensions require a different
number of elements. The product of the requested dimensions must
equal arr.size. Use one -1 dimension when
NumPy can infer its length.
matrix = arr.reshape(
3,
-1
)
Why are NumPy floating-point results sometimes inexact?
Many decimal values cannot be represented exactly in binary
floating-point form. Use np.isclose() for individual
values and np.allclose() for arrays instead of relying
on exact equality.
matches = np.allclose(
actual,
expected
)
How do I check which NumPy version is installed?
Import NumPy and print np.__version__. If NumPy cannot
be imported, confirm that it was installed into the same Python
environment used by the terminal, editor, or notebook.
import numpy as np
print(np.__version__)
Inspect shape, ndim, dtype, and
finite values first. These checks reveal the cause of many NumPy errors
before a complete array needs to be printed.
Printable reference
Download the Free NumPy Cheat Sheet PDF
Download the free printable NumPy cheat sheet for quick offline access to array creation, indexing, reshaping, broadcasting, statistics, random numbers, linear algebra, file handling, and troubleshooting.
Free printable reference
NumPy Cheat Sheet PDF
- 19 print-friendly A4 pages
- Copy-ready NumPy examples
- Quick-reference tables
- Practical tips and troubleshooting
- No registration required