Schlüsselwörter

figure a

Daten liegen in vielen unterschiedlichen Geschmacksrichtungen vor (siehe Kap. 3). Um daraus ein Gericht zuzubereiten – sie in eine Analyse einzuspeisen und konkrete Fragestellungen zu beantworten – müssen sie meist erst, wie in Abb. 4.1 veranschaulicht, zusammengestellt und aufbereitet werden. Dazu werden die für eine Analyse nötigen Daten zunächst aus dem gesamten Datenbestand selektiert (siehe Abschn. 4.1). Anschließend müssen die Daten noch bereinigt und fehlende Daten ergänzt werden. Schließlich werden sie so umgewandelt und strukturiert, dass sie ausgewertet werden können (siehe Abschn. 4.2). Das Ziel besteht meist darin, die verschiedenen Datenformate so zu manipulieren, dass Texte, Listen und Objekte am Ende rechteckig werden, sie also in tabellarische Form zu überführen. Für all diese Schritte, das Selektieren, Aufbereiten und Transformieren von Daten, werden nachfolgend einige Methoden und Werkzeuge vorgestellt.

Abb. 4.1
figure 1

Der Dataminingprozess. (Quelle: eigene Darstellung, in Anlehnung an Fayyad et al. 1996, S. 41)

Auch wenn dieses Schema eine erste Orientierung für die Datenextraktion geben kann, gestaltet sich der tatsächliche Ablauf einer Studie häufig anders. Im Verlauf einer Analyse lernt man immer weiter dazu und es kann sich herausstellen, dass man noch einmal einen Schritt zurückgehen muss, um die Daten anders auszuwählen. Auch sind diese Schritte mitunter ineinander verschachtelt. Wenn zum Beispiel während der Datenextraktion bereits eine Spracherkennung durchgeführt wird, um nur deutsch- oder englischsprachige Inhalte zu behalten, oder wenn nur Texte zu festgelegten Hashtags oder zu automatisiert erkannten Themen ausgewählt werden sollen, dann erfordert dies eine frühzeitige Aufbereitung und Analyse von Texten. Und auch während der Analyse beschäftigt man sich mitunter mit Ausschnitten der Daten, die noch einmal neu aufbereitet werden. Wichtig ist vor allem, dass man am Ende angeben kann, wie Erhebung, Auswahl, Aufbereitung und Analyse der Daten effektiv stattgefunden haben. Nur so lässt sich einschätzen, worauf sich die gewonnenen Erkenntnisse beziehen und inwiefern sie verallgemeinerbar sind.

1 Selektionsverfahren und -sprachen

Insbesondere schwach strukturierte Daten zeichnen sich dadurch aus, dass sie unübersichtlich sind und eine Unmenge irrelevanter Daten enthalten. Für die spätere Analyse werden deshalb zunächst die relevanten Datenpunkte aus dem Material extrahiert. Je nach Ausgangsformat eignen sich verschiedene Sprachen, um gezielt einzelne Daten anzusprechen und so zwischen Ausgangs- und Zielformat zu übersetzen. Im Folgenden werden vier solcher Sprachen vorgestellt.

  1. 1.

    Besonders flexibel einsetzbar sind reguläre Ausdrücke. Dabei handelt es sich um Ausdrücke mit Platzhaltern, die zum Durchsuchen beliebiger Texte verwendet werden können – dazu gehören auch die in Kap. 3 vorgestellten textbasierten Formate CSV, HTML, XML, JSON und SQL-Dumps. In Texten lassen sich mit regulären Ausdrücken etwa leicht Jahreszahlen identifizieren.

  2. 2.

    In den Markup-Formaten HTML und XML sind Texte hierarchisch bzw. baumartig vorstrukturiert. Um einzelne Elemente innerhalb dieser Strukturen zu finden, eignet sich insbesondere XPath. Die Sprache ist speziell für den Zugriff auf XML-basierte Formate konzipiert worden und Teil der Datenbankabfragesprache XQuery.

  3. 3.

    CSS-Selektoren sind eine leichtgewichtige Alternative zu XPath, auch wenn Cascading Style Sheets (CSS) eigentlich für die optische Formatierung von Webseiten entwickelt wurden, etwa um die Schriftfarbe von Überschriften und die Abstände zwischen Absätzen festzulegen.

  4. 4.

    Die letzte in diesem Kapitel besprochene Sprache ist SQL, mit der Datenbanken abgefragt werden können. Hier geht es also weniger darum, unstrukturierte Daten in Form zu bringen, als vielmehr in bereits stark strukturierten Daten die relevanten Felder auszulesen.

1.1 Reguläre Ausdrücke

Computer arbeiten mit Zahlen bzw. mit digitalen Binärwerten. Dennoch lassen sich alle Datenformate als Text darstellen, denn auch jede Zahl lässt sich als Zeichen interpretieren. Welche Zahl für welches Zeichen steht, wird über eine sogenannte Zeichenkodierung (engl. encoding) festgelegt (siehe Kap. 3). So stehen die Zahl 67 in der ASCII-Kodierung für den Großbuchstaben „C“ und die Zahl 77 für den Buchstaben „M“. Erstaunlich viele Dateien sind intern als Textdateien aufgebaut und auch viele Programmiersprachen können Zahlen als Text interpretieren (Datentyp Zeichenkette).

Liegen Daten in Textdateien vor, können sie mit Editoren wir Atom oder Notepad++ geöffnet werden (siehe Kap. 1). Eine besondere Stärke von Texteditoren ist die Suchfunktion. Im einfachsten Fall gibt man dafür eine Zeichenkette ein und es werden nach und nach alle Textstellen angesprungen, die mit dem Suchausdruck übereinstimmen. Mit Texteditoren können in der Regel auch alle Fundstellen auf einmal markiert oder durch einen anderen Ausdruck ersetzt werden.

Besonders mächtig werden Suchfunktionen dann, wenn sie Platzhalter unterstützen, verbreitet sind das Fragezeichen für einzelne Zeichen und der Stern oder ein Prozentzeichen für beliebige aufeinanderfolgende Zeichen. Noch mehr Möglichkeiten bieten reguläre Ausdrücke. Reguläre Ausdrücke sind eine Sprache, um Suchmuster zu formulieren.Footnote 1 Sie kommen vor allem dann zur Anwendung, wenn unstrukturierte Daten aufbereitet werden müssen. Zum Beispiel lassen sich damit Links, Datumsangaben oder andere Daten aus dem Quelltext von Webseiten extrahieren. Aber auch bei der Datenmanipulation über Programmiersprachen wie R und Python lassen sich reguläre Ausdrücke einsetzen – beispielsweise, wenn in einer Tabelle neue Spalten mit URLs erstellt oder wenn beim Text Mining verschiedene Schreibweisen von Wörtern wie ausgeschriebene und nichtausgeschriebene Umlaute (ae vs. ä) berücksichtigt werden sollen.

Als Übungsmaterial für den Umgang mit regulären Ausdrücken eignet sich HTML-Quellcode von Webseiten (siehe Abschn. 3.4). Einen Einblick in den Quelltext einer Webseite erhält man in den meisten Browsern wie zum Beispiel Firefox, indem man mit der rechten Maustaste auf ein Element der Seite klickt und Element untersuchen auswählt oder die Seite auf dem eigenen Computer abspeichert. Solche Quelltexte sehen wie folgt aus:Footnote 2

<li class="ep-hover" data-jahr="201" data-kategorie="9" data-land="158"> <a href="/real-humans-echte-menschen" title="Real Humans – Echte Menschen (S&nbsp;2012–)"> <span class="bold">Real Humans – Echte Menschen</span> (<abbr title="Schweden">S</abbr>&nbsp;2012–)</a> </li> <li class="ep-hover" data-jahr="201" data-kategorie="3" data-land="184"> <a href="/real-husbands-of-hollywood" title="Real Husbands of Hollywood (USA&nbsp;2013–)"><span class="bold">Real Husbands of Hollywood</span> (<abbr title="USA">USA</abbr>&nbsp;2013–)</a> </li>

Im Beispiel sind Namen, Länder und Erscheinungsjahre von zwei Serien enthalten, die sich gut über reguläre Ausdrücke extrahieren lassen. Im Browser würde der Text wie folgt aussehen, enthält dann aber weniger Informationen:

Real Humans – Echte Menschen (S 2012–) Real Husbands of Hollywood (USA 2013–)

Es fehlt beispielsweise die URL der Seite mit Detailinformationen zur Serie. Auch der im Browser angezeigte Text ist aus Maschinensicht unstrukturiert. Er müsste für die Datenverarbeitung zum Beispiel mit regulären Ausdrücken in eine Tabellenform überführt werden, bei der Name, Land und Datumsangaben durch ein einheitliches Trennzeichen getrennt sind (CSV-Dateien, siehe Abschn. 3.3). Kopieren Sie den Beispieltext in einen Texteditor und probieren Sie die folgenden Muster und eigene Variationen daran aus (☛ Repositorium)!

Platzhalter und Zeichenklassen

Die einfachste Form eines regulären Ausdrucks besteht aus einer Zeichenfolge, die buchstäblich gesucht wird, zum Beispiel ein Wort:

Hollywood

Als Platzhalter können nun Zeichenklassen oder Zeichenbereiche verwendet werden. Der Punkt kennzeichnet ein beliebiges Zeichen, sodass folgender Ausdruck unter anderem die Zeichenfolgen „Bollywood“ oder „Mollywood“ findet:

.ollywood

Statt eines beliebigen Zeichens kann in eckigen Klammern ein Bereich angegeben werden:

[A-Z]ollywood

In diesen eckigen Klammern kann auch eine Liste möglicher Zeichen angegeben werden, zum Beispiel um auf „Bollywood“ und „Hollywood“ einzugrenzen:

[HB]ollywood

Mehrere Listen können kombiniert werden, etwa um Groß- und Kleinschreibung zu berücksichtigen. Denn reguläre Ausdrücke unterscheiden in der Standardeinstellung zwischen Groß- und Kleinschreibung:

[a-zA-Z]ollywood

Während bislang formuliert wurde, welche Zeichen enthalten sein dürfen, kann auch eine Negativliste definiert werden. Wenn direkt nach der öffnenden eckigen Klammer ein Caret ^ angegeben wird, dann werden alle Zeichen gefunden, außer den angegebenen. Das Beispiel findet also ausgerechnet „Hollywood“ nicht:

