Быстрое преобразование востока и севера в широту и долготу для большого DataFrame местоположений

Я использую Pandas и PyProj для преобразования востока и севера в долготу и широту, а затем сохраняю разделенный вывод в 2 столбца, как это ...

v84 = Proj(proj="latlong",towgs84="0,0,0",ellps="WGS84")
v36 = Proj(proj="latlong", k=0.9996012717, ellps="airy",
        towgs84="446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894")
vgrid = Proj(init="world:bng")


def convertLL(row):

    easting = row['easting']
    northing = row['northing']

    vlon36, vlat36 = vgrid(easting, northing, inverse=True)

    converted = transform(v36, v84, vlon36, vlat36)

    row['longitude'] = converted[0]
    row['latitude'] = converted[1]

    return row


values = pd.read_csv("values.csv")
values = values.apply(convertLL, axis=1)

Это работает, но очень медленно и не работает для больших наборов данных. Стремясь улучшить ситуацию, я пытаюсь преобразовать это, чтобы вместо этого использовать функцию lamba в надежде, что это ускорит процесс. У меня это пока есть ...

def convertLL(easting, northing):

    vlon36, vlat36 = vgrid(easting, northing, inverse=True)

    converted = transform(v36, v84, vlon36, vlat36)

    row = row['longitude'] = converted[0]

    return row


values ['longitude'] = values.apply(lambda row: convertLL(row['easting'], row['northing']), axis=1)

Эта преобразованная версия работает и работает быстрее, чем моя старая, и не имеет тайм-аута для больших наборов данных, но это работает только для долготы, есть ли способ заставить ее также выполнять широту?

Кроме того, это векторизовано? Могу я еще ускорить процесс?

РЕДАКТИРОВАТЬ

Образец данных ...

name | northing | easting | latitude | longitude
------------------------------------------------
tl1  | 378778   | 366746  |          |
tl2  | 384732   | 364758  |          |

person fightstarr20    schedule 26.05.2020    source источник
comment
Не могли бы вы дать нам результат df.head(), чтобы мне было с чем поиграться?   -  person roganjosh    schedule 26.05.2020
comment
Я обновил сообщение образцом, этого достаточно?   -  person fightstarr20    schedule 26.05.2020
comment
Извините, меня перезвонили, поэтому у меня не было возможности взглянуть на это. Сначала я хорошо подумал, что мы, вероятно, сможем избавиться от всех этих вызовов функций PyProj и реализовать векторизованную версию, а затем я нашел это что действительно отталкивает меня от попытки использовать этот подход: P   -  person roganjosh    schedule 26.05.2020
comment
Да, PyProj, кажется, подходит для этой задачи, я посмотрел на вычисления с нуля и быстро передумал :)   -  person fightstarr20    schedule 26.05.2020
comment
Присматриваясь к этому, у нас может быть разумный шанс превратить это в тупик. Я попробую   -  person roganjosh    schedule 26.05.2020
comment
Ага, теперь я знаю, как это сделать. transform уже принимает входные данные массива. Пожалуйста, покажите ваш импорт (для vgrid) и где определены v36 и v84, чтобы я мог провести воспроизводимый тест?   -  person roganjosh    schedule 26.05.2020
comment
Обновили op   -  person fightstarr20    schedule 26.05.2020


Ответы (1)


Из-за сюжета, я думаю, мы не могли видеть лес за деревьями. Если мы посмотрим на документы для _1 _ вы увидите:

  • xx (скаляр или массив (numpy или python)) - введите координаты x.
  • yy (скаляр или массив (numpy или python)) - введите координаты y.

Здорово; массив numpy - это именно то, что нам нужно. pd.DataFrame можно рассматривать как словарь массивов, поэтому нам просто нужно изолировать эти столбцы и передать их функции. Есть небольшая загвоздка - столбцы DataFrame будут Series, которые transform отклонят, поэтому нам просто нужно использовать атрибут values. Этот мини-пример прямо эквивалентен вашему первоначальному подходу:

def vectorized_convert(df):
    vlon36, vlat36 = vgrid(df['easting'].values, 
                           df['northing'].values, 
                           inverse=True)
    converted = transform(v36, v84, vlon36, vlat36)
    df['longitude'] = converted[0]
    df['latitude'] = converted[1]
    return df

df = pd.DataFrame({'northing': [378778, 384732],
                   'easting': [366746, 364758]})

print(vectorized_convert(df))

И мы закончили. Помимо этого, мы можем взглянуть на тайминги для 100 строк (текущий подход взрывается для моих обычных 100000 строк для примеров времени):

def current_way(df):
    df = df.apply(convertLL, axis=1)
    return df


def vectorized_convert(df):
    vlon36, vlat36 = vgrid(df['easting'].values, 
                           df['northing'].values, 
                           inverse=True)

    converted = transform(v36, v84, vlon36, vlat36)
    df['longitude'] = converted[0]
    df['latitude'] = converted[1]
    return df


df = pd.DataFrame({'northing': [378778, 384732] * 50,
                   'easting': [366746, 364758] * 50})

Дает:

%timeit current_way(df)
289 ms ± 15.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vectorized_convert(df)
2.95 ms ± 59.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
person roganjosh    schedule 26.05.2020
comment
Выглядит очень хорошо, дайте мне немного времени, чтобы все переварить, и я вернусь. Большое спасибо! - person fightstarr20; 26.05.2020
comment
Я потратил некоторое время, пытаясь реализовать, но я не уверен, как передать свой фрейм данных функции. В вашем примере вы указываете значения, но как мне заставить его обрабатывать каждую строку без использования apply? Или дело в том, что теперь он векторизован, нам не нужно использовать apply? - person fightstarr20; 27.05.2020
comment
@ fightstarr20 суть в том, чтобы избегать использования apply. Если вы запустите первый фрагмент кода, вы увидите, что обе строки заполнены значениями latitutde и longitude за один вызов функции. - person roganjosh; 27.05.2020
comment
@ fightstarr20 - это природа векторизованных операций - они действуют на массивы, как если бы они были скалярами, поэтому нам не нужно перебирать строки (что медленно). PyProj, похоже, активно использует Cython, поэтому это кодовая база, которая компилируется до C ++. Мы хотим передавать массивы и заставить их работать со всеми значениями одновременно, что может использовать такие вещи, как BLAS / LAPACK и SIMD. apply по умолчанию будет использовать цикл python for, который имеет множество накладных расходов. Передайте весь df функции - person roganjosh; 27.05.2020
comment
Понятно, спасибо за разъяснение, удалось запустить его сейчас. Прирост скорости невероятный! Больше никаких тайм-аутов, не могу поблагодарить вас за вашу помощь! - person fightstarr20; 27.05.2020
comment
@ fightstarr20 Добро пожаловать. Я бы хотел переименовать ваш вопрос, чтобы сделать его более конкретным для темы, если это нормально? Я думаю, что это слишком широко, чтобы быть полезным для других - person roganjosh; 27.05.2020