NumPy is the core library for numerical computing in Python. Its ndarray stores numbers of one type in a compact block and runs maths on whole arrays at once in fast compiled code, so prices * 1.2 updates a million values without a Python loop. pandas, Matplotlib, scikit-learn and most scientific packages are built on it.
import numpy as np
counts = np.array([[8, 10], [12, 6]]) # 2 rows, 2 columns
print(counts.shape) # (2, 2)
print(counts.sum(axis=1)) # [18 18]: total of each rowInstall it with python -m pip install numpy. This tutorial covers array shape and type, vectorized operations, the axis argument, boolean filtering and broadcasting, and when slices share data. The examples analyse passenger counts on a ferry service: rows are days, columns are departures. If you're coming from lists, read arrays in Python first.
Array shape
Create arrays from lists or with helper functions:
import numpy as np
passengers = np.array([[8, 10, 7], [12, 6, 9]])
print(passengers.shape, passengers.ndim, passengers.size, passengers.dtype)
# (2, 3) 2 6 int64
zeros = np.zeros((3, 4)) # 3 rows of 4 zeros
steps = np.arange(0, 10, 2) # [0 2 4 6 8]
grid = np.arange(6).reshape(2, 3) # [[0 1 2] [3 4 5]]shapegives the size of each dimension as a tuple:(rows, columns)for a 2-D array.dtypeis the element type. Every element has the same one, chosen from the data (int64,float64,bool...). The default integer size can differ by platform.reshape()changes the shape without changing the data; the total size must stay the same.
Index 2-D arrays with array[row, column]: passengers[1, 0] is 12, passengers[0] is the first row and passengers[:, 2] is the third column. Operations between arrays need compatible shapes; combining a (2, 3) array with a (2, 2) one raises ValueError: operands could not be broadcast together.
Vectorization
Arithmetic and comparisons apply element by element to whole arrays:
fares = np.array([4.5, 4.5, 6.0])
print(fares * 1.1) # [4.95 4.95 6.6 ]
print(passengers * fares) # revenue per departure, per day
print(passengers > 8) # boolean array of the same shapeThis is called vectorization. The loop still happens, but inside NumPy's compiled code rather than in Python, which is typically tens to hundreds of times faster for large arrays. Functions such as np.sqrt, np.round, np.maximum and np.where work the same way.
Vectorization changes how you write the calculation, not what it means. passengers * fares gives the same numbers as a nested loop multiplying each count by its fare; it's just shorter and faster. If a calculation is hard to express with array operations, a clear loop is still acceptable for small data.
A boolean array can filter another array: passengers[passengers > 8] returns [10 12 9], the counts above 8. Combine conditions with & and |, with brackets: passengers[(passengers > 6) & (passengers < 10)].
Axes
Reductions such as sum, mean, min and max can work on the whole array or along an axis:
passengers = np.array([[8, 10, 7],
[12, 6, 9]])
print(passengers.sum()) # 52: everything
print(passengers.sum(axis=0)) # [20 16 16]: down each column
print(passengers.sum(axis=1)) # [25 27]: across each rowaxis=0 collapses the rows, giving one result per column; axis=1 collapses the columns, giving one result per row. A helpful way to remember it: the axis you name is the one that disappears from the shape. (2, 3) summed over axis 0 becomes shape (3,).
Mixing up the axis produces numbers that look plausible but answer the wrong question, such as totals per departure when you wanted totals per day. Check the result's shape, and label results clearly when you print or chart them.
Views and copies
Basic slicing returns a view: a new array object that shares the original's data. Changing the view changes the original:
week = np.array([8, 10, 7, 12, 6])
first_three = week[:3] # a view
first_three[0] = 99
print(week) # [99 10 7 12 6]
safe = week[:3].copy() # independent data
safe[0] = 0
print(week[0]) # still 99Views make slicing fast and memory-efficient, but surprise people used to lists, where slicing copies. Call .copy() when you need to modify a part without affecting the whole.
Fancy indexing, with a list of positions or a boolean mask such as week[[0, 2]] or week[week > 8], always returns a copy. reshape() returns a view when it can. np.shares_memory(a, b) tells you whether two arrays share data, which is handy while learning.
Independent practice and a worked check
Task: analyse a passenger grid with shape checks, row and column reductions and a view versus copy comparison.
import numpy as np
grid = np.array([[5, 9, 4, 8], [7, 3, 6, 10]])
assert grid.shape == (2, 4)
print("Day totals:", grid.sum(axis=1)) # [26 26]
print("Departure totals:", grid.sum(axis=0)) # [12 12 10 18]
print("Busiest departure:", grid.sum(axis=0).argmax()) # 3
view = grid[0]
copy = grid[0].copy()
view[0] = 0
print(grid[0, 0], copy[0]) # 0 5argmax() returns the position of the largest value. Changing the view altered grid, while the copy kept the original 5. For the worked check, axis=1 sums the values within each row, producing one total per row.
Reference and next practice
The official NumPy absolute beginner's guide and broadcasting guide go further. Continue with the pandas tutorial and Matplotlib tutorial. Practise with the Python exercises and Python quiz.
NumPy Tutorial Summary
- Array shape: ndarray stores numeric values with a shape and dtype.
- Vectorization: Array operations apply compiled elementwise work rather than requiring an explicit Python loop.
- Axes: Axis arguments select the dimensions along which reductions operate.
- Views and copies: Basic slicing often returns a view sharing storage, while copy makes independent array data.
NumPy Tutorial Examples
Each example shows the code, the output it produces and what each part does.
NumPy Tutorial in a small program
import numpy as np
counts = np.array([[8, 10], [12, 6]])
print(counts.sum(axis=1).tolist())
print((counts + 1).tolist())[18, 18]
[[9, 11], [13, 7]]Line-by-line explanation
- Line 2A 2 by 2 array: two days, two departures.
- Line 3
axis=1sums across each row: 8 + 10 and 12 + 6. - Line 4Adding 1 applies to every element;
tolist()converts back to Python lists for display.
Analysing a week of departures
import numpy as np
passengers = np.array([
[8, 10, 7],
[12, 6, 9],
[15, 14, 11],
])
fares = np.array([4.5, 4.5, 6.0])
days = np.array(["Mon", "Tue", "Wed"])
print("Shape:", passengers.shape)
print("Per day:", passengers.sum(axis=1).tolist())
print("Per departure:", passengers.mean(axis=0).round(1).tolist())
revenue = passengers * fares
print("Revenue per day:", revenue.sum(axis=1).tolist())
print("Busy days:", days[passengers.sum(axis=1) > 30].tolist())
print("Above 10:", passengers[passengers > 10].tolist())Shape: (3, 3)
Per day: [25, 27, 40]
Per departure: [11.7, 10.0, 9.0]
Revenue per day: [123.0, 135.0, 196.5]
Busy days: ['Wed']
Above 10: [12, 15, 14, 11]Line-by-line explanation
- Line 13
mean(axis=0)averages each column, giving one value per departure. - Line 14
fareshas shape (3,), so it's broadcast across every row: each column is multiplied by its fare. - Line 16A boolean array built from the row totals selects the matching day names.
Common NumPy Tutorial Mistakes
Using the wrong axis
passengers = np.array([[8, 10, 7], [12, 6, 9]])
daily = passengers.sum(axis=0) # [20 16 16]: these are per-departure totalsdaily = passengers.sum(axis=1) # [25 27]: one total per day (row)The named axis is the one that collapses. Check the result's shape matches what you expect.
Modifying a slice and changing the original
week = np.array([8, 10, 7])
adjusted = week[:2]
adjusted -= 1 # week is now [7 9 7]week = np.array([8, 10, 7])
adjusted = week[:2].copy()
adjusted -= 1 # week is unchangedNumPy Tutorial in Real Projects
NumPy in practice
NumPy is everywhere numbers are processed in Python: image processing treats pictures as 3-D arrays of pixel values, finance calculates returns over price arrays, physics and engineering simulate systems on grids, and machine learning stores features and model weights as arrays. pandas DataFrames hold their numeric columns as NumPy arrays, and scikit-learn models take arrays as input.
Typical workflows load data, check its shape and dtype, clean or filter with boolean masks, and summarise along axes, exactly the operations in this lesson. Performance-sensitive code avoids Python loops over elements entirely in favour of vectorized expressions.
NumPy Tutorial FAQ
What is the difference between a NumPy array and a Python list?
A NumPy array holds values of one type in a compact block and supports fast element-wise maths, multiple dimensions and axis reductions. A list holds any objects, can mix types, and * repeats it instead of multiplying the numbers.
What does axis=0 mean in NumPy?
It means "along the first dimension", down the rows. For a 2-D array, sum(axis=0) gives one total per column, and sum(axis=1) gives one total per row.
What is broadcasting in NumPy?
Broadcasting lets NumPy combine arrays of different but compatible shapes by stretching the smaller one, without copying it. A (3, 3) array times a (3,) array multiplies each row by the same three values. Shapes are compared from the right, and each dimension must be equal or 1.
Why did changing my slice change the original array?
Basic slices are views that share data with the original. Use .copy() to get an independent array. Boolean and list indexing return copies already.
Is NumPy faster than Python loops?
For numerical work on large arrays, usually by a large factor, because vectorized operations run in compiled code. For small arrays or logic that doesn't fit array operations, the difference may not matter.
NumPy Tutorial Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
What does axis=1 sum in a two dimensional array?
Which review concern is valid for numpy tutorial?
NumPy Tutorial Quiz
2 questions to check what you learned.
What does axis=1 sum in a two dimensional array?
- Every column into one row
- Values within each row
- Python ignores the distinction
Which statement correctly describes axes?
- Axis arguments select the dimensions along which reductions operate.
- Only the example output matters, not the rule
- Every operation has the same behavior