Получить матрицу координатного расстояния, используя панд без петель - PullRequest
0 голосов
/ 26 октября 2018

В настоящее время я получаю матрицу расстояний координат из двух фреймов данных (ref_df и comp_df), используя вложенный цикл for для строк в обоих фреймах данных, как показано ниже.

import geopy.distance
import pandas as pd

ref_df = pd.DataFrame({"grp_id":['M-00353','M-00353','M-00353','M-00538','M-00538','M-00160','M-00160','M-00160',
                  'M-00509','M-00509','M-00509','M-00509'],"name": ['B1','IIS','IISB I','BK',
                  'MM - BK','H(SL)','H(PKS SL)','PTH','ASSM 1','PKS SSM','SSM',
                  'Sukajadi Sawit Mekar 1'],"lat": [0.43462,0.43462,0.43462,1.74887222,1.74887222,-2.6081,
                   -2.6081,-2.6081, -2.378258,-2.378258,-2.378258,-2.378258],"long":[101.822603,101.822603,101.822603,101.3710944,101.3710944,
                   104.12525,104.12525,104.12525,112.542356,112.542356,112.542356,112.542356]})


comp_df = pd.DataFrame({"uml_id": ['PO1000000021','PO1000000054','PO1000000058','PO1000000106'],
                   "mill_name": ['PT IIS-BI','PT MM-BK','HL','PT SSM'],
                   "Latitude": [0.4344444,0.077043,-2.6081,-2.381111],"Longitude":[101.825,102.030838,104.12525,112.539722]})

matched_coords = []
for row in ref_df.index:
    mill_id = ref_df.get_value(row, "grp_id")
    mill_lat = ref_df.get_value(row, "lat")
    mill_long = ref_df.get_value(row, "long")

    for columns in comp_df.index:
        gm_id = comp_df.get_value(columns, "uml_id")
        gm_lat = comp_df.get_value(columns, "Latitude")
        gm_long = comp_df.get_value(columns, "Longitude")

        dist = geopy.distance.distance(
            (mill_lat, mill_long),
            (gm_lat, gm_long)).km 

        matched_coords.append([
            mill_id, mill_lat, mill_long,
            gm_id, gm_lat, gm_long, dist
        ])

# Convert to data frame        
mc_df = pd.DataFrame(matched_coords)
mc_df.columns = [
    'grp_id', 'grp_lat', 'grp_long',
    'match_id', 'match_lat', 'match_long', 'dist'
]

# Pivot to create wide data frame (matrix of distances)
mc_wide_df = mc_df.pivot_table(
    values="dist",
    index=["grp_id", "grp_lat","grp_long"],
    columns="match_id").reset_index()

Однако я хотел бы упростить процесс и код, просто создав вспомогательную функцию, используя apply для фреймов данных.Моя попытка ниже не работает.Кто-нибудь может помочь мне понять, что здесь происходит не так.

# Test apply!
def get_coords_dist(x):
    dist = geopy.distance.distance((x['lat'],x['long']),(comp_df['Latitude'],comp_df['Longitude'])).km
    return pd.Series({comp_df.iloc[i[2]]['uml_id']: i for i in dist})

mc_df = ref_df.merge(ref_df.sort_values('grp_id').apply(get_coords_dist, axis=1), left_index=True, right_index=True)

1 Ответ

0 голосов
/ 26 октября 2018

Вы хотите выполнить перекрестное соединение между двумя фреймами данных ref_df и comp_df.Один из способов сделать это - pd.merge на фиктивном столбце.

def distance_km(x, y):
    return geopy.distance.distance(x, y).km

# it looks like your coordinates depend only on grp_id
ref_df_dd = ref_df.drop_duplicates(['grp_id', 'lat', 'long'])

# assign a dummy "_" column in both data frames, merge, and drop the dummy
# column afterwards
merged_df = pd.merge(
    ref_df_dd.assign(_=1),
    comp_df.assign(_=1),
).drop('_', axis=1)

# apply your distance function on (lat, long) tuples in the Cartesian product
merged_df['distance'] = list(
    map(distance_km,
        merged_df[['lat', 'long']].apply(tuple, 1),
        merged_df[['Latitude', 'Longitude']].apply(tuple, 1)))

# pivot table
merged_df.set_index(['grp_id', 'uml_id']).distance.unstack()

На данный момент merged_df выглядит как

uml_id   PO1000000021  PO1000000054  PO1000000058  PO1000000106
grp_id                                                         
M-00160    422.745678    377.461999      0.000000    936.147322
M-00353      0.267531     45.832819    422.922708   1232.700696
M-00509   1232.642382   1200.904305    936.449658      0.430525
M-00538    153.871840    198.911938    571.009484   1324.234511

, что довольно близко к тому, что вы хотите.


Другое решение (которое более прозрачно и в 2 раза быстрее, чем описанный выше подход) использует itertools.product.

from itertools import product

# create a data frame by iterating over row pairs in the Cartesian product
merged_df = pd.DataFrame([{
    'grp_id': r.grp_id,
    'uml_id': c.uml_id,
    'distance': distance_km((r.lat, r.long), (c.Latitude, c.Longitude))
} for r, c in product(ref_df_dd.itertuples(), comp_df.itertuples())])

# pivot table
merged_df.set_index(['grp_id', 'uml_id']).distance.unstack()

Это дает тот же merged_df, что и выше.

...