[^H]ollywood

Diese Technik lässt sich besonders gut in Kombination mit den im Folgenden besprochenen Quantoren einsetzen, um etwa alle Zeichen innerhalb doppelter Anführungszeichen zu identifizieren. Der folgende etwas kryptische Ausdruck findet Zeichenketten, die mit Anführungszeichen beginnen, dann mindestens ein anderes Zeichen beinhalten und schließlich mit einem Anführungszeichen enden:

"[^"]*"

Besonders häufig verwendete Zeichenklassen haben eigene Namen, sodass der Tippaufwand verringert wird (Tab. 4.1). Zum Beispiel lassen sich alle alphanumerischen Zeichen mit \w finden und Weißraum mit \s.

Tab. 4.1 Häufig verwendete Zeichenklassen

Für einige Fälle hilfreich sind zudem Modifikatoren, die das Gesamtverhalten des regulären Ausdrucks steuern. Damit kann unter anderem eingestellt werden, ob ein Suchausdruck Groß- und Kleinschreibung berücksichtigen oder auf mehrere Zeilen angewendet werden soll – die konkrete Umsetzung hängt vom verwendeten Texteditor bzw. dem Dialekt ab (siehe unten).

Quantoren

Bislang wurden immer nur Platzhalter für einzelne Zeichen formuliert. In der Kombination mit Quantoren lassen sich auch Platzhalter für längere Zeichenfolgen festlegen. Ein Quantor gibt an, wie häufig ein Zeichen mindestens und wie häufig es höchstens vorkommen darf. Der folgende Suchausdruck findet beispielsweise alle vierstelligen Zahlen:

[0-9]{4,4}

Zunächst wird hier in eckigen Klammern festgelegt, welche Zeichen vorkommen dürfen. In den geschweiften Klammern wird dann vor dem Komma die Mindestanzahl und nach dem Komma die Höchstanzahl der erlaubten Vorkommen definiert. Hier werden also Zeichenausdrücke gefunden, die mindestens vier und gleichzeitig höchstens vier aufeinanderfolgende Ziffern enthalten. Wenn wie hier Minimum und Maximum identisch sind, kann der Ausdruck vereinfacht werden. Es reicht die Angabe der gewünschten Anzahl:

[0-9]{4}

Man kann die Maximalbedingung auch weglassen, etwa um alle Zahlen mit mindestens zwei Ziffern zu finden, die aber beliebig lang sein dürfen:

[0-9]{2,}

Für drei besonders häufig genutzte Quantoren gibt es spezielle Symbole. Das Fragezeichen entspricht der Bedingung {0,1} und findet alle Zeichenketten, in denen ein Zeichen keinmal oder höchstens einmal vorkommt. Das folgende Beispiel findet deshalb sowohl den Singular „Jahr“ als auch den Plural „Jahre“:

Jahre?

Das Pluszeichen wird verwendet, wenn ein Zeichen mindestens einmal vorkommen soll, zum Beispiel für beliebig lange Zahlen:

[0-9]+

Das Sternchen ist am lockersten und lässt beliebige Häufigkeiten zu, es ist keine Minimal- oder Maximalanzahl festgelegt. Das ist in Kombination mit Zeichenklassen (z. B. in eckigen Klammern) sehr mächtig. Der folgende Ausdruck findet alle Wörter, die mit „Jahr“ beginnen. Dazu zählen das Wort „Jahr“ selbst und Wörter wie „Jahreszahl“, „Jahre“ oder „Jahres“. Die Suche endet erst dann, wenn kein kleiner Buchstabe mehr folgt (sondern zum Beispiel ein Leerzeichen):

Jahr[a-z]*

Quantoren sind normalerweise gierig (engl. greedy), das heißt die Suche wird so lange fortgesetzt, wie es geht. Soll die Suche dagegen so früh wie möglich abgebrochen werden, kann das Suchverhalten umgeschaltet werden, indem an einen Quantor ein Fragezeichen angehängt wird.

Maskierung reservierter Zeichen und Sonderzeichen

Viele Zeichen haben innerhalb regulärer Ausdrücke also eine bestimmte Bedeutung, zum Beispiel Fragezeichen, Punkt, Schrägstrich oder auch Klammern. Was macht man nun, wenn man genau diese Zeichen finden will? Die Zeichen werden in diesen Fällen maskiert (engl. escaped), indem ein Backslash vorangestellt wird. Damit steht der Punkt im folgenden Beispiel tatsächlich für einen Punkt und nicht für ein beliebiges Zeichen.

3\. Januar

Nun verschiebt sich das Problem auf den Schrägstrich. Die Lösung bleibt gleich: Der Schrägstrich wird durch einen Schrägstrich maskiert. Sollen zwei Schrägstriche hintereinander gefunden werden, müssen beide maskiert werden, die Anzahl der Schrägstriche wird also verdoppelt.

Eine Besonderheit ergibt sich, wenn mit regulären Ausdrücken in R oder Python gearbeitet werden soll (siehe Kap. 5). Da der Schrägstrich innerhalb dieser Sprachen bereits eine Bedeutung hat, muss er maskiert werden, damit er überhaupt als Maskierungszeichen erkannt wird.Footnote 3 In R ist also der folgende Ausdruck identisch mit dem vorangegangenen Beispiel, wobei reguläre Ausdrücke in vielen Programmiersprachen als Zeichenkette gelten und zusätzlich in Anführungszeichen gesetzt werden:

"3\\. Januar"

Die Maskierung der Maskierung mag etwas verwirrend sein. Unter Python gibt es die Möglichkeit, Zeichenketten mit einem Präfix als raw-String zu kennzeichnen.Footnote 4 Dann werden Backslashes nicht als Maskierungszeichen gewertet und müssen nicht doppelt maskiert werden:

r'3\. Januar'

Der Schrägstrich wird auch eingesetzt, um Sonderzeichen zu markieren. Dazu gehören nicht sichtbare Zeichen wie Zeilenumbrüche oder Tabulatoren (Tab. 4.2).

Tab. 4.2 Sonderzeichen

Unter Windows werden Zeilenumbrüche in Textdateien meistens durch eine Kombination aus Line Feed und Carriage Return (\r\n) gekennzeichnet, während in anderen Betriebssystemen ein einfaches Line Feed (\n) ausreicht. Die Suche nach Zeilenumbrüchen ist nur selten notwendig, zum Beispiel, wenn sie über Suchen & Ersetzen gelöscht werden sollen. Auf diese Weise können erst alle Zeilenumbrüche entfernt werden, um dann gezielt an bestimmten Stellen neue Zeilenumbrüche einzufügen. Wenn das Ersetzen nicht im Vordergrund steht, ist es aber einfacher, mit Ankern zu arbeiten (siehe unten).

Wenn Unicode unterstützt wird (siehe Kap. 3), dann können Sonderzeichen auch über eines der folgenden beiden Muster angegeben werden:

\u00A0 \x{00A0}

In diesen Mustern steht die Folge 00A0 für die Unicode-Nummer des geschützten Leerzeichens. Bei der Arbeit mit echten Texten stößt man immer wieder auf Situationen, in denen Sonderzeichen nicht auf den ersten Blick erkennbar sind. So wie zwischen Groß- und Kleinschreibung unterschieden wird, lassen sich auch nicht alle Arten von Leerzeichen über ein einfaches Leerzeichen oder alle Arten von Bindestrichen über ein Minuszeichen finden. Im zu Beginn des Kapitels angegebenen Quelltext kommen sowohl geschützte Leerzeichen (engl. non breaking space) als auch ein Halbgeviertstrich (engl. en dash) vor. Im Zweifelsfall kann man die Zeichen direkt aus dem Text herauskopieren und in einen regulären Ausdruck übernehmen.

Der Kontext von Ausdrücken: Anker und Lookarounds

Ohne weitere Eingrenzung finden reguläre Ausdrücke Muster an beliebigen Stellen im Text. Manchmal ist es aber hilfreich, nur den Anfang oder das Ende eines Texts oder eine Zeile zu durchsuchen. Das Muster wird dafür mit einem Caret ^ am Anfang oder mit einem Dollarzeichen $ am Ende verankert. Beides kann kombiniert werden. In der Regel wird jede Zeile einzeln behandelt, sodass folgendes Suchmuster nur Zeilen finden würde, die nichts anderes als das Wort „Jahr“ enthalten:

^Jahr$

Eine weitere Möglichkeit, den Kontext eines gesuchten Texts zu berücksichtigen, sind Lookarounds. Hierbei wird angegeben, was in der Umgebung des gesuchten Texts vorkommen oder nicht vorkommen darf. Diese Muster gibt es einmal als vorausschauende (engl. look ahead) oder zurückschauende (engl. look behind) Muster und einmal als positive oder negative Muster. Ein positiver Lookahead würde nur Zeichenketten finden, auf die das definierte Muster folgt:

[0-9]+(?= Stunden)

Hiermit würden Zahlen gefunden werden, auf welche ein Leerzeichen und das Wort „Stunden“ folgen. Das folgende Muster wird also in runde Klammern gesetzt und zu Beginn mit ?= markiert. Ein negativer Lookahead findet Text, auf den nicht das angegebene Muster folgt, zum Beispiel alle Zahlen, die keine Stundenangaben sind. Anstelle des Gleichheitszeichens wird ein negativer Lookahead mit einem AusrufezeichenFootnote 5 markiert:

[0-9]+(?! Stunden)

Nach dem gleichen Prinzip lassen sich Zahlen finden, die auf einen bestimmten Text folgen. Das Muster wird wiederum in Klammern gesetzt und mit einem Fragezeichen eingeleitet, bei Lookbehinds folgt darauf aber ein Kleinerzeichen. Auf diese Weise können etwa Größenangaben gefunden werden:

(?<=Größe )[0-9]+

Ein negativer Lookbehind wird wieder mit einem Ausrufezeichen anstelle des Gleichheitszeichens formuliert und findet dann alle Zahlen, vor denen das angegebenen Muster nicht steht:

(?<!Größe )[0-9]+

