-4
votes

Calcul de la distance la plus courte entre les coordonnées dans des fichiers CSV séparés

J'ai deux CSV nommés Fichier A et Fichier B avec des coordonnées (latitude et longitude). Je voudrais automatiser le processus en parcourant toutes les coordonnées et en attribuant à chaque coordonnée celle où la distance est la plus courte. Le bloc de données a la même forme et la même longueur, ce qui signifie que chaque coordonnée doit être affectée à ses coordonnées uniques où la distance est plus courte. Actuellement, je le fais manuellement et c'est fastidieux. Voici mes codes.

from math import asin, cos, pi, sqrt

# coordinates in file A
lat1 = float(input('Latitude A'))
lon1= float(input('Longitude A'))

# coordinates in file B
lat2 = float(input('Latitude B'))
lon2= float(input('Longitude B'))


def distance(latA, lonA, latB, lonB):
    p = pi/180 # in radians
    a =  0.5 - cos((latB-latA)*p)/2 + cos(latA*p) * cos(latB*p) * (1-cos((lonB-lonA)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

Voici à quoi ressemblent les données dans les deux fichiers CSV, comme indiqué dans l'image ci-dessous, cependant, j'aimerais ajouter deux colonnes, une pour les distances calculées en mètres et l'autre pour la coordonnée correspondante avec la distance la plus courte.

entrez la description de l'image ici


9 commentaires

Pourriez-vous afficher une ligne du fichier CSV?


À quoi ressemble la ligne d'en-tête?


Les deux fichiers sont donc dans ce format? Essayez de modifier votre réponse pour montrer plus précisément un exemple de ce que vous voulez qu'il se passe.


Pourriez-vous clarifier un peu ce que vous voulez réaliser? Voulez-vous associer chaque coordonnée de file1 avec son point le plus proche dans file2 ?


Alors comparez chaque ligne du fichier 1 à chaque ligne du fichier 2 et affichez la paire avec la distance la plus courte? Deux lignes du fichier 1 sont-elles autorisées à être jumelées à la même ligne dans le fichier 2?


@tripleee c'est ce que j'essaie de faire, mais les deux lignes du fichier un ne sont pas autorisées à s'associer à la même ligne dans le fichier deux, elles devraient être uniques


@jpnadas ye c'est ce que j'essaye de faire


Quelle serait votre règle si pair_1 du file_1 est le plus proche de pair_1 du file_2 , mais que pair_2 du file_1 est également le plus proche de pair_1 du file_2 ? Trouver la prochaine paire la plus proche? Cela rendra les résultats dépendants de l'ordre de la boucle ...


À moins que vous ne demandiez spécifiquement comment résoudre un problème de compatibilité entre versions (auquel cas votre question devrait évidemment décrire ce problème), vous ne devriez pas mélanger les balises python-2.7 et python-3.x . J'ai supprimé la balise python-2.7 de cette question.


4 Réponses :


0
votes

En supposant que vous vouliez trouver la paire de coordonnées la plus proche pour une ligne donnée du fichier, il semble que vous vouliez que le module csv analyse les fichiers dans des listes de listes.

À partir de là, parcourez l'une des listes et trouvez la distance la plus courte en vérifiant toutes les coordonnées de la deuxième liste.

Quelque chose comme ça, en supposant que vous ayez déjà chargé les deux listes dans certaines variables

for coordinate in list1:
    closest_coordinate = None
    for second_coord in list2:
        displacement = distance(coordinate, second_coord)
        if closest_coordinate = None or displacement < closest_coord[1]:
            closest_coordinate = (second_coord, displacement)
   # Do something with this info, maybe have a list of coordinate pairs and their distance?



0 commentaires

1
votes

En supposant que votre fonction de distance est correcte, essayez quelque chose comme

import csv

with open('file2.csv') as file2:
    f2csv = csv.reader(file2)
    f2pairs = [line for line in f2csv]

with open('file1.csv') as file1:
    f1csv = csv.reader(file1)
    for lat1, lon1 in f1csv:
        mindist = None
        for lat, lon in f2pairs:
            dist = distance(lat1, lon1, lat, lon)
            if mindist is None or dist<mindist:
                mindist, lat2, lon2 = dist, lat, lon
        print(lat1, lon1, mindist, lat2, lon2)

Cela imprime uniquement les résultats sur la sortie standard et préfère le premier point de destination avec la distance la plus courte en cas d'égalité. Si vous voulez une sortie CSV ou une gestion différente des liens, ces modifications devraient être faciles à faire par vous-même (ou publier une nouvelle question à ce sujet si vous avez encore besoin d'aide). Si les fichiers CSV ont des lignes d'en-tête ou d'autres décorations stupides, vous devez modifier le code pour les ignorer également.


1 commentaires

Votre question ne mentionne pas l'exigence de sélectionner des paires uniques dans le fichier 2 pour chaque élément du fichier 1, qui a ensuite été mentionné dans un commentaire, et nous ne savons pas exactement comment y parvenir sur la base d'un seul commentaire, donc je n'ai pas tenté de s'attaquer à cela ici. Encore une fois, posez une nouvelle question spécifiquement sur ce problème si vous avez encore besoin d'aide (probablement un lien ici, mais consultez également les conseils pour fournir un exemple reproductible minimal ).



1
votes

Déterminez les points les plus proches

Pour que vous puissiez obtenir le résultat souhaité, vous devez d'abord déterminer quel est le point le plus proche correspondant du deuxième fichier. Cela peut être fait en itérant sur tous les points du premier fichier et en imbriquant cela avec une autre boucle itérant sur les coordonnées du deuxième fichier.

Puisque vous souhaitez tout enregistrer dans un fichier, j'ai enregistré chaque résultat attendu dans son propre tableau, correspondant à chaque point du premier fichier.

Remarque: J'utilise numpy comme np pour l'ensemble de cette réponse.

import numpy as np


def distance(point1, point2):
    R = 12742  # Earth radius, in KM
    p = np.pi / 180  # in radians
    lat1 = point1[0]
    lat2 = point2[0]

    lon1 = point1[1]
    lon2 = point2[1]

    a = 0.5 - np.cos((lat2 - lat1) * p) / 2 + np.cos(lat1 * p) * np.cos(lat2 * p) * (1 - np.cos((lon2 - lon1) * p)) / 2
    return R * np.arcsin(np.sqrt(a))


coordinates_1 = np.loadtxt('coordinates1.csv', delimiter=',')
coordinates_2 = np.loadtxt('coordinates2.csv', delimiter=',')

shortest_distances = []
closest_points = []

for c1 in coordinates_1:
    # Compute the distance for each point
    distances = [distance(c1, c2) for c2 in coordinates_2]
    # Determine the shortest
    index = np.argmin(distances)
    closest_points.append(coordinates_2[index])
    shortest_distances.append(distances[index])

result = np.hstack((coordinates_1, closest_points, np.reshape(shortest_distances, (3, 1))))

# save to a csv file
np.savetxt("result.csv", result, delimiter=",")

Empiler les résultats

Puisque chaque sortie sera corrélée avec son point sur le premier fichier, vous pouvez simplement l'empiler horizontalement pour obtenir ce que vous voulez.

np.savetxt("result.csv", result, delimiter=",")

Notez que j'ai dû invoquer np.reshape() sur la liste shortest_distances , donc il aurait 2 dimensions et ensuite np.hstack fonctionnerait.

Impression du résultat dans un CSV

Enfin, nous pouvons appeler np.savetxt() pour enregistrer le résultat dans un fichier csv.

result = np.hstack((coordinates_1, closest_points, np.reshape(shortest_distances, (3, 1))))

Code complet

Voici le code complet

shortest_distances = []
closest_points = []

for c1 in coordinates_1:
    # Compute the distance for each point
    distances = [distance(c1, c2) for c2 in coordinates_2]
    # Determine the shortest
    index = np.argmin(distances)
    closest_points.append(coordinates_2[index])
    shortest_distances.append(distances[index])

2 commentaires

Je suppose que np se réfère ici à NumPy, une bibliothèque tierce populaire mais non standard.


Ouais ... J'ai cet alias sur le code complet, mais bonne prise, je vais l'inclure au début pour éviter toute confusion.



1
votes

Voici ce que j'ai fait après avoir réinterprété la question.

  • analyser les fichiers et mettre les coordonnées dans des listes
  • puis trouvez toutes les paires uniques les plus courtes, mettez-les dans une liste
  • ramener les paires les plus courtes dans l'ordre du fichier 1
  • coordonnée de sortie 1, coordonnée 2 et distance au fichier de sortie coords_out.csv
CSV_DELIMITER = ',' 

filename_1 = 'file2.csv'
filename_2 = 'file1.csv'


class Coordinate():
    def __init__(self, latitude, longitude, file, line):
        self.lat = latitude
        self.lon = longitude
        self.file = file
        self.line = line

    def __str__(self):
        return f'{self.lat}, {self.lon}'

def extract(line):
    lat, lon = line.split(CSV_DELIMITER)
    return (float(lat), float(lon))

def distance(lat1, lon1, lat2, lon2):
    
    p = pi/180 # in radians
    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)) #2*R*asin...

