Bearbeitung meiner Anzeigen in Bonn 2022

Die Stadt Bonn stellt Daten zu Parkverstößen online. Ich habe einmal geschaut, ob meine Anzeigen eigentlich bearbeitet wurden.

Wenn mich Leute durch Parken auf Geh- oder Radwegen behindern, mache ich manchmal Fotos davon und schicke das an die Bußgeldstelle der Stadt Bonn. In letzter Zeit habe ich aber meist einfach beim Ordnungsamt angerufen. Trotzdem wollte ich einmal schauen, ob meine Anzeigen überhaupt bearbeitet worden sind. Wenn sie das nämlich nicht würden, so könnte man sich die Arbeit ja auch sparen. Wenn nur einzelne Anzeigen nicht bearbeitet wurden, so kann man vielleicht Muster erkennen.

Für dieses Projekt müssen wir also einmal die Daten der Stadt Bonn herunterladen und mit meinen Anzeigen von Weg-Li kombinieren.

Einlesen der Daten der Stadt Bonn

Im Open Data Portal der Stadt Bonn findet man eine Tabelle mit Parkverstößen für 2022, die ich einmal heruntergeladen habe. Das ganze ist eine CSV-Datei mit Semikola separiert. So sieht das in der Textdatei aus:

TATTAG;TATZEIT;TATORT;TATBESTANDBE_TBNR;GELDBUSSE;BEZEICHNUNG
01.01.2022;1614;An der Vogelweide gg�. 33;112454;55;PKW
01.01.2022;1140;Annaberger Str. Nothaltepunkt 1778-8 BN;141164;55;PKW
01.01.2022;1400;Annaberger Str. zwischen Hausnr. 400 u. 415;141118;25;PKW
01.01.2022;1513;Bonn, Bonner Talweg gegen�ber Krankenhaus;112322;10;PKW
16.01.2022;1422;Bonn, Im Tannenbusch Gegen�ber Laterne 1;112655;87,5;PKW

Das Arbeiten mit den Daten anderer Leute finde ich immer sehr mühsam, weil sie meist mühsam formatiert sind.

  • Das Datum ist in der deutschen DD.MM.YYYY Schreibweise geschrieben.
  • Die Zeit ist in einer separaten Spalte mit hmm geschrieben, was wirklich total furchtbar ist. Kein Trennzeichen wie ein Doppelpunkt.
  • Dann sieht man an den »�«-Zeichen, dass das Encoding eben nicht UTF-8 ist.
  • Der Tatort ist ein Freiform-Textfeld ohne Struktur. Das kann man so nicht einfach auf einer Karte darstellen. Auch ist da nichts normiert. Mal steht da »Bonn« am Anfang der Adresse, manchmal nicht. Das »gegenüber« ist einmal abgekürzt, einmal groß- und einmal kleingeschrieben.
  • Das Bußgeld ist ein EUR-Betrag, der allerdings mit Komma getrennt ist. Somit muss man das erst in einen Punkt als Dezimaltrenner konvertieren, damit man das einlesen kann.

Wenn man Encoding und Trenner richtig einstellt, kann man das ganze auch noch in Libre Office Calc öffnen:

Bildschirmfoto von Libre Office Calc mit Daten der Stadt Bonn

Ich nutze Python Pandas zum Arbeiten mit der Daten, in einem Jupyter Notebook in VS Code. Wir laden die Datei:

data = pd.read_csv("Parkverstoesse2022.csv", sep=";", encoding="latin1")

Als nächstes muss man dieses merkwürdige Datumsformat in etwas sinnvolles konvertieren. Mit dem folgenden Code werden daraus echte datetime Objekte, allerdings ohne Zeitzone.

import datetime

import dateutil.parser