Im ersten Moment erscheinen Lookarounds nicht nur kompliziert, sondern vielleicht auch unsinnig. Wenn man zum Beispiel eine Stundenangabe finden will, dann kann man das auch ohne Lookahead bewerkstelligen, das heißt ohne die zusätzlichen Klammern, das Fragezeichen und das Gleichheitszeichen. Dann allerdings wird nicht nur die Zahl gefunden, sondern auch die Einheit bzw. das Wort. Gerade wenn man Daten extrahieren oder durch andere Daten ersetzen will (Suchen & Ersetzen), kann man sich mit Lookarounds auf die wesentlichen Daten konzentrieren. Sie sind am Anfang möglicherweise etwas schwer zu verstehen, in der einen oder anderen Situation aber doch sehr hilfreich.

Alternativen, Gruppen und Ersetzungen

Lookarounds setzen voraus, dass ein Teil des Musters in runde Klammern gesetzt wird. Damit wird der entsprechende Teil des Musters gruppiert. Solche Gruppen können in Kombination mit einem senkrechten Strich | (auch Pipe genannt) zudem dazu genutzt werden, um Alternativen zu formulieren. Die Pipe kann also als „oder“ übersetzt werden. Das folgende Muster findet alle Zeichenketten, die mit einer Zahl beginnen und dann getrennt durch ein Leerzeichen mit einem der Wörter „Stunde“, „Minute“ oder „Sekunde“ fortgesetzt werden:

[0-9]+ (Stunde|Minute|Sekunde)

Gruppierungen können verschachtelt werden, das heißt, eingeklammerte Ausdrücke können bei Bedarf auch noch einmal eingeklammert werden, um so Alternativen innerhalb von Alternativen zu formulieren.

Aufeinanderfolgende Zeichen werden auch ohne Umklammerung als Gruppierung erkannt werden. Dies liegt an der Bindungskraft, die zwischen Zeichen besteht und die dafür verantwortlich ist, dass die Zeichen in einer gewissen Reihenfolge abgearbeitet werden. Dies folgt der gleichen Logik wie sie bei grundlegenden Rechenoperationen vorliegt. Beispielsweise gilt in der Mathematik die Regel Punkt- vor Strichrechnung: 6 × 5 + 3 = 33. Mit Klammern lässt sich die Reihenfolge und somit das Ergebnis verändern: 6 × (5 + 3) = 48. Bei regulären Ausdrücken werden standardweise aufeinanderfolgende Buchstaben oder Zahlen als Einheit gesehen. Muster in regulären Ausdrücken werden nach folgender Priorität berücksichtigt: Klammern vor Quantoren vor Sequenzen vor Ankern und schließlich vor Alternativen.

Gruppen sind vor allem dann hilfreich, wenn Teile eines Texts ersetzt werden sollen. Ein Texteditor wie Notepad++ erlaubt nicht nur die Formulierung von Suchmustern, sondern ebenso die Formulierung von Ersetzungen. In dem Ersetzungstext kann durch Einklammern auf einzelne Teile der Ausdrücke Bezug genommen werden. Dazu werden die runden Klammern von links nach rechts durchgezählt (immer nur die öffnende Klammer). So findet das folgende Beispiel eine Datumsangabe in dem in Europa verbreiten Format: TT.MM.JJJJ. Um nun einzelne Teile adressieren zu können, werden zunächst im Suchmuster Teile durch runde Klammern gruppiert, zusätzlich müssen die Punkte maskiert werden:

([0-9]{2})\.([0-9]{2})\.([0-9]{4})

Sollen nun einzelne Einheiten ersetzt oder vertauscht werden, dann wird dazu im Ersetzungsausdruck ein Backslash gefolgt von der Nummer der Klammer eingefügt. Wenn im Beispiel die Datumsangabe durch JJJJ.MM.TT. ersetzt werden soll, können die einzelnen Gruppen wie folgt vertauscht werden.

\3.\2.\1

Bei Ersetzungen müssen Punkte und andere Zeichen nicht maskiert werden. Der Vollständigkeit halber sei angemerkt, dass auf den gesamten gefundenen Text mit der Nummer \0 Bezug genommen werden kann.

Dialekte

Für das Erlernen von regulären Ausdrücken ist wie bei jedem Lernen von Sprachen viel Übung nötig. Die bislang angesprochenen Möglichkeiten stellen den Kern von regulären Ausdrücken dar und sollten ausreichen, um sich am Anfang zurechtzufinden. Weitere Möglichkeiten finden Sie in vielzähligen Tutorials.Footnote 6 Ein Tool, das beim Zusammenstellen von regulären Ausdrücken behilflich sein kann, ist RegExr.Footnote 7 Hier können Sie zunächst einen unstrukturierten Text eingeben und anschließend reguläre Ausdrücke formulieren, die Ergebnisse werden im Text markiert.

Zu beachten ist dabei, dass verschiedene Programme – fast so wie bei menschlichen Sprachen – verschiedene Dialekte sprechen. Am häufigsten sind POSIX- oder PCRE-kompatible Dialekte anzutreffen. Die jeweils korrekte Syntax sollte man in der Dokumentation der jeweiligen Software nachschlagen. In Notepad++ und R kommt PCRE zum Einsatz, in Python wird ein PCRE-ähnlicher Dialekt verwendet. Bevor Sie nun aber die Dokumentationen wälzen, probieren Sie erst einmal selbst aus, wie weit Sie mit den hier angeführten Möglichkeiten kommen.

Anwendungsbeispiel: URLs extrahieren

Reguläre Ausdrücke werden für das Extrahieren von Daten zwar häufig in Kombination mit Programmiersprachen wie R oder Python eingesetzt (siehe Kap. 5). Aber auch in Texteditoren lässt sich mit etwas Kreativität viel erreichen. Sie können damit zum Beispiel alle URLs aus einem Dokument auslesen. Im folgenden Beispiel geht es um die Frage, wie eine Webseite mit anderen Online-Diensten vernetzt ist. Grundsätzlich sind verschiedene Webseiten über URLs verlinkt, die im Quelltext zu finden sind (siehe Abschn. 2.1).

Eine Möglichkeit, die verlinkten Seiten zu identifizieren, bestünde also in der Analyse des Quelltexts ähnlich wie beim Webscraping. Gerade bei dynamischen Seiten werden aber auch noch später durch Skripte weitere Inhalte nachgeladen (siehe Abschn. 7.1). Um diese Abfragen nachgeladener Inhalte zu sehen, öffnen Sie im Browser die Entwicklerkonsole (in der Regel mit der Taste F12) und wechseln Sie in den Reiter Netzwerkanalyse (Abb. 4.2). Wenn Sie nun eine Webseite aufrufen, werden alle Zugriffe auf weitere Ressourcen im unteren Bereich protokolliert. Eine Webseite setzt sich meistens aus sehr vielen Einzelteilen zusammen; der Aufruf der Seite https://www.golem.de zieht zum Beispiel aktuell über 600 Abfragen nach sich. Wenn Werbeblocker deaktiviert sind, besteht ein Teil davon aus Werbeeinblendungen wie in der Abbildung am rechten Rand zu sehen ist. Mit der Funktion Netzwerkanalyse können Sie nachvollziehen, welche Tracking-Dienste oder externe Werbeinhalte in eine Seite eingebunden sind.

Abb. 4.2
figure 2

