NumPy Basics
A practical overview of NumPy's core object — the ndarray — and the operations you'll use
constantly.
import numpy as np
1. The ndarray
NumPy's main object is the homogeneous multidimensional array: a grid of elements
(usually numbers), all the same type, indexed by non-negative integers. In NumPy,
dimensions are called axes.
Key attributes:
Attribute Meaning
ndim number of axes (dimensions)
shape tuple of the array's size in each dimension, e.g. (3, 5)
size total number of elements (product of shape )
dtype the type of the elements ( int64 , float64 , etc.)
itemsize size in bytes of each element
a = [Link](15).reshape(3, 5)
[Link] # (3, 5)
[Link] # 2
[Link] # dtype('int64')
[Link] # 15
2. Creating arrays
From a Python list/tuple — the dtype is inferred:
a = [Link]([2, 3, 4]) # int64
b = [Link]([1.2, 3.5, 5.1]) # float64
⚠️ Pass a single sequence, not multiple arguments: [Link]([1,2,3]) , not
[Link](1,2,3) .
Nested sequences become 2D, 3D, etc.:
b = [Link]([(1.5, 2, 3), (4, 5, 6)]) # shape (2, 3)
Placeholder arrays (default dtype float64 ):
[Link]((3, 4))
[Link]((2, 3, 4), dtype=np.int16)
[Link]((2, 3)) # uninitialized — contents are whatever's in memory
Sequences of numbers:
[Link](10, 30, 5) # [10, 15, 20, 25] — like range(), but returns
an array
[Link](0, 2, 0.3) # accepts floats, but count is unpredictable
with float steps
[Link](0, 2, 9) # 9 evenly spaced numbers from 0 to 2 — safer
than arange for floats
3. Basic operations
Arithmetic on arrays is elementwise and returns a new array:
a = [Link]([20, 30, 40, 50])
b = [Link](4)
a - b # [20, 29, 38, 47]
b ** 2 # [0, 1, 4, 9]
a < 35 # [True, True, False, False]
* is elementwise, not matrix multiplication. For matrix product, use @ or .dot() :
A @ B # matrix product
[Link](B) # same thing
A * B # elementwise product — different result
In-place operators ( += , *= ) modify the existing array rather than creating a new one.
Reductions are methods on the array:
[Link]()
[Link]()
[Link]()
Use axis to apply along a specific dimension of a multi-dimensional array:
[Link](axis=0) # sum of each column
[Link](axis=1) # min of each row
[Link](axis=1) # running total along each row
Universal functions (ufuncs) operate elementwise and work across the whole array — no
loops needed:
[Link](b)
[Link](b)
[Link](b, c)
This is the "avoid for-loops" mindset that makes NumPy fast.
4. Indexing, slicing, iterating
1D arrays behave like Python lists:
a = [Link](10) ** 3
a[2] # 8
a[2:5] # [8, 27, 64]
a[::-1] # reversed
Multidimensional arrays take one index per axis, as a comma-separated tuple:
b[2, 3] # single element
b[0:5, 1] # column 1, all rows
b[:, 1] # same thing
b[-1] # last row (equivalent to b[-1, :])
Missing indices are treated as complete slices ( : ), and ... fills in as many : as needed
for the remaining axes — handy for high-dimensional arrays: x[..., 3] is the same as
x[:, :, :, :, 3] .
Iterating over a multidimensional array steps through the first axis (i.e., row by row). To
iterate over every individual element instead, use .flat :
for row in b:
print(row)
for element in [Link]:
print(element)
5. Shape manipulation
These return a modified array without changing the original:
[Link]() # flatten to 1D
[Link](6, 2) # new shape, same data
a.T # transpose
[Link](...) , by contrast, modifies the array in place.
Use -1 in reshape to auto-calculate one dimension:
[Link](3, -1)
Stacking arrays together:
[Link]((a, b)) # stack vertically (rows)
[Link]((a, b)) # stack horizontally (columns)
np.column_stack((a, b)) # stack 1D arrays as columns of a 2D array
Splitting arrays apart:
[Link](a, 3) # split into 3 equal pieces
[Link](a, (3, 4)) # split after columns 3 and 4
[Link](a, 3) # split vertically
6. Copies vs. views — the #1 source of beginner bugs
There are three levels:
No copy at all — plain assignment just gives two names for the same object:
b = a
b is a # True
View (shallow copy) — a new array object, but it shares the underlying data. Slicing returns
a view:
c = [Link]()
[Link] is a # True — c looks at a's data
s = a[:, 1:3]
s[:] = 10 # this changes `a` too!
Deep copy — an independent array with its own data:
d = [Link]()
d[0, 0] = 9999 # a is unaffected
Rule of thumb: if you slice a huge array and only need a small piece long-term, call
.copy() on the slice — otherwise the entire original array stays in memory as long as your
slice exists.
7. Broadcasting (short version)
Broadcasting lets NumPy apply operations to arrays of different shapes without writing loops:
1. If arrays have different numbers of dimensions, 1 s are prepended to the smaller shape
until dimensions match.
2. An axis of size 1 is treated as if it were stretched to match the other array's size along
that axis.
3. After that, shapes must match exactly.
This is why a + 5 works (the scalar is broadcast to every element), and why a (3,4) array
can be added to a (4,) array.
8. Advanced indexing (brief)
Indexing with arrays of integers — pick out arbitrary elements:
a = [Link](12) ** 2
i = [Link]([1, 1, 3, 8, 5])
a[i] # elements at those positions
Indexing with boolean arrays — select elements matching a condition:
b = a > 4
a[b] # 1D array of the selected elements
a[b] = 0 # set all matching elements to 0
This boolean-mask pattern ( arr[arr > threshold] ) is one of the most common things
you'll do in NumPy.
9. Sorting, argmax/argsort, and length
Length / size of an array:
len(a) # size of the first axis only (like len() on a list of rows)
[Link][0] # same thing, more explicit
[Link] # total number of elements across all axes
Finding the max/min value:
a = [Link]([3, 1, 4, 1, 5, 9, 2, 6])
[Link]() # 9 — the value
[Link]() # 5 — the index where that value occurs
[Link]()
[Link]()
With a multidimensional array, axis controls whether you're finding the max per row, per
column, or overall:
b = [Link]([[1, 7, 2],
[9, 3, 5]])
[Link](axis=0) # index of max in each column -> [1, 0, 1]
[Link](axis=1) # index of max in each row -> [1, 0]
Note: argmax / argmin return the index of the first occurrence if there's a tie.
Sorting values:
a = [Link]([3, 1, 4, 1, 5, 9, 2, 6])
[Link](a) # [1, 1, 2, 3, 4, 5, 6, 9] — new sorted array
[Link]() # sorts `a` in place, returns None
For 2D arrays, axis again controls the direction (default is the last axis, i.e. sorting each
row independently):
b = [Link]([[3, 1], [2, 4]])
[Link](b, axis=0) # sort each column
[Link](b, axis=1) # sort each row
argsort — getting the indices that would sort an array. This is often more useful than
sort itself, because it lets you reorder one array based on the order of another:
a = [Link]([30, 10, 20])
idx = [Link](a) # [1, 2, 0] <- indices that put a in ascending
order
a[idx] # [10, 20, 30] — a, sorted
names = [Link](['charlie', 'alice', 'bob'])
scores = [Link]([70, 95, 88])
order = [Link](scores)[::-1] # descending order of scores
names[order] # ['alice', 'bob', 'charlie'] — names
ranked by score
10. Random permutations
NumPy's recommended random API uses a Generator object ( [Link].default_rng )
rather than the older global [Link]() style:
rng = [Link].default_rng(42) # 42 = seed, for reproducibility
permutation returns a shuffled copy — the original array is untouched:
a = [Link](10)
shuffled = [Link](a)
a # unchanged
shuffled # a new, randomly reordered array
You can also call it with just an integer, which shuffles [Link](n) — handy for
generating random indices:
idx = [Link](10) # a random ordering of 0..9
This is a common pattern for shuffling multiple related arrays together (e.g. features and
labels in the same random order):
X = [Link](20).reshape(10, 2)
y = [Link](10)
idx = [Link](len(X))
X_shuffled = X[idx]
y_shuffled = y[idx] # X and y stay aligned with each other
shuffle does the same thing but in place, and only shuffles along the first axis:
a = [Link](10)
[Link](a) # a is now reordered; nothing is returned
Rule of thumb: use permutation when you want a new shuffled array (or a reusable index
for shuffling several arrays consistently); use shuffle when you're fine modifying the array
directly and don't need the original order back.
Quick reference: useful functions by category
Creation: arange , array , zeros , ones , empty , linspace , eye , fromfunction
Manipulation: reshape , ravel , transpose , concatenate , stack , hstack , vstack ,
hsplit , vsplit
Questions: all , any , where , nonzero
Ordering: sort , argsort , argmax , argmin , max , min
Stats: mean , std , var , cov
Linear algebra: dot , @ , cross , outer , [Link]
Random: default_rng , [Link] , [Link] , [Link] , [Link]
Further reading
NumPy quickstart (official)
NumPy: the absolute basics for beginners
Broadcasting
Copies and views