cd /news/developer-tools/python-numpy-library · home topics developer-tools article
[ARTICLE · art-85869] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Python NumPy Library

NumPy, the foundational open-source Python library for numerical computation, provides the N-dimensional array (ndarray) and underpins major data-science tools such as Pandas, SciPy, scikit-learn, and TensorFlow. The library offers a broad suite of array creation methods, properties, and manipulation techniques, including reshaping, flattening, and copy-versus-view semantics, as demonstrated in a practical tutorial.

read17 min views1 publishedAug 4, 2026

NumPy (Numerical Python) is a foundational open-source Python library for numerical and mathematical computation.

It introduces the N-dimensional array (ndarray

), a high-performance data structure for storing and manipulating large datasets efficiently. NumPy forms the computational foundation of the Python data-science ecosystem; major libraries such as Pandas, SciPy, scikit-learn, and TensorFlow build directly upon it.

This tutorial is designed to provide a concise yet practical overview of NumPy and to support day-to-day technical work through clear, task-oriented examples.

NumPy provides a broad suite of tools for numerical computation, including:

Using pip

pip install numpy

Using conda

conda install numpy

Using poetry

poetry add numpy

The following test script can be used to confirm that NumPy has been installed correctly.

import numpy as np

print(f"NumPy version: {np.__version__}")
NumPy version: 2.5.1

Ndarray

creation This section demonstrates several standard methods for creating NumPy arrays.

One-dimensional ndarray

arr1d = np.array([1, 2, 3, 4, 5])
print("From list:", arr1d)
From list: [1 2 3 4 5]

Two-dimensional ndarray

arr2d= np.array([[1, 2, 3], [4, 5, 6]])
print("\n2D array:\n", arr2d)
2D array:
 [[1 2 3]
 [4 5 6]]

Zero-filled and one-filled arrays

zeros = np.zeros(5)
print("\nZeros:", zeros)

ones = np.ones((3, 3))
print("\nOnes:\n", ones)
Zeros: [0. 0. 0. 0. 0.]