Hintergrundabfragen einer Webseite. (Quelle: eigene Darstellung; Golem (2022; https://www.golem.de))

Um diese Daten weiterzuverarbeiten, können sie mit einem Rechtsklick als HAR-Format abgespeichert werden. Das HAR-Format ist ein JSON-Format (siehe Abschn. 3.5) und kann mit entsprechenden Programmen weiterverarbeitet werden, aber auch mit einem einfachen Texteditor. Öffnen Sie die exportierte Datei anschließend mit einem Texteditor (siehe Kap. 1). In den ersten Zeilen werden allgemeine Daten zur Abfrage angegeben. Hinter dem Wert entries folgt eine Liste aller Abfragen. Im Schlüssel request.url ist für jede Abfrage die URL angegeben.

Da jede dieser Zeilen gleich formatiert ist, können Sie einfach danach suchen (Abb. 4.3). Das folgende Suchmuster findet alle Zeilen, die mit einer beliebigen Anzahl von Leerzeichen beginnen und dann den Schlüssel "url" (inklusive der Anführungszeichen) enthalten, sofern Sie im Texteditor den Suchmodus auf reguläre Ausdrücke einstellen:

^ *"url"

Abb. 4.3
figure 3

Zeilen mit regulären Ausdrücken in Notepad++ hervorheben. (Quelle: eigene Darstellung)

Mit dem Caret ^ wird der Text am Zeilenanfang verankert (siehe oben), sodass nur die Zeilen gefunden werden, die mit dem Ausdruck beginnen.Footnote 8

Wenn Sie nun eine Suche ausführen, sollten alle Zeilen mit den URLs angesprungen werden. Damit diese Zeilen weiterverarbeitet werden können, wechseln Sie zum Beispiel in Notepad++ in den Reiter Hervorheben, aktivieren Sie die Funktion Lesezeichen setzen und klicken Sie schließlich auf die Schaltfläche Alle hervorheben. Im Suchen-Menü (in der oberen Menüleiste, außerhalb der Suchfunktion) finden Sie weit unten einen Punkt Lesezeichen mit verschiedenen Optionen. Sie können hier zum Beispiel alle Zeilen mit Lesezeichen in die Zwischenablage kopieren. Um damit weiterzuarbeiten, legen Sie mit Notepad++ ein neues Textdokument an und fügen Sie den Inhalt der Zwischenablage ein – so haben Sie mit einfachen Mitteln alle Drittanbieter identifiziert, auf die eine Webseite zurückgreift.

1.2 CSS-Selektoren

Reguläre Ausdrücke sind mächtig, wenn es um die Behandlung von unstrukturiertem Text geht. Allerdings ist die Formulierung von regulären Ausdrücken in einigen Fällen auch aufwendig und fehleranfällig. Wenn beispielsweise aus einem HTML-Quelltext ein bestimmtes Element extrahiert werden soll, das selbst wieder Tags enthält, ist es nahezu unmöglich, einen verständlichen und dennoch alle Spezialfälle umfassenden Ausdruck zu formulieren. Das folgende Beispiel wäre ein solcher Fall, wenn man das komplette <li>-Element mit allen Inhalten inklusive der Unterelemente erfassen will:

<li class="ep-hover" data-jahr="201" data-kategorie="9" data-land="158"> <a href="/real-humans-echte-menschen" title="Real Humans – Echte Menschen (S&nbsp;2012–)"> <span class="bold">Real Humans – Echte Menschen</span> (<abbr title="Schweden">S</abbr>&nbsp;2012–)</a> </li>

Handelt es sich um Text, der einem definierten Format wie XML oder HTML folgt, besteht eine bessere Lösung darin, den Text mit einem darauf spezialisierten Parser einzulesen. Ein Parser erkennt einzelne Elemente und reproduziert die Struktur der Dokumente so, dass darauf gezielt zugegriffen werden kann. Der Quelltext ist danach in einem sogenannten Document Object Model (DOM) erfasst. Die direkt untergeordneten Elemente werden im DOM als Kindelemente (engl. children) bezeichnet, alle untergeordneten Elemente, also auch Kinder von Kindern, als Nachfahren (engl. descendants). Die direkt übergeordneten Elemente heißen Eltern (engl. parents) und die Kette aller übergeordneten Elemente Vorfahren (engl. ancestors). So kann beispielsweise das <li>-Element nicht nur als Abfolge der Zeichen <, l, i sowie > eingelesen, sondern vielmehr als Element mit den Kindelementen <a> sowie <span> geparsed werden. In vielen Programmiersprachen gibt es Parser, die eine Navigation in der DOM-Struktur ermöglichen, zum Beispiel verarbeitet das Python-Modul Beautiful Soup die Formate HTML und XML (Richardson 2022; siehe Abschn. 7.1).

Einzelne Elemente im DOM können anschließend über CSS-Selektoren identifiziert werden. CSS steht für Cascading Style Sheets und wird normalerweise dazu verwendet, die Darstellung von HTML-Seiten vorzugeben. In einer CSS-Datei kann etwa wie folgt festgelegt werden, dass alle Links grün erscheinen:

a { color:green; }

Der erste Teil dieser Angabe ist ein CSS-Selektor, der alle a-Elemente adressiert. Es folgt in geschweiften Klammern die Angabe der Formatierung. Für die Datenerhebung ist die Formatierung unwichtig, aber wenn ein Text etwa mit Beautiful Soup geparsed wurde, können über CSS-Selektoren einzelne Elemente zur Extraktion angesteuert werden.

Der einfachste CSS-Selektor benennt wie im Beispiel einfach den Tag-Namen – welcher im Quellcode direkt auf die öffnende spitze Klammer < folgt. Bezeichner, die mit einem Punkt beginnen, selektieren alle Elemente mit einem bestimmten Klassen-Attribut. Bezeichner mit einem Doppelkreuz # selektieren die Elemente über eine ID. Klassen- und ID-Attribute finden sich hinter dem Tag-Namen und sind nach dem Muster class="bezeichnung" bzw. id="bezeichnung" angegeben. Diese Notationsvarianten lassen sich auch kombinieren (Tab. 4.3). Wenn der Elementname, eine oder mehrere Klassen oder eine ID direkt ohne Leerraum aneinander gekettet werden, dann müssen alle diese Merkmale auf das ausgewählte Element zutreffen.

Tab. 4.3 Beispiele für CSS-Selektoren

Um dagegen in die Hierarchie eines Dokuments einzutauchen, werden mehrere der Selektoren nacheinander notiert. Dadurch werden nur diejenigen Elemente selektiert, die entsprechende Vorfahren haben. Soll ein Element ein direktes Kindelement sein, wird dies mit dem >-Zeichen zwischen den Angaben ausgedrückt. Ohne >-Zeichen werden auch alle Nachfahren adressiert, die nicht unmittelbare Kinder des Elternelements sind. Um einzelne Elemente zu erfassen, hangelt man sich also im ersten Schritt an der Hierarchie entlang (Abb. 4.4). Wenn die Elemente dann auch ohne Berücksichtigung der Hierarchiestufen eindeutig identifizierbar sind, kann der Ausdruck vereinfacht werden. Im Beispiel, solange es nur ein einziges span-Element gibt, würden die folgenden vier Ausdrücke alle das Gleiche leisten:

li > a > span.bold li a span.bold span.bold span

Abb. 4.4
figure 4

Die hierarchische Struktur von CSS-Selektoren (oben) und XPath (unten) im Vergleich. (Quelle: eigene Darstellung)

Die Beispiele umfassen die wichtigsten Fälle von CSS-Selektoren. Es sind darüber hinaus deutlich komplexere Ausdrücke möglich, bei denen etwa die Anzahl vorangegangener Elemente, der Textinhalt eines Attributs oder weitere Bedingungen erfüllt sein müssen.Footnote 9

CSS-Selektoren spielen beim Webscraping eine wichtige Rolle (siehe Kap. 7) und funktionierende Ausdrücke können über die Entwicklerkonsole des Browsers (aufrufbar über die Taste F12) gefunden werden. Steuern Sie dafür das gewünschte Element an und klicken Sie mit der rechten Maustaste auf die entsprechende Zeile in der Entwicklerkonsole, dort kann ein Selektor in der Regel über Copy Selector in die Zwischenablage kopiert werden. Diesen CSS-Ausdruck können Sie dann zum Beispiel mit FacepagerFootnote 10 ausprobieren. Dazu fügen Sie die URL einer Webseite in Facepager als Startknoten ein, laden die Seite herunter und passen dann mittels CSS-Ausdrücken die angezeigten Spalten an (☛ Repositorium).

1.3 XPath

Während CSS für die Gestaltung von Webseiten konzipiert wurde, ist XPath direkt für die Adressierung von Daten in XML-verwandten Dokumenten entworfen worden (Abb. 4.4). Auch hier erfolgt die Auswahl von Elementen zunächst über deren Namen. Ein Dokument wird wiederum als hierarchische Baumstruktur aufgefasst, die mit Achsen wie beispielsweise child (untergeordnetes Element), parent (übergeordnetes Element) oder preceding und following (vorangegangenes oder nachfolgendes Element) durchsucht wird. Der folgende Ausdruck würde ausgehend vom html-Element über das body-Element alle Tabellen table, darin die Zeilen tr und schließlich die Zellen td finden.

html/child::body/child::table/child::tr/child::td

Die Angabe der child-Achse kann im Gegensatz zu anderen Achsen entfallen, sodass der folgende vereinfachte Ausdruck die gleiche Funktion erfüllt:

html/body/table/tr/td

Das Wurzelelement eines Dokuments wird durch den vorgestellten Schrägstrich angesprochen. So würde der folgende Ausdruck immer noch das gleiche Ergebnis liefern, wenn das Wurzelelement html ist:

/body/table/tr/td

Sollen Elemente in beliebiger Tiefe des Baums angesprochen werden, kann der doppelte Schrägstrich verwendet werden. So werden sämtliche td-Elemente gefunden:

//td

Zusätzliche Bedingungen können in jedem Selektionsschritt in eckigen Klammern angegeben werden. Im einfachsten Fall gibt man die Nummer des Elements an, etwa um das dritte td-Element zu finden:

//td[3]

Sinnvoll kann auch mit dem @-Zeichen die Eingrenzung auf Attribute sein:

//td[@class == 'data']

Da Klassenattribute mehrere Werte enthalten können (siehe das Beispiel in Tab. 4.3), ist die Funktion contains() nützlich. Hiermit muss das Attribut nicht identisch mit dem Suchausdruck sein, sondern kann auch weitere Texte enthalten:

//td[contains(@class,'data')]

Die bisherigen Ausdrücke zielen genauso wie CSS-Selektoren immer auf die Tags oder Elemente ab. Ein Element besteht aus dem Elementnamen, Attributen und kann Text enthalten (siehe Kap. 3). Anders als bei CSS sind auch Textinhalte und Attribute direkt als sogenannte Knoten ansprechbar. Mit XPath können die Attribute also nicht nur zur Auswahl von ganzen Elementen verwendet, sondern auch direkt referenziert werden, sodass ein Element wie <a href="www.example.org">Beispielseite</a> nach dem Extrahieren nicht noch weiter zerlegt werden muss, um an die Inhalte zu gelangen. Die folgenden beiden Ausdrücke extrahieren einmal den Textinhalt aller Links und einmal alle Werte der href-Attribute, das heißt die Zieladressen von Links:

//a/text() //a/@href

XPath unterstützt zudem Funktionen, um die gefundenen Knoten weiterzuverarbeiten. Nützlich ist neben der im letzten Beispiel verwendeten text()-Funktion etwa die string()-Funktion, um den gesamten Text eines Elements zu extrahieren, selbst wenn darin weitere Elemente verschachtelt sind. Das ist zum Beispiel bei Tabellenzellen der Fall, wenn sie Links oder formatierten Text enthalten. Das Beispiel extrahiert den Text der ersten Tabellenzelle im Dokument:

string(//td[1])

Die Sprache XPath ist komplexer aufgebaut als CSS (Tab. 4.4), erlaubt dafür aber die Formulierung sehr präziser Bedingungen.Footnote 11 Mit diesem Wissen lassen sich zum Beispiel Tabellen aus Wikipedia-Seiten in CSV-Tabellen umwandeln. Probieren Sie es mit der Importfunktion von Google SpreadsheetsFootnote 12 oder mit FacepagerFootnote 13 aus! In der Anwendung Facepager finden Sie auch fertige Presets mit ausformulierten XPath-Ausdrücken, um Tabellen zu extrahieren (☛ Repositorium).

Tab. 4.4 Beispiele für XPath-Ausdrücke

1.4 SQL

Umfangreiche Datenbestände, die sich über mehrere Tabellen verteilen, liegen häufig in relationalen Datenbanken vor (siehe Kap. 3). Solche Datenbanken werden unter anderem in Content-Management-Systemen eingesetzt, angefangen von kleinen Wordpress-Seiten bis zu großen Projekten wie Wikipedia. Hier besteht die Herausforderung nicht vorrangig darin, die Daten in einer unübersichtlichen Struktur zu identifizieren, sondern aus den gesamten Beständen die gewünschten Daten auszuwählen. Ein Standard für die Abfrage von relationalen Datenbanken ist die Structured Query Language (SQL). Dazu werden in einem Datenbank-Client Abfragen formuliert, die vom Datenbankmanagementsystem (DBMS) bearbeitet werden.

Die Datenbanken selbst werden meistens auf Servern installiert. Gängige serverbasierte Datenbankmanagementsysteme im Open-Source-Bereich sind MySQL und MariaDB.Footnote 14 Sie können entsprechende Datenbank-Server durchaus auf dem eigenen Computer einrichten, um etwa die Wikipedia-Datenbank zu spiegeln (siehe Abschn. 6.3). Der Zugriff auf die Daten kann dann über vier Wege erfolgen:

  1. 1.

    Über die Kommandozeile wird eine eigene mySQL-Kommandozeile aufgerufen, um darin SQL-Befehle einzugeben.

  2. 2.

    Auf dem Datenbank-Server werden spezielle Weboberflächen wie phpMyAdmin oder Adminer eingerichtet, um SQL-Befehle über den Browser zu verschicken.

  3. 3.

    Desktop-Anwendungen wie DBWeaver, DataGrip oder HeidiSQL (Abb. 4.5) laufen auf dem eigenen Computer. Auch Entwicklungsumgebungen wie RStudio beinhalten Tools zum Umgang mit Datenbanken.

  4. 4.

    Datenbanken können über R- oder Python-Skripte und mit vielen anderen Programmiersprachen angesprochen werden.

Die verschiedenen Datenbank-Clients ähneln sich meistens und bieten mindestens getrennte Ansichten für die Verwaltung der Tabellenstruktur und für den Zugriff auf die Tabellendaten.

Abb. 4.5
figure 5

Heidi-SQL als Beispiel für einen SQL-Client unter Windows. (Quelle: eigene Darstellung)

Eine einfache und verbreitete Alternative zu Server-Datenbanken ist SQLite.Footnote 15 Diese Datenbanken bestehen aus einer einzigen Datei, die problemlos weitergegeben werden kann und auch keine Server-Installation benötigt. Der Zugriff ist über Programme wie DB Browser for SQLiteFootnote 16 (Abb. 4.6) möglich, aber auch direkt über R und Python (siehe Kap. 5). Ein Beispiel für eine SQLite-Datenbank finden Sie im ☛ Repositorium. Wenn Sie sich DB Browser for SQLite installieren und die Datenbank damit öffnen, können Sie die folgenden SQL-Beispiele direkt nachvollziehen.Footnote 17

Abb. 4.6
figure 6

DB Browser for SQLite. (Quelle: eigene Darstellung)

Ganz grundsätzlich lassen sich drei Arten von SQL-BefehlenFootnote 18 unterscheiden:

  1. 1.

    Data Definition Statements werden dazu eingesetzt, die Datenbankstruktur zu erzeugen. Beispielsweise legen Sie mit einem CREATE DATABASE-Befehl eine Datenbank an.

  2. 2.

    Data Manipulation Statements werden dazu eingesetzt, Daten zu ändern, einzufügen oder zu löschen. Mit INSERT-Befehlen werden Daten in eine Datenbank importiert, mit UPDATE verändert und mit DELETE gelöscht.

  3. 3.

    Eine Unterkategorie der Data Manipulation Statements sind Befehle zur Abfrage von Daten. Am wichtigsten ist dafür der SELECT-Befehl.

Wenn mehrere Befehle hintereinander ausgeführt werden sollen, dann wird jeder Befehl mit einem Semikolon abgeschlossen. Für die einfache Abfrage von Daten ist das aber nicht nötig. Eine Abfrage aller Spalten und Zeilen einer Tabelle sieht wie folgt aus, wobei titles eine Tabelle in der Datenbank bezeichnet:

SELECT * FROM titles

Diese Abfrage lässt sich um verschiedene Elemente erweitern. So kann statt des Sternchens eine Liste der gewünschten Spalten angegeben werden. Die Angaben title_id, genres und primary_title bezeichnen Spalten in der Tabelle titles:

SELECT title_id, genres, primary_title FROM titles

Die Daten können auch sortiert werden. Im folgenden Beispiel wird nach der Spalte premiered (Erscheinungsjahr) sortiert, die Angabe ASC (engl. ascending = aufsteigend) bewirkt eine aufsteigende Sortierung. Diese Angabe kann weggelassen werden oder durch die Angabe DESC (engl. descending = absteigend) ersetzt werden:

SELECT * FROM titles ORDER BY premiered ASC

Außerdem kann die Menge der Daten mit einer WHERE-Angabe eingeschränkt werden. Das heißt, es wird danach gefiltert, unter welchen Bedingungen Zeilen ausgewählt werden sollen. Im folgenden Beispiel werden nur Zeilen zurückgegeben, in denen in der Spalte premiered der numerische Wert 2019 steht:

SELECT * FROM titles WHERE premiered = 2019

Mehrere Kriterien werden mit den booleschen Operatoren OR, AND, NOT und Klammern angegeben. Es können mit dem Ungleichoperator <> anstelle des Gleichheitsoperators = auch alle Zeilen ausgegeben werden, die einen Wert nicht enthalten. Eine Besonderheit stellt der LIKE-Operator dar, mit dem Zeichenwerte verglichen werden. Zeichenwerte werden grundsätzlich in Anführungszeichen angegeben. Dabei steht das Prozentzeichen % innerhalb der Anführungszeichen als Platzhalter für beliebige Zeichen. Die folgende Anweisung gibt alle Zeilen mit einem Wert größer 2015 in der premiered-Spalte zurück, die in der genres-Spalte die Zeichenkette "Western" enthalten:

SELECT * FROM titles WHERE premiered > 2015 AND genres LIKE '%Western%'

Häufig müssen bei der Datenanalyse Daten aus mehreren Tabellen kombiniert werden (siehe Abschn. 4.2). In der Regel gibt es in jeder Tabelle eine Spalte mit einer eindeutigen ID. Diese ID kann dann in einer anderen Tabelle als sogenannter Fremdschlüssel verwendet werden, um die Daten miteinander zu verknüpfen. Soll zum Beispiel in einer Filmdatenbank erfasst werden, welche Schauspieler:innen in welchen Filmen mitgespielt haben, dann werden eine Tabelle mit den Akteuren und eine Tabelle mit den Filmen benötigt. Eine dritte Tabelle verknüpft die IDs der Akteure und der Filme miteinander.

Zur Abfrage verknüpfter Tabellen werden JOIN-Anweisungen verwendet (siehe unten Abschn. 4.2.3). Die folgende Anweisung verknüpft die Tabelle crew (eine Tabelle mit Filmbesetzungen) mit der Tabelle titles (eine Tabelle mit den Filmen). Welche Spalten miteinander abgeglichen werden sollen, steht in der ON-Anweisung, die ähnlich wie die WHERE-Anweisung funktioniert. Vor dem Spaltennamen muss allerdings der Tabellennamen gefolgt von einem Punkt angegeben werden, um die Spalten eindeutig zu identifizieren. Diese Zusatzangabe ist nötig, denn es könnten gleichnamige Spalten in verschiedenen Tabellen enthalten sein:

SELECT * FROM crew JOIN titles ON crew.title_id = titles.title_id

Bei umfangreichen Datenbeständen kann eine solche Abfrage sehr lange dauern. Um dem zu begegnen, kann die Datenmenge eingeschränkt werden. Die LIMIT-Anweisung bewirkt (nicht nur bei JOIN), dass nur die angegebene Anzahl an Datensätzen zurückgegeben wird. Dabei ist es sinnvoll, eine Sortierung anzugeben, sodass immer die ersten Datensätze aus der sortierten Liste ausgewählt werden:

SELECT * FROM crew JOIN titles ON crew.title_id = titles.title_id ORDER BY titles.premiered LIMIT 100

Außerdem kann die Datenmenge mit einer WHERE-Anweisung weiter eingegrenzt werden. Es werden immer zuerst Joins definiert, dann Where-Einschränkungen vorgenommen, dann folgt die Angabe der Sortierung und schließlich die Limitierung, diese Reihenfolge muss eingehalten werden:

SELECT * FROM crew JOIN titles ON crew.title_id = titles.title_id WHERE category = 'director' ORDER BY titles.premiered LIMIT 100

In einer Abfrage können mehrere Joins, Sortierungen und Where-Kriterien verwendet werden. Die folgende Abfrage gibt die Regisseur:innen von Filmen aus der IMDb zurück, indem die Crew-Tabelle sowohl mit der Filmtitel- als auch der Personentabelle verknüpft wird:

SELECT * FROM crew JOIN titles ON crew.title_id = titles.title_id JOIN people ON crew.person_id = people.person_id WHERE category = 'director' AND premiered > 2000 ORDER BY titles.premiered, titles.primary_title LIMIT 100

Zur Berechnung des Ergebnisses einer solchen Abfrage muss das DBMS mehrere Spalten auswerten, bevor das Ergebnis zurückgegeben werden kann. Das betrifft vor allem die Spalten in der ON-, der WHERE- und der ORDER BY-Anweisung. Diese Auswertung kann sehr ressourcenintensiv sein und entsprechend lange dauern. Um die Zugriffszeit zu verkürzen, werden deshalb sogenannte Indizes angelegt. Ein solcher Index enthält eine Art Adressbuch, das heißt, die Daten werden so vorsortiert, dass über den Wert einer Spalte schnell auf die anderen Inhalte der Tabelle zugegriffen werden kann. Er wird aus einer oder mehreren Spalten einer Tabelle gebildet und kann über den Befehl CREATE INDEX erstellt werden. Das Aktualisieren der Indizes erhöht zwar den Aufwand beim Verändern der Daten, meistens sind Lesezugriffe in einer SQL-Datenbank aber häufiger als Schreibzugriffe. Immer wenn eine Abfrage also sehr lange braucht, sollten Sie mit Ihrem Datenbank-Client die Indizes überprüfen und ggf. neue Indizes anlegen.

Übungsfragen

  1. 1.

    Was bedeutet der reguläre Ausdruck [0-9]+ ?

  2. 2.

    Welche Quantoren gibt es in regulären Ausdrücken?

  3. 3.

    Wie kann ein Wort am Anfang einer Zeile gefunden werden?

  4. 4.

    Wofür stehen die Zeichen \r und \n?

  5. 5.

    Öffnen Sie im ☛ Repositorium die Datei example_text.txt mit einem Texteditor und suchen Sie mit regulären Ausdrücken nach allen a) Jahreszahlen, b) Prozentzahlen und c) geschützten Leerzeichen!

  6. 6.

    Welche Schwierigkeiten ergeben sich bei der Datenextraktion in Bezug auf Sonderzeichen?

  7. 7.

    Wie können in einem HTML-Quelltext die URLs gefunden werden?

  8. 8.

    Mit welchem CSS-Selektor und mit welchem XPath-Ausdruck können alle Zeilen einer HTML-Tabelle gefunden werden?

  9. 9.

    Was sind eine Datenbank, ein Datenbank-Management-System und ein Datenbank-Client?

  10. 10.

    Formulieren Sie einen SQL-Befehl, mit dem die Felder titel und jahr aus einer Tabelle movies abgefragt werden. Grenzen Sie auf Titel aus den Jahren 2005 bis 2010 ein!

