comment puis-je optimiser un code python pour calculer la distance entre deux points GPS

Je cherche un moyen plus rapide d'optimiser mon code python pour calculer la distance entre deux points GPS, la longitude et la latitude. Voici mon code et je souhaite l'optimiser pour qu'il fonctionne plus rapidement.

 def CalcDistanceKM(lat1, lon1, lat2, lon2):
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = 6371 * c
return distance

Le comportement de ce code est de calculer une distance entre deux latitudes et longitudes à partir de deux fichiers Excel différents (fichiers CSV) et de renvoyer la distance entre eux.

Un code plus pour expliquer le comportement:

for i in range(File1):
for j in range(File2):
if File1['AA'][i] == File2['BB'][j]:
distance = CalcDistanceKM(File2['LATITUDE'][j], File2['LONGITUDE'][j],
File1['Latitude'][i],File1['Longitude'][I])
File3 = File3.append({'DistanceBetweenTwoPoints': (distance) })

Merci.


Solution du problème

préparez vos points dans des tableaux numpy, puis appelez cette fonction haversine une fois avec les tableaux préparés pour tirer parti des performances c et des optimisations de vectorisation - deux cadeaux de la brillante bibliothèque numpy:


def haversine(x1: np.ndarray,
x2: np.ndarray,
y1: np.ndarray,
y2: np.ndarray
) -> np.ndarray:
"""
input in degrees, arrays or numbers.

compute haversine distance between coords (x1, y1) and (x2, y2)
Parameters
----------
x1: np.ndarray
X/longitude in degrees for coords pair 1
x2: np.ndarray
Y/latitude in degrees for coords pair 1.
y1: np.ndarray
X/longitude in degrees for coords pair 2.
y2: np.ndarray
Y/latitude in degrees for coords pair 2.
Returns
-------
np.ndarray or float
haversine distance (meters) between the two given points.
"""
x1 = np.deg2rad(x1)
x2 = np.deg2rad(x2)
y1 = np.deg2rad(y1)
y2 = np.deg2rad(y2)
return 12730000*np.arcsin(((np.sin((y2-y1)*0.5)**2) + np.cos(y1)*np.cos(y2)*np.sin((x2-x1)*0.5)**2)**0.5)

Je vois dans File1 et File 2 que vous parcourez les deux à plusieurs reprises, recherchez-vous des correspondances là-bas ? Les boucles for sont très lentes, ce sera donc un gros goulot d'étranglement, mais sans un peu plus d'informations sur les csv utilisés et sur la façon dont les enregistrements du fichier 1 sont mis en correspondance avec le fichier 2, je ne peux pas vous aider. Peut-être ajouter les deux premiers enregistrements des deux fichiers à la question pour lui donner un peu de contexte ?

mise à jour: Merci d'avoir inclus le lien colab.

Vous commencez avec deux dataframes drive_test et Cells. une de vos conditions "si":

if drive_test['Serving Cell Identity'][i] == Cells['CI'][j] \
or drive_test['Serving Cell Identity'][i] == Cells['PCIG'][j] \
and drive_test['E_ARFCN'][i] == Cells['EARFCN_DL'][j]:
# btw this is ambiguous, use bracket, python reads this as (a or b) and c but that may not be the intention.

peut être écrit comme une fusion et un filtre pandas, basé sur cette méthode de fusion croisée Créer une combinaison de deux dataframes pandas en deux dimensions

new_df = drive_test.assign(merge_key = 1).merge(Cells.assign(merge_key = 1), on = 'merge_key', suffixes = ("", "")).drop('merge_key', axis = 1)
# will need to use suffixes if your dataframes have common column names
cond1_df = new_df[((new_df['Serving Cell Identity'] == new_df.CI) | (new_df['Serving Cell Identity'] == new_df.PCIG)) & (new_df.E_ARFCN == new_df.EARFCN_DL)]
cond1_df = cond1_df.assign(distance_between = haversine(cond1_df.Longitude.to_numpy(), cond1_df.LONGITUDE.to_numpy(), cond1_df.Latitude.to_numpy(), cond1_df.LATITUDE.to_numpy()))
# note that my haversine input args are differently ordered to yours

et ensuite vous devriez avoir tous les résultats pour la première condition, et cela peut être répété pour les conditions restantes. Je ne suis pas en mesure de tester cela sur votre csv, il faudra donc peut-être un peu de débogage, mais l'idée devrait convenir.

notez que, selon la taille de votre csv, cela pourrait exploser en une trame de données extrêmement volumineuse et maximiser votre RAM, auquel cas vous êtes pratiquement obligé de l'itérer un par un, à moins que vous ne vouliez créer une méthode par morceaux où vous itérez les colonnes dans une trame de données et faites correspondre toutes les colonnes soumises aux conditions de l'autre. ce sera toujours plus rapide que d'itérer les deux un à la fois, mais probablement plus lent que de tout faire en même temps.

mise à jour - essayer la deuxième idée puisque la nouvelle trame de données semble planter le noyau

Dans votre boucle, vous pouvez faire quelque chose comme ça pour la première condition (et similaire pour toutes les conditions de correspondance suivantes)

for i in range(drive_test_size):
matching_records = Cells[((Cells.CI == drive_test['Serving Cell Identity'][i]) | (Cells.PCIG == drive_test['Serving Cell Identity'][i])) & (Cells.EARFCN_DL == drive_test['E_ARFCN'][i])]
if len(matching_records) > 0:
matching_records = matching_records.assign(distance_between = haversine(cond1_df.Longitude.to_numpy(), cond1_df.LONGITUDE.to_numpy(), cond1_df.Latitude.to_numpy(), cond1_df.LATITUDE.to_numpy()))

ce qui devrait être beaucoup plus rapide de toute façon puisque vous n'utiliserez qu'une seule boucle python "for" et que vous laisserez ensuite la requête numpy/pandas ultra-rapide faire la suivante. ce modèle devrait également s'appliquer à vos conditions restantes.

Commentaires

Posts les plus consultés de ce blog

Erreur Symfony : "Une exception a été levée lors du rendu d'un modèle"

Détecter les appuis sur les touches fléchées en JavaScript

Une chaîne vide donne "Des erreurs ont été détectées dans les arguments de la ligne de commande, veuillez vous assurer que tous les arguments sont correctement définis"