Unit 3 • Lesson 3

Indexing and Slicing Arrays

Overview

Slices look like list slices and behave differently in one important way.

Intermediate 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will be able to:

  • Index and slice a one-dimensional array the way you already index a list.
  • Explain what a view is, and why writing into one changes the original.
  • Use .copy() when you need the array left alone.
  • Index a two-dimensional array with one pair of brackets.

Why This Matters

Array slicing looks identical to list slicing and behaves differently in exactly one respect — it does not copy. That single difference is behind a whole class of bug where a function quietly edits its caller's data. It is worth meeting deliberately, once, rather than discovering it in an analysis you have already published.

Step 1: Indexing reads the same as a list

Positions from zero, negatives from the end, and a slice for a run of them. Everything you know from lists transfers.

1

Positions and ranges

A single index gives one element. A slice gives a run, with the stop exclusive, exactly as in a list.

import numpy as np

a = np.array([10, 20, 30, 40, 50])
print(a[0])
print(a[-1])
print(a[1:4])
print(a[:2])
print(a[::2])
2

Two dimensions, one pair of brackets

For a 2-D array you can write a[1][2] as you would with nested lists, but a[1, 2] is the numpy way and it is what lets you slice both directions at once.

import numpy as np

table = np.array([[1, 2, 3], [4, 5, 6]])
print(table[1, 2])      # row 1, column 2
print(table[0])         # a whole row
print(table[:, 1])      # a whole column

table[:, 1] reads as "every row, column 1". Pulling a column out of a list of lists took a comprehension; here it is a slice.

Mini Practice #1: Take a column

Try It Yourself

From the table below, print the second column, then the last row, then the single value in the bottom right — each in one expression.

Press Run to see output

Step 2: A slice is a view, not a copy

This is the difference that catches people. Slicing a list gives you a new list; slicing an array gives you a window onto the same memory.

1

Writing through the window

Assigning into a slice changes the array it came from. Nothing warns you, because as far as numpy is concerned you asked for a view and then wrote into it.

import numpy as np

a = np.array([1, 2, 3, 4])
part = a[1:3]
part[0] = 99
print(part)
print(a)          # the 99 is in here too
2

The same code with a list does not do this

Worth running side by side once, so the difference is a thing you have seen rather than a thing you were told.

values = [1, 2, 3, 4]
part = values[1:3]
part[0] = 99
print(part)
print(values)     # untouched
3

Ask for a copy when you want one

If a function is going to modify what it was handed, take a copy first — or return something new instead of editing in place. The cost of the copy is almost always smaller than the cost of the bug.

import numpy as np

a = np.array([1, 2, 3, 4])
part = a[1:3].copy()
part[0] = 99
print(part)
print(a)          # safe

Mini Practice #2: Prove it to yourself

Try It Yourself

Run this as it stands and note what a prints. Then add .copy() to the slice and run it again.

Press Run to see output

Step 3: When to Use It

Reading part of an array

A slice is a cheap view: ideal for looking at, summarising or passing along a range of values.

first_week = daily[:7]

Changing part of an array on purpose

Assigning into a slice updates the original — useful when that is exactly what you mean.

When Not to Use It

Do not modify a slice you meant to be a separate copy. Call .copy() first, or a change to the “copy” quietly rewrites the original data.

End-of-Lesson Exercises

Exercise 1: Everything but the ends

Write middle(values) that returns a list of everything except the first and last item.

A slice from 1 to -1 drops one from each end, and slices clamp rather than raise when there is nothing left to take.

Press Run to try it, then Check my work to mark it.

Exercise 2: Zero a range without touching the original

Write blanked(values, start, stop) that returns a new array with the elements from start up to (not including) stop set to 0. The array you were given must come back unchanged.

Take a .copy() first, then assign 0 into the slice of the copy. Assigning into a slice of the original would change the caller's array, which is the thing this exercise is checking.

Press Run to try it, then Check my work to mark it.