Datenaufbereitung - MrJaimba/Projektseminar Wiki

Original URL: https://github.com/MrJaimba/Projektseminar/wiki/Datenaufbereitung
Ansprechpartner
Philip Herchenröder

Um Daten für Machine Learning verwenden zu können, müssen sie einige Anforderungen und Qualitätsstandards erfüllen. Sind diese nicht gewährleistet können die Machine Learning Modelle fehlerhafte Prognosen liefern oder gar nicht erst trainiert und angewendet werden. Wie zuvor beschrieben, war die Qualität der Rohdaten diesbezüglich ungenügend. Daher mussten einige Anpassungen vorgenommen werden. Die zugehörigen Methoden sind im File data_modeling.py zu finden. Weitere Machine Learning spezifische Anpassungen, wie der Umgang mit Ausreißern sind im Verlauf des Projekts in ein eigenes File machine_learning.py ausgegliedert worden.

Rohdaten und Explorative Datenanalyse

Um ein grundlegendes Verständnis der gewonnen Daten zu erhalten, ist es zwingend notwendig den Datensatz mittels explorativer Datenanalyse zu untersuchen. Zu diesem Zweck wurden die Daten gründlich untersucht. Zunächst in den Excel Files der Scraper, anschließend wurden die üblichen pandas-Methoden info() describe() verwendet und einige Grafiken mit matplotlib in der Methode eda() erzeugt. Für die detaillierte Untersuchung wurde dann die API Pandas Profiling verwendet. Der entsprechende Code wurde später in das File GUI.py überführt, um ihn direkt aus der Anwendung mit der gegebenen Streamlit-Integration starten zu können.

Wie im vorherigen Abschnitt beschrieben setzt sich die Datenbasis der Immobilien aus zwei heterogenen Quellen (Immonet und Immoscout) zusammen. Im Folgenden dienen die Grafiken einem besseren Verständnis der Bearbeitungsstufen von den Rohdaten bis zum fertigen Datensatz, der in die Datenbank übernommen wird und als Basis für die Machine Learning Verfahren dient.

Immonet Datensatz

Immoscout Datensatz Häuser

Immoscout Datensatz Wohnungen

Daten zusammenführen

Zunächst werden die Rohdaten, welche ursprünglich in Excel-Dateien vorliegen, in einen pandas Dataframe überführt. Dies erfolgt in den Methoden read_data_from_immonet() und read_data_from_immoscout() . Zu beachten ist, dass sich der Immoscout Dataframe aus zwei separaten Tabellen, eine für Häuser und eine für Wohnungen zusammensetzt, wohingegen die gescrapten Daten von Immonet bereits in einer Tabelle vorliegen.

Die nächste Aufgabe besteht somit in der Zusammenführung der beiden Datensätze. Hierfür wird das Format des Immobilienscout Datensatzes auf das Format des Immonet Datensatzes angepasst. Dies erfolgt in der Methode merge_data().

Im ersten Schritt werden die Namen (Spaltenbezeichnungen) von inhaltlich gleichen Features angepasst. Da die entsprechenden Informationen in den den beiden Datensätzen teilweise anders codiert sind, müssen im nächsten Schritt die Spalteninhalte bearbeitet werden. Allgemein liegen die Immonet Daten durch Anpassungen während des Scraping-Vorgangs in einem einheitlicheren Format vor. Die Variabilität der Ausprägngen ist in den Daten von Immoscout durch den vorgefertigten Scraper höher.

Beispielsweise liegen binäre "JA/NEIN"-Features wie "Aufzug" im Immonetdatensatz nur mit diesen Ausprägungen vor. Ausprägungen wie "Personenaufzug", welche im Immoscoutdatensatz vorkommen, müssen daher zu "JA" oder "NEIN" transformiert werden. Im selben Zug werden leere Einträge in diesen Features mit "NEIN" aufgefüllt. Dies geschieht unter der Annahme, dass der Anbieter falls ein Feature, wie zum Beispiel ein Aufzug, vorhanden ist, dieses auch angibt. Existiert es nicht, wird das entsprechende Feld auf der Website leer gelassen.

Eine weitere wichitge Anpassung ist die Vereinheitlichung von Tausendertrennzeichen und Dezimaltrennzeichen bei numerischen Features. Dies dient sowohl der inhaltlichen Konsistenz, als auch der späteren Verarbeitung mit den entsprechenden Python Datentypen. Darüber hinaus werden Einheitenbezeichnungen wie Quadratmeter bei Flächenangaben aus den Einträgen entfernt.

Im Anschluss werden die Spaltennamen für eine bessere Übersicht alphabetisch sortiert und die beiden Datensätze mit einem Innerjoin vereint. Da es vorkommt, dass Anbieter ihre Immobilien auf beiden Portalen eingestellt haben, müssen diese Duplikate anschließend noch entfernt werden.

Daten bereinigen

In der Methode preprocess_data() wird der zusammengesetzte Datensatz weiter verarbeitet. Zunächst wird ein Scraperfehler korrigiert. Der vorgefertigte Immoscout Scraper verliert bei Immobilien mit einem Angebotspreis über 10 Millionen drei Tausenderstellen.

Danach werden alle Beobachtungen entfernt, die keinen Angebotspreis enthalten. Da es sich hierbei um das zu vorhersagende Feature handelt, können Tupel ohne diese Information nicht für das Modelltraining oder den anschließenden Test verwendet werden. Außerdem werden drei Spalten entfernt, die nahezu keine Werte enthalten und daher auch nicht zur Vorhersage beitragen können.