Referenzen

2 Transformationsverfahren

Daten liegen selten in der Form vor, die für eine Analyse notwendig ist. Vor allem bei der Arbeit mit Textdaten besteht ein wesentlicher Schritt darin, unstrukturierte Daten zu strukturieren. In der Regel strebt man dabei an, eine oder mehrere Tabellen zu erstellen. Dafür haben sich einige hilfreiche Regeln etabliert (Wickham 2014, S. 4):Footnote 19

  1. 1.

    Jede Beobachtungseinheit wird in einer eigenen Tabelle abgespeichert. Wenn es beispielsweise um Webseiten, Nutzer:innen und Posts geht, wird für jeden dieser Typen in der Regel eine eigene Tabelle verwendet.

  2. 2.

    Jede Beobachtung entspricht einer eigenen Zeile. Beobachtungen oder Fälle sind etwa die Posts. Die Eigenschaften und Inhalte eines Posts befinden sich möglichst alle in einer einzigen Zeile.

  3. 3.

    Jede Variable wird in einer eigenen Spalte vorgehalten. Eine Variable wäre zum Beispiel die Anzahl der Kommentare eines Posts. Im Kopf der Tabelle steht dann der Name der Variable und die Werte werden darunter aufgeführt.

  4. 4.

    Jeder Wert ist in einer eigenen Zelle abgelegt. Eine Zelle ist dabei der Ort, an dem sich Spalte (vertikal) und Zeile (horizontal) treffen. Die Anzahl der Kommentare eines jeden Posts liegt also in einzelnen Zellen, es sollten möglichst nicht mehrere Angaben in einer gemeinsamen Zelle liegen.

