2016-12-08 4 views
1

У меня есть функция, которая использует генератор для петли над большими 2D-списками питонов поплавковых координат, чтобы создать плоские списки целых чисел, которые представляют расстояние между координатами.Cython - вычисление массива расстояний между 2D-координатами

point_input = {"x": -8081441.0, "y": 5685214.0} 
output = [-8081441, 5685214] 

polyline_input = {"paths" : [[-8081441.0, 5685214.0], [-8081446.0, 5685216.0], [-8081442.0, 5685219.0], [-8081440.0, 5685211.0], [-8081441.0, 5685214.0]]} 
output = [[-8081441, 5685214, 5, -2, -4, -3, -2, 8, 1, -3]] 

polygon_input = {"rings" : [[-8081441.0, 5685214.0], [-8081446.0, 5685216.0], [-8081442.0, 5685219.0], [-8081440.0, 5685211.0], [-8081441.0, 5685214.0]]} 
output = [[-8081441, 5685214, 5, -2, -4, -3, -2, 8, 1, -3]] 

чисто питон:

def geometry_to_distance(geometry, geometry_type): 
    def calculate_distance(coords): 
     iterator = iter(coords) 
     previous_x, previous_y = iterator.next() 
     yield int(previous_x) 
     yield int(previous_y) 
     for current_x, current_y in iterator: 
      yield int(previous_x - current_x) 
      yield int(previous_y - current_y) 
      previous_x, previous_y = current_x, current_y 

    if geometry_type == "POINT": 
     distance_array = [int(geometry["x"]), int(geometry["y"])] 
    elif geometry_type == "POLYLINE": 
     distance_array = [list(calculate_distance(path)) for path in geometry["paths"]] 
    elif geometry_type == "POLYGON": 
     distance_array = [list(calculate_distance(ring)) for ring in geometry["rings"]] 
    else: 
     raise Exception("{} geometry type not supported".format(geometry_type)) 

    return distance_array 

Для выполнения скорости, я хочу использовать реализацию Cython той же функции. Я использую объявление типа для целочисленных переменных в функции calculate_distance. Реализация

Cython:

def geometry_to_distance(geometry, geometry_type): 
    def calculate_distance(coords): 
     cdef int previous_x, previous_y, current_x, current_y 
     iterator = iter(coords) 
     previous_x, previous_y = iterator.next() 
     yield previous_x 
     yield previous_y 
     for current_x, current_y in iterator: 
      yield previous_x - current_x 
      yield previous_y - current_y 
      previous_x, previous_y = current_x, current_y 

    if geometry_type == "POINT": 
     distance_array = [geometry["x"], geometry["y"]] 
    elif geometry_type == "POLYLINE": 
     distance_array = [list(calculate_distance(path)) for path in geometry["paths"]] 
    elif geometry_type == "POLYGON": 
     distance_array = [list(calculate_distance(ring)) for ring in geometry["rings"]] 
    else: 
     raise Exception("{} geometry type not supported".format(geometry_type)) 

    return distance_array 

здесь сценарий, который может быть использован для сравнения функции:

import time 
from functools import wraps 
import numpy as np 
import geometry_converter as gc 

def timethis(func): 
    '''Decorator that reports the execution time.''' 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
     start = time.time() 
     result = func(*args, **kwargs) 
     end = time.time() 
     print(func.__name__, end-start) 
     return result 
    return wrapper 


def prepare_data(featCount, size): 
    ''' Create arrays of polygon geometry (see polygon_input above)''' 
    input = [] 
    for i in xrange(0, featCount): 
     polygon = {"rings" : []} 
     #random x,y coordinates inside a quadrant of the world bounding box in a spherical mercator (epsg:3857) projection 
     ys = np.random.uniform(-20037507.0,0,size).tolist() 
     xs = np.random.uniform(0,20037507.0,size).tolist() 
     polygon["rings"].append(zip(xs,ys)) 
     input.append(polygon) 
    return input 

@timethis 
def process_data(data): 
    output = [gc.esriJson_to_CV(x, "POLYGON") for x in data] 
    return output 

data = prepare_data(100, 100000) 
process_data(data) 

Есть улучшения, которые могли бы повысить производительность в реализации Cython? возможно, используя 2D-цитонные массивы или карри?

+0

Почему бы просто не использовать 'numpy.diff', чтобы принять 1-ю разницу в координатах X и Y? – pbreach

+0

Потому что кажется, что создание numpy.array из огромных 2D-списков python происходит слишком медленно. –

+0

У вас будет такая же проблема с cython или c массивами. Списки не сохраняются в непрерывной памяти, тогда как (однородные) массивы numpy, cython и c. Таким образом, преобразование займет некоторое время независимо от этих подходов. Я удивлен, что 'numpy.diff' не быстрее, чем реализация cython, учитывая использование генераторов и списков. – pbreach

ответ

1

Питон, переписаны без генератора, является

In [362]: polyline_input = {"paths" : [[-8081441.0, 5685214.0], [-8081446.0, 568 
    ...: 5216.0], [-8081442.0, 5685219.0], [-8081440.0, 5685211.0], [-8081441.0 
    ...: , 5685214.0]]} 
In [363]: output=polyline_input['paths'][0][:] # copy 
In [364]: i0,j0 = output 
    ...: for i,j in polyline_input['paths'][1:]: 
    ...:  output.extend([i0-i, j0-j][:]) 
    ...:  i0,j0 = i,j 
    ...:  
In [365]: output 
Out[365]: [-8081441.0, 5685214.0, 5.0, -2.0, -4.0, -3.0, -2.0, 8.0, 1.0, -3.0] 

Я просто думаю, хотя альтернативные способы выражения расчета. Я мог бы использовать append для списка пар вместо плоского списка.

Массив эквивалент:

In [375]: arr=np.array(polyline_input['paths']) 
In [376]: arr[1:,:]=arr[:-1,:]-arr[1:,:] 
In [377]: arr.ravel().tolist() 
Out[377]: [-8081441.0, 5685214.0, 5.0, -2.0, -4.0, -3.0, -2.0, 8.0, 1.0, -3.0] 

Игнорирование стоимости преобразования списка в массив, который выглядит как эффективная работа Numpy. Чтобы улучшить его на cython, я ожидаю, что вам нужно будет преобразовать массив в memoryview и повторить стиль c над парами значений.

Я забыл, почему вы переходите на этот формат расстояния. Вы пытаетесь сохранить некоторое пространство? Или ускорить вычисление вниз по течению?

+0

Благодарим вас за ответ! Да, цель состоит в том, чтобы получить легкое представление геометрии, чтобы увеличить скорость обмена в сети –