Why does numpy's broadcasting sometimes allow comparing arrays of different lengths? - python

I'm trying to understand how numpy's broadcasting affects the output of np.allclose.
>>> np.allclose([], [1.])
True
I don't see why that works, but this does not:
>>> np.allclose([], [1., 2.])
ValueError: operands could not be broadcast together with shapes (0,) (2,)
What are the rules here? I can't finding anything in the numpy docs regarding empty arrays.

Broadcasting rules apply to addition as well,
In [7]: np.array([])+np.array([1.])
Out[7]: array([], dtype=float64)
In [8]: np.array([])+np.array([1.,2.])
....
ValueError: operands could not be broadcast together with shapes (0,) (2,)
Let's look at the shapes.
In [9]: np.array([]).shape,np.array([1.]).shape,np.array([1,2]).shape
Out[9]: ((0,), (1,), (2,))
(0,) and (1,) - the (1,) can be adjusted to match the shape of the other array. A 1 dimension can be adjusted to match the other array, from example increased from 1 to 3. But here it was (apparently) adjusted from 1 to 0. I don't usually work with arrays with a 0 dimension, but this looks like a proper generalization of higher dimensions.
Try (0,) and (1,1). The result is (1,0):
In [10]: np.array([])+np.array([[1.]])
Out[10]: array([], shape=(1, 0), dtype=float64)
(0,), (1,1) => (1,0),(1,1) => (1,0)
As for the 2nd case with shapes (0,) and (2,); there isn't any size 1 dimension to adjust, hence the error.
Shapes (0,) and (2,1) do broadcast (to (2,0)):
In [12]: np.array([])+np.array([[1.,2]]).T
Out[12]: array([], shape=(2, 0), dtype=float64)

Broadcasting doesn't affect np.allclose in any other way than it affects any other function.
As in the comment by #cel, [1.] is of dimension 1 and so can be broadcasted to any other dimension, including 0. On the other hand [1., 2.] is of dimension 2 and thus cannot be broadcasted.
Now why allclose([],[1.]) == True? This actually makes sense: it means that all elements in [] are close to 1.. The opposite would mean that there is at least one element in [] which is not close to 1. which is obviously False since there are no elements at all in [].
Another way to think about it is to ask yourself how you would actually code allclose():
def allclose(array, target=1.):
for x in array:
if not isclose(x, target):
return False
return True
This would return True when called with [].

Related

Numpy matrix multiplication fails ("shapes not aligned") when second element is a vector/array

When I multiply a NxN numpy matrix by a N elements numpy array I get an error saying that the shapes are not aligned.
from numpy import matrix,ones,eye
A = matrix(eye(3))
b = ones(3)
A*b
ValueError: shapes (3,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)
Also trasposing the vector does not solve the issue.
A*b.T
ValueError: shapes (3,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)
This make sense as numpy does not distinguish between column and row vectors so b.T is equal to b.
How can I perform a simple matrix-vector multiplication?
(Don't use np.matrix, it's deprecated. Instead just use 2D arrays for linear algebra.)
Use the matrix multiplication operator #:
In [177]: from numpy import ones,eye
...: A = eye(3)
...: b = ones(3)
...: A # b
Out[177]: array([1., 1., 1.])
Because A is a matrix, Python calls A's __mul__ method to compute A*b, with b as its argument. That is, it calls A.__mul__(b). A numpy matrix insists on making everything a 2-d matrix, so it converts b to a matrix before performing the matrix multiplication. When b is converted to a numpy matrix, the result has shape (1, 3):
In [248]: b
Out[248]: array([1., 1., 1.])
In [249]: np.matrix(b).shape
Out[249]: (1, 3)
Then __mul__ complains that the shapes are not aligned, because A has shape (3, 3) and the converted b has shape (1, 3).
One way to fix this is to ensure that b is 2-d with shape (3, 1) before doing the multiplication:
In [250]: b = ones((3, 1))
In [251]: A * b
Out[251]:
matrix([[1.],
[1.],
[1.]])
In the long term, though, it would be better to modify your code to not use matrix at all, as mentioned by #w-m.
The problem arises from the fact that the operator '*' is performing element-wise multiplication and NOT matrix multiplication as you intend. In python 3 this can be done using the '#' operator, as suggested by w-m. In python 2, however, you must use.
import numpy as np
result = np.dot(A,b)
The '*' operator will try to multiple every element of A with the corresponding element of b. If these are not the same shape you will get an error, which is what you see.
EDIT: I misunderstood OP's question. '*' will work for matrix multiplication if both objects are matrices. However, np.ones(3) produces a numpy array, which is not a numpy matrix object, so it doesn't work and tries to do element-wise multiplication.
if b becomes:
b = np.matrix((1,1,1)).T
then the code will work. It should also be noted that np.dot(A,b) will work even if both A and b are matrix objects, numpy arrays, or a mix of the two, making it the most general solution.