Ones:
 [[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

Range-based and evenly spaced sequences

range_arr = np.arange(0, 10, 2)
print("\nRange (0 to 10, step 2):", range_arr)

linspace_arr = np.linspace(0, 10, 5)
print("\nLinspace (0 to 10, 5 points):", linspace_arr)
Range (0 to 10, step 2): [0 2 4 6 8]

Linspace (0 to 10, 5 points): [ 0.   2.5  5.   7.5 10. ]

Identity and uninitialised arrays

identity = np.eye(3)
print("\nIdentity matrix:\n", identity)

empty = np.empty((2, 2))
print("\nEmpty array shape:", empty.shape)
Identity matrix:
 [[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

Empty array shape: (2, 2)

Randomly generated arrays

random_arr = np.random.rand(3, 3)
print("\nRandom array (0-1):\n", random_arr)

random_int = np.random.randint(1, 10, size=(2, 3))
print("\nRandom integers (1-10):\n", random_int)
Random array (0-1):
 [[0.55874991 0.81386435 0.31782834]
 [0.39704509 0.89016825 0.82541621]
 [0.10668708 0.15977588 0.65121931]]

Random integers (1-10):
 [[2 2 9]
 [8 4 4]]

Ndarray

properties and attributes Reference array

arr = np.array([[1, 2, 3], [4, 5, 6]])

Array properties

print("Shape:", arr.shape)
print("Dimensions:", arr.ndim)
print("Size (total elements):", arr.size)
print("Data type:", arr.dtype)
print("Item size (bytes):", arr.itemsize)
print("Strides:", arr.strides)
Shape: (2, 3)
Dimensions: 2
Size (total elements): 6
Data type: int64
Item size (bytes): 8
Strides: (24, 8)

Reshaping arrays

reshaped = arr.reshape(3, 2)
print("\nReshaped to (3, 2):\n", reshaped)
Reshaped to (3, 2):
 [[1 2]
 [3 4]
 [5 6]]

Flattening arrays

flattened = arr.flatten()
print("\nFlattened:", flattened)
Flattened: [1 2 3 4 5 6]

Copy versus view

arr_copy = arr.copy()
arr_view = arr.view()
arr_copy[0, 0] = 999
print("\nOriginal:", arr[0, 0])
print("Copy modified:", arr_copy[0, 0])
arr_view[0, 0] = 888
print("Original after view modified:", arr[0, 0])
print("View modified:", arr_view[0, 0])
Original: 1
Copy modified: 999
Original after view modified: 888
View modified: 888

A view is typically faster than creating a copy, but it shares underlying data with the original ndarray. Consequently, modifying values through a view also modifies the original array. Views are particularly useful when adjusting shape or data-type representations without duplicating data.

Changing the shape of a view

original = np.array([10, 20, 30, 40, 50, 60])

matrix_view = original.reshape(2, 3)

print("Original Shape:", original.shape)
print("View Shape:    ", matrix_view.shape)
print("\nOriginal Array:\n", original)
print("\nMatrix View:\n", matrix_view)
Original Shape: (6,)
View Shape:     (2, 3)

Original Array:
 [10 20 30 40 50 60]

Matrix View:
 [[10 20 30]
 [40 50 60]]

Changing the data type representation of a view

original = np.array([10, 20, 30, 40, 50, 60])

matrix_view = original.view(np.int16)

print("Original dtype:", original.dtype)
print("View dtype:    ", matrix_view.dtype)
print("\nOriginal Array:\n", original)
print("\nMatrix View:\n", matrix_view)
Original dtype: int64
View dtype:     int16

Original Array:
 [10 20 30 40 50 60]

Matrix View:
 [10  0  0  0 20  0  0  0 30  0  0  0 40  0  0  0 50  0  0  0 60  0  0  0]

where

conditions Reference arrays

arr = np.arange(20)
arr_2d = np.arange(24).reshape(4, 6)

print("Original 1D:", arr)
print("\n2D array:\n", arr_2d)
Original 1D: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

2D array:
 [[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]

Basic indexing

print("\nElement at index 5:", arr[5])
print("Element at [0, 2]:", arr_2d[0, 2])
print("First row:", arr_2d[0])
print("Last column:", arr_2d[:, -1])
Element at index 5: 5
Element at [0, 2]: 2
First row: [0 1 2 3 4 5]
Last column: [ 5 11 17 23]

Boolean indexing

mask = (arr > 10) & (arr < 15)
print("\nArr > 10 and < 15:", arr[mask])
Arr > 10 and < 15: [11 12 13 14]

Fancy indexing through explicit index selection

indices = [0, 5, 10, 15]
print("arr[[0, 5, 10, 15]]:", arr[indices])
arr[[0, 5, 10, 15]]: [ 0  5 10 15]

Slicing operations

print("\narr[5:10]:", arr[5:10])
print("arr[::2]:", arr[::2])  # Every 2nd element
print("arr[::-1]:", arr[::-1])  # Reversed
arr[5:10]: [5 6 7 8 9]
arr[::2]: [ 0  2  4  6  8 10 12 14 16 18]
arr[::-1]: [19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0]

Two-dimensional slicing

print("\narr_2d[1:3, 2:5]:\n", arr_2d[1:3, 2:5])
print("\narr_2d[:, 1]:", arr_2d[:, 1])  # All rows, column 1
arr_2d[1:3, 2:5]:
 [[ 8  9 10]
 [14 15 16]]

arr_2d[:, 1]: [ 1  7 13 19]

Conditional selection with where

result = np.where(arr > 10, arr, 0)
print("\nWhere arr > 10:", result)
Where arr > 10: [ 0  0  0  0  0  0  0  0  0  0  0 11 12 13 14 15 16 17 18 19]

Unlike Python lists, NumPy applies operations across entire ndarrays.

Reference arrays

a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])

Basic arithmetic operations

print("a + b:", a + b)
print("a - b:", a - b)
print("a * b:", a * b)
print("b / a:", b / a)
print("a ** 2:", a ** 2)
a + b: [11 22 33 44 55]
a - b: [ -9 -18 -27 -36 -45]
a * b: [ 10  40  90 160 250]
b / a: [10. 10. 10. 10. 10.]
a ** 2: [ 1  4  9 16 25]

Universal functions

print("\nSquare root:", np.sqrt(a))
print("Absolute value:", np.abs(np.array([-1, -2, 3])))
print("Exponential:", np.exp(np.array([1, 2, 3])))
print("Logarithm:", np.log(np.array([1, 2.718, 10])))
Square root: [1.         1.41421356 1.73205081 2.         2.23606798]
Absolute value: [1 2 3]
Exponential: [ 2.71828183  7.3890561  20.08553692]
Logarithm: [0.         0.99989632 2.30258509]

Trigonometric functions

angles = np.array([0, np.pi/4, np.pi/2, np.pi])

print("\nSine:", np.sin(angles))
print("Cosine:", np.cos(angles))
print("Tangent:", np.tan(angles))
Sine: [0.00000000e+00 7.07106781e-01 1.00000000e+00 1.22464680e-16]
Cosine: [ 1.00000000e+00  7.07106781e-01  6.12323400e-17 -1.00000000e+00]
Tangent: [ 0.00000000e+00  1.00000000e+00  1.63312394e+16 -1.22464680e-16]

Rounding functions

decimals = np.array([1.234, 5.678, 2.567])

print("\nCeiling:", np.ceil(decimals))
print("Floor:", np.floor(decimals))
print("Round:", np.round(decimals, 2))
Ceiling: [2. 6. 3.]
Floor: [1. 5. 2.]
Round: [1.23 5.68 2.57]

Reference arrays

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
arr_2d = np.arange(1, 13).reshape(3, 4)

Basic descriptive statistics

print("Sum:", np.sum(arr))
print("Mean:", np.mean(arr))
print("Median:", np.median(arr))
print("Std Dev:", np.std(arr))
print("Variance:", np.var(arr))
Sum: 55
Mean: 5.5
Median: 5.5
Std Dev: 2.8722813232690143
Variance: 8.25

Axis-wise statistics (2D example)

print("\n2D array:\n", arr_2d)
print("\nSum along axis 0 (columns):", np.sum(arr_2d, axis=0))
print("Sum along axis 1 (rows):", np.sum(arr_2d, axis=1))
print("Mean along axis 0:", np.mean(arr_2d, axis=0))
print("Mean along axis 1:", np.mean(arr_2d, axis=1))
2D array:
 [[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]

Sum along axis 0 (columns): [15 18 21 24]
Sum along axis 1 (rows): [10 26 42]
Mean along axis 0: [5. 6. 7. 8.]
Mean along axis 1: [ 2.5  6.5 10.5]

Minimum and maximum functions

print("\nMin:", np.min(arr))
print("Max:", np.max(arr))
print("Argmin (index):", np.argmin(arr))
print("Argmax (index):", np.argmax(arr))
Min: 1
Max: 10
Argmin (index): 0
Argmax (index): 9

Percentiles

print("\n25th percentile:", np.percentile(arr, 25))
print("50th percentile (median):", np.percentile(arr, 50))
print("75th percentile:", np.percentile(arr, 75))
25th percentile: 3.25
50th percentile (median): 5.5
75th percentile: 7.75

Cumulative operations

print("\nCumulative sum:", np.cumsum(arr[:5]))
print("Cumulative product:", np.cumprod(np.array([1, 2, 3, 4])))
print("Cumulative max:", np.maximum.accumulate(np.array([1, 3, 2, 5, 4])))
Cumulative sum: [ 1  3  6 10 15]
Cumulative product: [ 1  2  6 24]
Cumulative max: [1 3 3 5 5]

Reference arrays

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([[6, 7, 8, ], [9, 10, 11]])

Concatenation

concat = np.concatenate([a, b])
print("Concatenate:", concat)
Concatenate: [1 2 3 4 5 6]

stack

, hstack

, and vstack

stacked = np.stack([a, b])
print("\nStack:\n", stacked)

hstacked = np.hstack([a, b])
print("\nHStack:", hstacked)

vstacked = np.vstack([[a], [b]])
print("\nVStack:\n", vstacked)
Stack:
 [[1 2 3]
 [4 5 6]]

HStack: [1 2 3 4 5 6]

VStack:
 [[1 2 3]
 [4 5 6]]

Splitting arrays

arr = np.arange(10)
split_result = np.array_split(arr, 3)
print("\nArray_split into 3 parts:")
for i, part in enumerate(split_result):
    print(f"  Part {i}: {part}")
Array_split into 3 parts:
  Part 0: [0 1 2 3]
  Part 1: [4 5 6]
  Part 2: [7 8 9]

Transposition

print("\nOriginal:\n", c)
print("Transposed:\n", c.T)
Original:
 [[ 6  7  8]
 [ 9 10 11]]
Transposed:
 [[ 6  9]
 [ 7 10]
 [ 8 11]]

Unique values

arr_with_dupes = np.array([1, 2, 2, 3, 3, 3, 4])
print("\nUnique values:", np.unique(arr_with_dupes))
Unique values: [1 2 3 4]

Sorting

arr_unsorted = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print("Sorted:", np.sort(arr_unsorted))
print("Argsort (indices):", np.argsort(arr_unsorted))
Sorted: [1 1 2 3 4 5 6 9]
Argsort (indices): [1 3 6 0 2 4 7 5]

Dot product and matrix multiplication

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

dot_product = np.dot(a, b)
print("Dot product:", dot_product)  # 1*4 + 2*5 + 3*6 = 32

mat_a = np.array([[1, 5], [3, 4]])
mat_b = np.array([[5, 6], [7, 8]])

matrix_product = np.dot(mat_a, mat_b)
print("\nMatrix product:\n", matrix_product)

matrix_product_operator = mat_a @ mat_b
print("\nMatrix product using @ operator:\n", matrix_product_operator)
Dot product: 32

Matrix product:
 [[40 46]
 [43 50]]

Matrix product using @ operator:
 [[40 46]
 [43 50]]

Trace (sum of diagonal elements)

print("Trace:", np.trace(mat_a))
Trace: 5

linalg

: linear algebra submodule

det = np.linalg.det(mat_a)
print("\nDeterminant:", det)

inv = np.linalg.inv(mat_a)
print("\nInverse:\n", inv)

eigenvalues, eigenvectors = np.linalg.eig(mat_a)
print("\nEigenvalues:", eigenvalues)
print("Eigenvectors:\n", eigenvectors)

print("\nRank:", np.linalg.matrix_rank(mat_a))

print("\nNorm (default):", np.linalg.norm(a))
print("Norm (L2):", np.linalg.norm(a, ord=2))
print("Norm (L1):", np.linalg.norm(a, ord=1))
Determinant: -11.000000000000002

Inverse:
 [[-0.36363636  0.45454545]
 [ 0.27272727 -0.09090909]]

Eigenvalues: [-1.65331193+0.j  6.65331193+0.j]
Eigenvectors:
 [[-0.88333068+0.j -0.66249905+0.j]
 [ 0.46875037+0.j -0.74906275+0.j]]

Rank: 2

Norm (default): 3.7416573867739413
Norm (L2): 3.7416573867739413
Norm (L1): 6.0

Broadcasting enables operations on ndarrays with different shapes, provided that their dimensions are compatible.

Array and scalar broadcasting

arr = np.array([1, 2, 3, 4, 5])
result = arr + 10
print("Array + scalar:", result)
Array + scalar: [11 12 13 14 15]

One-dimensional and two-dimensional ndarray broadcasting

arr_1d = np.array([1, 2, 3])
arr_2d = np.array([[10], [20], [30]])

result = arr_1d + arr_2d
print("\n1D + 2D (broadcasting):")
print("Shape (3,) + (3, 1) = (3, 3)")
print(result)
1D + 2D (broadcasting):
Shape (3,) + (3, 1) = (3, 3)
[[11 12 13]
 [21 22 23]
 [31 32 33]]

Operations across dimensions

matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
column = np.array([10, 20, 30])

print("\nSubtract column from matrix:")
print(matrix - column)
Subtract column from matrix:
[[ -9 -18 -27]
 [ -6 -15 -24]
 [ -3 -12 -21]]

Broadcasting rules:

Broadcasting rules: examples

Shape (5,) broadcasts with (3, 5) -> (3, 5)
Shape (3, 1) broadcasts with (3, 4) -> (3, 4)
Shape (1, 5) broadcasts with (3, 5) -> (3, 5)

Set seed for reproducibility

np.random.seed(1000)  # For reproducibility

Uniform distribution on [0, 1)

uniform = np.random.rand(5)
print("Uniform [0, 1):", uniform)
Uniform [0, 1): [0.65358959 0.11500694 0.95028286 0.4821914  0.87247454]

Random integers

ints = np.random.randint(1, 10, size=5)
print("Random integers [1, 10):", ints)
Random integers [1, 10): [9 5 5 5 3]

Normal (Gaussian) distribution

normal = np.random.randn(5)
print("Normal distribution:", normal)
Normal distribution: [ 0.57363145 -0.74841131 -0.4122031  -0.07400906 -0.92893693]

Normal distribution with custom mean and standard deviation

custom_normal = np.random.normal(loc=100, scale=15, size=5)
print("\nNormal (μ=100, σ=15):", custom_normal)
Normal (μ=100, σ=15): [120.85092205 117.92603993 110.61013587 114.8944316  102.09195908]

Exponential distribution

exponential = np.random.exponential(scale=2.0, size=5)
print("Exponential (λ=0.5):", exponential)
Exponential (λ=0.5): [4.69093541 0.02095277 0.1549649  0.56109307 0.28613573]

Random choice from an ndarray

arr = np.arange(10)
choices = np.random.choice(arr, size=5, replace=False)
print("\nRandom choice (no replace):", choices)
Random choice (no replace): [4 2 8 0 3]

In-place shuffling

arr = np.arange(10)
np.random.shuffle(arr)
print("Shuffled:", arr)
Shuffled: [4 9 5 1 3 6 2 0 8 7]

Shuffling with a copied permutation

arr = np.arange(10)
shuffled = np.random.permutation(arr)
print("Permutation:", shuffled)
Permutation: [2 8 7 1 4 0 5 6 9 3]

Binomial distribution

binomial = np.random.binomial(n=10, p=0.5, size=5)
print("\nBinomial (n=10, p=0.5):", binomial)
Binomial (n=10, p=0.5): [6 4 5 5 5]

Setup code

import os
import tempfile
import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

temp_dir = tempfile.mkdtemp()

print(f"original ndarray: {arr}")
print(f"Temporary directory created at: {temp_dir}")
original ndarray: [[1 2 3]
 [4 5 6]
 [7 8 9]]
Temporary directory created at: /tmp/tmpr0nzny9i

Save in .npy format (binary)

npy_path = os.path.join(temp_dir, 'array.npy')
np.save(npy_path, arr)
print(f"Saved .npy file to {npy_path}")
Saved .npy file to /tmp/tmpr0nzny9i/array.npy

Load .npy file

loaded_arr = np.load(npy_path)
print("Loaded from .npy:\n", loaded_arr)
Loaded from .npy:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]

Save multiple arrays as .npz (compressed)

npz_path = os.path.join(temp_dir, 'arrays.npz')
arr2 = np.array([10, 20, 30, 40])
np.savez(npz_path, array1=arr, array2=arr2)
print(f"\nSaved .npz file to {npz_path}")
Saved .npz file to /tmp/tmpr0nzny9i/arrays.npz

Load .npz file

loaded = np.load(npz_path)
print("Loaded from .npz:")
print("  array1:\n", loaded['array1'])
print("  array2:", loaded['array2'])
Loaded from .npz:
  array1:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
  array2: [10 20 30 40]

Save as a text file (CSV-like format)

txt_path = os.path.join(temp_dir, 'array.txt')
np.savetxt(txt_path, arr, delimiter=',', fmt='%d')
print(f"\nSaved text file to {txt_path}")
Saved text file to /tmp/tmpr0nzny9i/array.txt

Load from a text file

loaded_txt = np.loadtxt(txt_path, delimiter=',')
print("Loaded from text:\n", loaded_txt)
Loaded from text:
 [[1. 2. 3.]
 [4. 5. 6.]
 [7. 8. 9.]]

Apply a function to each element

arr = np.array([1, 2, 3, 4, 5])
squared = np.vectorize(lambda x: x**2)(arr)
print("Vectorized function (square):", squared)
Vectorized function (square): [ 1  4  9 16 25]

Piecewise operations

arr = np.array([1, 2, 3, 4, 5])
result = np.piecewise(arr, [arr < 3, arr >= 3], [lambda x: x**2, lambda x: x*10])
print("\nPiecewise (x<3: x², x≥3: 10x):", result)
Piecewise (x<3: x², x≥3: 10x): [ 1  4 30 40 50]

Apply operations along an axis

matrix = np.array([[1, 2, 3], [4, 5, 6]])
sums0 = np.apply_along_axis(np.sum, axis=0, arr=matrix)
sums1 = np.apply_along_axis(np.sum, axis=1, arr=matrix)

print("\nApply sum along axis 0:", sums0)
print("Apply sum along axis 1:", sums1)
Apply sum along axis 0: [5 7 9]
Apply sum along axis 1: [ 6 15]

Repeat and tile

arr = np.array([1, 2, 3])
print("\nRepeat (each element 2 times):", np.repeat(arr, 2))
print("Tile (whole array 2 times):", np.tile(arr, 2))
Repeat (each element 2 times): [1 1 2 2 3 3]
Tile (whole array 2 times): [1 2 3 1 2 3]

Reduction operations

arr = np.array([1, 2, 3, 4, 5])
result = np.add.reduce(arr)  # Sum
print("\nReduce with add (sum):", result)
Reduce with add (sum): 15

searchsorted

(binary search)

sorted_arr = np.array([1, 3, 5, 7, 9])
indices = np.searchsorted(sorted_arr, [2, 4, 6, 8])
print("\nSearchsorted indices:", indices)
Searchsorted indices: [1 2 3 4]

Extract diagonal elements

matrix = np.arange(9).reshape(3, 3)
diagonal0 = np.diag(matrix, k=0)  # Main diagonal
diagonal1 = np.diag(matrix, k=1)  # Diagonal above main
print("\nDiagonal of matrix:\n", matrix)
print("Diagonal elements:", diagonal0)
print("Diagonal above main:", diagonal1)
Diagonal of matrix:
 [[0 1 2]
 [3 4 5]
 [6 7 8]]
Diagonal elements: [0 4 8]
Diagonal above main: [1 5]

Create a diagonal matrix

diag_matrix = np.diag([1, 2, 3])
print("\nDiagonal matrix from [1, 2, 3]:\n", diag_matrix)
Diagonal matrix from [1, 2, 3]:
 [[1 0 0]
 [0 2 0]
 [0 0 3]]

Count and display non-zero values

arr = np.array([0, 1, 0, 2, 3, 0])
print("\nNonzero count:", np.count_nonzero(arr))
print("Nonzero indices:", np.nonzero(arr))
Nonzero count: 3
Nonzero indices: (array([1, 3, 4]),)

Setup code

import time

Avoid Python loops by using vectorisation

arr = np.arange(1_000_000)

start = time.time()
result = np.array([x**2 for x in arr])
loop_time = time.time() - start
print(f"Python loop: {loop_time:.6f} seconds")

start = time.time()
result = arr ** 2
vectorized_time = time.time() - start
print(f"NumPy vectorized: {vectorized_time:.6f} seconds")
print(f"Speedup: {loop_time/vectorized_time:.1f}x faster\n")
=== Performance: Vectorization ===
Python loop: 0.238411 seconds
NumPy vectorized: 0.001625 seconds
Speedup: 146.7x faster

Use in-place operations where appropriate

arr = np.arange(5)
print("Original:", arr)
arr += 10  # In-place (more memory efficient)
print("After += 10:", arr)
Original: [0 1 2 3 4]
After += 10: [10 11 12 13 14]

Data types and memory usage

arr_float64 = np.arange(1000, dtype=np.float64)
arr_float32 = np.arange(1000, dtype=np.float32)
arr_int32 = np.arange(1000, dtype=np.int32)

print(f"Float64: {arr_float64.nbytes} bytes")
print(f"Float32: {arr_float32.nbytes} bytes")
print(f"Int32: {arr_int32.nbytes} bytes")
Float64: 8000 bytes
Float32: 4000 bytes
Int32: 4000 bytes

Memory efficiency: views versus copies

original = np.arange(10)
view = original[:]  # This is a view, shares memory
copy = original[:].copy()  # This is a copy

print(f"View shares memory: {view.base is original}")
print(f"Copy doesn't share memory: {copy.base is original}")
View shares memory: True
Copy doesn't share memory: False

Useful diagnostics for debugging

arr = np.random.randn(3, 4, 5)

print(f"Shape: {arr.shape}")
print(f"Ndim: {arr.ndim}")
print(f"Dtype: {arr.dtype}")
print(f"Size: {arr.size}")
print(f"Memory: {arr.nbytes} bytes")
Shape: (3, 4, 5)
Ndim: 3
Dtype: float64
Size: 60
Memory: 480 bytes

Check for NaN and infinite values

arr = np.array([1, 2, np.nan, 4, np.inf, -np.inf])

print(f"Array: {arr}")
print(f"Has NaN: {np.isnan(arr).any()}")
print(f"Has Inf: {np.isinf(arr).any()}")
print(f"Is finite: {np.isfinite(arr)}")
Array: [  1.   2.  nan   4.  inf -inf]
Has NaN: True
Has Inf: True
Is finite: [ True  True False  True False False]

Type casting

arr = np.array([1.5, 2.7, 3.2])

print(f"Original (float): {arr}")
print(f"As int: {arr.astype(int)}")
print(f"As str: {arr.astype(str)}")
Original (float): [1.5 2.7 3.2]
As int: [1 2 3]
As str: ['1.5' '2.7' '3.2']

NumPy is a core component of scientific computing in Python. This tutorial has outlined how NumPy arrays differ from Python lists, how they can be created and inspected, and how indexing, arithmetic, and statistical operations can be performed efficiently.

A key strength of NumPy lies in its speed and vectorised programming model. Rather than relying on explicit Python loops for every calculation, practitioners can apply concise operations to complete datasets. This capability makes NumPy an essential tool for data analysis, machine learning, and numerical modelling.

To develop proficiency, practise regularly with arrays of different shapes, slicing patterns, and reshaping strategies. Comparing NumPy workflows with equivalent pure-Python approaches is particularly useful for understanding performance and expressiveness benefits. These foundations also support more advanced work with libraries such as Pandas, SciPy, and TensorFlow.

This article has presented a concise, practice-oriented reference for fundamental NumPy workflows. For continued development, readers are encouraged to extend these examples to domain-specific datasets and to evaluate computational trade-offs in realistic analytical pipelines.

Did this article help you? Let me know in the comments below, and don't forget to drop a like if you enjoyed the read! Thank you.

── more in #developer-tools 4 stories · sorted by recency
── more on @numpy 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/python-numpy-library] indexed:0 read:17min 2026-08-04 ·