Anschließend werden die Datentypen der Spalten an die darin codierte Information angepasst. Dies betrifft vorrangig kategorische Features, die zuvor als String codiert waren. Damit ist es im nächsten Schritt leichter die Kategorien zu bearbeiten. Gleichbedeutende Ausprägungen, wie "Pellets" und "Holzpellets" bei "Energietyp" werden zusammengefasst. Sehr kleine Kategorieausprägungen, wie "Herrenhaus" im Feature "Immobilienart" werden zu größeren Ausprägungen, wie "Villa" hinzugefügt. Außerdem wird ein Sammler "Sonstige" hinzugefügt, der abweichende Ausprägungen enthält und beispielsweise Tippfehler bei der Dateneingabe abfängt. Über die Einteilung der Kategorieausprägungen wurde intern viel diskutiert, da diese in der fertigen Anwendung die Auswahlmöglichkeiten für die entsprechenden Features bestimmen.

Daten auffüllen

Das größte Problem neben der Heterogenität der Daten war ihre Unvollständigkeit. Da bei der Dateneingabe auf Immonet und Immoscout nur rudimentäre Validierungsregeln gelten sind viele Beobachtungen lückenhaft. Dies resultiert in einer hohen Anzahl an NaN und null Werten in den gescrapten Daten (vergleiche Grafiken oben). Mit Imputation können solche Lücken aufgefüllt werden. Die theoretischen Methoden werden in der Auftaktseminararbeit dargelegt. Die Umsetzung im Code erfolgt in der Methode impute_data() .

Die Anzahl der Bäder wird mit einem Wert zwischen 1 und 3 aufgefüllt, mit abnehmender Wahrscheinlichkeit. Dies entspricht der Verteilung im Datensatz und verhindert beim auffüllen die Generierung neuer Ausreißer (bspw. 8 Bäder in einer Villa zum Auffüllen einer 3 Zimmer-Wohnung). Die Anzahl der Zimmer und das Baujahr wurden zufällig mit vorhandenen Werten aus den anderen Beobachtungen aufgefüllt. Hier wurde absichtlich auf eine explizite Wahrscheinlichkeitsangabe verzichtet, um die Verteilung der Beobachtungen nicht zu verfälschen.

Außerdem finden sich in der Methode die ersten inhaltlichen Einschränkungen des Datensatzes, wie das Entfernen von Ausreißern auf Basis von Baujahr, Zimmeranzahl und Angebotspreis. Weitere inhaltliche spezifischere Anpassungen sind später ins File machine_learning.py ausgegliedert worden und werden im nächsten Wiki-Abschnitt beschrieben. Während des Projektverlaufs wurde eine Trennung des Datensatzes in Wohnungen und Häuser geprüft, aber nach Modelltests mit schlechteren Ergebnissen wieder verworfen. Daher wird abschließend bei allen Wohnungen die Grundstücksfläche gleich 0 gesetzt und Häuser ohne Grundstücksfläche entfernt.

Resultierender Datensatz

Datenbank

Schon zu Beginn des Planung war eine Datenbank für eine geordnete Datenverwaltung vorgesehen. Die Überführung der Datenbasis in die Datenbank verzögerte sich im Projektverlauf allerdings lange. Dies lag daran, dass der vorgefertigte Immoscout-Scraper die Daten als CSV- oder Excel-Datei zurück gibt und auch der Immonet Scraper ursprünglich diese Ausgabemethode vorsah und die Beschaffung und Einigung auf einen Datenstandard deutlich länger gedauert hat, als ursprünglich geplant. Die Implementierung einer SQLite Datenbank bot sich auf Grund der guten Python Integration und der geringen Relationskomplexität und der lokalen Datenspeicherung an. Für das Aufsetzen und die Verwaltung der Datenbank wurde das Tool SQLite Browser verwendet.

Übersicht Datenbanktabellen

Die Encoding Tabellen dienen als Hilfstabellen, um die Usereingaben in der GUI an das Datenformat der Trainingsdaten anpassen zu können (vgl. Machine Learning Modelle). Die Tabelle Imputed_data_raw enthält die ursprünglichen Daten, die aus den oben beschriebenen Methoden generiert wurden. Meta_Data_upd2 ist die aktuelle Version der Tabelle, in der die postleitzahlbezogenen Metadaten gespeichert werden (Metadaten). Die beiden älteren Versionen der Tabelle wurden als Backup behalten. ML_Trainingsdaten_upd ist die aktuelle Version der Tabelle, in der die gejointen Immobilen- und Metadaten gespeichert sind. Diese wird für das Training der verschiedenen Machine Learning Modelle verwendet und bildet somit die finale Datenbasis. Die Machine Learning spezifischen Anpassungen, welche im Rahmen der Trainingspipeline durchgeführt werden, bauen hierauf auf. Die Tabelle profile_report_data, enthält diese Änderungen und wird in der fertigen Anwendung zur Darstellung der EDA Ergebnisse genutzt. X_train und X_test dienen als Hilfstabellen, um Trainings- und Testdaten separat evaluieren zu können.

Die übrigen Tabellen sind Test-Überbleibsel, die auf Grund ihrer geringen Größe nicht gelöscht wurden. Beispielsweise war die Tabelle Machine_Learning_Modelle dazu angedacht die Pckl-Dateien der trainierten Machine Learning Modelle zu speichern. Allerdings gestaltete sich die Umsetzung wegen des Datentyps als umständlich, da die Dateien zunächst in ein binäres Format umgewandelt und anschließend wieder entschlüsselt werden müssen. Daher wurde dieser Ansatz im Projektverlauf verworfen.