Can I produce the result of np.outer using np.dot? - python

I am trying to improve my understanding of numpy functions. I understand the behaviour of numpy.dot. I'd like to understand the behaviour of numpy.outer in terms of numpy.dot.
Based on this Wikipedia article https://en.wikipedia.org/wiki/Outer_product I'd expect for array_equal to return True in the following code. However it does not.
X = np.matrix([
[1,5],
[5,9],
[4,1]
])
r1 = np.outer(X,X)
r2 = np.dot(X, X.T)
np.array_equal(r1, r2)
How can I assign r2 so that np.array_equal returns True? Also, why does numpy's implementation of np.outer not match the definition of outer multiplication on Wikipedia?
Using numpy 1.9.2

In [303]: X=np.array([[1,5],[5,9],[4,1]])
In [304]: X
Out[304]:
array([[1, 5],
[5, 9],
[4, 1]])
In [305]: np.inner(X,X)
Out[305]:
array([[ 26, 50, 9],
[ 50, 106, 29],
[ 9, 29, 17]])
In [306]: np.dot(X,X.T)
Out[306]:
array([[ 26, 50, 9],
[ 50, 106, 29],
[ 9, 29, 17]])
The Wiki outer link mostly talks about vectors, 1d arrays. Your X is 2d.
In [310]: x=np.arange(3)
In [311]: np.outer(x,x)
Out[311]:
array([[0, 0, 0],
[0, 1, 2],
[0, 2, 4]])
In [312]: np.inner(x,x)
Out[312]: 5
In [313]: np.dot(x,x) # same as inner
Out[313]: 5
In [314]: x[:,None]*x[None,:] # same as outer
Out[314]:
array([[0, 0, 0],
[0, 1, 2],
[0, 2, 4]])
Notice that the Wiki outer does not involve summation. Inner does, in this example 5 is the sum of the 3 diagonal values of the outer.
dot also involves summation - all the products followed summation along a specific axis.
Some of the wiki outer equations use explicit indices. The einsum function can implement these calculations.
In [325]: np.einsum('ij,kj->ik',X,X)
Out[325]:
array([[ 26, 50, 9],
[ 50, 106, 29],
[ 9, 29, 17]])
In [326]: np.einsum('ij,jk->ik',X,X.T)
Out[326]:
array([[ 26, 50, 9],
[ 50, 106, 29],
[ 9, 29, 17]])
In [327]: np.einsum('i,j->ij',x,x)
Out[327]:
array([[0, 0, 0],
[0, 1, 2],
[0, 2, 4]])
In [328]: np.einsum('i,i->',x,x)
Out[328]: 5
As mentioned in the comment, np.outer uses ravel, e.g.
return a.ravel()[:, newaxis]*b.ravel()[newaxis,:]
This the same broadcasted multiplication that I demonstrated earlier for x.

numpy.outer only works for 1-d vectors, not matrices. But for the case of 1-d vectors, there is a relation.
If
import numpy as np
A = np.array([1.0,2.0,3.0])
then this
np.matrix(A).T.dot(np.matrix(A))
should be the same as this
np.outer(A,A)

Another (clunky) version similar to a[:,None] * a[None,:]
a.reshape(a.size, 1) * a.reshape(1, a.size)

Related

Tensordot of 2 vector fields

I want to compute the element-wise tensor product of 2 tensors of the shape (1144,3) meaning I want to compute the tensordot along the second axis if I understood it correctly.
I'd expect my result to be of the shape (1144,3,3).
I am currently trying to achieve this using numpys tensordot() function, but I can't figure out the correct axes to use to get a shape of (1144,3,3).
You can use numpy.einsum for this.
In [30]: a
Out[30]:
array([[0, 1, 2],
[3, 4, 5]])
In [31]: np.einsum('ij,ik->ijk', a, a)
Out[31]:
array([[[ 0, 0, 0],
[ 0, 1, 2],
[ 0, 2, 4]],
[[ 9, 12, 15],
[12, 16, 20],
[15, 20, 25]]])
As numpy.tensordot support only 2 element axes this means there is no way to imitate the
->...-like behavior. So I don't see how this can be done with numpy.tensordot.

indexing in python numpy module

So, I'm new to python and learning about the NumPy module.
Here is my array
c = np.array([[[ 0, 1, 2],
[ 10, 12, 13]],
[[100, 101, 102],
[110, 112, 113]]])
in the above array if I try to access it through
c[:1,0:]
it produces expected output that
# expected because print from initial to row 1,0 excluding row 1,0
array([[[ 0, 1, 2],
[10, 12, 13]]])
but now when I try to access it through
c[:1,1:]
it produces output that
array([[[10, 12, 13]]])
why???
This is a 3D array. You can check it with
print(c.shape)
that yields
(2, 2, 3)
Is 3D array really what you wish to do ?
If so, if you slice it with two indices instead of three, that means that the third is implicitly :. So c[1, 1] is equivalent to c[1, 1, :] which is equivalent to c[1, 1, 0:3].
And your query c[:1,1:] is equivalent to c[0, 1, 0:3]: that is the correct result.
Now as per your comment I guess you wish to reshape, filter and reshape:
c.reshape(4, -1)[:3,:].reshape(1, 3, -1)
yields
array([[[ 0, 1, 2],
[ 10, 12, 13],
[100, 101, 102]]])

