Search code examples
pythonnumpymatrix-multiplication

Apply function to numpy matrix dependent on position


Given a 2-d numpy array, X, of shape [m,m], I wish to apply a function and obtain a new 2-d numpy matrix P, also of shape [m,m], whose [i,j]th element is obtained as follows:

P[i][j] = exp (-|| X[i] - x[j] ||**2)

where ||.|| represents the standard L-2 norm of a vector. Is there any way faster than a simple nested for loop?

For example,

X = [[1,1,1],[2,3,4],[5,6,7]]

Then, at diagonal entries the rows accessed will be the same and the norm/magnitude of their difference will be 0. Hence,

P[0][0] = P[1][1] = P[2][2] = exp (0) = 1.0

Also,

P[0][1] = exp (- || X[0] - X[1] ||**2) = exp (- || [-1,-2,-3] || ** 2) = exp (-14)

etc.

The most trivial solution using a nested for loop is as follows:

import numpy as np
X = np.array([[1,2,3],[4,5,6],[7,8,9]])
P = np.zeros (shape=[len(X),len(X)])
for i in range (len(X)):
    for j in range (len(X)):
        P[i][j] = np.exp (- np.linalg.norm (X[i]-X[j])**2)
        
print (P)

This prints:

P = [[1.00000000e+00 1.87952882e-12 1.24794646e-47]
    [1.87952882e-12 1.00000000e+00 1.87952882e-12]
    [1.24794646e-47 1.87952882e-12 1.00000000e+00]]

Here, m is of the order of 5e4.


Solution

  • As hpaulj mentioned cdist does it better. Try the following.

    from scipy.spatial.distance import cdist
    import numpy as np
    
    np.exp(-cdist(X,X,'sqeuclidean'))
    

    Notice the sqeuclidean. This means that scipy does not take the square root so you don't have to square like you did above with the norm.