
Machine
Learning
Joern Ploennigs
Beschreibende Datenanalyse
Erste Schritte: Kenne deine Daten¶
Als nächstes machen wir einige Beispiele für explorative Datenanalyse mit Pandas. Hierfür laden wir als Beispiel die Liste aller aktuellen Baustellen in Rostock an, die von der Stadt Rostock im Open Data Portal zur Verfügung gestellt wird. Wir lesen die komprimierte baustdatendatei direkt in Pandas ein. Da diese durch Komma getrennt ist, müssen keinen Separator angeben, da das Komma der Standardseparator ist.
import numpy as np # Import von NumPy
import pandas as pd # Import von Pandas
baust = pd.read_csv("../data/Baustellen/baustellen_flat.csv")
Als ersten Schritt können wir die Größe des Datensatzes abfragen. Datenrahmen haben ein Attribut .shape
, genau wie numpy-Arrays:
baust.shape
(204, 19)
Die Antwort sagt uns die Anzahl der Zeilen und Spalten an.
Als nächstes könnten wir einen schnellen Blick auf ein paar Zeilen der Daten werfen. Die Attribute .head()
und .tail()
sind nützlich, um sich die ersten und letzten Zeilen anzusehen, .sample()
extrahiert ein paar zufällige Zeilen:
baust.head()
latitude | longitude | uuid | kreis_name | kreis_schluessel | gemeindeverband_name | gemeindeverband_schluessel | gemeinde_name | gemeinde_schluessel | strasse_name | strasse_schluessel | sparte | von | nach | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 54.089282 | 12.111994 | 063a229c-db25-45af-9fba-8d963b287910 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Budapester Str. | 01640 | Fernwärmeleitung | 21 | NaN | 2024-03-04 07:00:00+01:00 | 2024-04-12 17:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Verkeh... | FW-Leitung i.a. der Hanseatic | 39.375000 |
1 | 54.068210 | 12.078264 | 07165e95-4e5f-429c-88b6-125d261cbcda | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Satower Str. | 08180 | Grünpflege | 56 | 65 | 2024-03-07 07:00:00+01:00 | 2024-03-31 15:00:00+02:00 | halbseitige Sperrung, Sicherungsmaßnahmen entl... | Baumpflanzung | 24.291667 |
2 | 54.174303 | 12.081928 | 077f9448-c35e-4074-8491-7d142045a8db | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Am Markt | 00460 | Wasserleitung | 6 | NaN | 2024-03-06 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Einbau Trinkwasserschieber | 22.458333 |
3 | 54.082628 | 12.126542 | 0c04e4b5-6e82-49b6-8b60-1c2cf9c1b1cf | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Klopstockstr. | 06070 | Gebäudesanierung | 5 | NaN | 2024-01-08 00:00:00+01:00 | 2024-03-30 00:00:00+01:00 | NaN | NaN | 82.000000 |
4 | 54.074173 | 12.123860 | 0c122d6b-a6bd-4c41-b25b-09fe10a23a2e | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Ziolkowskistr. | 09920 | Wasserleitung | NaN | NaN | 2023-03-06 00:00:00+01:00 | 2024-08-01 00:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Sanierung Mischwasser- und Trinkwasserleitung | 513.958333 |
baust.tail(3)
latitude | longitude | uuid | kreis_name | kreis_schluessel | gemeindeverband_name | gemeindeverband_schluessel | gemeinde_name | gemeinde_schluessel | strasse_name | strasse_schluessel | sparte | von | nach | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
201 | 54.080468 | 12.126524 | fd2e99b4-9ac8-4dd2-94c2-b521b6d91455 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Südring | 07400 | Gleisbau | Platz der Freundschaft (07650) | Goetheplatz (02960) | 2022-05-04 00:00:00+02:00 | 2025-01-01 00:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Baustelleneinrichtung / Belieferung für Neubau... | 973.041667 |
202 | 54.151488 | 12.080890 | fd32f630-333c-4752-9989-8c41a8eb5b30 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Taklerring | 08880 | Hochbau | 45 | NaN | 2023-06-01 00:00:00+02:00 | 2025-08-31 00:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Baustraße für Neubau Hort Taklerring | 822.000000 |
203 | 54.086911 | 12.104815 | fdc7ab16-67ac-4d33-ae98-b0bbd9703f18 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Dethardingstr. | 04330 | Telefonnetz | 18 | 29 | 2024-03-25 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Breitbandausbau | 3.458333 |
baust.sample(4)
latitude | longitude | uuid | kreis_name | kreis_schluessel | gemeindeverband_name | gemeindeverband_schluessel | gemeinde_name | gemeinde_schluessel | strasse_name | strasse_schluessel | sparte | von | nach | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
141 | 54.091433 | 12.118810 | b9bb822b-8a36-40d9-8c46-d593f58a7294 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Patriotischer Weg | 07520 | Gebäudesanierung | 100 | NaN | 2024-03-27 00:00:00+01:00 | 2024-04-13 00:00:00+02:00 | NaN | NaN | 16.958333 |
171 | 54.076552 | 12.140925 | df9dd373-2e39-4943-8a5e-a1d9551dcbf0 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Lessingstr. | 06560 | Straßenbau | 13 | NaN | 2023-10-30 00:00:00+01:00 | 2024-06-01 00:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Baustelleneinrichtung, Containerstellung | 214.958333 |
187 | 54.169199 | 12.033579 | ecea23d2-c496-4c8a-9073-8d9f7def4942 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Stolteraer Weg | 08770 | Gebäudesanierung | 29 | NaN | 2024-03-11 07:00:00+01:00 | 2024-05-31 16:00:00+02:00 | Sicherungsmaßnahmen entlang des Gehwegs, Volls... | Sanierungsarbeiten | 81.333333 |
166 | 54.144972 | 12.060393 | dc496ced-c5de-45a1-8816-358db54d715b | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Möllner Str. | 00600 | Hochbau | 11 | 12b | 2021-10-04 07:00:00+02:00 | 2024-07-12 16:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße | NaN | 1012.375000 |
Diese Funktionen zeigt uns zwar im ersten Eindruck viele Spalten, aber nicht immer alle, wenn der Bildschirmplatz nicht ausreicht. Deshalb ist es bei solchen Datensätzen mit vielen Spalten sinnvoll alle Spaltennamen in einem Datensatz aufzulisten.
baust.columns
Index(['latitude', 'longitude', 'uuid', 'kreis_name', 'kreis_schluessel', 'gemeindeverband_name', 'gemeindeverband_schluessel', 'gemeinde_name', 'gemeinde_schluessel', 'strasse_name', 'strasse_schluessel', 'sparte', 'von', 'nach', 'baubeginn', 'bauende', 'verkehrsbeeintraechtigungen', 'baumassnahme', 'dauer'], dtype='object')
Als nächstes überprüfen wir die Datentypen der Daten. Obwohl einige, z.B. kreis_name
und baumassnahme
aussehen wie ein String, könnten sie tatsächlich einen anderen Typ haben. Dies kann über das Attribut .dtypes
abgefragt werden:
baust.dtypes
latitude float64 longitude float64 uuid object kreis_name object kreis_schluessel float64 gemeindeverband_name object gemeindeverband_schluessel float64 gemeinde_name object gemeinde_schluessel float64 strasse_name object strasse_schluessel object sparte object von object nach object baubeginn object bauende object verkehrsbeeintraechtigungen object baumassnahme object dauer float64 dtype: object
Die Datentypen geben an, dass sowohl kreis_name
und baumassnahme
keine Strings sondern Objekte sind. Hier bedeutet es Zeichenfolgen, im Prinzip können es auch komplexere Objekte sein. Wenn wir zum Beispiel mal die Anzahl der Werte für kreis_name
berechnen
baust.kreis_name.count()
203
so ist diese Zahl niedriger als die Anzahl der Zeilen die baust.shape
zurückgeliefert hat. Das liegt an fehlenden Werten. Dazu mehr im Abschnitt Datenvorverarbeitung.
Was sind die Werte?¶
Einer der ersten Schritte mit einem neuen Datensatz ist es, die Werte der relevanten Variablen zu überprüfen. Die Pandas-Bibliothek enthält viele Tools für die beschreibenden Datenanalyse. Bei den kategorischen Variablen möchten wir normalerweise die expliziten Werte sehen, bei den numerischen können wir die minimalen und maximalen Werte überprüfen.
Verschiedene diskrete Werte¶
Was sind die möglichen Werte für Kreisname? Ein schneller Blick auf die Daten zeigt, dass es einige Werte für die Spalte sparte
, aber gibt es noch mehr? Das können wir mit der Methode .unique()
abfragen:
baust.sparte.unique()
array(['Fernwärmeleitung', 'Grünpflege', 'Wasserleitung', 'Gebäudesanierung', 'Hochbau', 'Straßenbau', 'Stromnetz', 'Kabelnetz', 'Gasleitung', 'Telefonnetz', 'Lichtsignalanlage', 'Straßenbeleuchtung', 'privat', 'Gleisbau', 'Kranarbeiten'], dtype=object)
Das Ergebnis zeigt uns, dass alle Passagiere in diesen Daten als "männlich" oder "weiblich" kategorisiert sind. Das scheint vollkommen plausibel zu sein. Wir können auch ein technisches Detail erkennen, nämlich dass das Ergebnis als ein numpy-Array zurückgegeben wird.
Ein Wort über Methoden und Methodenverkettung. Lassen Sie uns den vorherigen Befehl, baust.sparte.unique()
, überprüfen. Dieser enthält drei Komponenten, und der Prozess besteht darin, diese in einer sequenziellen Reihenfolge anzuwenden:
baust
ist der Datensatz. Das ist das, womit wir anfangen..sparte
ist ein Attribut vonbaust
. Hier bedeutet es, das die Spaltesparte
aus dem Datensatz extrahiert wird und als Serie zurückgegeben wird.- Schließlich ist
.unique()
eine Methode, welche die Serie an Werten auf ihrer linken Seite analysiert (hier die Seriesparte
), und diese analysiert um die eindeutigen Werten zu identifizieren.
Einige der Methoden können entweder auf den gesamten Datenrahmen oder auf einzelne Variablen angewendet werden. Wenn sie auf den gesamten Datenrahmen angewendet werden, gelten sie für jede einzelne Variable separat, sozusagen wie eine Schleife über einzelne Variablen, auf die sie angewendet werden.
Häufig ist es nicht nur interessant bei kategorischen Werten zu verstehen, welche eindeutigen Werte auftreten, sondern wie oft diese Auftreten. Hierfür nutzt man die Funktion
baust.sparte.value_counts()
sparte Hochbau 40 Gebäudesanierung 35 Straßenbau 35 Wasserleitung 25 Fernwärmeleitung 18 Telefonnetz 16 Kabelnetz 11 Stromnetz 8 Grünpflege 4 Lichtsignalanlage 3 Straßenbeleuchtung 3 Gleisbau 2 Kranarbeiten 2 Gasleitung 1 privat 1 Name: count, dtype: int64
Das gibt uns schon mal ein gutes Bild der Sparten. Aber wie sieht es mit der Spalte baumassnahme
aus?
baust.baumassnahme.unique()
array(['FW-Leitung i.a. der Hanseatic', 'Baumpflanzung', 'Einbau Trinkwasserschieber', nan, 'Sanierung Mischwasser- und Trinkwasserleitung', 'Containerstellung', 'Kennzeichnung Baustellenausfahrten', 'Wartungs- und Instandsetzungsarbeiten innerhalb der konzessionierten Strecke', 'Grünraumarbeiten, Sanierung Außenanlagen', 'Ausschleifung - Muffung, StS Bussebart', 'Sanierungsarbeiten', 'Schutzrohrverlegung', 'Herstellung Gehweg und Parkflächen', 'Verlegung Fernwärme', 'Fernwärmeerweiterung', 'LWL- Verlegung im Gehweg und Grünstreifen', 'Reparatur Trinkwasserschieber', 'Ausbau Gewerbestraße mit Zufahrt CNG-Tankstelle', 'Straßenneubau', 'Tiefbauarbeiten für Breitbandausbau', 'Breitbandausbau', 'Sanierung Gehweg', 'Neubau Trinkwassergrundstücksanschluss', 'Fernwärme', 'Sanierung Altbau', 'Herstellung Aufpflasterung', 'Wartungs- und Instandsetzungsarbeiten an Wechselverkehrszeichen (WVZ) - AQ0 und AQ1', 'Neubau Wohnhaus', 'Hausanschluss Fernwärme', 'Gehwegsanierung und Verkehrsberuhigung', 'Neubau Fernwärmeleitung -abschnittsweise-', 'Gehwegsanierung', 'Hochbauarbeiten', 'Reparatur Hauptleitung Fernwärme', 'Verlegung LWL', 'Neubau Mehrfamilienhaus', 'Neubau von zwei Häusern', 'Arbeiten am Behördenzentrum', 'Erweiterung Fernwärme Warnemünde', 'Erneuerung Straßenbeleuchtung', 'Neubau von Wohneinheiten', 'Querschläge/Suchschachtungen/Bau provisorische Bushaltestelle', 'Umverlegung Trinkwasserleitung, SW & RW-Kanal', 'Gebäudesanierung', 'Neubau Wohn- und Geschäftshaus Teil B', 'Herstellung Trinkwassergrundstücksanschluss', 'Betonierungen/ Materiallieferungen', 'Sanierung TWL und Einflechtung Mischwasserkanal', 'Reinigung der verschmutzten Fahrbahn', 'Kanalreparaturen', 'Tiefgründiger Ausbau', 'Herstellug Zufahrt', 'Herstellung Hausanschluss', 'Baustelleneinrichtung für Sanierung Kirrchplatz 10', 'Gehwegsperrung für Hochbau / Umstrukturierung des gesamten Grundstücks Möllner Str.-Plöner Str. 1-6 und 15, 16, 19', 'Sanierung Geh- und Radweg', 'Reparatur Fernwärmehausanschluss', 'Neubau Amtsgebäude', 'Sanierung Mischwasserkanal', 'Modernisierung Mehrfamilienhaus', 'Grundhafter Ausbau', 'Hochbauarbeiten, Zimmerarbeiten (Dachstuhl, Dachdecker), Erschließung Tiefbauarbeiten Keller, Lieferverkehr', 'Neubau straßenbegleitender Geh- und Radweg', 'Erneuerung Hausanschluss Trinkwasser', 'Rohrleitungsbau', 'LWL-Anbindung -Bohrung-', 'Fundamentarbeiten Balkonanlage -Betonpumpe & Betonmischer-', 'Sanierung Gebäude', 'Hochbau Schröderplatz 3 - 4 -Baustr. Az.', 'Lkw Entladung am 02. & 04.04.2024', 'Sanierung Trinkwasserleitung 3. BA', 'Vermessungsarbeiten auf der Brücke', 'Verlegung Erdkabel', 'LWL- Anbindung in vorhandene Rohrtrasse', 'Lieferverkehr und Materiallagerung für Hochbau', 'Abrissarbeiten', 'Arbeiten an der Turnhalle', 'Neubau Mehrfamilienhaus -Materiallieferung mit Vollsperrung-', 'Sanierung Wohnhaus', 'Verlegung Breitbandkabel', 'Neubau Trinkwasserleitung, Neuanschluss Trinkwassergrundstücksanschluss', 'Ferwärmeerweiterung und Fernwärmehausanschluss', 'Kabelsanierung', 'Deckenschluss', 'Baustellenausfahrt', 'Schachtdeckel Havarie Übernahme VRAO Az.', 'Hochbauarbeiten -Gerüststellung, Verbau, Betonpumpengänge mit Vollsperrung-', 'Neubau Hausanschluss Fernwärme, Vollsperrung ab 02.04.2024', 'Neubau Geh- und Radweg', 'Arbeiten an den Außenanlagen -BE-Fläche-', 'Neubau Entwässerungsleitachse', 'Neubau Kita Gänseblümchen -urspr. Az.', 'Verlegung Fernwärmeleitung', 'Verlegung Erdkabel - Hausanschluss-', 'Breitbandanbindung Platz der Freundschaft 13', 'Hochbau -urspr. Az.', 'Erschließung Wohngebiet Kiefernweg Los 4', 'Neubau Einfamiliehaus', 'Prüfung und Isolierung Hausanschluss', 'Sanierung Schachtdeckel', 'Fernwärmehausanschluss', 'Kraneinsatz', 'Sanierung Regen- und Trinkwasserleitung', 'Störungsbeseitigung', 'Havarie MW-Kanal - Baustellenzufahrten', 'Neubau Geschäftshaus', 'Trennung Trinkwassergrundstücksanschluss', 'Sicherung Baustellenausfahrt', 'Ertüchtigung Ost-West-Str. zw. Zufahrt A 19 & Gleis 666', 'Sanierung/Neubau Hausanschluss', 'Neubau FWL', 'Be- und Entlladung für Umbau des Hotels', 'Sicherung Baufeld & Baustellenzufahrt', 'Sperrung eines Teils des Parkplatzes An der Fischerbastion für vorbereitende Maßnahmen (Munitionssondierung/BE-Fläche)', 'Ersatzneubau Goetheplatzbrücke', 'Gerüststellung & BE für Fassadensanierung', 'Haussanierung (Gerüststellung, BE entlang des Hauses und Lieferzone)', 'Neubau MFH', 'Baustelleneinrichtung, Containerstellung', 'Sanierung Banhunterweg/Erneuerung Stromwandler', 'Fassadenarbeiten -Gerüststellung und Baustelleneinrichtung-', 'Erweiterung Glasfasernetz', 'Errichtung Hotelkomplex', 'Sanierung Wasser/ Abwasser in versch. Bauphasen', 'Reparatur Gehweg', '2 Einsätze mit Lkw-Kran für Wechsel Mobilfunkantennen', 'Herstellung Neubau Haus mit Kran', 'Grundhafte Sanierung in 2 Abschnitten, Herstellung Bushaltestellen', 'Neubau Hausanschluss Fernwärme', 'Baugrube für Telekom', 'Neubau Mehrfamilienhaus mit Tiefgarage', 'Sanierung Trinkwasserleitung', 'Grundhafte Erneuerung', 'Neubau Ampelanlage', 'Erneuerung TW-Leitung', 'Anschluss Personenbahnhof', 'Arbeiten Hausanschluss Trink- und Schmutzwasser', 'Errichtung einer Kabelbrücke für E-Versorgung Baustelle', 'Anbindung Breitband Stadtentsorgung HRO', 'Baustelleneinrichtung / Belieferung für Neubau Goetheplatzbrücke', 'Baustraße für Neubau Hort Taklerring'], dtype=object)
Diese Liste gibt ein viel komplexeres Bild und listet eine Vielzahl an Typen von Baumaßnahmen. Wenn wir hier die Funktion value_counts()
verwenden, so listet diese auch nicht alle Werte auf:
baust.baumassnahme.value_counts()
baumassnahme Breitbandausbau 12 Erneuerung Straßenbeleuchtung 3 Sanierung Trinkwasserleitung 3 Verlegung Fernwärmeleitung 2 Verlegung Breitbandkabel 2 .. Gebäudesanierung 1 Umverlegung Trinkwasserleitung, SW & RW-Kanal 1 Querschläge/Suchschachtungen/Bau provisorische Bushaltestelle 1 Neubau von Wohneinheiten 1 Baustraße für Neubau Hort Taklerring 1 Name: count, Length: 140, dtype: int64
Bei Datensätzen mit hunderttausenden von Zeilen kann das sehr lange dauern und kein verwertbares Ergebnis liefern. Deshalb ist es meist sinnvoll erstmal zu prüfen wie viele eindeutige Werte existieren. Diese Anzahl können wir mit der Methode .nunique
abfragen:
baust.baumassnahme.nunique()
140
Numerische Werte¶
Für numerische Variablen sollte es vermieden werden unique()
weil diese meist sehr große Ergebnismengen zurück geben, die selten nützlich sind. Stattdessen ist interessante den Wertebereich der Variable zu verstehen (siehe "Normierung"). Dies gibt einen schnellen Überblick darüber, ob die Werte plausibel sind, z.B. ob negative Alterswerte auftreten oder ungewöhnlich große Alterswerte:
baust.longitude.min(), baust.longitude.max()
(12.0120276853359, 12.2248919062465)
baust.latitude.min(), baust.latitude.max()
(54.057580297901, 54.2396815337323)
Der Wertebereich für beide ist für Rostock durchaus plausibel und wir machen uns daher keine Sorgen über zu große oder zu kleine Werte. Siehe Abschnitt "Entfernen unerwünschter Variablen" unten, um fehlende Werte zu erkennen und zu analysieren.
Pandas bietet die Methode describe()
an, die es erlaubt diese Statistiken für alle numerische Spalten in einem Aufruf zu extrahieren. Als Ergebnis erhalten wir einen neun Dataframe mit den Statistiken.
baust.describe()
latitude | longitude | kreis_schluessel | gemeindeverband_schluessel | gemeinde_schluessel | dauer | |
---|---|---|---|---|---|---|
count | 204.000000 | 204.000000 | 203.0 | 203.0 | 2.030000e+02 | 204.000000 |
mean | 54.104472 | 12.110911 | 13003.0 | 130030000.0 | 1.300300e+11 | 229.857230 |
std | 0.031356 | 0.034811 | 0.0 | 0.0 | 0.000000e+00 | 278.425382 |
min | 54.057580 | 12.012028 | 13003.0 | 130030000.0 | 1.300300e+11 | 0.166667 |
25% | 54.083985 | 12.084762 | 13003.0 | 130030000.0 | 1.300300e+11 | 30.572917 |
50% | 54.090294 | 12.116659 | 13003.0 | 130030000.0 | 1.300300e+11 | 97.125000 |
75% | 54.124206 | 12.136534 | 13003.0 | 130030000.0 | 1.300300e+11 | 336.750000 |
max | 54.239682 | 12.224892 | 13003.0 | 130030000.0 | 1.300300e+11 | 1347.000000 |
Zählungen und Anteile¶
Eine weitere häufige Aufgabe, mit der wir konfrontiert sind, besteht darin, Werte zu zählen oder Anteile von bestimmten Werten zu berechnen. Die Methode .value_counts
(siehe oben) kann verwendet werden, um die Anzahl kontinuierlicher Werte zu erhalten. Bei numerischen Werten funktioniert das aufgrund der Vielfalt and Werte nicht gut. Außerdem hilft es auch nicht Fragen die von Bedingungen abhängig sind. Zum Beispiel wollen wir wissen wie viele Baustellen im Jahr 2024 begonnen worden sind. Dafür müssen wir einen logischen Vektor erstellen, der die Bedingung beschreibt, und diesen summieren.
seit2024 = baust.baubeginn > "2024-01-01 00:00:00" # Erzeuge eine logische Variable die True ist wenn der baubeginn nach 2024 ist
seit2024.sum() # Zähle die Baustellen
127
Lassen Sie uns diese Schritte genauer erklären. Zuerst die Zeile
seit2024 = baust.baubeginn > "2024-01-01 00:00:00"
seit2024.head(5)
0 True 1 True 2 True 3 True 4 False Name: baubeginn, dtype: bool
Erzeuge eine logische Variable die True
oder False
ist, wenn das Datum des Baubeginns nach 2024 liegt (um genau zu sein machen wir hier einen Stringvergleich und keinen Datumsvergleich. Da wir die Spalte nicht in ein Datum umgewandelt haben.). Ein Beispiel für diese Variable sieht so aus:
Als nächstes addiert seit2024.sum()
diese Werte zusammen - denken Sie daran, sobald wir mit logischen Werten rechnen, wird True
in "1" und False
in "0" umgewandelt, und letzteres spielt beim Addieren keine Rolle. Es ist also so, als würden wir Baustellen zählen. In der Praxis müssen Sie keine separate Variable erstellen, sondern können es direkt berechnen
(baust.baubeginn > "2024-01-01 00:00:00").sum()
127
Die Berechnung von Proportionen kann auf ähnliche Weise erfolgen. Statt .sum
verwenden wir einfach .mean()
:
(baust.baubeginn > "2024-01-01 00:00:00").mean()
0.6225490196078431
Also gibt es 62% der Baustellen seit 2024.
Filtern von Daten¶
Schließlich, lassen wollen wir die Teilmengen der Baustellen erhalten, die entweder im "Hochbau" oder "Straßenbau" liegen. Dies kann mit der Methode .isin()
durchgeführt werden, um zu überprüfen, ob ein String in einer gegebenen Liste vorhanden ist:
ab = baust[baust.sparte.isin(["Hochbau", "Straßenbau"])]
ab.head(5)
latitude | longitude | uuid | kreis_name | kreis_schluessel | gemeindeverband_name | gemeindeverband_schluessel | gemeinde_name | gemeinde_schluessel | strasse_name | strasse_schluessel | sparte | von | nach | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
5 | 54.147466 | 12.065587 | 0cfabba6-211c-42b0-b10c-3f2c6b2103e0 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Flensburger Str. | 01930 | Hochbau | 25 | 26 | 2023-11-22 07:00:00+01:00 | 2024-04-02 17:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Verkeh... | Containerstellung | 132.375000 |
8 | 54.143917 | 12.061527 | 140210b9-601a-482a-a14b-8f13a46879ef | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Möllner Str. | 00600 | Hochbau | NaN | NaN | 2024-03-25 07:00:00+01:00 | 2025-12-31 18:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße | Kennzeichnung Baustellenausfahrten | 646.458333 |
9 | 54.135178 | 12.093463 | 150a9ed8-a7b1-4bf1-8e3f-95e569f480e6 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Schmarl-Dorf | 08280 | Straßenbau | NaN | NaN | 2024-01-01 00:00:00+01:00 | 2025-01-01 00:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße, Verkeh... | Wartungs- und Instandsetzungsarbeiten innerhal... | 366.000000 |
13 | 54.179967 | 12.084384 | 18728d03-7a43-4372-8564-f7519b29a64f | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Luisenstr. | 06760 | Hochbau | 5 | NaN | 2024-03-22 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | Sicherungsmaßnahmen entlang der Straße | Sanierungsarbeiten | 6.458333 |
14 | 54.089303 | 12.108991 | 1a589275-aebe-497f-b981-1e8f09b81473 | Rostock | 13003.0 | Rostock, Hanse- und Universitätsstadt | 130030000.0 | Rostock, Hanse- und Universitätsstadt | 1.300300e+11 | Waldemarstr. | 09410 | Straßenbau | 20 | 34 | 2024-03-04 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | halbseitige Sperrung, Sicherungsmaßnahmen entl... | Schutzrohrverlegung | 24.458333 |
Auswahl von Variablen¶
Häufig enthalten Datensätze Variablen, die für die aktuelle Analyse irrelevant sind. In einem solchen Fall möchten wir entweder nur die relevanten auswählen (wenn es nur wenige solcher Variablen gibt) oder die irrelevanten entfernen (wenn es nicht zu viele davon gibt). Das Entfernen irrelevanter Informationen hilft uns, die Daten besser zu sehen, sie besser zu verstehen und somit weniger Codierfehler zu haben. Es hilft auch bei bestimmten Transformationen, bei denen wir jetzt möglicherweise auf den gesamten reduzierten Datenrahmen anstatt nur auf ausgewählte Variablen arbeiten möchten. Schließlich hilft es auch, Speicher zu sparen und die Verarbeitungsgeschwindigkeit zu erhöhen.
Auswahl der gewünschten Variablen¶
Wir haben besprochen, wie man die gewünschten Variablen auswählt, aber wir werden es hier auch kurz überprüfen. Sie können nur die gewünschten Variablen mit dataframe[["var1", "var2", ..."]]
auswählen.
baust[["sparte", "baumassnahme", "verkehrsbeeintraechtigungen", "baubeginn", "bauende", "latitude", "longitude", "dauer"]].sample(4)
sparte | baumassnahme | verkehrsbeeintraechtigungen | baubeginn | bauende | latitude | longitude | dauer | |
---|---|---|---|---|---|---|---|---|
152 | Telefonnetz | Störungsbeseitigung | Sicherungsmaßnahmen entlang der Straße, Sicher... | 2024-03-04 07:00:00+01:00 | 2024-03-28 17:00:00+01:00 | 54.139985 | 12.040008 | 24.416667 |
114 | Gebäudesanierung | NaN | NaN | 2024-03-25 00:00:00+01:00 | 2024-05-25 00:00:00+02:00 | 54.087081 | 12.146371 | 60.958333 |
168 | Gebäudesanierung | Gerüststellung & BE für Fassadensanierung | Sicherungsmaßnahmen entlang der Straße, Sicher... | 2023-09-21 00:00:00+02:00 | 2024-04-01 00:00:00+02:00 | 54.083904 | 12.114623 | 193.000000 |
39 | Straßenbau | Gehwegsanierung | Sicherungsmaßnahmen entlang der Straße, Sicher... | 2024-02-26 00:00:00+01:00 | 2024-03-29 00:00:00+01:00 | 54.087086 | 12.150628 | 32.000000 |
Anstatt den gesamten Datensatz zu laden und später die gewünschten Variablen auszuwählen, können wir auch angeben, welche Spalten mit pd.read_csv
gelesen werden sollen.
pd.read_csv("../data/Baustellen/baustellen_flat.csv",
usecols=["latitude", "longitude", "sparte", "baubeginn", "bauende", "verkehrsbeeintraechtigungen", "baumassnahme", "dauer"]).sample(4)
latitude | longitude | sparte | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|
106 | 54.091988 | 12.145594 | Hochbau | 2023-11-06 00:00:00+01:00 | 2024-09-28 00:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Neubau Mehrfamilienhaus -Materiallieferung mit... | 326.958333 |
57 | 54.078469 | 12.126196 | Wasserleitung | 2023-07-03 00:00:00+02:00 | 2024-05-01 00:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Umverlegung Trinkwasserleitung, SW & RW-Kanal | 303.000000 |
14 | 54.089303 | 12.108991 | Straßenbau | 2024-03-04 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | halbseitige Sperrung, Sicherungsmaßnahmen entl... | Schutzrohrverlegung | 24.458333 |
12 | 54.093918 | 12.088166 | Gebäudesanierung | 2024-03-25 00:00:00+01:00 | 2024-03-29 00:00:00+01:00 | NaN | NaN | 4.000000 |
Dies erreicht ein ähnliches Ergebnis, obwohl die Spalten jetzt in der ursprünglichen Reihenfolge und nicht in der angegebenen Reihenfolge sind.
Entfernen unerwünschter Variablen¶
Alternativ können wir, wenn wir die meisten Variablen behalten möchten, stattdessen angeben, welche entfernt werden sollen. Dies kann mit der Methode .drop
erfolgen. Im Folgenden lassen wir eine große Anzahl von Variablen fallen:
baust.drop(["uuid", "kreis_name", "kreis_schluessel", "gemeindeverband_name", "gemeindeverband_schluessel", "gemeinde_name", "gemeinde_schluessel", "strasse_name", "strasse_schluessel"],
axis=1 ).sample(4)
latitude | longitude | sparte | von | nach | baubeginn | bauende | verkehrsbeeintraechtigungen | baumassnahme | dauer | |
---|---|---|---|---|---|---|---|---|---|---|
195 | 54.177670 | 12.091828 | Stromnetz | 5 | 7 | 2024-03-11 07:00:00+01:00 | 2024-03-28 18:00:00+01:00 | Sicherungsmaßnahmen entlang des Gehwegs | Anschluss Personenbahnhof | 17.458333 |
89 | 54.085382 | 12.137370 | Hochbau | 90 | NaN | 2023-06-27 00:00:00+02:00 | 2024-05-01 00:00:00+02:00 | NaN | NaN | 309.000000 |
151 | 54.123262 | 12.078209 | Wasserleitung | Schmarler Damm (08290) | NaN | 2024-01-31 07:00:00+01:00 | 2024-05-10 17:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße, Sicher... | Sanierung Regen- und Trinkwasserleitung | 100.375000 |
82 | 54.113939 | 12.140877 | Hochbau | 42 | NaN | 2023-04-17 07:00:00+02:00 | 2024-05-03 18:00:00+02:00 | Sicherungsmaßnahmen entlang der Straße | Modernisierung Mehrfamilienhaus | 382.458333 |
Beachten Sie, dass wir .drop
mitteilen müssen, dass wir Spalten mit diesen Namen zu entfernen und nicht Zeilen mit diesem Index. Dies ist, was das Argument axis=1
bewirkt. Andernfalls erhalten wir einen Fehler:
baust.drop(["uuid", "kreis_name", "kreis_schluessel", "gemeindeverband_name", "gemeindeverband_schluessel", "gemeinde_name", "gemeinde_schluessel", "strasse_name", "strasse_schluessel"]).sample(4)
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[26], line 1 ----> 1 baust.drop(["uuid", "kreis_name", "kreis_schluessel", "gemeindeverband_name", "gemeindeverband_schluessel", "gemeinde_name", "gemeinde_schluessel", "strasse_name", "strasse_schluessel"]).sample(4) File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/frame.py:5568, in DataFrame.drop(self, labels, axis, index, columns, level, inplace, errors) 5420 def drop( 5421 self, 5422 labels: IndexLabel | None = None, (...) 5429 errors: IgnoreRaise = "raise", 5430 ) -> DataFrame | None: 5431 """ 5432 Drop specified labels from rows or columns. 5433 (...) 5566 weight 1.0 0.8 5567 """ -> 5568 return super().drop( 5569 labels=labels, 5570 axis=axis, 5571 index=index, 5572 columns=columns, 5573 level=level, 5574 inplace=inplace, 5575 errors=errors, 5576 ) File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/generic.py:4785, in NDFrame.drop(self, labels, axis, index, columns, level, inplace, errors) 4783 for axis, labels in axes.items(): 4784 if labels is not None: -> 4785 obj = obj._drop_axis(labels, axis, level=level, errors=errors) 4787 if inplace: 4788 self._update_inplace(obj) File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/generic.py:4827, in NDFrame._drop_axis(self, labels, axis, level, errors, only_slice) 4825 new_axis = axis.drop(labels, level=level, errors=errors) 4826 else: -> 4827 new_axis = axis.drop(labels, errors=errors) 4828 indexer = axis.get_indexer(new_axis) 4830 # Case for non-unique axis 4831 else: File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/indexes/base.py:7070, in Index.drop(self, labels, errors) 7068 if mask.any(): 7069 if errors != "ignore": -> 7070 raise KeyError(f"{labels[mask].tolist()} not found in axis") 7071 indexer = indexer[~mask] 7072 return self.delete(indexer) KeyError: "['uuid', 'kreis_name', 'kreis_schluessel', 'gemeindeverband_name', 'gemeindeverband_schluessel', 'gemeinde_name', 'gemeinde_schluessel', 'strasse_name', 'strasse_schluessel'] not found in axis"
Gruppierte Operationen¶
Wie andere große DataFrame-Bibliotheken unterstützt auch Pandas gruppierte Operationen. Gruppierte Operationen werden im Wesentlichen wie folgt durchgeführt: Zuerst wird die Daten anhand der Gruppierungsvariablenwerte in Gruppen aufgeteilt. Zweitens werden die folgenden Operationen auf allen Gruppen unabhängig voneinander durchgeführt. Und schließlich werden alle Ergebnisse wieder zusammengeführt, zusammen mit Gruppenindikatoren.
Gruppieren in Pandas kann mit der Methode .groupby
durchgeführt werden. Es erwartet eine Liste von Gruppierungsvariablen. Wir demonstrieren dies, indem wir den Baustellendauer nach Sparte berechnen. .groupby
erstellt lediglich ein gruppiertes Datenframe, der dann um eine Aggregationsfunktion erweitert wird um einen Ergebnisdataframe zu erhalten.
baust.groupby("sparte").dauer.mean()
sparte Fernwärmeleitung 75.138889 Gasleitung 185.750000 Gebäudesanierung 179.201786 Gleisbau 939.541667 Grünpflege 68.593750 Hochbau 466.585417 Kabelnetz 51.462121 Kranarbeiten 3.500000 Lichtsignalanlage 260.472222 Straßenbau 295.767857 Straßenbeleuchtung 46.416667 Stromnetz 71.640625 Telefonnetz 54.661458 Wasserleitung 198.724167 privat 2.250000 Name: dauer, dtype: float64
Das Ergebnis ist eine Serie mit zwei Werten (die numerische dauer) mit dem Index der kategorischen Werte von sparte.
Wir können auch nach mehr als einer Variable gruppieren. In diesem Fall müssen wir diese als Liste angeben, z.B. um die Baustellendauer nach Sparte und Baumaßnahme zu berechnen, können wir das tun.
baust.groupby(["sparte", "baumassnahme"]).dauer.mean()
sparte baumassnahme Fernwärmeleitung Erweiterung Fernwärme Warnemünde 316.375000 FW-Leitung i.a. der Hanseatic 39.375000 Fernwärme 39.333333 Fernwärmeerweiterung 186.041667 Fernwärmehausanschluss 32.000000 ... Wasserleitung Sanierung Wasser/ Abwasser in versch. Bauphasen 494.958333 Schachtdeckel Havarie Übernahme VRAO Az. 190.000000 Trennung Trinkwassergrundstücksanschluss 3.000000 Umverlegung Trinkwasserleitung, SW & RW-Kanal 303.000000 privat Fundamentarbeiten Balkonanlage -Betonpumpe & Betonmischer- 2.250000 Name: dauer, Length: 145, dtype: float64
Die Methode .groupby
unterstützt weitere Optionen, z.B. kann man die Gruppenindikatoren als Variablen behalten anstatt als Index.
Zeichenkettenoperationen¶
Pandas öffnet Zeichenkettenvariablen für eine große Liste von Zeichenkettenfunktionen mit dem Attribut .str
. Diese replizieren größtenteils das re Modul, aber die Syntax ist anders und oft sind auch die Funktionsnamen unterschiedlich. Wir gehen hier durch eine Reihe von Beispielen, nämlich
str.contains
zum Suchen von Mustern in Zeichenkettenstr.match
um festzustellen, ob eine Zeichenkette mit einem bestimmten Muster beginntstr.replace
um Teile von Zeichenketten zu ersetzenstr.split
zum Zerteilen von Zeichenketten in Wörter
Viele der Zeichenkettenfunktionen nutzen reguläre Ausdrücke, daher ist es nützlich, eine Vorstellung von den Grundlagen regulärer Ausdrücke zu haben.
Lassen Sie uns Baustellendaten verwenden und analysieren, ob es ein regelmäßiges Muster in den Baumaßnahmen gibt. Wenn wir nur unique
aufrufen, erhalten wir eine sehr unübersichtliche Liste.
baust.baumassnahme.unique()[:10]
array(['FW-Leitung i.a. der Hanseatic', 'Baumpflanzung', 'Einbau Trinkwasserschieber', nan, 'Sanierung Mischwasser- und Trinkwasserleitung', 'Containerstellung', 'Kennzeichnung Baustellenausfahrten', 'Wartungs- und Instandsetzungsarbeiten innerhalb der konzessionierten Strecke', 'Grünraumarbeiten, Sanierung Außenanlagen', 'Ausschleifung - Muffung, StS Bussebart'], dtype=object)
Allerdings sehen wir auch, dass einige Wörter wie z.B. "Sanierung" wiederholt vorkommen. Vieleicht ist es sinnvoller, die Zeichenkette in einzelne Worte zu zerlegen und diese zu zählen. Hierfür müssen wir mehrere Methoden verknüpfen, indem wir die Zeichenkette zuerst in Listen an Wörten mit split
an allen vorkommenden Leerzeichen und Satzzeichen zerlegen. Mit stack()
kombinieren wir diese Liste in eine neue Serie und zählen dann mit value_counts()
wie häufig welche Wörter vorkommen.
baust.baumassnahme.str. (expand=True).stack().value_counts()
Neubau 25 und 21 Sanierung 19 Breitbandausbau 13 für 12 .. straßenbegleitender 1 Trinkwasser 1 LWL-Anbindung 1 -Bohrung- 1 Taklerring 1 Name: count, Length: 283, dtype: int64
Siehe da: Worte wie "Neubau" und "Sanierung" kommen durchaus oft vor.
Finde Muster in Zeichenketten¶
Als ersten Schritt wollen wir alle Baumaßnamen identifizieren, die das Wort "Sanierung" enthalten. Muster in Zeichenfolgen können mit str.contains(Muster)
identifiziert werden. Diese Funktion gibt einen Vektor von Wahrheitswerten zurück, je nachdem, ob der ursprüngliche Zeichenvektor das Muster enthält:
baust.sparte.str.contains("sanierung", na=False, regex=False, case=False).head(4)
0 False 1 False 2 False 3 True Name: sparte, dtype: bool
Das Argument na
gibt an, was mit fehlenden Werten zu tun ist, hier zwingen wir sie, den Wert false
zurückzugeben. regex=False
bedeutet, dass das Muster, das wir angeben, kein regulärer Ausdruck ist. Einfache Muster sind einfacher und schneller zu verwenden als reguläre Ausdrücke, aber letztere sind viel leistungsfähiger, aber auch deutlich komplizierter. Der Parameter case
gibt an, dass die Funktion nicht auf Groß/Kleinschreibung achten soll.
Jetzt wollen wir uns mal die Baumaßnahmen ansehen, die den Begriff Sanierung enthalten
baust.baumassnahme[baust.baumassnahme.str.contains("sanierung", na=False, regex=False, case=False)].value_counts()
baumassnahme Sanierung Trinkwasserleitung 3 Sanierungsarbeiten 2 Sanierung Trinkwasserleitung 3. BA 1 Grundhafte Sanierung in 2 Abschnitten, Herstellung Bushaltestellen 1 Sanierung Wasser/ Abwasser in versch. Bauphasen 1 Sanierung Banhunterweg/Erneuerung Stromwandler 1 Haussanierung (Gerüststellung, BE entlang des Hauses und Lieferzone) 1 Gerüststellung & BE für Fassadensanierung 1 Sanierung/Neubau Hausanschluss 1 Sanierung Regen- und Trinkwasserleitung 1 Sanierung Schachtdeckel 1 Kabelsanierung 1 Sanierung Wohnhaus 1 Sanierung Mischwasser- und Trinkwasserleitung 1 Grünraumarbeiten, Sanierung Außenanlagen 1 Sanierung Mischwasserkanal 1 Sanierung Geh- und Radweg 1 Baustelleneinrichtung für Sanierung Kirrchplatz 10 1 Sanierung TWL und Einflechtung Mischwasserkanal 1 Gebäudesanierung 1 Gehwegsanierung 1 Gehwegsanierung und Verkehrsberuhigung 1 Sanierung Altbau 1 Sanierung Gehweg 1 Sanierung Gebäude 1 Name: count, dtype: int64
Bitte beachten Sie: Zuerst müssen Sie das Attribut .str
verwenden, um eine Spalte für Zeichenfolgenoperationen zu öffnen. .cabin.contains()
funktioniert nicht.
.contains
gibt einen logischen Wert zurück oder NA im Falle eines fehlenden Eintrags.
Ersetzen von Zeichenfolgen¶
Zuletzt wollen wir im Datensatz die deutschen Umlaute ersetzen, z.B. weil diese Probleme mit ML-Modellen bereiten könnten. Dies kann mit der Methode str.replace
gemacht werden. Sie hat zwei Argumente: Muster und Ersatz. Wir geben das erstes einen Umlaut wie ä
an und ersetzen ihn einfach durch ae
:
baust.sparte.str.replace("ä", "ae").replace("ö", "oe").replace("ü", "ue").replace("ß", "ss").value_counts()
sparte Hochbau 40 Gebaeudesanierung 35 Straßenbau 35 Wasserleitung 25 Fernwaermeleitung 18 Telefonnetz 16 Kabelnetz 11 Stromnetz 8 Grünpflege 4 Lichtsignalanlage 3 Straßenbeleuchtung 3 Gleisbau 2 Kranarbeiten 2 Gasleitung 1 privat 1 Name: count, dtype: int64
In praktischer Hinsicht ist es oft nützlich, den Originalspalte zu behalten, um zu prüfen, ob die Ersetzung richtig durchgeführt wurde. Dies kann erreicht werden, indem eine neue Spalte im Dataframe erstellt wird und eine Stichprobe der alten und neuen Variablen ausgedruckt wird:
baust["sparte_ASCII"] = baust.sparte.str.replace("ä", "ae").replace("ö", "oe").replace("ü", "ue").replace("ß", "ss")
baust[["sparte", "sparte_ASCII"]].value_counts()
sparte sparte_ASCII Hochbau Hochbau 40 Gebäudesanierung Gebaeudesanierung 35 Straßenbau Straßenbau 35 Wasserleitung Wasserleitung 25 Fernwärmeleitung Fernwaermeleitung 18 Telefonnetz Telefonnetz 16 Kabelnetz Kabelnetz 11 Stromnetz Stromnetz 8 Grünpflege Grünpflege 4 Lichtsignalanlage Lichtsignalanlage 3 Straßenbeleuchtung Straßenbeleuchtung 3 Gleisbau Gleisbau 2 Kranarbeiten Kranarbeiten 2 Gasleitung Gasleitung 1 privat privat 1 Name: count, dtype: int64
Umgang mit Zeitstempeln¶
Wenn man Zeitreihen analysiert, muss man immer mit Zeitstempeln umgehen. Das ist leider fehleranfälliger, als man vermuten mag, weil es sehr viele unterschiedliche Arten gibt Zeitstemmpel darzustellen und es Besonderheiten wie Zeitzonen, Sommer/Winter-Zeit, oder Schaltjahre gibt.
Prinzipiell können wir Datumsangaben und Zeiten mit der Funktion pd.to_datetime
in ein richtiges Datetime-Objekt umwandeln, mit dem man auch korrekte Zeitberechnungen machen kann (und nicht solche Workarounds, wie oben). Bei einzelnen Werten kann die Funktion sogar Datums und Zeitformate selbst erkennen.
print(pd.to_datetime("2023-03-08"))
print(pd.to_datetime("03/09/2023"))
print(pd.to_datetime("10-Mar-2023"))
print(pd.to_datetime("2023-03-11"))
2023-03-08 00:00:00 2023-03-09 00:00:00 2023-03-10 00:00:00 2023-03-11 00:00:00
print(pd.to_datetime("12:00"))
print(pd.to_datetime("14:30:00"))
print(pd.to_datetime("10:00:00.400"))
print(pd.to_datetime("18:00+0030"))
2024-04-29 12:00:00 2024-04-29 14:30:00 2024-04-29 10:00:00.400000 2024-04-29 18:00:00+00:30
print(pd.to_datetime("2023-03-08 12:00"))
print(pd.to_datetime("03/09/2023 14:30:00"))
print(pd.to_datetime("10-Mar-2023 10:00:00.400"))
print(pd.to_datetime("2023-03-11T18:00+0030"))
2023-03-08 12:00:00 2023-03-09 14:30:00 2023-03-10 10:00:00.400000 2023-03-11 18:00:00+00:30
Wenn wir diese allerdings in einer Serie kombinieren, funktioniert das nicht mehr
# Beispieldaten
data = pd.DataFrame({
"Datumzeit": ["2023-03-08 12:00","03/09/2023 14:30:00","10-Mar-2023 10:00:00.400","2023-03-11T18:00+0030"]
})
# Spalte "Uhrzeit" in Zeitformat konvertieren
data["DatumzeitDT"] = pd.to_datetime(data["Datumzeit"])
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[39], line 2 1 # Spalte "Uhrzeit" in Zeitformat konvertieren ----> 2 data["DatumzeitDT"] = pd.to_datetime(data["Datumzeit"]) File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:1067, in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache) 1065 result = arg.map(cache_array) 1066 else: -> 1067 values = convert_listlike(arg._values, format) 1068 result = arg._constructor(values, index=arg.index, name=arg.name) 1069 elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)): File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:433, in _convert_listlike_datetimes(arg, format, name, utc, unit, errors, dayfirst, yearfirst, exact) 431 # `format` could be inferred, or user didn't ask for mixed-format parsing. 432 if format is not None and format != "mixed": --> 433 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors) 435 result, tz_parsed = objects_to_datetime64( 436 arg, 437 dayfirst=dayfirst, (...) 441 allow_object=True, 442 ) 444 if tz_parsed is not None: 445 # We can take a shortcut since the datetime64 numpy array 446 # is in UTC File ~/miniconda3/envs/lehre4/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:467, in _array_strptime_with_fallback(arg, name, utc, fmt, exact, errors) 456 def _array_strptime_with_fallback( 457 arg, 458 name, (...) 462 errors: str, 463 ) -> Index: 464 """ 465 Call array_strptime, with fallback behavior depending on 'errors'. 466 """ --> 467 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc) 468 if tz_out is not None: 469 unit = np.datetime_data(result.dtype)[0] File strptime.pyx:501, in pandas._libs.tslibs.strptime.array_strptime() File strptime.pyx:451, in pandas._libs.tslibs.strptime.array_strptime() File strptime.pyx:583, in pandas._libs.tslibs.strptime._parse_with_format() ValueError: time data "03/09/2023 14:30:00" doesn't match format "%Y-%m-%d %H:%M", at position 1. You might want to try: - passing `format` if your strings have a consistent format; - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format; - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
Deshalb muss man zuerst darauf achten, dass entsprechende Zeitstempel immer in einem festen Format sind.
# Beispieldaten
data = pd.DataFrame({
"Datumzeit": [
"2023-03-08T12:00:00.000+0030",
"2023-03-09T14:30:00.000+0030",
"2023-03-10T10:00:00.400+0030",
"2023-03-11T18:00:00.000+0030"]
})
# Spalte "Uhrzeit" in Zeitformat konvertieren
data["DatumzeitDT"] = pd.to_datetime(data["Datumzeit"], format='ISO8601')
data.DatumzeitDT
0 2023-03-08 12:00:00+00:30 1 2023-03-09 14:30:00+00:30 2 2023-03-10 10:00:00.400000+00:30 3 2023-03-11 18:00:00+00:30 Name: DatumzeitDT, dtype: datetime64[ns, UTC+00:30]
Bei unsicheren Mustern kann man das Format direkt spezifizieren, Dafür wird die strftime-Notation verwendet
# Spalte "Uhrzeit" in Zeitformat konvertieren
data["DatumzeitDT"] = pd.to_datetime(data["Datumzeit"], f ormat="%Y-%m-%dT%H:%M:%S.%f%z")
data.DatumzeitDT
0 2023-03-08 12:00:00+00:30 1 2023-03-09 14:30:00+00:30 2 2023-03-10 10:00:00.400000+00:30 3 2023-03-11 18:00:00+00:30 Name: DatumzeitDT, dtype: datetime64[ns, UTC+00:30]
Mit dem gleichen Muster können Datumsangaben auch wieder in Strings umgewandelt werden.
data.DatumzeitDT.dt.strftime('%Y-%m-%d %H:%M')
0 2023-03-08 12:00 1 2023-03-09 14:30 2 2023-03-10 10:00 3 2023-03-11 18:00 Name: DatumzeitDT, dtype: object