What is a numpy array?

Hold many numbers in one array and do maths on all of them at once, without writing a loop.

A numpy array holds many numbers and does maths on all of them at once. Multiply an array of prices by a share count and every price gets multiplied, with no loop.

In this lesson I build arrays of prices, run arithmetic straight on them, show that a plain list does something else entirely, and turn prices into returns in one line.

Step 1. Maths on a whole row of prices

np.array() turns a list into an array. From then on, the operators work on every element.

import numpy as np

prices = np.array([185.40, 187.20, 184.90])   # three closes

print(prices)             # -> [185.4 187.2 184.9]
print(type(prices))       # -> <class 'numpy.ndarray'>
print(prices.shape)       # -> (3,)    <- 3 numbers, in one dimension
[185.4 187.2 184.9]
<class 'numpy.ndarray'>
(3,)

shape is a tuple, so (3,) means one row of three. Multiply by a number and each price is multiplied:

value = prices * 10       # 10 shares held on each of the three days

print(value)              # -> [1854. 1872. 1849.]
[1854. 1872. 1849.]

Two arrays of the same shape combine position by position: the first with the first, the second with the second.

aapl = np.array([185.40, 187.20, 184.90])
msft = np.array([410.00, 408.50, 415.20])

print(aapl + msft)        # -> [595.4 595.7 600.1]    <- one share of each, per day
print(msft - aapl)        # -> [224.6 221.3 230.3]
[595.4 595.7 600.1]
[224.6 221.3 230.3]

Step 2. A list does not do this

The same two expressions on a list mean something else. * repeats the list and + joins two lists end to end.

print([1, 2] * 2)                             # -> [1, 2, 1, 2]
print(np.array([1, 2]) * 2)                   # -> [2 4]

print([1, 2] + [3, 4])                        # -> [1, 2, 3, 4]
print(np.array([1, 2]) + np.array([3, 4]))    # -> [4 6]
[1, 2, 1, 2]
[2 4]
[1, 2, 3, 4]
[4 6]

[1, 2] * 2 gives four items. np.array([1, 2]) * 2 gives two items, each doubled. To double every number in a list you write a loop. To double every number in an array you write * 2.

Step 3. Prices to returns in one line

Slicing an array works the way it did on lists in Lesson 5. closes[1:] is every price from the second onwards, and closes[:-1] is every price except the last.

closes = np.array([185.40, 187.20, 184.90, 188.10, 190.50])

print(closes[1:])         # -> [187.2 184.9 188.1 190.5]
print(closes[:-1])        # -> [185.4 187.2 184.9 188.1]
[187.2 184.9 188.1 190.5]
[185.4 187.2 184.9 188.1]

Line those two up and the pairs are today and yesterday. Dividing one by the other divides every pair, so the whole set of returns comes out at once.

returns = closes[1:] / closes[:-1] - 1        # today over yesterday, minus 1

print(np.round(returns, 4))                   # -> [ 0.0097 -0.0123  0.0173  0.0128]
print(closes.shape, returns.shape)            # -> (5,) (4,)
[ 0.0097 -0.0123  0.0173  0.0128]
(5,) (4,)

Five prices, four returns, the same count as in Lesson 8.

Step 4. Three things to ask an array

.shape is an attribute, so no brackets. .mean() and .sum() are methods, so brackets.

print(f"mean: {returns.mean():.4%}")    # -> mean: 0.6872%
print(f"sum:  {returns.sum():.6f}")     # -> sum:  0.027488
print(returns.shape)                    # -> (4,)
mean: 0.6872%
sum:  0.027488
(4,)

.sum() adds the four returns. That is not the return for the week, because simple returns do not add across time. Multiply the growth factors instead:

print(f"compounded: {(1 + returns).prod() - 1:.4%}")   # -> compounded: 2.7508%
print(f"last / first: {closes[-1] / closes[0] - 1:.4%}")   # -> last / first: 2.7508%
compounded: 2.7508%
last / first: 2.7508%

1 + returns adds 1 to all four returns and .prod() multiplies them together.

Your turn

Take closes = np.array([50.0, 55.0, 55.0, 44.0]). Get the returns without a loop, print their shape, and compound them into a total.

import numpy as np

closes  = np.array([50.0, 55.0, 55.0, 44.0])
returns = closes[1:] / closes[:-1] - 1

print(returns)                                  # -> [ 0.1  0.  -0.2]
print(returns.shape)                            # -> (3,)
print(f"{(1 + returns).prod() - 1:.4%}")        # -> -12.0000%

Every element of an array shares one dtype. np.array([1, 2]).dtype is int64 and np.array([185.4, 187.2]).dtype is float64. Mix an integer into a float array and it becomes a float.