Wer viel Datenanalyse betreibt, für den werden diese Regeln nach und nach selbstverständlich. Es geht darum, die Welt aus einer rechteckigen Brille zu betrachten, weil diese Perspektive übersichtlich ist. Vor allem für die Zusammenarbeit mit anderen ist es sehr hilfreich, sich an diese Konventionen zu halten. Denn dadurch lassen sich auch fremde Daten schnell verstehen. Das gilt auch, wenn Sie mit Excel oder ähnlichen Programmen arbeiten: Die erste Zeile sollte für Variablennamen reserviert bleiben, alle anderen Zeilen sind für die Fälle da. Verbundene Zellen sollten vermieden werden. Anmerkungen sollten nicht irgendwo in die Zellen geschrieben werden, dafür lässt sich die Anmerkungsfunktion verwenden oder Anmerkungen werden in einer eigenen Spalte oder einem eigenen Tabellenblatt erfasst. So wird sichergestellt, dass Dateien für beliebige Verarbeitungsprogramme kompatibel sowie dokumentiert und damit auch für andere intuitiv lesbar sind.

2.1 Umformen von Tabellen: Wide- und Long-Format

Die rechteckige Brille reduziert nicht nur Komplexität, sondern erlaubt auch einen vielseitigen Blick auf Daten, mitunter muss man dazu jedoch die Brille drehen. Stellt man sich einen Datensatz mit Posts auf einer Social-Media-Plattform vor, in der jede Zeile ein Post enthält und in den Spalten Eigenschaften wie die Anzahl der Likes oder Kommentare angegeben sind (Tab. 4.5), kann dieser Datensatz in verschiedene Richtungen gedreht werden. Wenn man nicht an der Analyse von Posts, sondern an den Reaktionen auf die Posts interessiert ist, kann sich eine Umformung der Tabelle lohnen: Aus den Variablen (Likes oder Kommentare) werden dann Fälle. Man unterscheidet dabei zwei grundlegende Formate.

Tab. 4.5 Wide-Format

Im Wide-Format (Tab. 4.5) ist für jede Eigenschaft oder Variable eine Spalte vorgesehen. So gibt es etwa Spalten mit den Anzahlen der Likes, Kommentare und Shares eines Posts. Je mehr Eigenschaften in einem Datensatz gespeichert werden, desto breiter wird die Tabelle.

Im Long-Format (Tab. 4.6) reichen für die gleichen Daten drei Spalten aus. In einer Spalte wird der Name einer Eigenschaft oder Variablen festgehalten und in einer zweiten Spalte der Wert. Damit klar ist, welche Eigenschaften zusammengehören, wird für jeden Fall eine Nummer oder ein anderer eindeutiger Bezeichner verwendet (post_id). Je mehr Eigenschaften in einem Datensatz gespeichert werden, desto länger wird die Tabelle.

Tab. 4.6 Long-Format

Außerdem kommt es vor, dass mehrere Werte in einer Zeile zusammengefasst sind. Man spricht auch von verschachtelten (engl. nested) Daten. Wenn man beispielsweise mit Facepager Tweets erhebt (siehe Abschn. 7.2), kann daraus eine Liste von Hashtags, die durch Semikola getrennt sind, erzeugt werden (Tab. 4.7). Um die Hashtags auswerten zu können, müssen sie aufgetrennt werden, sodass in jeder Zeile ein einzelnes Hashtag steht. Dieses Verfahren nennt sich Unnesting (deutsch entschachteln). Wenn Text in einzelne Teile zerlegt wird, etwa in die Wörter eines Satzes, spricht man auch von Tokenisierung. Auf diese Weise, wenn jedes Token (= Wort oder Hashtag) in einer eigenen Zeile steht, lässt sich leicht auszählen, welche Wörter oder Hashtags besonders häufig vorkommen.

Tab. 4.7 Beispiel mit mehreren Hashtags in einer Zelle

Statistikprogramme und die entsprechenden Programmiersprachen stellen in der Regel Funktionen zur Umformung zwischen Wide- und Long-, Nested- und Unnested-Formaten zur Verfügung (Tab. 4.8). In R erstellt zum Beispiel die Funktion separate_rows() für jedes Token eine neue Zeile, es wird also vertikal entschachtelt. Dagegen kann man mit separate() horizontal entschachteln, das heißt, eine Spalte wird in mehrere neue Spalten aufgeteilt. Umgekehrt lassen sich auch mehrere Spalten zu einer zusammenfassen. Beim Verschachteln und Entschachteln muss man sich also entscheiden, ob eine Tabelle im Long- oder im Wide-Format entstehen soll.

Tab. 4.8 Befehle zum Umformen von Tabellen (Beispiele)

Die konkrete Umsetzung hängt vom Anwendungsfall und von der verwendeten Software ab (☛ Repositorium). Wichtig ist zunächst, die grundlegenden Konzepte zu verstehen. Denn die Umformung von Datensätzen kann die Arbeit wesentlich erleichtern und erweitert die Möglichkeiten der Datenanalyse.

2.2 Zusammenführen von Daten: Joins

Viele Fragestellungen erfordern, dass mehrere Datensätze aus unterschiedlichen Quellen zusammengeführt oder miteinander abgeglichen werden. Im einfachsten Fall haben die verschiedenen Datensätze die gleiche Form und können untereinander oder nebeneinander kopiert werden. Mit R können dafür etwa die tidyverse-Funktionen bind_rows() oder bind_cols() verwendet werden. In Python wird dafür die pandas-Funktion concat() verwendet, mit dem Achsenparameter wird gesteuert, ob Spalten (axis=1) oder Zeilen (axis=0) aneinandergebunden werden sollen (siehe Kap. 5).

Häufig sind jedoch nicht alle benötigten Daten bereits in gleichartigen Tabellen vorhanden. Angenommen: In einer Tabelle sind Zeitschriften erfasst und es sollen die Webseiten dieser Zeitschriften analysiert werden – es fehlen aber die Webadressen. Die Webadressen (URLs) liegen in einer zweiten Tabelle, wobei die beiden Tabellen nicht deckungsgleich sind. In beiden Tabellen finden sich Zeitschriften, die in der jeweils anderen fehlen. Im besten Fall liegen aber ein oder mehrere Merkmale vor, die zum Abgleich verwendet werden können, zum Beispiel eine International Standard Serial Number (ISSN).

In Abb. 4.7 sind zwei Tabellen dargestellt, die sich nicht vollständig überschneiden. Die beiden orange markierten ISSN kommen nicht in der jeweils anderen Tabelle vor. Die grün markierte ISSN kommt beispielsweise in beiden Tabellen vor.

Abb. 4.7
figure 7

Zwei Tabellen vor dem Zusammenführen. Quelle: Eigene Darstellung in Anlehnung an Taylor & Francis (2022)

Beim Zusammenführen der Tabellen ist zu entscheiden:

  • Sollen nur die übereinstimmenden Zeilen erhalten bleiben, wird ein sogenannter Inner Join verwendet.

  • Sollen alle Zeilen in beiden Tabellen erhalten bleiben, dann kann ein Full Join eingesetzt werden. In den Zeilen ohne Übereinstimmung bleiben die Felder leer.

  • Wenn im Ergebnis nur die erste Tabelle wichtig ist, kann ein Left Join eingesetzt werden. Hierbei bleiben alle Zeilen der linken Tabelle erhalten und überall wo es möglich ist, werden die Zeilen aus der rechten Tabelle zugeordnet. Das Gegenstück ist der Right Join, der nach dem gleichen Prinzip funktioniert. Hier bleiben alle Datensätze aus der rechten Tabelle erhalten.

Aufpassen muss man, wenn das Vergleichskriterium mehrfach vorkommt, wenn also zum Beispiel eine Zeitschrift doppelt aufgeführt wurde oder eine ISSN versehentlich doppelt vergeben ist. Dann werden auch im Ergebnis die Zeilen vervielfältigt. Das Zusammenführen kann man sich so vorstellen, dass zunächst alle Kombinationen aus allen Zeilen in den beiden Datensätzen gebildet werden (= Kreuzprodukt). Im nächsten Schritt werden dann diejenigen Zeilen wieder aussortiert, die der Join-Bedingung nicht entsprechen.

