3

How do I use a function or set of functions from another module as a parameter in my own function I'm defining?

I'm trying to write a function that compares one vector q and calculates its distance from each vector in a set of vectors x. The function get_distances should take the one vector q, the set of vectors x, and a parameter dist_method. dist_method is supposed to take any one of the distance calculations from scipy.spatial.distance and use that for the calculation, so I could call the function something like this: distances = get_distances(q, x, 'euclidean') Here's the scipy reference page - get_distances should be able to take any of the distance functions braycurtis, canberra, ... , sqeuclidean, wminkowski: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html

At the top of the file1.py file where this function is located, I've imported scipy.spatial.distance, from which I thought I should have access to functions to use just like distance.euclidean(), but when I call get_distances in the interpreter, I get AttributeError: 'module' object has no attribute 'dist_method'.

I found a lot of answers already like the below that say functions are 'first-class objects' and that I should be able to use them as arguments just like any other argument, and I've tried using the **kwargs concept, but I can't put it all together.

Can someone help me understand what I'm missing?

KNN.py:

import numpy as np
import scipy.spatial.distance as dist

def get_distances(q, x, dist_method='euclidean', *args, **kwargs):
    """Query dataset to get distances for KNN

    Given a numpy array of vectors x and
    query point q, use dist_method to calculate
    distance from q to each vector in x

    Parameters:
        q: tuple
        x: numpy array
        dist_method (optional): distance function from scipy.spatial.distance

    Returns: list of distances
    """

    return [dist.dist_method(q, x_i) for x_i in x]

def load_samples():

    x = np.array([[1, 6],[2, 4],[3, 7],[6, 8],[7, 1],[8, 4]])

    y = np.array([[7],[8],[16],[44],[50],[68]])

    q = (4, 2)

    return x, y, q

Here's what I'm doing in the interpreter:

>>> import KNN as knn
>>> x, y, q = knn.load_samples()
>>> x
array([[1, 6],
       [2, 4],
       [3, 7],
       [6, 8],
       [7, 1],
       [8, 4]])
>>> d = knn.get_distances(q, x, 'cityblock')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "KNN.py", line 19, in get_distances
    return [dist.dist_method(q, x_i) for x_i in x]
AttributeError: 'module' object has no attribute 'dist_method'
jseery
  • 125
  • 1
  • 6

3 Answers3

3

The problem with:

return [dist.dist_method(q, x_i) for x_i in x]

is that your attempting to use dist_method to access a function from dist whose name matches the value of the string (i.e "euclidean"), however dist.dist_method will look up a function named "dist_method" within the dist object, which does not exist.

To access an object's function by name, you would use getattr, which will return the object attribute matching the string.

What you want to do is:

[getattr(dist,dist_method)(q, x_i) for x_i in x]
TheoretiCAL
  • 19,461
  • 8
  • 43
  • 65
0

@TheoretiCAL's answer shows you one approach to get desired result and here is another one:

methods = {
    'canberra': dist.canberra,
    'euclidean': dist.euclidean,
    # etc
}


def get_distances(q, x, dist_method=None, *args, **kwargs):
    method = methods.get(dist_method, dist.euclidean)
    return [method(q, x_i) for x_i in x]
Yaroslav Surzhikov
  • 1,568
  • 1
  • 11
  • 16
-2

The problem is that dist_method is not the name of anything in the module you're trying to use. You can use the modules __dict__ attribute to "fetch" the appropriate method.

[dist.__dict__[dist_method](q, x_i) for x_i in x]
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96