2017-10-04 13 views
2

j'ai une latitude et la longitude stockée dans une trame de données de panda (df) avec des points de remplissage comme NaN pour stop_id, stoplat, stoplon, et dans une autre trame de données areadf, qui contient plus de lats/lons et arbitraire id; c'est l'information qui doit être remplie en df. J'essaie de connecter les deux pour que les colonnes d'arrêt dans df contiennent des informations sur l'arrêt le plus proche de ce point lat/lon, ou laissez NaN s'il n'y a pas d'arrêt dans un rayon R du point.Accélérer un imbriquée pour une boucle à travers deux Pandas DataFrames

En ce moment, mon code est le suivant, mais il faut un temps très long (> 40 minutes pour ce que je suis en train de faire, avant de changer de zone en df et d'utiliser des cela fera?) car il y a des milliers de points de latitude/longitude et d'arrêts pour chaque ensemble de données, ce qui est un problème car je dois l'exécuter sur plusieurs fichiers. Je cherche des suggestions pour le faire fonctionner plus vite. J'ai déjà apporté quelques améliorations mineures (par exemple, passer à un dataframe, utiliser des itertuples au lieu de iterrows, définir des lats et des lons en dehors de la boucle pour éviter d'avoir à le récupérer depuis df) mais je n'ai plus d'idée l'accélérer. getDistance utilise la formule de Haversine telle que définie pour obtenir la distance entre le signe d'arrêt et le point lat, lon donné.

import pandas as pd 
from math import cos, asin, sqrt 

R=5 
lats = df['lat'] 
lons = df['lon'] 
for stop in areadf.itertuples(): 
    for index in df.index: 
     if getDistance(lats[index],lons[index], 
         stop[1],stop[2]) < R: 
      df.at[index,'stop_id'] = stop[0] # id 
      df.at[index,'stoplat'] = stop[1] # lat 
      df.at[index,'stoplon'] = stop[2] # lon 

def getDistance(lat1,lon1,lat2,lon2): 
    p = 0.017453292519943295  #Pi/180 
    a = (0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * 
     cos(lat2 * p) * (1 - cos((lon2 - lon1) * p))/2) 
    return 12742 * asin(sqrt(a)) * 100 

données de l'échantillon:

df 
lat  lon   stop_id stoplat stoplon 
43.657676 -79.380146 NaN  NaN  NaN 
43.694324 -79.334555 NaN  NaN  NaN 

areadf 
stop_id stoplat stoplon 
0   43.657675 -79.380145 
1   45.435143 -90.543253 

Souhaité:

df 
lat  lon   stop_id stoplat stoplon 
43.657676 -79.380146 0   43.657675 -79.380145 
43.694324 -79.334555 NaN  NaN  NaN 
+0

vous pouvez utiliser pypy au lieu de cython, pypy à c compile pour accélérer les boucles en python –

+3

1. Ne pas itérer sur les dataframes comme ça, tirer parti des pandas géants 2. utiliser la distance euclidienne comme une première passe, Soustrayez vos données en grilles lat/lon, où rien dans la grille x et ses 8 cellules environnantes ne se trouve à l'intérieur de R de quoi que ce soit dans la grille y, et courez sur les arrêts de sous-ensembles vs points . – jeremycg

+0

@jeremycg Avez-vous des fonctions que vous suggérez de mieux prendre en compte pour profiter des pandas? Merci pour votre réponse! – amper

Répondre

1

Une façon serait d'utiliser la fonction numpy de Haversine de here, légèrement modifiée afin que vous puissiez tenir compte du rayon tu veux.

Le juste itérer votre df avec appliquer et trouver la valeur la plus proche dans un rayon donné

def haversine_np(lon1, lat1, lon2, lat2,R): 
    """ 
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees) 
    All args must be of equal length.  
    """ 
    lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2 
    c = 2 * np.arcsin(np.sqrt(a)) 
    km = 6367 * c 
    if km.min() <= R: 
     return km.argmin() 
    else: 
     return -1 

df['dex'] = df[['lat','lon']].apply(lambda row: haversine_np(row[1],row[0],areadf.stoplon.values,areadf.stoplat.values,1),axis=1) 

fusionner ensuite les deux dataframes.

df.merge(areadf,how='left',left_on='dex',right_index=True).drop('dex',axis=1) 

     lat  lon stop_id stoplat stoplon 
0 43.657676 -79.380146  0.0 43.657675 -79.380145 
1 43.694324 -79.334555  NaN  NaN  NaN 

REMARQUE: Si vous choisissez de suivre cette méthode, vous devez vous assurer que les deux indices de dataframes sont remis à zéro ou qu'ils sont commandés de manière séquentielle de 0 à len total de df. Assurez-vous donc de réinitialiser les index avant de lancer cette opération.

df.reset_index(drop=True,inplace=True) 
areadf.reset_index(drop=True,inplace=True) 
+1

Cela a bien fonctionné pour accélérer l'algorithme!De quelques heures à quelques secondes, et aussi quelques secondes plus vite que l'approche que j'avais mise en œuvre en utilisant l'optimisation pycon mentionnée ci-dessus. Merci beaucoup! – amper