2
\$\begingroup\$

Here is the problem,

Given a 2D numpy array 'a' of sizes n×ばつm and a 1D numpy array 'b' of size m. You need to find the distance(Euclidean) of the 'b' vector from the rows of the 'a' matrix. Fill the results in the numpy array. Follow up: Could you solve it without loops?

a = np.array([[1, 1],
 [0, 1],
 [1, 3],
 [4, 5]])
b = np.array([1, 1])
print(dist(a, b))
>>[0,1,2,5]

And here is my solution

import math
def dist(a, b):
 distances = []
 for i in a:
 distances.append(math.sqrt((i[0] - b[0]) ** 2 + (i[1] - b[1]) ** 2))
 return distances
a = np.array([[1, 1],
 [0, 1],
 [1, 3],
 [4, 5]])
print(dist(a, [1, 1]))

I wonder how can this be solved more elegant, and how the additional task can be implemented.

asked Apr 12, 2021 at 16:40
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Use a combination of array broadcasting and universal functions to eliminate the loop. Also take a look at numpy.linalg.norm() \$\endgroup\$ Commented Apr 13, 2021 at 18:24
  • \$\begingroup\$ Yeah, I've already found out about that method, however, thank you! \$\endgroup\$ Commented Apr 13, 2021 at 19:02

1 Answer 1

2
\$\begingroup\$

Don't use math in a Numpy context.

You should vectorise your loop. You had figured this out in a now-deleted answer, where you also passed axis=1. However, in context, I think the axis more likely to hold true for other array shapes is -1, not 1.

If you're writing this as a convenience function - which the question seems to suggest - then you can make it more generic by accepting array-likes that may or may not already be Numpy arrays; and still supporting a parametric axis that only defaults to the last one. subtract() will do the array coercion and broadcasting for you.

import numpy as np
def dist(
 a: np.typing.ArrayLike, b: np.typing.ArrayLike, axis: int = -1,
) -> np.ndarray:
 """
 A wrapper for norm() that always operates on the difference between two vectors using
 standard broadcasting rules, and by default aggregates on the last axis.
 """
 diff = np.subtract(a, b)
 return np.linalg.norm(diff, axis=axis)
def test() -> None:
 a = (
 (1, 1),
 (0, 1),
 (1, 3),
 (4, 5),
 )
 b = (1, 1)
 actual = dist(a, b)
 assert np.allclose(actual, (0, 1, 2, 5), rtol=0, atol=1-14)
if __name__ == '__main__':
 test()
answered Dec 5, 2024 at 13:14
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.