Die Tabellen werden in der Regel so zusammengeführt, dass im Ergebnis alle Spalten aus den ursprünglichen Tabellen vorhanden sind. Einige Programmiersprachen unterstützen Join-Varianten, bei denen nur die Spalten der ersten Tabelle erhalten bleiben:

  • Mit einem Semi Join bleiben in der ersten Tabelle nur die Zeilen erhalten, für die ein Eintrag in der zweiten Tabelle gefunden werden konnte. Das entspricht im Prinzip einem Left Join, es werden aber keine Daten aus der zweiten Tabelle übernommen.

  • Bei einem Anti Join bleiben in der ersten Tabelle nur die Zeilen erhalten, für die kein Eintrag in der zweiten Tabelle gefunden werden konnte. So können Datensätze identifiziert werden, die in der zweiten Tabelle fehlen.

In der Programmiersprache R (siehe Abschn. 5.1) werden die genannten Verfahren unter anderem über Bibliotheken aus dem Tidyverse umgesetzt.Footnote 20 Das folgende R-Skript vollzieht einen Left Join, wobei journal und url jeweils den in Abb. 4.7 dargestellten Datensatz enthalten. Das Kriterium zum Abgleichen wird über den by-Parameter angegeben und das Ergebnis in x abgelegt.

x <- left_join(journal, urls, by = "issn")

Daraus entsteht die Tabelle in Abb. 4.8; nur bei den passenden Zeilen konnte die URL ergänzt werden, in allen anderen Fällen bleibt die neue Spalte leer. Es können auch mehrere Kriterien gleichzeitig zum Abgleich verwendet werden. Würden die Ausgangstabellen eine weitere Spalte mit einer Jahresangabe enthalten und soll diese Angabe zum Abgleich verwendet werden, kann über den by-Parameter eine Liste c() mit mehreren Spaltennamen angegeben werden:

x <- left_join( journal, downloads, by = c("issn", "jahr") )

Abb. 4.8
figure 8

Mit einem Left Join zusammengeführte Tabelle. Quelle: Eigene Darstellung in Anlehnung an Taylor & Francis (2022)

Bislang wurde vorausgesetzt, dass die Spaltennamen in beiden Tabellen übereinstimmen. In diesem Fall könnte der by-Parameter sogar entfallen, R würde automatisch mit den übereinstimmenden Spaltennamen arbeiten. Wenn sich die Namen unterscheiden, kann ein benannter Vektor verwendet werden. Links vom Gleichheitszeichen steht dann der Spaltenname des linken Datensatzes (hier: name) und rechts der Name im rechten Datensatz (hier: titel). Nach dem Zusammenführen bleibt in der Regel nur eine Spalte übrig, denn die beiden ursprünglichen Spalten enthalten die gleiche Information:

x <- left_join( journal, urls, by = c("name" = "titel") )

Die meisten Statistik- und Datenbankprogramme sehen Befehle zum Zusammenführen von Datensätzen vor (Tab. 4.9). Besonders umständlich ist das Zusammenführen mit Excel. Hier müssen die Werte Spalte für Spalte und Zeile für Zeile mit der Funktion SVERWEIS (engl. VLOOKUP) aus der zweiten Tabelle geholt werden. Hilfreich ist bei der Arbeit mit Excel auch die Funktion ZÄHLENWENN, mit der festgestellt werden kann, wie viele Datensätze in einem anderen Tabellenblatt enthalten sind. Das folgende Beispiel setzt voraus, dass im Tabellenblatt zeitschriften in der Spalte C und im Tabellenblatt urls in der Spalte A das übereinstimmende Merkmal angegeben ist, etwa eine ISSN. Es wird dann berechnet, wie häufig der Wert in zeitschriften!C2 im Bereich urls!A:A vorhanden ist:

=ZÄHLENWENN(urls!A:A;zeitschriften!C2)

Tab. 4.9 Beispiele für Left Joins

Mit etwas Kreativität und Übung lassen sich damit in Kombination mit den Filter- und Sortierfunktionen auch in Excel Datensätze zusammenführen.

2.3 Aggregieren von Daten: Map-Reduce und Split-Apply-Combine

Das Gegenstück zum Zusammenführen von Daten ist die Aggregation, bei der die Daten nicht mehr, sondern weniger werden. Dieser Vorgang ist ganz wesentlich für die Datenanalyse, um Komplexität auf wenige interessante Muster einzuschmelzen. Ein Beispiel dafür ist die Berechnung von Mittelwerten. Beschäftigt man sich mit der Anzahl von Zeitungsberichten zur Coronapandemie, mit der Anzahl der Logins auf einer Webseite oder mit der Anzahl von Terroranschlägen im Zeitverlauf, kann die Anzahl der Einzelaktivitäten zunächst für jeden Tag zusammengezählt werden. Um nun einen typischen Tag zu beschreiben, kann als ein einfaches Maß der Mittelwert aller Tageswerte gebildet werden.Footnote 21 Dabei ist man möglicherweise nicht nur daran interessiert, alle Tage gleich zu erfassen, sondern etwa Wochentage vom Wochenende zu unterscheiden.

Hinter einer solchen Datenaufbereitung stehen konzeptionell vier Vorgänge:

  • Map: Einzelne Werte müssen zunächst Zeile für Zeile transformiert werden. Beispielsweise wird aus dem Zeitpunkt des Logins (2023-08-09T13,00:12.234) eine neue Spalte mit dem Tag des Logins gebildet, das heißt, die Uhrzeit wird abgeschnitten (2023-08-09) oder der Wochentag wird bestimmt (Mittwoch). Die Loginzeit wird also auf einen Tag abgebildet (engl. mapped).

  • Split: Der gesamte Datensatz wird für den folgenden Schritt in Teile zerlegt, zum Beispiel in sieben Teile für jeden einzelnen Wochentag.

  • Reduce: Jeder Teil wird zusammengefasst, indem ein neuer Kennwert wie der Mittelwert berechnet wird. Mitunter wird dieser Schritt auch als Apply bezeichnet.

  • Combine: Die Einzelergebnisse werden wieder zu einer Tabelle zusammengefasst, sodass in einer Spalte der Wochentag und in einer anderen Spalte die mittlere Anzahl der Logins stehen.

In verschiedenen Programmiersprachen und Tools werden diese Vorgänge unterschiedlich bezeichnet oder sind teilweise auch nicht explizit benannt. Unter R findet sich der Vorgang des Abbildens unter anderem in der Tidyverse-Funktion map()wieder, das Zerlegen ist über Funktionen wie group_by()umgesetzt und summarize()reduziert und kombiniert die Werte innerhalb der Gruppen. Im Python-Package pandas finden sich äquivalent dazu die Funktionen map(), groupby() und aggregate().

Unabhängig von der konkreten Programmiersprache ist eine Zergliederung von Problemen in diese vier Vorgänge immer dann besonders hilfreich, wenn umfangreiche Daten verarbeitet werden müssen. Denn selbst wenn Map, Split, Reduce und Combine hintereinander ablaufen, lässt sich im besten Fall innerhalb dieser Schritte parallelisieren. Das bedeutet, die Bearbeitung jeder einzelnen Zeile (map) oder Gruppe (reduce) kann auf unterschiedlichen Computern oder innerhalb eines Computers auf unterschiedlichen Kernen (siehe Abschn. 6.3) ausgeführt werden. Nur auf diese Weise können Suchmaschinen wie Google nahezu das gesamte Web für die Suche aufbereiten. Eine Software dafür ist beispielsweise Apache Hadoop,Footnote 22 in der zunächst Funktionen für das Mapping und das Reducing entwickelt werden, die sodann auf einem Computercluster verteilt ausgeführt werden.

2.4 Umformen von Matrizen: Ähnlichkeitsberechnungen

Für eine möglichst effiziente Verarbeitung von Daten werden Tabellen als Matrix aufgefasst. Eine Matrix setzt sich aus Vektoren (Zeilen oder Spalten) zusammen und Vektoren beinhalten wiederum eine Sammlung von Elementen (Werten). Je nach Perspektive besteht eine Matrix somit aus einer Liste mit Zeilen oder aus einer Liste mit Spalten (siehe Abschn. 3.1).Footnote 23 Eine Matrix m x n besteht aus m Zeilen und n Spalten, die Matrix in Abb. 4.9 ist somit eine 6 x 3 Matrix. Mathematisch werden Matrizen in eckigen Klammern dargestellt und üblicherweise mit Großbuchstaben benannt, wohingegen Vektoren an Kleinbuchstaben erkennbar sind.

Abb. 4.9
figure 9

Darstellung der Reaktionen auf Social-Media-Posts als Tabelle (links) und Matrix (rechts). (Quelle: eigene Darstellung)

In einer Tabelle lassen sich einzelne Elemente über die Zeilen- oder Spaltenbezeichnungen adressieren. In Matrizen werden dagegen sogenannte Indizes verwendet – das sind die Zeilen- und Spaltennummern. Wenn im Beispiel die Like-Anzahl des Posts mit der ID 4 ausgelesen werden soll, findet sich dieser Wert in der ersten Spalte der vierten Zeile der Matrix. Diese Zelle lässt sich über Indizes adressieren, wobei der Zeilenindex meistens als erstes angegeben wird. Je nach Programmiersprache beginnt die Zählung des Index bei 0 oder bei 1 wie in folgenden beiden Ausdrücken ersichtlich ist:

  • Adressierung in R: A[4,1]

  • Adressierung in Python: A[3,0]

Wenngleich die Betrachtung von Tabellen als Matrizen zunächst weniger intuitiv und etwas sperrig erscheinen mag, lohnt sich die Auseinandersetzung damit und das Denken in Matrizen. So können einige Programme Matrizen effizienter verarbeiten, wenn die Funktionen vektorisiert sind. Das heißt, diese Funktionen bearbeiten einen ganzen Vektor, also alle Einträge in einer Zeile oder Spalte, gleichzeitig und man muss keine Schleife programmieren, mit der jeder Eintrag Zeile für Zeile und Spalte für Spalte durchlaufen wird. Matrizen spielen deshalb in vielen Algorithmen eine wichtige Rolle, insbesondere beim Machine Learning (siehe Kap. 8).

