Aus Adressen Koordinate abfragen (Problem mit For-Schleife)

Code-Stücke können hier veröffentlicht werden.
Antworten
darhubat
User
Beiträge: 2
Registriert: Donnerstag 22. Dezember 2022, 13:30

Hallo zusammen

Ich bin neu hier im Forum und dies ist mein erster Beitrag, daher gerne darauf aufmerksam machen, falls etwas nicht korrekt ist oder fehlen sollte beim untenstehenden Beschrieb. Bei einem persönlichen Projekt bin ich auf gewisse Performance-Probleme gestossen bzw. funktioniert meine For-Schleife zwar , aber sie beendet sich nicht, obwohl alle Pandas-Zeilen "durchgearbeitet" wurden.

Kurzer Beschrieb meines privaten Projektes:
1. Ich habe mir einen Scraper gebaut mit Scrapy, der gewisse Daten sammelt und mir in eine CSV-Datei schreibt. Bei jedem Scrapy-Durchlauf werden die neu generierten Daten in der gleichen csv-Datei ergänzt inkl. Generierungs-Datum. Unter anderem beinhaltet diese CSV-Datei eine Spalte mit einer Adresse. In einer neue Spalte möchte ich dann die Latitude/Longitude ergänzen, um es auf einer interaktiven Karte anzuzeigen.
2. Die gescrapten Daten werden bereinigt und in die richtige Form gebracht, um sie danach in einem interaktiven Dashboard (mit Dash) darzustellen.

Meine Probleme:
1. Die Schwierigkeit besteht darin, dass es bei jedem Scraping-Durchgang ca. 1'000 neue Zeilen zur CSV-Datei hinzukommen. Teils sind die Latitude/Longitude bereits mit Geopy generiert worden aus der Adress-Spalte, neu hinzugefügte Zeilen sind aber ohne Latitude/Longitude vorhanden. Untenstehender Python-Code liest diese CSV-Datei jeweils als Pandas DataFrame ein, und mit einer For-Schleife iteriere ich über die DataFrame-Zeilen und falls die Spalte "Latitude/Longitude" nicht "nan" ist, soll es weitergehen und sonst soll die Spalte "Latitude/Longitude" mittels Geolocator aus der Spalte "Wohnungs-Adresse" mit der Latitude und Longitude befüllt werden. Soweit funktioniert die For-Schleife, ich muss diese aber in der Pycharm-Console selbst abbrechen, sonst läuft sieendlos. Vermutlich habe ich einen Überlegungsfehler, weshalb die For-Schleife nicht beendet wird und endlos läuft.
2. Die Performance ist nicht wirklich gut...einerseits aufgrund der iterrows()-Anwendung und der apply-Benutzung. Fällt euch da vielleicht eine elegantere Lösung ein für mein Vorhaben? Die Daten werden immer mehr und wenn ich immer für alle Zeilen die Lat/Long generieren müsste, würde dies irgendwann auch aufgrund der Beschränkung nicht mehr funktionieren. Daher möchte ich immer nur die fehlenden Lat/Long-Werte jeweils ergänzen.

So sehen die generierten Daten in der CSV-Datei aus:
2022-12-21,serp_responsive#badge_new_building_lite,2.5,55,0,"Rue du Port 31, 1820 Montreux, VD",VD,,clean
2022-12-21,serp_responsive#badge_new_building_lite,3.5,88,590000,"Telmoos 10, 1716 Plaffeien, FR",FR,,clean

So mit generierter Latitude/Longitude aus der For-Schleife:
2022-12-21,serp_responsive#badge_new_building_lite,2.5,55,0,"Rue du Port 31, 1820 Montreux, VD",VD,"(46.442212049999995, 6.896893654157193)",clean
2022-12-21,serp_responsive#badge_new_building_lite,3.5,88,590000,"Telmoos 10, 1716 Plaffeien, FR",FR,"(46.7372459, 7.2921369)",clean

Code: Alles auswählen

import pandas as pd
import geopy.geocoders as geo
from geopy.extra.rate_limiter import RateLimiter

# csv-Datei wird eingelesen (Spalte "Wohnungs_Adresse" mit der Adresse;  Spalte "Latitude/Longitude" mit teils generierten Koordinaten und neue Zeilen werden mit "nan" hinterlegt
df = pd.read_csv(r'output\appartements_bereinigt.csv', delimiter=',', parse_dates=True)

