2016-06-28 2 views
3

Итак, продолжая обсуждение @TheBlackCat и я имели в this answer, я хотел бы знать, как лучше всего передать аргументы в векторную функцию Numpy. Функция вопроса определяется следующим образом:Лучший способ передать повторяющийся параметр в векторизованную функцию Numpy

vect_dist_funct = np.vectorize(lambda p1, p2: vincenty(p1, p2).meters) 

где vincenty происходит от Geopy package.

я в настоящее время называю vect_dist_funct таким образом:

def pointer(point, centroid, tree_idx): 
    intersect = list(tree_idx.intersection(point)) 
    if len(intersect) > 0: 
     points = pd.Series([point]*len(intersect)).values 
     polygons = centroid.loc[intersect].values 
     dist = vect_dist_funct(points, polygons) 
     return pd.Series(dist, index=intercept, name='Dist').sort_values() 
    else: 
     return pd.Series(np.nan, index=[0], name='Dist') 

points['geometry'].apply(lambda x: pointer(point=x.coords[0], centroid=line['centroid'], tree_idx=tree_idx)) 

(Пожалуйста, обратитесь к вопросу здесь: Labelled datatypes Python)

Моего вопроса относится к тому, что происходит внутри функций pointer. Причина, по которой я конвертирую points в pandas.Series, а затем получать значения (в 4-й строке, как раз под оператором if), сделать ее в том же виде, что и полигоны. Если я просто назову точки либо points = [point]*len(intersect), либо как points = itertools.repeat(point, len(intersect)), Numpy жалуется, что он «не может передавать массивы размера (n, 2) и размера (n,) вместе» (n - длина intersect).

Если я звоню vect_dist_funct так: dist = vect_dist_funct(itertools.repeat(points, len(intersect)), polygons), vincenty жалуется, что я передал ему слишком много аргументов. Я полностью потеряю, чтобы понять разницу между ними.

Обратите внимание, что это координаты, поэтому они всегда будут попарно. Вот примеры того, как point и polygons выглядеть:

point = (-104.950752 39.854744) # Passed directly to the function like this. 
polygons = array([(-104.21750802451864, 37.84052458697633), 
        (-105.01017084789603, 39.82012158954065), 
        (-105.03965315742742, 40.669867471420886), 
        (-104.90353460825702, 39.837631505433706), 
        (-104.8650601872832, 39.870796282334744)], dtype=object) 
      # As returned by statement centroid.loc[intersect].values 

Что такое лучший способ вызвать vect_dist_funct в этом обстоятельстве, так что я могу иметь векторизованный вызов, и оба Numpy и vincenty не будут жаловаться, что я прохождение неправильных аргументов? Также требуются методы, которые приводят к минимальному потреблению памяти и увеличению скорости. Цель состоит в том, чтобы вычислить расстояние между точкой до каждого центроида многоугольника.

+0

После беглого взгляда я нахожу ваш вопрос неясен. В любом случае, поскольку «* не может передавать массивы размера (n, 2) и размер (n,) вместе *»: вы можете передавать только «(n, 2)» с помощью формы '(n, 1)', так что у вас есть чтобы ввести конечный синглтон с '[:, None]' в ваш 1d-массив, чтобы сделать что-то вроде этой работы. –

+0

Также обратите внимание на такие вещи, как '[point] * len (intersect)': вы можете получить список, содержащий * ссылку на ту же переменную *. Если позже вы измените этот список, вы можете ошибочно перепутать все элементы списка сразу. Обычно безопаснее делать что-то вроде '[p для _ в диапазоне (len (intersect))]'. –

+1

@ AndrasDeak, какая часть вопроса непонятна? Я отредактирую его соответствующим образом. В основном то, что я пытаюсь сделать, - это вычислить расстояния между одной точкой и набором точек наиболее эффективным образом. Я думал, что новинка Numpy ускорит все для меня. – Kartik

ответ

3

np.vectorize на самом деле вам не поможет. Согласно documentation:

Функция векторизации предоставляется в первую очередь для удобства, а не для производительности. Реализация по существу является циклом for.

В самом деле, vectorize активно вредит вам, так как он преобразует вклад в Numpy массивов, делает ненужное и дорогостоящее преобразование типов и продуцирующие ошибки, которые вы видите. Вам гораздо лучше использовать функцию с циклом for.

Лучше использовать функцию, а не lambda для функции уровня, поскольку она позволяет иметь докструмент.

Так вот как я бы осуществить то, что вы делаете:

def vect_dist_funct(p1, p2): 
    """Apply `vincenty` to `p1` and each element of `p2`. 

    Iterate over `p2`, returning `vincenty` with the first argument 
    as `p1` and the second as the current element of `p2`. Returns 
    a numpy array where each row is the result of the `vincenty` function 
    call for the corresponding element of `p2`. 
    """ 
    return [vincenty(p1, p2i).meters for p2i in p2] 

Если вы действительно хотите использовать vectorize, вы можете использовать excluded аргумент не векторизации p1 аргумент, или еще лучше создать lambda, что обертывания vincenty и только векторизация второго аргумента:

def vect_dist_funct(p1, p2): 
    """Apply `vincenty` to `p1` and each element of `p2`. 

    Iterate over `p2`, returning `vincenty` with the first argument 
    as `p1` and the second as the current element of `p2`. Returns 
    a list where each value is the result of the `vincenty` function 
    call for the corresponding element of `p2`. 
    """ 
    vinc_p = lambda x: vincenty(p1, x) 
    return np.vectorize(vinc_p)(p2) 
+0

Я не совсем понял, в чем вопрос, но ваш ответ звучит очень разумно :) –

+0

Спасибо. Так что 'for' loop - лучшая реализация. Хотелось бы что-то параллельное. Я на сервере Windows, поэтому выполнение параллельных операций с использованием многопроцессорной библиотеки занимает много памяти. – Kartik

+1

Алгоритм 'vincenty' по своей сути является итеративным, поэтому трудно, если не полностью невозможно эффективно векторизовать. – TheBlackCat

Смежные вопросы