foo[:, None] extends 1 dimensional array foo in the second dimension. In fact, numpy uses the alias np.newaxis to do this.
consider foo
foo = np.array([1, 2]) print(foo) [1 2]
A one-dimensional array has limitations. For example, what to transpose?
print(foo.T) [1 2]
Same as array itself
print(foo.T == foo) [ True True]
This restriction has many implications, and it becomes useful to consider foo in a higher dimensional context. numpy uses np.newaxis
print(foo[np.newaxis, :]) [[1 2]]
But this np.newaxis is just syntactic sugar for None
np.newaxis is None True
So, often we use None because it is less than characters and means the same thing
print(foo[None, :]) [[1 2]]
Ok, let's see what else we could do. Notice that I used the None example in the first position, while the OP used the second position. This position determines which dimension is expanded. And we could do it further. May these examples help explain
print(foo[None, :]) # same as foo.reshape(1, 2) [[1 2]]
print(foo[:, None]) # same as foo.reshape(2, 1) [[1] [2]]
print(foo[None, None, :])
print(foo[None, :, None]) # same as foo.reshape(1, 2, 1) [[[1] [2]]]
print(foo[:, None, None]) # same as foo.reshape(2, 1, 1) [[[1]] [[2]]]
Keep in mind which dimension matters when numpy prints an array
print(np.arange(27).reshape(3, 3, 3)) dim2 ────────⇀ dim0 → [[[ 0 1 2] │ dim1 [ 3 4 5] │ [ 6 7 8]] ↓ ────────⇀ → [[ 9 10 11] │ [12 13 14] │ [15 16 17]] ↓ ────────⇀ → [[18 19 20] │ [21 22 23] │ [24 25 26]]] ↓