by Benjamin 'kristall' Kierdorf | kristall@c-base.org
cc some rights reserved by-sa (my e-mail is enough to satisfy the 'by')
@kristall:c-base.org (matrix)
@kristallpirat (@chaos.social, @bsky.social and @twiiter)
Unter Normalisierung fasst man Funktionen zusammen, die Daten auf eine sog. Normalform abbilden. Wie genau eine solche Normalform aussieht, hängt meist von den weiteren Verarbeitungsschritten ab, also wofür man sie nutzen möchte.
Bei Paragraphenketten in digitalen Texten ist eine Normalisierung mit Blick auf die Auffindbarkeit im Information Retrieval von entscheidender Bedeutung. Denn nur so lassen sich verschiedene Schreibweisen in Texten vereinheitlichen für einen Index.
Beispiel:
PPK_1 = "§ 92 I, § 97 I, § 269 III ZPO"
PPK_2 = "§ 92 Abs. 1, § 97 Abs. 1, § 269 Abs. 3 ZPO"
PPK_1 und PPK_2 sind unterschiedliche Schreibweisen eines im Ergebnis gleichen Normverweises. Beim Abbilden in eine Datenbank sollten diese also so normalisiert werden, dass beide auf die selbe Normalform abgebildet werden.
Das cbtc (c-base täccet (gesprochen "c-base täg set")) ist eine vom Autor entwickelte Software zur Analyse der deutschen (Rechts-)Sprache, welche eine solche Normalisierung vornimmt.
from cbtc_Pipe import analysiere as a
nPPK_1 = a(PPK_1)['Absätze'][0]['PPKn']
nPPK_2 = a(PPK_2)['Absätze'][0]['PPKn']
print("Ist PPK_1 gleich PPK_2?\t\t", PPK_1 == PPK_2)
print("Ist nPPK_1 gleich nPPK_2?\t", nPPK_1 == nPPK_2)
Ist PPK_1 gleich PPK_2? False Ist nPPK_1 gleich nPPK_2? True
nPPK_1 und nPPK_2 werden also auf die selbe Normalform abgebildet.
Allerdings ist die gebildete Normalform umfangreicher, als man zunächst vermuten könnte:
print(nPPK_1)
['92 I ZPO', '92 ZPO', '97 I ZPO', '97 ZPO', '269 III ZPO', '269 ZPO']
Die Normalform enthält nämlich nicht nur die explizit genannten Normen, sondern auch die korrespondierenden Normen ohne Angabe untergeordneter Gliederungseinheiten.
Der Grund dafür ist, dass Suchsoftware wie Elasticsearch aus Texten sog. Bag-of-Words macht, bei denen die Reihenfolge der Wörter und Nummern durcheinandere geraten (alles wird in einen Sack gesteckt und "geschüttelt").
Die gebildete Normalform für PPK_1 und PPK_2 kann aber für Keywordsuche benutzt werden, bei der es aber auf die exakte Schreibweise ankommt. Wenn nun aber nur 92 ZPO gesucht wird, aber das Keyword lautet 92 I ZPO, dann gäbe es keinen Suchtreffer. Somit erhöht diese Art der Normalform den Recall.
Auch wird die genannte Norm (hier: ZPO) in jedes Element übernommen, da sonst das Keyword unvollständig wäre; 92 I ohne Norm ist mehrdeutig und somit ungeeignet als Keyword.
Dabei werden alle Gliederungseinheiten von speziell bis zur Basisnorm gebildet, wie folgendes Beispiel zeigt:
PPK_3 = "§ 263 Abs. 3 Satz 2 Nr. 2 StGB"
nPPK_3 = a(PPK_3)['Absätze'][0]['PPKn']
print(nPPK_3)
['263 III 2 Nr. 2 StGB', '263 III 2 StGB', '263 III StGB', '263 StGB']
Die Normalform schreibt also Abs. 3 zu III um und nennt den Satz (in den meisten Fällen) ohne das Schlüsselwort. Diese Schreibweise ist unter Juristen bekannt und somit leicht lesbar, außerdem spart sie ein paar Bytes. Aus dem gleichen Grund (Datensparsamkeit) wird auf "§", "§§" oder "Art." in den Keywords verzichtet und allgemeinere Gliederungen werden nur ergänzt, wenn diese nicht schon in den Normalformen vorkommt:
Satz_1 = "Der Unterlassungsanspruch der Antragstellerin zu 1) ergibt sich aus §§ 8, 3, 4 Nr. 3 a UWG, derjenige der Antragstellerin zu 2) aus § 14 II Nr. 2, V MarkenG."
nPPK_Satz_1 = a(Satz_1)['Absätze'][0]['PPKn']
print(nPPK_Satz_1) # enthält "14 MarkenG" nur einmal
['8 UWG', '3 UWG', '4 Nr. 3 Bst. a UWG', '4 Nr. 3 UWG', '4 UWG', '14 II Nr. 2 MarkenG', '14 II MarkenG', '14 MarkenG', '14 V MarkenG']
Wenn innerhalb von Gesetzen auf Normen im selben Gesetz verwiesen wird, so erfolgt dies ohne Angabe der Norm im Text:
Satz_2 = "Die §§ 88, 89 Abs. 4, 90, 93 und 94 sind auf Bildfolgen und Bild- und Tonfolgen, die nicht als Filmwerke geschützt sind, entsprechend anzuwenden." ## § 95 UrhG
nPPK_Satz_2 = a(Satz_2)['Absätze'][0]['PPKn']
print(nPPK_Satz_2)
[]
Da die Normalisierungsfunktion nicht weiß, welches Gesetz hier zitiert wird, können keine Normalformen gebildet werden. Um dieses Problem zu lösen, kann der Analysefunktion eine "Defaultnorm" übergeben werden:
nPPK_Satz_2 = a(Satz_2, default_GVO='UrhG')['Absätze'][0]['PPKn']
print(nPPK_Satz_2)
['88 UrhG', '89 IV UrhG', '89 UrhG', '90 UrhG', '93 UrhG', '94 UrhG']
(Der Autor überlegt noch, ob die entsprechende Anwendung, die hier angeordnet wird, in der Normalform berücksichtigt werden sollte.)
Bei analoger Anwendung einer Norm wird das Wort "analog" Teil der Normalform und das in jeder Gliederungstiefe:
PPK_4 = "§ 42 Abs. 2 VwGO analog"
nPPK_4 = a(PPK_4)['Absätze'][0]['PPKn']
print(nPPK_4)
['42 II VwGO analog', '42 VwGO analog']
Bei Zusätzen wie "a.F.", "n.F." und anderen werden Normalformen mit und ohne den Zusatz gebildet:
PPK_5 = "§ 130a Abs. 1 Satz 1 HGB a.F."
nPPK_5 = a(PPK_5)['Absätze'][0]['PPKn']
print(nPPK_5)
['130a I 1 HGB', '130a I HGB', '130a HGB', '130a I 1 HGB a.F.', '130a I HGB a.F.', '130a HGB a.F.']
Nicht immer ist die Normnennung linear oder enthält die Kurzschreibweise der Norm. Auch solche Fälle werden aber problemlos normalisiert:
Satz_3 = "Nach der Berufsordnung für Rechtsanwälte (BORA) sind Anwälte gemäß § 20 dazu verpflichtet, eine Robe zu tragen."
nPPK_Satz_3 = a(Satz_3)['Absätze'][0]['PPKn']
print(nPPK_Satz_3)
['20 BORA']
Satz_4 = "Die Absätze 2 und 3 des § 137f UrhG lauten:" # BGH I ZR 80/04
nPPK_Satz_4 = a(Satz_4)['Absätze'][0]['PPKn']
print(nPPK_Satz_4)
['137f II UrhG', '137f UrhG', '137f III UrhG']
PPK_6 = "§ 174 Zivilprozessordnung; § 14 Berufsordnung für Rechtsanwälte"
nPPK_6 = a(PPK_6)['Absätze'][0]['PPKn']
print(nPPK_6)
['174 ZPO', '14 BORA']
Eine weitere Besonderheit sind PPKn, die den Konnektor "in Verbindung mit" (egal ob und wie abgekürzt) enthalten. Bei diesen wird auch die Verbindung eine Normalform, allerdings nur auf höchster Gliederungsebene:
PPK_7 = "Art. 2 Abs. 1 in Verbindung mit Art. 20 Abs. 3 GG"
nPPK_7 = a(PPK_7)['Absätze'][0]['PPKn']
print(nPPK_7)
['2 I GG', '2 GG', '20 III GG', '20 GG', '(2 GG) + (20 GG)']
Die Klammern werden auch gesetzt, wenn vor dem Konnektor, oder danach, nur eine Norm genannt wird. Wichtig sind sie aber insbesondere dann, wenn mehrere Normen genannt werden:
PPK_8 = "§ 4 Abs. 4, § 6 Abs. 1 Nr. 2 des Einkommensteuergesetzes (EStG) i. V. m. § 8 Abs. 1 des Körperschaftsteuergesetzes (KStG)"
nPPK_8 = a(PPK_8)['Absätze'][0]['PPKn']
print(nPPK_8)
['4 IV EStG', '4 EStG', '6 I Nr. 2 EStG', '6 I EStG', '6 EStG', '8 I KStG', '8 KStG', '(4 EStG, 6 EStG) + (8 KStG)']
Alle Beispiel hier sind Teil eines Unittests.
Code für den Test.
Daten für den Test.
import time
print("Zuletzt geändert:", time.strftime("%d.%m.%y %H:%M:%S", time.localtime()))
Zuletzt geändert: 21.05.26 04:26:39