Why do we have a different use of brackets when using Numpy append column vs row?

Viewed 480

I'm learning NumPy with a Udacity class.

We create a 2D matrix and then we create 2 other arrays, one appending a row and the other one a column:

Y = np.array([[1, 2, 3], [4, 5, 6]])
v = np.append(Y, [[7, 8, 9]], axis=0)
q = np.append(Y, [[9], [10]], axis=1)

Why is the syntax (use of the square brackets) of the value lists different in those 2 cases? I realize that the use of brackets matches the order in which we create the original matrix, but it would seem that the interpreter would have enough information with the axis=n argument, no?

3 Answers
Y = np.array([[1,2,3],[4,5,6]])

This creates a (2,3) array.

v = np.append(Y, [[7,8,9]], axis=0)

This is actually implemented as

v = np.concatenate(( Y, np.array([[7,8,9]]) ), axis=0)

That is, it joins a (2,3) array with (1,3) on axis 0 to make a (3,3).

q = np.append(Y,[[9],[10]], axis=1)

Here it's joining (2,3) with (2,1) on axis 1 to make (2,4)

np.concatenate assumes axis 0 unless you specify it. It does not attempt to guess which axis works or not. In the q case using axis 0 would result in an error.

Also concatenate requires matching number of dimensions, and match sizes on the non-concatenating axes. Try something like np.concatenate((Y, [2,1])) to see what I mean.

I don't like np.append since it looks too much like the list append, and gives beginners lots of problems. Without axis it ravels the inputs (read the docs and code if that isn't obvious). With axis it is just the same as concatenate (again read the code). concatenate takes a list of many arrays, not just 2. np.append artificially limits that action to 2.

np.vstack, np.hstack, and np.column_stack are variants that tweak the input dimensions in various ways, before calling concatenate. They compensate for some mis-matches, such as when joining rows:

np.vstack((Y, [7,8,9]))

turns the (3,) array into a (1,3) before concatenating. This is, in effect, an convenient and safe 'second-guessing'. It rarely gives problems like np.append.

numpy (and Python) tries to avoid ambiguities. It's more likely to raise an error than to second guess the user. And the trend in development is remove legacy cases where it allowed 'rule-breaking'.

append is very similar to concatenate: it merges two arrays along an existing axis. Brackets are how we define distribute elements across the dimensions.

Y is shape (2, 3). That means that if you want to append to the first axis, the extra data must have shape (M, 3). If you want to append to the second axis, the new data must have shape (2, N). This generalizes to any number of dimensions: all the dimensions of the new array, except the one you are appending to must have the same shape.

The array [[7, 8, 9]] has M = 1: its shape is (1, 3), so you can append it to the first axis just fine. That adds an extra row to Y. If you tried to append something like [[7], [8], [9]], you would get an error, since the shape is now (3, 1). You won't end up with a uniform array if you try that.

The array [[9], [10]] has N = 1: its shape is (2, 1), so you can append it to the second axis. That adds an extra column to Y. [[9, 10]] has shape (1, 2), so clearly it won't work.

The reason that the axis keyword does not give you enough information is that you don't always append single rows or columns. Take the following example:

np.append(Y, Y, axis=0)
np.append(Y, Y, axis=1)

Just a visual addition:

#  (3, 2)     (3, 1)
# 
# [[1, 4],  |  [[7],
#  [2, 5],  |   [8],
#  [3, 6]]  |   [9]],
# --------- 
# [[7, 8]]   
#
#  (1, 2)

When concatenating two arrays

The arrays must have the same shape, except in the dimension corresponding to axis

#   (i, j0   , k)
# + (i,    j1, k)  # (concatenate)
# ---------------
# = (i, j0+j1, k)

You are right that when concatenating a 1D array to a 2D array the axis argument should be enough to unambiguously combine the two arrays even without the dimensions matching, but the nested brackets help to make this more explicit,

Related