# Geolocator Einstellungen für Generierung von Koordinaten aus einer Adresse
geolocator = geo.Nominatim(user_agent='Daten_Bereinigung')
geo.options.default_timeout = 10
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=2)

# Funktion zur Generierung von Latitude/Longitude (try/except, da es nicht bei jeder Adresse möglich ist aufgrund unvollständiger Angaben)
def eval_results(x):
    try:
        return (x.latitude, x.longitude)
    except:
        return (None, None)


# Die Schleife prüft, ob bereits Latitude/Longitude in jeder Zeile vorhanden ist (falls Zeilen nicht "nan" wird die Schleife weitergeführt), falls nicht, werden diese generiert, durch apply-Anwendung von Geolocator und obiger Python-Funktion
anzahl_zeilen_df = len(df)
for row in df[['Latitude/Longitude']].iloc[:anzahl_zeilen_df].iterrows():
    if str(row[1][0]) != 'nan':
        continue
    else:
        df['Latitude/Longitude'] = df['Wohnungs_Adresse'].apply(geolocator.geocode, timeout=30).apply(
        lambda x: eval_results(x))
df.to_csv(r'output_clean\appartements_clean_lat_lon.csv', sep=',', index=False)
Ich bin absolut nicht Python-Experte und versuche mich mit eigenen Vorhaben daher stetig zu verbessern. Daher ist der Code vielleicht nicht immer sehr elegant. Hier wollte ich von der Datengewinnung, Datenbereinigung/-ergänzung bis hin zu Dashboarderstellung einen funktionierenden Kreislauf erstellen. Ich freue mich auf eure Hilfe bzw. Vorschläge zu meinen beiden Problemen (endlos laufender For-Schleife und schlechte Performance bei der Koordinaten-Generierung).

Falls ihr noch mehr Angaben braucht, gerne melden, dann werde ich diese ergänzen.

Viele Grüsse
Dario
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Arbeite zuerst einmal das Pandas-Tutotrial durch.
Nakte `except` benutz man niemals, weil damit auch viele Programmierfehler verschleiert werden. Exceptions immer so konkret wie möglich abfangen. Hier wäre aber eine Prüfung auf None besser.
Was denkst Du, bewirkt der iloc-Teil? Wenn man über die einzelnen Elemente iteriert macht man mit großer Wahrscheinlichkeit etwas falsch. Hier möchtest Du alle nan-Werte filtern.
`continue` sollte man nicht benutzen, ist hier auch völlig überflüssig, weil man die if-Bedingung einfach umdrehen könnte.
Auf NaN prüft man mit isna und nicht die Stringrepräsentation auf 'nan'.
Weißt Du was `apply` macht? Warum bestimmst Du bei jeder nan-Zeile immer wieder für alle Zeilen die Positionen?
Warum gibst Du geolocator.geocode direkt an, bei eval_results benutzt Du aber überflüssigerweise einen Lambda-Ausdruck?
Man speichert keine Tuple in einem Dataframe, longitude und latitude sollten getrennte Spalten sein.

Das ganze könnte dann so aussehen:

Code: Alles auswählen

missing = df['Latitude'].isna()
locations = df.loc[missing, 'Wohnungs_Adresse'].apply(geolocator.geocode, timeout=30)
df.loc[missing, 'Latitude'] = locations.apply(lambda location: location.latitude)
df.loc[missing, 'Longitude'] = locations.apply(lambda location: location.longitude)
darhubat
User
Beiträge: 2
Registriert: Donnerstag 22. Dezember 2022, 13:30

Danke dir Sirius für die Zeit, welche du dir genommen hast. Wie gesagt, ich versuche besser zu werden durch solche Projekte und aus entsprechenden Programmierfehlern zu lernen. Ich sehe aber, dass ich mich mit den Möglichkeiten von Pandas noch vertiefter auseinandersetzen muss. Die Missing-Data mit der Loc-Funktion einzugrenzen, war genau das, was ich gesucht habe und kompliziert über eine Schleife versuchte zu lösen.

Vielen Dank nochmals für die Anregungen. Sehr hilfreich!

Viele Grüsse & frohe Festtage!
Antworten