coords_1 = []
coords_2 = []

    
with open(filename_1) as file1, open(filename_2) as file2:

    # skip the header lines
    file1.readline()
    file2.readline()

    # iterate over both files simultaneously
    for i, (line1, line2) in enumerate(zip(file1, file2)):
        print(f'{line1}')

        # extract values and add them to a list
        lat1, lon1 = extract(line1)
        lat2, lon2 = extract(line2)
        coords_1.append(Coordinate(lat1, lon1, 'file1', i))
        coords_2.append(Coordinate(lat2, lon2, 'file2', i))

# now we find the shortest distance between coordinates
coords_1_order = coords_1[:] # copy order for later
shortest_distances = []

for x in range(min(len(coords_1), len(coords_2))):
    smallest_d = None
    
    for i, coord_1 in enumerate(coords_1):
        for j, coord_2 in enumerate(coords_2):
            d = distance(coord_1.lat, coord_1.lon,
                coord_2.lat, coord_2.lon)
            if (smallest_d == None) or (d < smallest_d[0]):
                smallest_d = (d, coord_1, coord_2, i, j)
    
    # add the smallest found distance to a list and make sure these coordinates aren't used again in the next loop
    shortest_distances.append(smallest_d)
    coords_1.pop(smallest_d[3])
    coords_2.pop(smallest_d[4])