Numpy dot over of shapes (2,) (3,1) gives error but multiplication doesn't

I'm looking for a bit clarification on broadcasting rules and numpy.dot method over multiplication factor. I have created two arrays of shape (2,) and (3,) which can be multiplied by adding a new axis (3,1 shape) but it couldn't be through np.dot method even though adding a new axis and turning into (3,1) shape. here's the below little test done.
x_1 = np.random.rand(2,)
print(x_1)
x_2 = np.random.rand(3,)
print(x_2)
> [ 0.48362051 0.55892736]
> [ 0.16988562 0.09078386 0.04844093]
x_8 = np.dot(x_1, x_2[:, np.newaxis])
> ValueError: shapes (2,) and (3,1) not aligned: 2 (dim 0) != 3 (dim 0)
x_9 = x_1 * x_2[:, np.newaxis]
print(x_9)
> [[ 0.47231067 0.30899592]
[ 0.17436521 0.11407352]
[ 0.01312074 0.00858387]]
x__7 = x_1[:, np.newaxis] * x_2[:, np.newaxis]
> ValueError: operands could not be broadcast together with shapes (2,1) (3,1)
I understand np.dot of (2,1) & (1,3) works, but why not (2,1) & (3,1) because broadcasting rule number two says, Two dimensions are compatible when one of them is 1. So if one of its dimension is 1, np.dot should work or have I understood rule number two wrong ? Also why X_9 works (multiplication) but not x_8 (np.dot), when both are same shapes.
np.dot is for matrix-matrix multiplication (where a column vector can be considered to be a matrix with one column and a row vector as a matrix with one row).
* (multiplication) is for scalar multiplication in the case that one of the arguments is a scalar, and broadcasting otherwise. So the broadcasting rules are not for np.dot.
x_9 works because, as stated in the broadcasting rules here https://docs.scipy.org/doc/numpy-1.12.0/user/basics.broadcasting.html
When operating on two arrays, NumPy compares their shapes element-wise. It starts with the trailing dimensions, and works its way forward. Two dimensions are compatible when
they are equal, or
one of them is 1
so your (only) dimension of x_1, (which is 2) is compatible with the last dimension of x_2 (which is 1 because you added a new dimension), and the remaining dimension is 3.

How can I initialize an empty Numpy array with a given number of dimensions?

I basically want to initialize an empty 6-tensor, like this:
a = np.array([[[[[[]]]]]])
Is there a better way than writing the brackets explicitly?
You can use empty or zeros.
For example, to create a new array of 2x3, filled with zeros, use: numpy.zeros(shape=(2,3))
You can do something like np.empty(shape = [1] * (dimensions - 1) + [0]).
Example:
>>> a = np.array([[[[[[]]]]]])
>>> b = np.empty(shape = [1] * 5 + [0])
>>> a.shape == b.shape
True
Iteratively adding rows of that rank-1 using np.concatenate(a,b,axis=0)
Don't. Creating an array iteratively is slow, since it has to create a new array at each step. Plus a and b have to match in all dimensions except the concatenation one.
np.concatenate((np.array([[[]]]),np.array([1,2,3])), axis=0)
will give you dimensions error.
The only thing you can concatenate to such an array is an array with size 0 dimenions
In [348]: np.concatenate((np.array([[]]),np.array([[]])),axis=0)
Out[348]: array([], shape=(2, 0), dtype=float64)
In [349]: np.concatenate((np.array([[]]),np.array([[1,2]])),axis=0)
------
ValueError: all the input array dimensions except for the concatenation axis must match exactly
In [354]: np.array([[]])
Out[354]: array([], shape=(1, 0), dtype=float64)
In [355]: np.concatenate((np.zeros((1,0)),np.zeros((3,0))),axis=0)
Out[355]: array([], shape=(4, 0), dtype=float64)
To work iteratively, start with a empty list, and append to it; then make the array at the end.
a = np.zeros((1,1,1,1,1,0)) could be concatenated on the last axis with another np.ones((1,1,1,1,1,n)) array.
In [363]: np.concatenate((a,np.array([[[[[[1,2,3]]]]]])),axis=-1)
Out[363]: array([[[[[[ 1., 2., 3.]]]]]])
You could directly use the ndarray constructor:
numpy.ndarray(shape=(1,) * 6)
Or the empty variant, since it seems to be more popular:
numpy.empty(shape=(1,) * 6)
This should do it:
x = np.array([])

