简体   繁体   中英

Is it bad to use numpy arrays in this way?

Main Question: Is it bad (perhaps in terms of computation time or memory) to using np.array([[1, 2, 3]]) everywhere instead of np.array([1, 2, 3]) ?

Motivation:

I come form a math background, so I like to think of things in terms of vectors and matrices. For example, I think of y = np.array([1, 2, 3]) as a row vector, or as a 1 X 3 matrix. However, numpy doesn't treat y quite like a 1 X 3 matrix. For instance, if we take a 2 X 3 matrix A = np.array([[1, 2, 3], [4, 5, 6]]) numpy will allow us to do the matrix multiplication A @ y even though the dimensions are (2 X 3) times (1 X 3) don't make sense mathematically.

On the other hand, y @ AT gives an error even though the dimensions (1 X 3) times (3 X 2) make sense.

So in conclusion np.array([1, 2, 3]) does not behave exactly as a matrix. However, from my experiments it seems that numpy does treat np.array([[1, 2, 3]]) as a bona fide 1 X 3 matrix. So if there are no downsides, I would prefer to use this version.

numpy has an old subclass np.matrix that makes sure everything has exactly 2 dimensions. But it is no longer recommended.

numpy tries to work equally well with 0,1,2, and more dimensions.

In [69]: A = np.array([[1,2,3],[4,5,6]])
In [70]: x = np.array([1,2,3])
In [71]: y = np.array([[1,2,3]])
In [72]: A.shape
Out[72]: (2, 3)

Matrix product of a (2,3) with (3,) resulting a (2,). It's docs say it expands the (3,) to (3,1), getting a (2,1) result, and then squeezing out that 1:

In [73]: A@x
Out[73]: array([14, 32])

The (2,3) with (1,3) transposed produces (2,1):

In [74]: A@y.T
Out[74]: 
array([[14],
       [32]])

(3,) with (3,2) => (2,):

In [78]: x@A.T
Out[78]: array([14, 32])

(1,3) with (3,2) => (1,3):

In [79]: y@A.T
Out[79]: array([[14, 32]])

How does your math intuition handle 3d or higher arrays? matmul/@ handles them nicely. np.einsum does even better.


While you can create a (1,n) arrays, if it makes you more comfortable. But beware that you'll still end up with 1 or even 0d results.

For example with indexing:

In [80]: A
Out[80]: 
array([[1, 2, 3],
       [4, 5, 6]])
In [81]: A[1,:]
Out[81]: array([4, 5, 6])
In [82]: A[:,1]
Out[82]: array([2, 5])
In [83]: A[1,1]
Out[83]: 5
In [84]: A[1,1].shape
Out[84]: ()
In [85]: A[1,1].ndim
Out[85]: 0

or reduction along an axis:

In [86]: A.sum(axis=1)
Out[86]: array([ 6, 15])

though it's possible to retain dimensions:

In [87]: A.sum(axis=1, keepdims=True)
Out[87]: 
array([[ 6],
       [15]])
In [88]: A[[1],:]
Out[88]: array([[4, 5, 6]])
In [89]: A[:,[1]]
Out[89]: 
array([[2],
       [5]])

Another to keep in mind is that numpy operators most operation element-wise. The main exception being @ . Where as in MATLAB A*B is matrix multiplication, and A.*B is element-wise. Add to that broadcasting , which allows us to add a (2,3) and (3,) array:

In [90]: A+x
Out[90]: 
array([[2, 4, 6],
       [5, 7, 9]])

Here (2,3) + (3,) => (2,3) + (1,3) => (2,3). The (3,) 1d array often behaves as a (1,3) or even (1,1,3) if needed. But expansion in the other direction has to be explicit.

In [92]: A / A.sum(axis=1)          # (2,3) with (2,) error
Traceback (most recent call last):
  File "<ipython-input-92-fec3395556f9>", line 1, in <module>
    A / A.sum(axis=1)
ValueError: operands could not be broadcast together with shapes (2,3) (2,) 

In [93]: A / A.sum(axis=1, keepdims=True)   # (2,3) with (2,1) ok
Out[93]: 
array([[0.16666667, 0.33333333, 0.5       ],
       [0.26666667, 0.33333333, 0.4       ]])
In [94]: A / A.sum(axis=1)[:,None]
Out[94]: 
array([[0.16666667, 0.33333333, 0.5       ],
       [0.26666667, 0.33333333, 0.4       ]])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM