2

Let's consider a as an 1D row/horizontal array:

import numpy as np
N = 10
a = np.arange(N) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a.shape # (10,)

now I want to have b a 1D column/vertical array transposed of a:

b = a.transpose() # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
b.shape # (10,)

but the .transpose() method returns an identical ndarray whith the exact same shape!

What I expected to see was

np.array([[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]])

which can be achieved by

c = a.reshape(a.shape[0], 1) # or c = a; c.shape = (c.shape[0], 1)
c.shape # (10, 1)

and to my surprise, it has a shape of (10, 1) instead of (1, 10).

In Octave/Scilab I could do:

N = 10
b = 0:(N-1)
a = b'
size(b) % ans = 1   10
size(a) % ans = 10   1

I understand that numpy ndarrays are not matrices (as discussed here), but the behavior of the numpy's transpose function just doesn't make sense to me! I would appreciate it if you could help me understand how this behavior makes sense and what am I missing here.

P.S. So what I have understood so far is that b = a.transpose() is the equivalent of b = a; b.shape = b.shape[::-1] which if you had a "2D array" of (N, 1) would return a (1, N) shaped array, as you would expect from a transpose operator. However, numpy seems to treat the "1D array" of (N,) as a 0D scalar. I think they should have named this method something else, as this is very misleading/confusing IMHO.

Foad S. Farimani
  • 12,396
  • 15
  • 78
  • 193
  • 2
    You aren't taking the difference between a (10,) shape and a (1,10) seriously! – hpaulj Oct 19 '19 at 16:02
  • @hpaulj you are right. so numpy considers (10,) as 1D while treating it as 0D scalar. Wierd! – Foad S. Farimani Oct 19 '19 at 16:05
  • 2
    `np.array(1)` is a 0d array, `np.array([1,2,3])` is 1d. `np.array([[1,2,3]])` is 2d. Just count the `[]`. Octave/MATLAB has an artificial lower limit of 2d. – hpaulj Oct 19 '19 at 16:07
  • @hpaulj I think I now understand that the numpy's transpose is merely an inverse of the shape tuple property of the ndarray object. And has nothing to do with the mathematical matrix transpose we see in for example Octave. but I still do not agree that the behavior is ok. A `(N,)` array is not scalar and only scalars (or symmetric matrices) should have identical transpose IMHO. – Foad S. Farimani Oct 19 '19 at 16:14
  • 2
    But a 1d numpy array is not a `matrix`. It's not a `row vector` either. `numpy` has defined `transpose` to work with any dimension array, 0, 1 or even 3. In Octave if `size(b)` is (2,3,4), `b'` raises an error. The 'mathematical matrix transpose' that you are used to is defined only for matrices, 2d arrays. – hpaulj Oct 19 '19 at 16:56
  • `transpose` takes an optional parameter that lets you specify the desired permutation. With this a (2,3,4) shape could be changed to (3,2,4) or (4,2,3). Matrix transpose only allows one change, from (n,m) to (m,n). – hpaulj Oct 19 '19 at 17:37

2 Answers2

4

To understand the numpy array better, you should take a look at this review paper: The NumPy array: a structure for efficient numerical computation

In short, numpy ndarrays have this attribute called the stride, which is

the number of bytes to skip in memory to proceed to the next element. For a (10, 10) array of bytes, for example, the strides may be (10, 1), in other words: proceed one byte to get to the next column and ten bytes to locate the next row.

For your ndarray a, a.stride = (8,), which shows that it is only 1 dimensional, and that to get to the next element on this single dimension, you need to advance 8 bytes in memory (each int is 64-bit).

Strides are useful for representing transposes:

By modifying strides, for example, an array can be transposed or reshaped at zero cost (no memory needs to be copied).

So if there was a 2-dimensional ndarray, say b = np.ones((3,5)) for example, then b.strides = (40, 8), while b.transpose().strides = (8, 40). So as you see a transposed 2D-ndarray is simply the exact same array, whose strides have been reordered. And since your 1D ndarray has only 1 dimension, swapping the the values of its strides (i.e. taking its transpose), doesn't do anything.

Jethro Cao
  • 968
  • 1
  • 9
  • 19
1

As you already mentioned that numpy array are not matrix. The defination of transpose function is like below

Permute the dimensions of an array.

Which means that numpy's transpose method will move data from one dimension to another. As 1D array has only one dimension there is no other dimension to move the data t0. So you need add a dimension before transpose has any effect. This behavior make sense also to be consistent with higher dimensional array (3D, 4D ...) array.

There is a clean way to achive what you want

N = 10
a = np.arange(N)
a[ :, np.newaxis]
Dev Khadka
  • 5,142
  • 4
  • 19
  • 33