dates = [
    datetime.datetime.combine(
        dateutil.parser.parse(row["TATTAG"], dayfirst=True),
        datetime.time(hour=row["TATZEIT"] // 100, minute=row["TATZEIT"] % 100),
    )
    for index, row in data.iterrows()
]

Zuletzt müssen bei den Bußgeldern noch die Kommata durch Punkte ersetzt werden, anschließend kann ich sie als Fließkommazahl einlesen:

fines = [elem if isinstance(elem, int) else float(elem.replace(',', '.')) for elem in data['GELDBUSSE']]

Daraus erstelle ich dann einen neuen Data Frame, denn ich im Apache Parquet Format ablege:

cleaned = pd.DataFrame({"Zeit": dates, "Adresse": data["TATORT"], "Tatbestandsnummer": data["TATBESTANDBE_TBNR"], "Bußgeld": fines})
cleaned.to_parquet("Parkverstöße-2022.parquet")

Einlesen der Daten von Weg-Li

Bei Weg-Li gibt es praktisch direkt eine API-Methode, mit der man alle seine Anzeigen als JSON herunterladen kann. Die Daten kommen dann auch mit vielen Metadaten mit. Das ganze strukturiert und die Daten als String im ISO-Format mit Zeitzone. Weg-Li wird also von Leuten entwickelt die wissen, was sie tun. So sieht das aus:

{
    "token": "…",
    "status": "shared",
    "street": "…",
    "city": "Sankt Augustin",
    "zip": "53757",
    "latitude": ,
    "longitude": ,
    "registration": "…",
    "color": "black",
    "brand": "Audi",
    "charge": {
        "tbnr": "141091",
        "description": "Sie hielten auf einem Geh- und Radweg (Zeichen \u003c240/241\u003e) und behinderten +) dadurch Andere.",
        "fine": "55.0",
        "bkat": "§ 41 Abs. 1 iVm Anlage 2, § 1 Abs. 2, § 49 StVO; § 24 Abs. 1, 3 Nr. 5 StVG; -- BKat; § 19 OWiG",
        "penalty": null,
        "fap": null,
        "points": 0,
        "valid_from": "2021-11-09T00:00:00.000+01:00",
        "valid_to": null,
        "implementation": 3,
        "classification": 5,
        "variant_table_id": 741034,
        "rule_id": 39,
        "table_id": null,
        "required_refinements": "00000000000000000000000000000000",
        "number_required_refinements": 1,
        "max_fine": "0.0",
        "created_at": "2021-11-11T16:52:31.604+01:00",
        "updated_at": "2021-11-11T16:52:31.604+01:00"
    },
    "tbnr": "141091",
    "date": "…",
    "duration": 1,
    "severity": 0,
    "photos": [
        {
            "filename": "…",
            "url": "https://www.weg.li/rails/active_storage/blobs/redirect/…"
        }
    ],
    "created_at": "2023-08-06T14:26:03.258+02:00",
    "updated_at": "2023-08-06T14:28:53.863+02:00",
    "sent_at": "2023-08-06T14:28:53.857+02:00",
    "vehicle_empty": false,
    "hazard_lights": false,
    "expired_tuv": false,
    "expired_eco": false,
    "over_2_8_tons": false
}

Weil die Daten der Stadt Bonn allerdings keine Zeitzonen enthalten und die von Weg-Li aber alle jeweils in Lokalzeit angegeben sind, habe ich beim Einlesen die Zeitzonen einfach weggeschnitten. Dadurch ist es dann auch im Format np.datetime64[ns], welches ich dann später auch konsistent bearbeiten kann. Hier ist viel weniger Code nötig:

with open("dump.json") as f:
    dump = json.load(f)

meine = pd.DataFrame(dump["notices"])
meine["datetime"] = pd.to_datetime([elem[:-6] for elem in meine["date"]])

Weil in den Weg-Li Daten alle Jahre und Kommunen enthalten sind, muss ich da noch entsprechend filtern:

meine_2022 = meine.loc[(meine.datetime.dt.year == 2022) & (meine.city == "Bonn")].copy()

Zusammenführen der Daten

Nun kann ich einen Left-Join von meinen Anzeigen auf die Daten der Stadt machen und als Schlüssel die Zeit nehmen.

merged = pd.merge(meine_2022, stadt, right_on="Zeit", left_on="datetime", how="left")

Anschließend muss man noch kontrollieren, ob die Adresse auch stimmt. Es kann ja sein, dass in der gleichen Minute eine Anzeige an einem anderen Ort bearbeitet worden ist.

Ich habe insgesamt 62 relevante Anzeigen gehabt. Davon kann ich aber 34 nicht in den Daten der Stadt Bonn finden. Es führten also nur 28 Anzeigen zu einem Bußgeld, das ist mit 45 % eine eher traurige Quote.

Nachfrage bei der Bußgeldstelle

Ich habe mir die Anzeigen ohne Bußgeldbescheid einmal angeschaut. Die Fotos sehen für mich in Ordnung aus, der Text ebenfalls. Mir ist nicht ersichtlich, warum sie die nicht bearbeitet haben.

Aus Weg-Li habe ich fünf Beispiele nochmal als PDF exportiert und diese am 25.08.2023 per E-Mail an die Bußgeldstelle geschickt. Dort habe ich gebeten mir zu erklären warum diese Anzeigen nicht bearbeitet worden sind.

Ich habe immer wieder nachgehakt, man hat mich aufgrund hoher Arbeitsbelastung immer wieder vertröstet. Im Dezember 2023 hatte ich dann noch immer keine Antwort bekommen.