Data Science and Advanced Programming · Session 5

Slicing in pictures

Lists first, then 2-D numpy arrays. Gold boxes are what the slice keeps.

Before you look at a result, say out loud which boxes you expect to turn gold. Then check. Every picture and every result on this page was produced by running the code.

Slicing a list

Five closing prices, one per day:

closes = [190.5, 187.2, 193.8, 185.1, 191.4]

One index: one item

closes[1]
index0190.5-51187.2-42193.8-33185.1-24191.4-1from the end

It gives 187.2 · one number, not a list

The number in the brackets is a position, counted from 0. You get the item itself, not a list.

A slice cuts between the boxes

closes[1:4]
index0190.5-51187.2-412193.8-323185.1-234191.4-1from the end✂ start 1✂ stop 4

It gives [187.2, 193.8, 185.1] · a list of 3

Think of start and stop as cuts BETWEEN the boxes. You keep what lies between the cuts: boxes 1, 2 and 3. Box 4 is not included.

Leave out start: from the beginning

closes[:2]
index0190.5-511187.2-422193.8-33185.1-24191.4-1from the end✂ start 0✂ stop 2

It gives [190.5, 187.2] · a list of 2

No start means: cut at the very left. [:2] is the first two items. Length = stop − start = 2.

Negative numbers count from the end

closes[-2:]
index0190.5-51187.2-42193.8-33185.1-214191.4-12from the end✂ start 3✂ stop 5

It gives [185.1, 191.4] · a list of 2

−1 is the last box, −2 the one before it. [-2:] is the last two items, however long the list is.

A step: every second item

closes[::2]
index0190.5-511187.2-42193.8-323185.1-24191.4-13from the end

It gives [190.5, 193.8, 191.4] · a list of 3

start:stop:step. With no start and no stop the slice covers the whole list; step 2 takes every second box. The small numbers show the order.

Step −1: backwards

closes[::-1]
index0190.5-551187.2-442193.8-333185.1-224191.4-11from the end

It gives [191.4, 185.1, 193.8, 187.2, 190.5] · a list of 5

A negative step walks from the right to the left. [::-1] is the whole list, reversed. Look at the small numbers: the last box comes first.

One item or a list of one?

closes[1]

It gives 187.2 · one number, not a list

closes[1:2]

It gives [187.2] · a list of 1

An index gives the item. A slice ALWAYS gives a list, even a list of one. That difference comes back in numpy as a difference in shape.

Slicing a 2-D array

The price grid from the lecture: one row per position, one column per day. In prices[rows, columns], each side of the comma is sliced on its own.

prices = np.array([[185.0, 188.2, 190.5],    # AAPL
                   [104.1, 103.0, 102.3],    # NESN
                   [505.0, 509.9, 512.0]])   # VOO

A row: one number, then nothing

prices[1]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives [104.1, 103.0, 102.3] · shape (3,)

One number in the brackets picks a row: NESN. The row has 3 prices, so the shape is (3,): one dimension.

A column: : for every row

prices[:, -1]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives [190.5, 102.3, 512.0] · shape (3,)

: before the comma means every row. −1 after it is the last day. You get the last price of each position, again shape (3,).

Two numbers: one cell

prices[1, 2]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives 102.3 · shape ()

Row 1, column 2. Two single numbers remove both dimensions: one number, shape ().

Two slices: a block

prices[:2, 1:]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives [[188.2, 190.5], [103.0, 102.3]] · shape (2, 2)

Rows up to 2 (AAPL, NESN), columns from 1 on (days 1 and 2). Slices keep their dimension, so the result is still a table: (2, 2).

A list of columns

prices[:, [0, 2]]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives [[185.0, 190.5], [104.1, 102.3], [505.0, 512.0]] · shape (3, 2)

A LIST in a position picks exactly those columns, here the first and the last day. All rows, two columns: (3, 2).

A row as a slice keeps the table shape

prices[1:2]
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0

It gives [[104.1, 103.0, 102.3]] · shape (1, 3)

Same cells as prices[1], but a slice keeps the dimension: a table with one row, shape (1, 3), not (3,).

The rule to count shapes

Count what is in the brackets. Every single number removes a dimension. Every slice (with :) or list keeps one. prices[1] → (3,) · prices[1:2] → (1, 3) · prices[1, 2] → () · prices[:2, 1:] → (2, 2). When numpy surprises you, print .shape first.

Two shifted slices: daily returns

Each day divided by the day before. The two slices have the same shape, so numpy divides cell by cell.

prices[:, 1:]   # days 1, 2
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0
prices[:, :-1]  # days 0, 1
day 0day 1day 20 AAPL185.0188.2190.51 NESN104.1103.0102.32 VOO505.0509.9512.0
returns = prices[:, 1:] / prices[:, :-1] - 1

It gives [[0.0173, 0.0122], [-0.0106, -0.0068], [0.0097, 0.0041]] (rounded to 4) · shape (3, 2)

Three days give two returns per position, so the shape is (3, 2).