Reputation: 2874
When we have a numpy
array, and want to perform an operation along one axis, why is axis=1
equivalent to working across a row, and axis=0
equivalent to working down a column?
As can be seen in the example below, creating a numpy
array requires specification of the dimensions, via the kwarg size
, in:
np.random.randint: (low, high=None, size=None, dtype='l')
This is a tuple, for example(of the forms: size=(number_of_rows, number_of_columns)
>>> import numpy as np
>>> a = np.random.randint(0, 20, (3, 4))
>>> a
array([[16, 7, 4, 7],
[ 4, 10, 8, 5],
[ 7, 1, 15, 7]])
>>> np.sum(a, axis=0)
array([27, 18, 27, 19])
>>> np.sum(a, axis=1)
array([34, 27, 30])
>>> a.shape[0]
3
>>> a.shape[1]
4
Summing across rows is performed by setting the axis=1
, where I would expect axis=0
.
Is there a good reason for this? It just confused me every time I used it, until I got simply became accustomed to it. Why does it contradict (in my opinion!) the standard way of indexing arrays, as in the example in the code with the shape
attribute?
Here is some more (confusing?) code, showing that axis=0
works for a 1d array, as one might expect.
>>> b
array([1, 2, 3, 2, 3, 4])
>>> np.sum(b, axis=0)
15
>>> np.sum(b, axis=1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
np.sum(b, axis=1)
File "/usr/local/lib/python3.6/site-packages/numpy/core/fromnumeric.py", line 1814, in sum
out=out, **kwargs)
File "/usr/local/lib/python3.6/site-packages/numpy/core/_methods.py", line 32, in _sum
return umr_sum(a, axis, dtype, out, keepdims)
ValueError: 'axis' entry is out of bounds
I would expect this as the default, given a 1d array can only really be summed in one direction. Thanks for the input in the comments. I think the answer regarding performance from Stephan is the right one. Is produces this small quirk in numpy that becomes the norm.
Upvotes: 2
Views: 620
Reputation: 53029
You are arguing that the axis
argument for numpy "reduce" operations, i.e. operations that reduce the number of dimensions - typically by one is operating illogically. Your argument is that given a 2d array the "surviving" dimension is not the one specified. Tellingly, you are using the phrase summing across.
Please allow me to demonstrate that this criticism is untenable. The much more consistent concept is summing along an axis and specifying not the surviving axis but the one that is consumed by the reduce operation.
To see this think 3d or 100d. If you average a stack of images you get one average image, so you are averaging along the stacking axis which is consumed in the process. By your logic - you'd specify this process in terms of the surviving axes, x and y - which is clearly misguided, think 100d - where you'd have to enumerate 99 surviving axes to describe a single reduction along a single axis.
If you are comfortable with maths you can also easily convince yourself, that the integration or summation variable is the one that's gone afterwards. Matrix multipliction: The summation dimension is the one that does not feature in the result AB = C -> c_jl = sum_k=1^n a_jk b_kl
Similarly thinking of a shape (M, N) as number-of-rows, number-of-columns works in 2d, but nowhere else. In 3d it would become number-of-x-y-planes, number-of-x-z-planes, number-of-y-z-planes, in 100d number-of-x1-x2-x3-...-x99-hyperplanes, number-of-x0-x2-x3-...-x99-hyperplanes, ... Much better thinking of (M, N) as column-length, row-length.
I could go on like this but if by now you are not convinced then I don't see what could convince you.
Upvotes: 1
Reputation: 49784
Array indexing is a common source of confusion in numpy
. There is an excellent discussion of this topic in the numpy docs.
The source of confusion:
The first thing to understand is that there are two conflicting conventions for indexing 2-dimensional arrays. Matrix notation uses the first index to indicate which row is being selected and the second index to indicate which column is selected. This is opposite the geometrically oriented-convention for images where people generally think the first index represents x position (i.e., column) and the second represents y position (i.e., row). This alone is the source of much confusion; matrix-oriented users and image-oriented users expect two different things with regard to indexing.
The answer:
Like so much else in numpy, the answer to the question is because: performance:
So if this is true, why not choose the index order that matches what you most expect? In particular, why not define row-ordered images to use the image convention? (This is sometimes referred to as the Fortran convention vs the C convention, thus the 'C' and 'FORTRAN' order options for array ordering in numpy.) The drawback of doing this is potential performance penalties. It's common to access the data sequentially, either implicitly in array operations or explicitly by looping over rows of an image. When that is done, then the data will be accessed in non-optimal order.
Upvotes: 1