import numpy as np
x = np.array([3, 8, 2, 9, 5])
print(x > 4) # -> [False True False True True]
print(x[x > 4]) # -> [8 9 5][False True False True True]
[8 9 5]
A mask is an array of True and False, one entry per element, and you put it inside square brackets to keep only the elements where it is True. You get one by comparing an array: the comparison answers once per element, not once for the whole array.
In this lesson I build masks on an array of daily returns, count and average with them, combine two conditions with & and |, and use np.where to turn a condition into a position of 1 or 0.
[False True False True True]
[8 9 5]
x > 4 compares every element and reports five answers. x[x > 4] reads the five answers and keeps the three elements that scored True.
Ten daily returns. The mask picks out the negative ones.
[False True False True False False True False True False]
[-0.004 -0.021 -0.009 -0.013]
mask has ten entries, one per day. returns[mask] has four, because four days were negative.
True counts as 1 and False counts as 0, so summing a mask counts the days and averaging it gives the fraction of days.
4
0.4
10
~ flips a mask, so ~mask is the up days. Select with each and average what comes back.
& means and, | means or. Each one compares two masks element by element and gives a third mask.
Every condition needs its own brackets. & binds tighter than < and >, so without the brackets Python tries 0 & returns first.
[False True False False False False True False False False]
[-0.004 -0.009]
2
[ 0.012 -0.021 0.015 -0.013]
4
Here is what dropping the brackets does:
TypeError
ufunc 'bitwise_and' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
The and and or of Lesson 3 do not work here either. They ask for one True or False, and an array of ten answers cannot supply it.
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
So: and and or for single values, & and | for arrays, and brackets round each condition.
np.where(condition, a, b) walks the mask and takes a where it is True and b where it is False. Feed it 1 and 0 and the mask becomes a position: in the market or out of it.
[1 0 1 0 1 1 0 1 0 1]
int64
6
0.6
The two outputs can be any numbers, not just 1 and 0. Half size on the big-move days:
[0.5 1. 1. 0.5 1. 0.5 1. 1. 0.5 1. ]
position here sits on the same day as the return that produced it, which is a position decided with that day’s return already known. Moving a signal forward one day before it earns anything is shift, in Lesson 20.
Take r = np.array([0.004, -0.011, 0.022, -0.003, 0.000, -0.018, 0.009]). Build a mask for days where the move was bigger than 0.5% in either direction. Print those returns, how many there were, and what fraction of the week they made up. Then use np.where to build an array holding 0 on those days and 1 on the rest.
import numpy as np
r = np.array([0.004, -0.011, 0.022, -0.003, 0.000, -0.018, 0.009])
m = (r > 0.005) | (r < -0.005) # brackets round each condition
print(m) # -> [False True True False False True True]
print(r[m]) # -> [-0.011 0.022 -0.018 0.009]
print(m.sum()) # -> 4
print(m.mean()) # -> 0.5714285714285714
print(np.where(m, 0, 1)) # -> [1 0 0 1 1 0 0]