2

Ok, I know how to transpose a matrix, with for instance:

A = np.arange(25).reshape(5, 5)
print A
array([[ 0,  1,  2,  3,  4],
   [ 5,  6,  7,  8,  9],
   [10, 11, 12, 13, 14],
   [15, 16, 17, 18, 19],
   [20, 21, 22, 23, 24]])
A.T
array([[ 0,  5, 10, 15, 20],
   [ 1,  6, 11, 16, 21],
   [ 2,  7, 12, 17, 22],
   [ 3,  8, 13, 18, 23],
   [ 4,  9, 14, 19, 24]])

In the case of unidimensional arrays, it's not possible to use this ".T" tool (I don't know why, honestly) so to transpose a vector you have to change the paradigm and use, for instance:

B = np.arange(5) 
print B
array([0, 1, 2, 3, 4])

and because B.T would give the same result, we, applying this change of paradigm, use:

B[ :, np.newaxis]
array([[0],
   [1],
   [2],
   [3],
   [4]])

and I find this change of paradigm a little bit antiesthetic because a 1-D vector is in no way a different entity to a 2-D vector (a matrix), in the sense that mathematically speaking they come from the same family and share many things.

My question is: is it possible to do this tranposition with the (sometimes called) jewel of the crown of numpy that is einsum, in a more compact and unifying way for every kind of tensor? I know that for a matrix you do

np.einsum('ij->ji', A)

and you get, as previosuly with A.T:

array([[ 0,  5, 10, 15, 20],
   [ 1,  6, 11, 16, 21],
   [ 2,  7, 12, 17, 22],
   [ 3,  8, 13, 18, 23],
   [ 4,  9, 14, 19, 24]])

is it possible to do it with 1-D arrays?

Thank you in advance.

David
  • 1,155
  • 1
  • 13
  • 35

2 Answers2

4

Yes, you can transpose a 1D array using einsum

In [17]: B = np.arange(5)
In [35]: np.einsum('i,j->ji', np.ones(1), B)
Out[35]: 
array([[ 0.],
       [ 1.],
       [ 2.],
       [ 3.],
       [ 4.]])

but that isn't really what einsum is for, since einsum is computing a sum of products. As you might expect, it is slower than simply adding a new axis.

In [36]: %timeit np.einsum('i,j->ji', np.ones(1), B)
100000 loops, best of 3: 5.43 µs per loop

In [37]: %timeit B[:, None]
1000000 loops, best of 3: 230 ns per loop

If you are looking for a single syntax for transposing 1D or 2D arrays here are two options:

  • Use np.atleast_2d(b).T:

    In [39]: np.atleast_2d(b).T
    Out[39]: 
    array([[0],
           [1],
           [2],
           [3],
           [4]])
    
    In [40]: A = np.arange(25).reshape(5,5)
    
    In [41]: np.atleast_2d(A).T
    Out[41]: 
    array([[ 0,  5, 10, 15, 20],
           [ 1,  6, 11, 16, 21],
           [ 2,  7, 12, 17, 22],
           [ 3,  8, 13, 18, 23],
           [ 4,  9, 14, 19, 24]])
    
  • Use np.matrix:

    In [44]: np.matrix(B).T
    Out[44]: 
    matrix([[0],
            [1],
            [2],
            [3],
            [4]])
    
    In [45]: np.matrix(A).T
    Out[45]: 
    matrix([[ 0,  5, 10, 15, 20],
            [ 1,  6, 11, 16, 21],
            [ 2,  7, 12, 17, 22],
            [ 3,  8, 13, 18, 23],
            [ 4,  9, 14, 19, 24]])
    

    A matrix is a subclass of ndarray. It is a specialized class which provides nice syntax for dealing with matrices and vectors. All matrix objects (both matrices and vectors) are 2-dimensional -- a vector is implemented as a 2D matrix with either a single column or a single row:

    In [47]: np.matrix(B).shape     # one row
    Out[47]: (1, 5)
    
    In [48]: np.matrix(B).T.shape   # one column
    Out[48]: (5, 1)
    

    There are other differences between matrixs and ndarrayss. The * operator computes matrix multiplication for matrixs, but performs element-wise multiplication for ndarrays. Be sure to study the differences if you use np.matrix.


By the way, there is a certain beauty to the way NumPy defines transpose for ndarrays. Remember that the nd in ndarray alludes to the fact that these objects can represent N-dimensional arrays. So whatever definition these objects use for .T must apply in N dimensions.

In particular, .T reverses the order of the axes.

In 2 dimensions, reversing the order of the axes coincides with matrix transposition. In 1 dimension, the transpose does nothing -- reversing the order of a single axis returns the same axis. The beautiful part is that this definition works in N-dimensions.

Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • I agree with the beauty but that shouldn't have been the default behavior. It's basically annoying everyone who work exclusively in 2D in expense of impressing a very few working with N-D – percusse Apr 25 '16 at 13:00
  • That's actually worse than 1D arrays. But anyway nevermind – percusse Apr 25 '16 at 13:02
  • which you almost never transpose... I'm also doing applied math for living. This 1D business or matrix (which is discouraged by numpy themselves) is super annoying for all of us in the business – percusse Apr 25 '16 at 13:14
  • Thank you. Yes, the `np.matrix` seems very nice. I think that my problem was the apparent contradiction between the mathematical point of view of transposition and the python point of view. – David Apr 25 '16 at 13:57
  • @percusse - looks like you are coming from a MATLAB world. In old MATLAB (e.g.3.0) EVERYTHING was 2d. No dimensionless scalars, no 3d or larger, no cells or structures. `numpy` has different roots. – hpaulj Apr 25 '16 at 16:20
  • @hpaulj Not quite but I see your point. The choice is very very programmer-like and unnatural. But they implemented matrix multiplication last year so who am I to say things are unnatural. – percusse Apr 25 '16 at 22:48
0

The basic action of einsum is to iterate on all dimensions performing some sum of products. It takes short cuts in a few cases, even returning views if it can.

But I think it will complain about 'i->j' or 'i->ij'. You can't have indices on right that aren't already present on left.

einsum('i,j->ji',A,[1]) or some variant, might work. But it will be much slower.

In [19]: np.einsum('i,j->ij',[1,2,3],[1])
Out[19]: 
array([[1],
       [2],
       [3]])

In [30]: %%timeit x=np.arange(1000)
    ...: y=x[:,None]
1000000 loops, best of 3: 439 ns per loop

In [31]: %%timeit x=np.arange(1000)
    ...: np.einsum('i,j->ij',x,[1])
100000 loops, best of 3: 15.3 µs per loop
hpaulj
  • 221,503
  • 14
  • 230
  • 353