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.
4 Réponses :
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?
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.
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 ).
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=",")
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.
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))))
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])
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.
Voici ce que j'ai fait après avoir réinterprété la question.
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.
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: ')
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 dansfile2
?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
dufile_1
est le plus proche depair_1
dufile_2
, mais quepair_2
dufile_1
est également le plus proche depair_1
dufile_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.