Stackoverflow answered By Gareth Rees

The original problem

In Numpy, some operations return Shape as (R, 1) while others return (R,). 0 This will make matrix multiplication more tedious because of the need for explicit callback For example, suppose we have a matrix M, and if we want to execute numpy.dot(M[:,0], numpy.ones((1, R)), where R is the number of rows (of course, switching to columns causes the same problem). We get an exception for matrices are not aligned because the shape of M[:,0] is (R,) but the shape of numpy.ones((1, R)) is (1, R).

So my question is:

  1. What’s the difference between shape (R, 1) and shape (R,)? I know that literally one is a list of numbers, and the other is a list of only one number. Just curious why numpy isn’t designed to be more shaped (R, 1) than (R,) to make matrix multiplication easier.

  2. Is there a better way to do the above example? Without having to explicitly call like this reshape: numpy. Dot (M] [: 0. Reshape (R, 1), numpy. ‘ones ((1, R))).

answer

1. Shape in NumPy

You write, “I know that one is literally a list of numbers and the other is a list of lists of just one number,” but it’s kind of useless to think about it that way.

The best way to think about NumPy arrays is that they contain two parts, a data buffer, which is just a block of raw elements, and a view that describes how to interpret the data buffer.

For example, if we create an array of 12 integers:

>>> a = numpy.arange(12)
>>> a
array([ 0.1.2.3.4.5.6.7.8.9.10.11])
Copy the code

In this case, a contains a data buffer, arranged as follows:

┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ │ │ │ 0 1 2 3 4 5 6 7 │ │ │ │ │ │ 8 9 10 11 │ │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

And a view describing how to interpret the data:

>>> a.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8.)>>> a.shape
(12.)Copy the code

Shape (12,) here means that the array is indexed by a single index, ranging from 0 to 11. Conceptually, if we mark index I, array A looks like this:

I = 0 1 2 3 4 5 6 7 8 9 10 11 ┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ │ │ │ 0 1 2 3 4 5 6 │ │ │ │ 7 8 9 10 11 │ │ │ │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

0 If we 0 an array, it won’t change the data buffer. Instead, it creates a new view that describes different ways to interpret the data. So when running the following:

>>> b = a.reshape((3.4))
Copy the code

Arrays B and A have the same data buffer, but are now indexed by two indexes, 0 to 2 and 0 to 3, respectively. If we mark two indexes as I and j, array B looks like this:

I = 0 0 0 0 1 1 1 1 2 2 2 2 j = 0 1 2 3 0 1 2 3 0 1 2 3 ┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ 0 │ │ │ 1 2 3 4 5 6 7 │ │ │ │ │ 8 9 10 11 │ │ │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

This means:

>>> b[2.1]
9
Copy the code

You can see that the second index changes faster and the first index changes slower. If you want to do the opposite, you can specify the order argument:

>>> c = a.reshape((3.4), order='F')
Copy the code

This causes the array index to look like this:

I = 0 1 2 0 1 2 0 1 2 0 1 2 j = 0 0 0 1 1 1 2 2 2, 3, 3 3 ┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ 0 │ │ │ 1 2 3 4 5 6 7 │ │ │ │ │ 8 9 10 11 │ │ │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

This means:

>>> c[2.1]
5
Copy the code

It should now be clear what it means to have an array with one or more dimensions of 1 shape. After execution:

>>> d = a.reshape((12.1))
Copy the code

The array d is indexed by two indexes, the first from 0 to 11, and the second by all zeros:

I = 0 1 2 3 4 5 6 7 8 9 10 11 j = 0 0 0 0 0 0 0 0 0 0 0 0 ┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ 0 │ │ │ 1 2 3 4 5 6 7 │ │ │ │ │ 8 9 10 11 │ │ │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

So:

>>> d[10.0]
10
Copy the code

Dimension 1 is “free” (in a sense), so no one can stop you from doing it:

>>> e = a.reshape((1.2.1.6.1))
Copy the code

Makes all of an array look like this:

i= 0 0 0 0 0 0 0 0 0 0 0 0 j= 0 0 0 0 0 0 1 1 1 1 1 1 k= 0 0 0 0 0 0 0 0 0 0 0 0 l= 0 1 2 3 4 5 0 1 2 3 4 5 m= 0 0 0 0 0 0 0 0 0 0 0 0 ┌ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ┐ │ │ │ │ 0 1 2 3 4 5 6 7 │ │ │ │ │ │ │ 9 8 10 11 │ │ └ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ┘Copy the code

Therefore:

>>> e[0.1.0.0.0]
6
Copy the code

For more details on how to implement arrays, see the NumPy internal documentation.

2. What should I do?

0 Since Numpy. 0 is just creating a new view, 0 Don’t be afraid to use it if necessary This is the right tool when you want to index arrays in different ways.

However, in longer computations, it’s usually possible to first arrange to construct arrays with the “correct” shape to minimize 0 0 and transpose. But without seeing the actual context that led to the 0 0 demand, it’s hard to say what should be changed

Examples from your question are:

numpy.dot(M[:,0], numpy.ones((1, R)))
Copy the code

But this is unrealistic. First, this expression:

M[:,0].sum()
Copy the code

It’s easier to calculate the results. Second, is there really anything special about column 0? Maybe what you really need is:

M.sum(axis=0)
Copy the code