Nicht nur Zahlen, sondern auch Texte lassen sich in Matrizen erfassen: In einer Dokument-Term-Matrix wird jedes Dokument (z. B. ein Post) in einer Zeile erfasst und für jedes Wort wird eine Spalte angelegt. In den Zellen wird dann angegeben, wie häufig dieses Wort im Dokument auftritt (siehe Abschn. 9.1). Vergleicht man die Zeilenvektoren von zwei Dokumenten, so lässt sich daraus die Ähnlichkeit von Texten berechnen: In ähnlichen Texten treten die gleichen Wörter ungefähr gleich häufig auf. Ebenso können Netzwerke als Matrizen erfasst werden, wobei die Zeilen und Spalten dann Personen darstellen. In den Zellen wird zum Beispiel mit einer 1 angegeben, wenn sich zwei Personen kennen (siehe Kap. 10). Auch hier lassen sich Erkenntnisse aus dem Vergleich von Vektoren ziehen. Die Zeilen oder Spalten von zwei Personen sind sich ähnlich, wenn sie die gleichen Bekanntenkreise haben. Auf die gleiche Weise funktionieren kollaborative Empfehlungssysteme, bei denen ausgewertet wird, welche Filme Personen mit einer ähnlichen Nutzungshistorie gesehen, welche Social-Media-Posts ähnliche Nutzer:innen gelikt oder welche Produkte Personen mit ähnlichen Interessen gekauft haben (Ricci et al. 2015).

Text- oder Netzwerkmatrizen zeichnen sich meistens dadurch aus, dass sie viele Zeilen und Spalten enthalten, aber dass die meisten Zellen leer sind (bzw. den Wert 0 enthalten). Wenn man etwa viele unterschiedliche Texte miteinander vergleicht, kommen in jedem Text je nach Thema sehr spezifische Wörter vor. Geht es in einem Social-Media-Post um Kochrezepte und in einem anderen um Volleyball, dann braucht es für jedes WortFootnote 24 in den verschiedenen Vokabularen eigene Spalten, ohne dass alle Texte alle Spalten nutzen. Man unterscheidet deshalb zwei Arten von Matrizen:

  • Matrizen sind dense (deutsch dichtbesetzt), wenn die meisten Zellen Werte enthalten, zum Beispiel, wenn alle Entfernungen zwischen den Großstädten der Welt erfasst werden. Diese Matrizen werden vollständig im Arbeitsspeicher des Computers vorgehalten und können dann schnell über die Indizes angesprochen werden. Beim Abspeichern werden einfach alle Werte in einer langen Reihe abgespeichert und zusätzlich die Information angegeben, an welchen Stellen diese lange Reihe in Zeilen oder Spalten umgebrochen werden muss.Footnote 25

  • Matrizen sind sparse (deutsch dünnbesetzt), wenn die meisten Zellen leer sind. In diesem Fall kann beim Speichern einer Matrix Platz eingespart werden, indem in speziellen Datenformaten nur die tatsächlich vorhandenen Werte abgelegt werden.Footnote 26 Insbesondere wenn Millionen von Texten verglichen werden, stößt das andernfalls, ohne eine sinnvolle Komprimierung und optimierte Zugriffsverfahren, schnell an die Grenzen eines einzelnen Computers.

Für beide Varianten werden in den verschiedenen Programmiersprachen entsprechend angepasste Funktionen eingesetzt. Eine besonders häufig benötigte Rechenoperation, die unter anderem zur Berechnung von Textähnlichkeiten verwendet wird, ist die Matrixmultiplikation, bei der eine Matrix mit einer anderen Matrix multipliziert wird. Damit zwei Matrizen miteinander multipliziert werden können, muss eine grundlegende Voraussetzung erfüllt sein: Die Anzahl der Spalten des Multiplikators A (vor dem Mal-Zeichen) muss der Anzahl der Zeilen des Multiplikanden B (nach dem Mal-Zeichen) entsprechen. Das Produkt ist eine dritte Matrix C, die m Zeilen der ersten Matrix A und n Spalten der zweiten Matrix B aufweist (Abb. 4.10).

Abb. 4.10
figure 10

Das Resultat einer Matrixmultiplikation. Die Farben kennzeichnen, welche Dimensionen der Spalten bzw. Zeilen übereinstimmen müssen. (Quelle: eigene Darstellung)

Konkret wird das Produkt ermittelt, indem zunächst die erste Zeile der einen Matrix Element für Element mit der ersten Spalte der anderen Matrix multipliziert wird. Die Zwischenergebnisse werden dann addiert und das ergibt den Wert in der ersten Spalte der ersten Zeile der neu errechneten Matrix. Anschließend wird die erste Zeile der einen Matrix mit der zweiten Spalte der anderen Matrix elementweise multipliziert und summiert, um das zweite Element in der ersten Zeile von Matrix C zu ermitteln. Dieses Vorgehen wird für alle weiteren Zeilen und Spalten von Matrix A und B wiederholt (Abb. 4.11). Anders als bei der einfachen Multiplikation von zwei Zahlen, kann man die Operanden A und B nicht einfach vertauschen (= nicht kommutativ) – selbst wenn die Bedingung erfüllt sein sollte, dass die Spaltenanzahl von A der Zeilenanzahl von B entspricht, ergibt das ein anderes Ergebnis. Probieren Sie es einmal aus!

Abb. 4.11
figure 11

Rechenweg bei der Matrixmultiplikation. Die Farben und Pfeile illustrieren, aus welcher Zeile bzw. Spalte einzelne Werte in der Berechnung stammen. (Quelle: eigene Darstellung)

Um nun die Ähnlichkeit zwischen zwei Texten, Personen oder anderen Vektoren zu berechnen, muss man noch einen Schritt tiefer in die Matrizenmultiplikation eintauchen und eine weitere Operation heranziehen: das Transponieren von Matrizen. Hierbei wird eine Matrix A so gedreht, dass die Zeilen zu Spalten und die Spalten zu Zeilen werden. Die transponierte Matrix wird als A′ bezeichnet. Da Spalten und Zeilen dann automatisch der oben angesprochenen Bedingung entsprechen, kann eine Matrix immer auch mit der transponierten Matrix multipliziert werden. Standardisiert man die Matrizen vorher, erhält man ein weitverbreitetes und vielseitig einsetzbares Ähnlichkeitsmaß, die Kosinusähnlichkeit (Abb. 4.12):Footnote 27

  1. 1.

    Nimmt man den oben in Abb. 4.9 dargestellten Datensatz, berechnet für jede Zeile die Summe der quadrierten Werte und zieht daraus die Wurzel, entspricht das der sogenannten L2-Norm. Die Zahl drückt die Gesamtzahl der Reaktionen auf einen Post aus, wobei umfangreiche Reaktionen besonders stark gewichtet sind.

  2. 2.

    Mittels Division aller Werte durch die L2-Norm, werden die Zellenwerte auf den Bereich 0 bis 1 normalisiert. Das Resultat ist ein Wert, der ausdrückt wie charakteristisch eine bestimmte Reaktion für den Post ist. Zum Beispiel bestehen alle Reaktionen (1,0) von Post 6 aus Likes, dieser Post erhält keine Kommentare (0,0).

  3. 3.

    Transponiert man die normalisierte Matrix, so werden die Posts zu Spalten.

  4. 4.

    Multipliziert man die normalisierte Matrix mit der transponierten normalisierten Matrix, ergibt sich bei Betrachtung der höchsten Werte ein Muster.

Abb. 4.12
figure 12

Berechnung der Kosinusähnlichkeit. (Quelle: eigene Darstellung)

Das Ergebnis ist eine Matrix mit Posts in den Zeilen und den gleichen Posts in den Spalten. Die Werte an den Kreuzungen stellen ein Ähnlichkeitsmaß dar: Je höher der Wert, umso ähnlicher sind sich die Aktivitätsschemata dieser Posts. Der erste und fünfte Post gleichen sich darin, dass sie keine Likes, dafür aber andere Reaktionen erhalten haben. Andere Posts sind sich dahingehend ähnlich, dass die Likes augenscheinlich eine stärkere Rolle im Aktivitätsmuster spielen.

Eine solche Aufbereitung von Daten führt Schritt für Schritt zum Destillieren von Mustern. Und wenn Sie tatsächlich etwas nachgerechnet haben, wird auch klar: Per Hand ist das aufwendig. In den nächsten Kapiteln lernen Sie, wie Sie Computer für sich arbeiten lassen.

Übungsfragen

  1. 1.

    Was unterscheidet das Long-Format vom Wide-Format?

  2. 2.

    Was unterscheidet einen Left Join vom Inner Join und vom Full Join?

  3. 3.

    Wofür kann das Map-Reduce-Verfahren eingesetzt werden?

  4. 4.

    Wie werden die einzelnen Werte in einer Matrix adressiert?

  5. 5.

    Nennen Sie einen Anwendungsfall für die Matrixmultiplikation!

Weiterführende Literatur

  • Beaulieu, A. (2021). Einführung in SQL. Daten erzeugen, bearbeiten und abfragen. (3. Aufl.). Heidelberg: O’Reilly.

  • Becher, M. (2011). XML. DTD, XML-Schema, XPath, XQuery, XSLT, XSL-FO, SAX, DOM. Herdecke, Dortmund: W3L-Verlag.

  • Cleve, J. & Lämmel, U. (2014): Data Mining. München: Oldenbourg.

  • Fitzgerald, M. (2012). Einstieg in reguläre Ausdrücke. Schritt für Schritt reguläre Ausdrücke verstehen. Beijing: O’Reilly.

  • Freeman, A. (2011). The definitive guide to HTML5. All you need to know to use HTML5 professionally. New York: Apress Springer.

  • Friedl, J. E. F. (2006). Mastering regular expressions. (3. Aufl.). Sebastapol: O’Reilly.

  • Kirchgessner, K. (2012). Vektor- und Matrizenrechnung für Dummies. Weinheim: John Wiley & Sons Incorporated.

  • Meyer, E. A. (2019). CSS. kurz & gut. (5. Aufl.). Heidelberg: dpunkt.verlag.

  • Schicker, E. (2017). Datenbanken und SQL. Eine praxisorientierte Einführung mit Anwendungen in Oracle, SQL Server und MySQL. (5. Aufl.). Wiesbaden: Springer Vieweg.

  • Stubblebine, T. (2004). Reguläre Ausdrücke. kurz & gut. Beijing: O’Reilly.

  • Wickham, H. & Grolemund, G. (2016). R for Data Science. Sebastopol: O’Reilly UK Ltd.