Unexpected result from Numpy Matrix insert, How does this work?

My goal was to insert a column to the right on a numpy matrix. However, I found that the code I was using is putting in two columns rather than just one.
# This one results in a 4x1 matrix, as expected
np.insert(np.matrix([[0],[0]]), 1, np.matrix([[0],[0]]), 0)
>>>matrix([[0],
[0],
[0],
[0]])
# I would expect this line to return a 2x2 matrix, but it returns a 2x3 matrix instead.
np.insert(np.matrix([[0],[0]]), 1, np.matrix([[0],[0]]), 1)
>>>matrix([[0, 0, 0],
[0, 0, 0]]
Why do I get the above, in the second example, instead of [[0,0], [0,0]]?
While new use of np.matrix is discouraged, we get the same result with np.array:
In [41]: np.insert(np.array([[1],[2]]),1, np.array([[10],[20]]), 0)
Out[41]:
array([[ 1],
[10],
[20],
[ 2]])
In [42]: np.insert(np.array([[1],[2]]),1, np.array([[10],[20]]), 1)
Out[42]:
array([[ 1, 10, 20],
[ 2, 10, 20]])
In [44]: np.insert(np.array([[1],[2]]),1, np.array([10,20]), 1)
Out[44]:
array([[ 1, 10],
[ 2, 20]])
Insert as [1]:
In [46]: np.insert(np.array([[1],[2]]),[1], np.array([[10],[20]]), 1)
Out[46]:
array([[ 1, 10],
[ 2, 20]])
In [47]: np.insert(np.array([[1],[2]]),[1], np.array([10,20]), 1)
Out[47]:
array([[ 1, 10, 20],
[ 2, 10, 20]])
np.insert is a complex function written in Python. So we need to look at that code, and see how values are being mapped on the target space.
The docs elaborate on the difference between insert at 1 and [1]. But off hand I don't see an explanation of how the shape of values matters.
Difference between sequence and scalars:
>>> np.insert(a, [1], [[1],[2],[3]], axis=1)
array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])
>>> np.array_equal(np.insert(a, 1, [1, 2, 3], axis=1),
... np.insert(a, [1], [[1],[2],[3]], axis=1))
True
When adding an array at the end of another, I'd use concatenate (or one of its stack variants) rather than insert. None of these operate in-place.
In [48]: np.concatenate([np.array([[1],[2]]), np.array([[10],[20]])], axis=1)
Out[48]:
array([[ 1, 10],
[ 2, 20]])

Arbitary 1D slices (elements along an axis) across an ndarray - NumPy

There are a few questions I've found that are close to what I am asking but they are different enough that they don't seem to solve my problem. I am trying to grab a 1d slice along one axis for an ndarray. As an example for a 3d 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,25,26]]]
I want the following 1d slices
[0,1,2]
...
[24,25,26]
[0,3,6]
...
[20,23,26]
[0,9,18]
...
[8,17,26]
which effectively equates to the following (for a 3d arrays):
ary[i,j,:]
ary[i,:,k]
ary[:,j,k]
I want this to generalize to an array of n dimensions
(for a 2d array we would get ary[i,:] and ary[:,j], etc.)
Is there a numpy function that lets me do this?
EDIT: Corrected the 2nd dimension indexing
We could permute axes by selecting each one of the axes one at a time pushing it at the end and reshape. We would make use of ndarray.ndim to generalize to generic n-dim ndarrays. Also, np.transpose would be useful here to permute axes and np.roll to get rolled axes order. The implementation would be quite simple and is listed below -
# a is input ndarray
R = np.arange(a.ndim)
out = [np.transpose(a,np.roll(R,i)).reshape(-1,a.shape[i]) for i in R]
Sample run -
In [403]: a = np.arange(27).reshape(3,3,3)
In [325]: R = np.arange(a.ndim)
In [326]: out = [np.transpose(a,np.roll(R,i)).reshape(-1,a.shape[i]) for i in R]
In [327]: out[0]
Out[327]:
array([[ 0, 1, 2],
[ 3, 4, 5],
...
[24, 25, 26]])
In [328]: out[1]
Out[328]:
array([[ 0, 3, 6],
[ 9, 12, 15],
....
[20, 23, 26]])
In [329]: out[2]
Out[329]:
array([[ 0, 9, 18],
[ 1, 10, 19],
....
[ 8, 17, 26]])

Is it possible to use einsum to transpose everything?

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.
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.
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

Categories

Resources