DataLens

Indexing and slicing

Pick single values, ranges, rows, columns and sub-grids out of arrays, and change them in place.

Level: Foundations · 8 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

Most questions are about part of your data: last week, the Bandung store, March, the first three products. Indexing and slicing are how you point at those parts. If you know Python list slicing, 1-D arrays work the same way; 2-D arrays add one powerful idea: a comma between the row part and the column part.

1-D: like lists

Here are two weeks of daily order counts in one array. Getting a single day, or a stretch of days, works exactly like it does with a Python list:

import numpy as np

daily_orders = np.array([42, 38, 51, 47, 60, 75, 68, 44, 39, 55, 49, 63, 80, 71])
print(daily_orders[0])     # first day
print(daily_orders[-1])    # last day
print(daily_orders[2:5])   # days 3, 4, 5 (index 2 up to, not including, 5)
42
71
[51 47 60]
print(daily_orders[:7])    # first week
print(daily_orders[7:])    # second week
print(daily_orders[-3:])   # last three days
[42 38 51 47 60 75 68]
[44 39 55 49 63 80 71]
[63 80 71]

A third number is the step:

print(daily_orders[::7])   # every 7th day: each Monday
print(daily_orders[::-1])  # reversed
[42 44]
[71 80 63 49 55 39 44 68 75 60 47 51 38 42]

2-D: rows and columns

With a table, one position isn't enough to point at a value: you need to say which row and which column. Here the rows are four stores and the columns are six months. To get Bandung's units in March, give the row number and the column number, separated by a comma:

stores = ["Jakarta", "Bandung", "Surabaya", "Medan"]
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
units = np.array([
    [120, 135, 150, 160, 155, 170],
    [ 80,  85,  90,  88,  95, 100],
    [ 95,  92, 105, 110, 108, 115],
    [ 40,  45,  43,  50,  52,  55],
])
print(units[1, 2])      # row 1 (Bandung), column 2 (Mar)
90

units[row, column], with a comma. (You'll also see units[1][2], which works but is slower and doesn't extend to slices, so prefer the comma.)

Each side of the comma can be a slice. A lone : means "all":

print(units[0])         # row 0: Jakarta, all months
print(units[:, 0])      # all rows, column 0: every store in January
print(units[:2, -3:])   # first two stores, last three months
[120 135 150 160 155 170]
[120  80  95  40]
[[160 155 170]
 [ 88  95 100]]
Key idea

Read units[rows, columns] as "which rows, comma, which columns". units[:, 0] is "all rows, column 0", a whole column. This is the same idea pandas uses in df.iloc[rows, columns].

Slicing a grid gives a smaller grid, with its own shape:

q2 = units[:, 3:]       # April to June
print(q2.shape)
print(q2.sum())
(4, 3)
1258

Using names to find positions

Arrays don't have labels like pandas. Keep the labels in a list and look up positions with .index():

b = stores.index("Bandung")
m = months.index("May")
print(stores[b], months[m], units[b, m])
Bandung May 95

That's one reason pandas exists: it keeps the labels attached. NumPy is the faster, simpler layer underneath.

Changing values

Assign to an index or a slice to change the array in place:

units[3, 5] = 58          # correct one value
units[2, :2] = [97, 94]   # correct two values
units[3, :] = 0           # the Medan store closed: zero the whole row
print(units)
[[120 135 150 160 155 170]
 [ 80  85  90  88  95 100]
 [ 97  94 105 110 108 115]
 [  0   0   0   0   0   0]]

Assigning a single value to a slice fills every position (= 0 above). Assigning an array needs a matching shape.

A slice is a view

One difference from lists matters a lot. Slicing a list makes a copy; slicing an array makes a view: a window onto the same data. Change the view and the original changes too:

prices = np.array([185, 320, 95, 65, 110])
cheap = prices[2:4]
cheap[0] = 0
print(prices)
[185 320   0  65 110]

Views make slicing instant even on huge arrays (no data is copied), but they surprise people. When you want an independent copy, say so with .copy(): cheap = prices[2:4].copy(). A later module covers views and copies in depth.

Why this matters for clean code

Indexes are "magic numbers" that are easy to get wrong: is column 2 March or April? Clean NumPy code keeps the labels nearby (months.index("Mar")) or gives slices names (q2 = units[:, 3:]), so the intent is readable. And use .copy() whenever you plan to modify a slice but keep the original intact.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Boolean and fancy indexing →