# Then we sort the distances back to the order of file 1
sorted_back = []

for c in coords_1_order:
    for d_info in shortest_distances:
        if c == d_info[1]:
            sorted_back.append(d_info)          

# check if the sorting went okay
assert len(sorted_back) == len(shortest_distances), "sorting went horribly wrong, array lengths dont match"
                        
with open('coords_out.csv', 'w') as out:
    
    # write headers
    out.write(f'lat1, lon1, lat2, lon2, distance\n')
    
    for distance_info in sorted_back:
        d, c1, c2, i, j = distance_info
        # output coordinate 1, coordinate 2 and the distance to output file
        out.write(f'{c1}, {c2}, {d}\n')

J'ai ajouté une petite classe de Coordinate qui conserve également les informations sur le fichier et la ligne sur laquelle une coordonnée a été trouvée.


4 commentaires

Je pense que vous rendez les choses plus compliquées qu'elles ne le devraient. Dans tous les cas, des points supplémentaires pour l' assert !


@jpnadas Eh bien, compliqué est un terme relatif, mais je suis d'accord que ma solution est un peu alambiquée. Je voulais trouver un moyen sans utiliser de bibliothèques supplémentaires, principalement parce que je n'ai pas beaucoup d'expérience avec différentes bibliothèques moi-même. En regardant votre réponse, j'aurais aimé savoir comment rechercher même de telles méthodes! hstack ?? J'ai peur d'être dépendant d'un code que je ne comprends pas, donc j'essaie toujours de le garder aussi brut que possible. Je pense que ma façon de trouver les distances les plus courtes est également très fastidieuse, mais cela fonctionne. Diviser pour vaincre aurait été mieux ...


Bon point @Queuebee. Je suis d'accord avec vous que ne pas compter sur des bibliothèques tierces est souvent une bonne approche. Cependant, vous courez également le risque de devoir toujours réinventer la roue vous-même, et votre implémentation ne sera probablement pas aussi efficace que celle d'une bibliothèque. Cela dit, votre point est que c'est exactement pourquoi j'ai tendance à éviter les bibliothèques avec des structures de données sophistiquées qui visent à tout faire pour vous (lisez pandas ici). Dans tous les cas, numpy est une bibliothèque assez pratique pour se familiariser avec, et elle est très similaire au fonctionnement de matlab .


Je vais certainement commencer à explorer plus numpy ! Et sera probablement obligé d'apprendre les pandas bientôt: ')