why do we need np.squeeze()?

Very often, arrays are squeezed with np.squeeze(). In the documentation, it says
Remove single-dimensional entries from the shape of a.
However I'm still wondering: Why are zero and nondimensional entries in the shape of a? Or to put it differently: Why do both a.shape = (2,1) and (2,) exist?
Besides the mathematical differences between the two things, there is the issue of predictability. If your suggestion was followed, you could at no point rely on the dimension of your array. So any expression of the form my_array[x,y] would need to be replaced by something that first checks if my_array is actually two-dimensional and did not have an implicit squeeze at some point. This would probably obfuscate code far more than the occasional squeeze, which does a clearly specified thing.
Actually, it might even be very hard to tell, which axis has been removed, leading to a whole host of new problems.
In the spirit of The Zen of Python, also Explicit is better than implicit, we can also say that we should prefer explicit squeeze to implicit array conversion.
This helps you get rid of useless one dimension arrays like using
[7,8,9] instead of [[[7,8,9]]]
or [[1,2,3],[4,5,6]] instead of [[[[1,2,3],[4,5,6]]]].
Check this link from tutorials point for example.
One example of the importance is when multiplying arrays. Two 2-dimensional arrays will multiply each value at a time
e.g.
>>> x = np.ones((2, 1))*2
>>> y = np.ones((2, 1))*3
>>> x.shape
(2,1)
>>> x*y
array([[ 6.],
[ 6.]])
If you multiply a 1d array by a 2d array then the behaviour is different
>>> z = np.ones((2,))*3
>>> x*z
array([[ 6., 6.],
[ 6., 6.]])
Secondly, you also might want to squeeze the earlier dimensions e.g. a.shape = (1,2,2) to a.shape = (2,2)
When you squeeze a (2,1) array, you get (2,) which works as both (2,1) and (1,2):
>>> a = np.ones(2)
>>> a.shape
(2,)
>>> a.T.shape
(2,)
>>> X = np.ones((2,2))*2
>>> np.dot(a,X)
[4. 4.]
>>> np.dot(X,a)
[4. 4.]
This cannot happen with a (2,1) array:
>>> b = np.ones((2,1))
>>> np.dot(b,X)
Traceback (most recent call last):
ValueError: shapes (2,1) and (2,2) not aligned: 1 (dim 1) != 2 (dim 0)

Element wise comparison between 1D and 2D array

Want to perform an element wise comparison between an 1D and 2D array. Each element of the 1D array need to be compared (e.g. greater) against the corresponding row of 2D and a mask will be created. Here is an example:
A = np.random.choice(np.arange(0, 10), (4,100)).astype(np.float)
B = np.array([5., 4., 8., 2. ])
I want to do
A<B
so that first row of A will be compared against B[0] which is 5. and the result will be an boolean array.
If I try this I get:
operands could not be broadcast together with shapes (4,100) (4,)
Any ideas?
You need to insert an extra dimension into array B:
A < B[:, None]
This allows NumPy to properly match up the two shapes for broadcasting; B now has shape (4, 1) and the dimensions can be paired up:
(4, 100)
(4, 1)
The rule is that either the dimensions have the same length, or one of the lengths needs to be 1; here 100 can be paired with 1, and 4 can be paired with 4. Before the new dimension was inserted, NumPy tried to pair 100 with 4 which raised the error.

Categories