Docs/abschlussarbeit tim (#521)

First Version of my Documentation.
This commit is contained in:
Tim Ronneburg 2024-01-21 12:53:05 +01:00 committed by GitHub
commit 05f692294a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1268 additions and 138 deletions

View File

@ -0,0 +1,178 @@
# Recherche zu benötigten Techniken (Tim Ronneburg)
In diesem Kapitel werden die Prämissen behandelt, welche zu Beginn des Projektes festgelegt und anhand derer eine Auswahl an benötigten Techniken getroffen wurde.
Die im Folgenden genannten Werkzeuge und Technologien werden an den entsprechenden Stellen später detaillierter behandelt. Dieses Kapitel dient daher als Zusammenfassung der Anforderungen mit nachfolgender Auswahl der Technologien. Diese Auswahl wird im anschließenden Kapitel 3, das die Zwischenberichte des Projekts umfasst, eingehender und ausführlicher behandelt.
Bei der Auswahl der Technologien für das Projekt werden verschiedene Gesichtspunkte berücksichtigt. Insbesondere stehen folgende Fragen im Fokus:
- Welches Tool zur Visualisierung soll verwendet werden?
- Auf welche Weise erfolgt die Berechnung und Visualisierung von Graphen und Netzwerken?
- Wie wird die Bereitstellung der Webseite durchgeführt?
- Welche Datenbanken kommen für die Sammlung und Bereitstellung der Informationen zum Einsatz?
- Wie wird die Stimmung der Nachrichten zu einer Firma ausgewertet?
Auf die Fragestellung wie in der Projektgruppe zusammengearbeitet wird und welche Werkzeuge dafür zum tragen kommen - Stichwort: Git - wurde bereits im vorherigen Kapitel behandelt und wird hier nicht weiter betrachtet.
Für die aufgelisteten Fragestellungen setzt sich jeweils ein Gruppenmitglied intensiv mit der Thematik auseinander und stellt eine Auswahl an möglichen Technologien der Projektgruppe vor. Nach mehreren Iterationen der Evaluation wird eine Auswahl an Werkzeugen getroffen.
Generell werden für sämtliche Aspekte die allgemeinen Leitlinien berücksichtigt. Die eingesetzten Werkzeuge und Technologien sollen Open Source sein, also frei verfügbar und ohne zusätzliche Kosten. Die Programmiersprache basiert auf Python, da sie von allen Gruppenmitgliedern beherrscht wird und die Verwendung mehrerer Sprachen das Projekt unnötig komplex machen würde. Die gewählten Technologien integrieren sich nahtlos in die Arbeitsweise mit DevOps und Git und sind durch automatisierte Tests überprüfbar. Dadurch wird eine einheitliche Arbeitsweise gewährleistet, und Engpässe durch isolierte Anwendungen werden vermieden. Schließlich legen wir Wert darauf, dass alle zu berücksichtigenden Werkzeuge einfach bedienbar sind, um einen reibungslosen Einstieg in die Technologie zu ermöglichen und die Beteiligung aller Teammitglieder zu erleichtern.
## Technische Anforderungen
Neben den allgemein geltenden Anforderungen wird in diesem Unterkapitel auf die technischen Anforderungen je Kategorie eingegangen. Die Kategorien wurden aus den oben ganannten Fragestellungen abgeleitet und entsprechen den Arbeitsbereichen der einzelnen Gruppenmitglieder sowie die Kernelemente des Projektes. Die Gruppen/Kategorien lauten:
- DevOps
- Text Mining
- Datenspeicherung
- Sentimentanalyse
- Verflechtungsanalyse
- Visualisierung
Diese Einteilung findet sich im Verlauf der Dokumentation immer wieder. Zusätzlich kommt in diesem Teil noch das Unterkapitel "Provisionierung" hinzu indem auf die Vorgaben für die Bereitstellung der Anwendung für die Nutzer eingegangen wurde.
Im Folgenden wird kurz auf die Kategorien eingegangen und die speziellen Anforderungen für den jeweiligen Bereich dargelegt, welche es bei der Rechereche zu betrachten galt.
### DevOps
Dieser Abschnitt befasst sich mit der Versionskontrolle des Quellcodes, automatisierten Tests und der automatisierten Bereitstellung der Anwendung auf einem Server. Im Kapitel 3.1 wird dieses Thema theoretisch und in Kapitel 4.2 praktisch behandelt, weshalb sich dieser Abschnitt auf die Anforderungen auf höchster Ebene konzentriert.
Bei der Implementierung von DevOps ist es entscheidend herauszufinden, welche Versionskontrollsoftware genutzt werden soll, wie DevOps darin integriert werden kann und für welche Aspekte des Projekts DevOps sinnvoll eingesetzt wird.
Die Versionskontrollsoftware muss für alle Beteiligten leicht zugänglich sein und keine zusätzlichen Konten erfordern. Sie sollte die Zusammenarbeit im Team ermöglichen, indem mehrere Personen an denselben Dateien arbeiten können, die dann von der Versionskontrollsoftware zusammengeführt werden.
DevOps muss in dieser Software implementiert werden können, um eigene Pipelines zu erstellen, die die Anwendung testen und bereitstellen.
Unter Berücksichtigung der allgemeinen Vorgaben lauten die Prämissen für die Recherche der DevOps-Technologien wie folgt:
- Auswahl einer Versionskontrollsoftware, die eine kollaborative Arbeit des Teams am Quellcode ermöglicht.
- Die Technologie sollte das Erstellen eigener Pipelines unterstützen.
- Die Technologie sollte kostenlos und frei verfügbar sein.
- Die Technologie sollte mit Python kompatibel sein.
### Text Mining
Die Text Mining-Kategorie konzentriert sich auf das Sammeln von relevanten Unternehmensinformationen für das Projekt. Diese Daten werden im Abschnitt zur Datenspeicherung gesichert und der Projektgruppe zur Verfügung gestellt. Ähnlich wie im vorherigen Kapitel wurde dieses Thema in den Kapiteln 3.2 sowie 4.3 bis 4.4 eingehender behandelt.
In diesem Bereich ist es erforderlich, ein Werkzeug zu finden, das APIs aus dem Internet abfragt und die Daten entweder sichert oder transformiert. Dies betrifft insbesondere Beziehungsdaten aus dem Unternehmensregister für die Netzwerkanalyse, Stammdaten, verschiedene Nachrichten-APIs für die Stimmungsanalyse bezogen auf jedes einzelne Unternehmen sowie Finanzdaten aus dem Bundesanzeiger.
Besonders beim Extrahieren von Daten aus dem Bundesanzeiger besteht die Herausforderung darin, die Informationen in ein einheitliches Format zu überführen. Die Schwierigkeit ergibt sich aus der Tatsache, dass die Informationen als Jahresberichte vorliegen, die von jedem Unternehmen in eigener Form erstellt werden. Das Tool muss daher in der Lage sein, die Berichte zu analysieren und die gesuchten Daten zu extrahieren, auch wenn sie in Tabellen oder Fließtexten vorliegen.
Zusammengefasst sind die Kriterien für die Technologie:
- Abfrage und Auswertung des Unternehmensregisters und von Nachrichten-APIs
- Aufbereitung der Daten in ein einheitliches Format
- Sicherung der Daten in einer Datenbank gemäß dem vorgegebenen Datenbankschema
- Die Technologie sollte kostenlos und frei verfügbar sein.
- Die Technologie sollte mit Python kompatibel sein.
### Datenspeicherung
Für die Datenspeicherung ist die Entwicklung eines Schemas erforderlich, das nicht nur die durch Text Mining gewonnenen Daten sichert, sondern auch während der Analyse generierte Daten speichert. Zudem müssen die Daten für die Analyse und die Programm-Aufbereitung abrufbar sein. Dieser Aspekt wird im Kapitel 3.3 genauer behandelt.
Es wird also nach einer oder mehreren Datenbanken gesucht, die folgende Anforderungen abdecken:
- Auswahl einer Datenbank als Data Lake für das Text Mining.
- Auswahl einer Datenbank für die transformierten Daten aus dem Data Lake.
- Abwägung zwischen NoSQL- und SQL-Datenbanken.
- Die Technologie sollte kostenlos und frei verfügbar sein.
- Die Technologie sollte mit Python kompatibel sein
### Sentimentanalyse
Die Sentimentanalyse beinhaltet die Bewertung von Nachrichten im Hinblick auf die darin erwähnten Unternehmen. Es wird untersucht, ob die betreffenden Unternehmen positiv oder negativ dargestellt werden. Diese Information wird, bei ausreichender Datenlage, im Transparenzregister als Stimmung angegeben.
Da eine manuelle Analyse aufgrund der großen Menge täglicher Nachrichten nicht durchführbar ist, wird nach einer Technologie gesucht, die automatisch die Daten analysiert, die Unternehmensnamen identifiziert, die Stimmung des Textes gegenüber dem Unternehmen bewertet und diese Informationen dann für die Speicherung weiterleitet. Eine weitere Schwierigkeit besteht in der Übersetzung der deutschen Nachrichten ins Englische, da für die Analyse KI-Modelle zum Einsatz kommen, die oft auf englische Texte ausgerichtet sind.
Zusätzlich muss der Unternehmensname standardisiert werden, sodass bei unterschiedlichen Schreibweisen dennoch dasselbe Unternehmen erkannt wird und die gewonnenen Informationen zu diesem Unternehmen angezeigt werden können. Beispielsweise wird in Artikeln häufig von VW, Volkswagen, Volkswagen AG oder dem Volkswagen Konzern gesprochen, was alles dasselbe Unternehmen repräsentiert.
Die Prämissen für die Lösungstechnologie lauten:
- Auswahl einer Technologie zur automatisierten Auswertung von Unternehmensdaten in Nachrichten.
- Automatisierte Analyse der Stimmung in Bezug auf ein Unternehmen in den Nachrichten.
- Übersetzen der Texte ins Englische
- Standardisierung der Unternehmensnamen.
- Die Technologie sollte kostenlos und frei verfügbar sein.
- Die Technologie sollte mit Python kompatibel sein
### Verflechtungsanalyse
Die Verflechtungsanalyse konzentriert sich auf die Auswertung von Daten in Form eines Netzwerks, in dem Unternehmen und Akteure mit ihren Verbindungen zu anderen dargestellt werden. Ziel ist es, Gruppierungen zu identifizieren, die für weitere Analysen relevant sind. Diese Aspekte werden in den Kapiteln 3.5 und 4.5.2 näher erläutert.
Es wird daher nach einem Werkzeug gesucht, das die Akteure mit ihren Beziehungen visualisiert. Dabei ist es wichtig, dass das Tool die Positionen der Knoten und Kanten berechnet, um Überlappungen zu vermeiden. Bei großen Datenmengen können Performanzprobleme auftreten, die zu einer längeren Generierung führen. Das Tool sollte darauf ausgelegt sein, solche Probleme zu minimieren. Darüber hinaus muss es Metriken für die Analyse der Beziehungen berechnen können. Zusätzlich soll die Visualisierung des Tools in die grafische Benutzeroberfläche der Anwendung integrierbar sein.
Demnach ergeben sich folgende Anforderungen:
- Integration mit dem Visualisierungstool.
- Performante Berechnung der Positionen der Knoten und Kanten.
- Berechnung der klassischen Metriken für die Netzwerkanalyse.
- Visualisierung des Netzwerkgeflechts.
- Benutzerfreundlichkeit.
- Die Technologie sollte kostenlos und frei verfügbar sein.
- Die Technologie sollte mit Python kompatibel sein
### Visualisierung
Nachdem sich die anderen Bereiche um die Zulieferung und Aufbereitung der Daten gekümmert haben, steht in der Visualisierungsphase die Auswahl eines Frameworks für die ansprechende Darstellung der Ergebnisse im Fokus. Dies beinhaltet die Auswahl eines Frontends, das in der Lage ist, Daten in ansprechenden Grafiken zu präsentieren. Das Frontend soll auf Python basieren und webserverfähig sein. Zusätzlich sollte es benutzerfreundlich sein und nahtlos in die Netzwerkanalyse integriert werden können.
Die formulierten Anforderungen sind demnach:
- Auswahl eines benutzerfreundlichen Frontends.
- Das Framework sollte die Erstellung von Grafiken ermöglichen.
- Das Framework sollte die Darstellung eines Netzwerks unterstützen.
- Das Framework sollte webserverfähig sein.
- Das Framework sollte auf Python basieren.
- Die Technologie sollte kostenlos und frei verfügbar sein.
### Provisionierung
Die Provisionierung befasst sich mit der Bereitstellung der entstehenden Anwendung für die Benutzer. Dabei soll die Anwendung über das Internet erreichbar sein, jedoch durch ein Passwort geschützt werden. Aus finanziellen Gründen ist das Hosting kostenfrei und erfolgt über Container. Das Projektteam hat sich für den Container-Ansatz entschieden, um die Anwendung auf mehrere kleine Teilanwendungen zu verteilen. Diese können separat getestet und von einzelnen Projektentwicklern auch lokal ausgeführt werden.
Daraus ergeben sich die folgenden Prämissen:
- Auswahl einer Technologie für das Hosting mit Docker/Container.
- Kostenfreiheit.
- Zugriff über das Internet für alle Projektteilnehmer mit Passwortschutz.
## Lösungsansätze: Überblick über relevante Technologien und Werkzeuge
Auf Basis der im vorherigen Kapitel festgelegten Prämissen erfolgt in diesem Abschnitt eine kurze Beschreibung der verfügbaren Technologien, gefolgt von der Auswahl der für dieses Projekt geeigneten Technologien. Wie bereits erwähnt, können detailliertere Informationen den Zwischenberichten im folgenden Kapitel 3 entnommen werden.
### DevOps
Basierend auf den oben beschriebenen Anforderungen besteht die Wahl zwischen GitHub und GitLab als Versionsverwaltungstool. Es wird auch entschieden , ob eine öffentliche Organisation verwendet wird, die Fachhochschule das Tool hostet oder ob es über einen Home Server erfolgt.
Nach sorgfältiger Prüfung der vorhandenen Optionen wurde die Entscheidung für die Lösung mit GitHub über die GitHub-Organisation der Fachhochschule getroffen. Diese Lösung ist naheliegend, da bereits alle Projektteilnehmenden einen GitHub-Account besitzen und gleichzeitig den Professoren und der Fachhochschule Zugriff auf den Quellcode gewährt werden kann. Darüber hinaus bleibt der Quellcode in der Verwaltung der Fachhochschule und kann späteren Studierenden zur Verfügung gestellt werden.
Diese Entscheidung bringt für das Projekt weitere Vorteile mit sich, da keine zusätzlichen Kosten entstehen. Im Hinblick auf DevOps bietet GitHub mit GitHub Actions eine ausgezeichnete Möglichkeit, eigene Pipelines zu erstellen, diese automatisch auszuführen und über bestimmte Hooks zu triggern. So kann beispielsweise beim Push auf einen Branch die Pipeline gestartet werden.
GitLab steht über die Fachhochschule nicht zur Verfügung und bedeutet somit einen größeren Mehraufwand für das Projekt.
### Text Mining
Für das Text Mining konnte kein einsatzbereites, kostenloses Tool gefunden werden, das alle Anforderungen erfüllt. Daher wurde die Entscheidung getroffen, einen eigene Lösung zu entwickeln, der die erforderlichen APIs abfragt und die Daten entsprechend aufbereitet. Der Quellcode wurde in Python verfasst, und die genaue Vorgehensweise wird ausführlich im Kapitel 3.2 beschrieben.
### Datenspeicherung
Für die Datenspeicherung steht die gesamte Palette an SQL- und NoSQL-Datenbanken zur Verfügung. Basierend auf dem Kenntnisstand der Projektteilnehmer wird die Entscheidung getroffen, für den Data Lake die NoSQL-Datenbank MongoDB zu verwenden. Diese steht kostenfrei zur Verfügung, kann in Docker-Containern gehostet werden und eignet sich aufgrund ihres schemenlosen Designs gut für die unstrukturierte Speicherung von zahlreichen Daten. Außerdem kann über MongoDB Atlas eine kostenlose global verfügbare Instanz bezogen werden.
Für die aufbereiteten Daten wird, ebenfalls aufgrund des Kenntnisstands der Studierenden, PostgreSQL ausgewählt. Dies ist eine SQL-Datenbank, die ebenfalls in einem Docker-Container gehostet werden kann und einfach zu bedienen ist. Das Schema sowie weitere Details sind im Kapitel 3.3 nachzulesen.
Beide Technologien sind kostenfrei zugänglich und können mithilfe verfügbarer Bibliotheken leicht über Python angesprochen werden.
### Sentimentanalyse
Bei der Sentimentanalyse besteht eine kleinere Auswahl an möglichen Ansätzen bereit. Diese stellt unter anderem den KI Anteil des Projektes dar, weshalb hier die Analyse mithilfe eines Modells stattfindet.
### Verflechtungsanalyse
Für das Netzwerktool der Verflechtungsanalyse stehen mehrere Optionen zur Auswahl, darunter [NetworkX](https://networkx.org/), Scatter Graphen, NetworkX mit [Pyvis](https://pyvis.readthedocs.io/en/latest/), [Graphviz](https://graphviz.org/) und [Cytoscape](https://cytoscape.org/). Jedes dieser Frameworks hat seine Vor- und Nachteile. Cytoscape bietet beispielsweise ansprechende Visualisierungen, während Graphviz eine benutzerfreundliche Schnittstelle zur Erstellung von Graphen und Netzwerken bietet. Dennoch ließ sich NetworkX am besten integrieren. Es zeichnet sich durch eine einfache Bedienung aus und kann Metriken direkt berechnen. Im Vergleich dazu erfordert Graphviz, dass der Graph zuerst durch eine Methode in einen NetworkX-Graphen konvertiert wird, was zusätzliche Arbeit bedeutet.
Die Visualisierung mit Pyvis stellt die schönste Darstellung dar, da dem Nutzer direkte Interaktionen ermöglicht werden. Dieser kann die Nodes weiter auseinander ziehen oder in einzelne Bereiche hineinzoomen. Die Herausforderung besteht jedoch darin, dass bei großen Datenmengen die Generierung erheblich verlangsamt wird und teilweise Minuten dauert. Ein weiterer Nachteil besteht darin, dass Pyvis eine HTML-Seite mit JavaScript erstellt, die nicht nahtlos in das gewählte Visualisierungstool eingebunden werden kann. Es kann lediglich über ein IFrame eingefügt werden, was die Performance erheblich beeinträchtigt.
Daher wurde die Entscheidung getroffen, auf das native NetworkX zurückzugreifen, das mithilfe eines Plotly Scatter Graphen visualisiert wird. Hierbei werden die Positionen aus dem NetworkX-Graphen ausgelesen und in Punkte für einen Scatter Graphen transformiert.
### Visualisierung
Für das Visualisierungstool stehen verschiedene Optionen zur Auswahl, darunter Plotly Dash, Django sowie Webframeworks auf JavaScript-Basis wie Angular oder Vue.js. Da die Kenntnisse im Team hauptsächlich auf Python basieren, wurde sich trotz der vielen Vorteile von JavaScript-Frameworks gegen diese entschieden.
Schließlich wird aufgrund des vorhandenen Kenntnisstands und der geringeren Komplexität die Wahl auf Plotly Dash getroffen. Dieses Tool ist darauf ausgelegt, einfache Anwendungen für die Visualisierung von Graphen und Daten zu erstellen. Es kann zudem in einem Container gehostet werden und verwendet Python als Programmiersprache.
Die Einarbeitung in Django wäre erheblich umfangreicher und wurde daher zugunsten von Plotly Dash verworfen.
### Provisionierung
Bei der Provisionierung gilt es, zwischen der Nutzung eines der Hyperscaler (AWS, GCP oder Azure) mit ihren kostenfreien Kontingenten oder der Eignung der Server der Universität Südwestfalen zu unterscheiden. Als letzte Alternative besteht die Möglichkeit des Selbsthostings, wobei dies aufgrund des Aufwands, einen eigenen Server einzurichten und abzusichern, wirklich nur als allerletzte Option betrachtet wird.
Nach mehreren Gesprächen ergab sich die Möglichkeit, den Container-Cluster der Fachhochschule zu nutzen, was sich als kostengünstige Option für das Projekt herausstellte. Die Entscheidung fiel daher schnell. Obwohl das Hosting über einen Public Cloud Provider ebenfalls die Anforderungen erfüllt, birgt es das Risiko, dass eine Kreditkarte angegeben wird und bei Fehlverhalten oder falschen Einstellungen in der Cloud erhebliche Kosten verursacht werden können. Zudem ist das Team in Bezug auf Cloud-Ressourcen begrenzt geschult. Daher wurde eindeutig die Lösung des FH-Clusters bevorzugt.
## Proof-of-Concept mit Jupyter Notebooks
Die in diesem Abschnitt beschriebene Recherche ist am Projektbeginn durchgeführt worden in dem auf Basis der Prämissen für die einzelnen kategorien Jupyternotebooks erstellt wurden um die Technologien zu evaluieren und einen Eindruck für den Aufwand des Prjektes zu erhalten.
Dieses Vorgehen bezeichnet man als Proof-of-Concept (POC) und reduziert spätere Arbeitsaufwände die entstehen, wenn sich durch fehlende Evaluation für falsche Technologien oder Konzepte entschieden wurde.
Die Jupyternotebooks finden sich im Projekt unter den Ordnern
- [research](documentations/research)
- [Jupyter](Jupyter)

View File

@ -0,0 +1,4 @@
# Grundlagen und Basistechniken (Tim Ronneburg)
In diesem Abschnitt sind die Zwischenberichte des Projekts zu finden. In diesen Berichten wurde die Recherche zu den einzelnen Kategorien durchgeführt und in den entsprechenden Jupyter Notebooks im Git-Repository bewertet. Dieses Kapitel präsentiert eine detaillierte Analyse der zuvor beschriebenen Recherche mit zusätzlichen Begründungen und Erläuterungen, warum bestimmte Technologien, Konzepte und Vorgehensweisen für das Projekt ausgewählt wurden.
Es ist zu beachten, dass die Zwischenberichte zu einem frühen Zeitpunkt des Projektes erstellt wurden und eine Hausarbeit darstellen, weshalb in einigen Abschnitten von zukünftigen Vorgehensweisen und Ausblick gesprochen wird.

View File

@ -0,0 +1,64 @@
# Netzwerkanalyse (Tim Ronneburg)
In diesem Abschnitt wird die Umsetzung der Netzwerkanalyse behandelt, die den Aufbau und die Analyse eines Netzwerks gemäß den im Kapitel 3.5 erwähnten Verflechtungsanalysen betrifft.
Wie bereits im vorherigen Abschnitt erläutert, kommt im Rahmen des Projekts Plotly Dash zum Einsatz, ein Tool, das für die Erstellung von Dashboards mit Python entwickelt wurde. Zusätzlich wird dieses Tool um die Bibliothek NetworkX erweitert, um Netzwerkgraphen darzustellen.
Die Netzwerkanalyse wird an verschiedenen Stellen im Projekt integriert. Zunächst auf der Hauptseite, wo der gesamte Graph dargestellt wird, dann auf der Unternehmensdetailseite zur Untersuchung der Verflechtungen aus der Perspektive des ausgewählten Unternehmens, sowie auf der Personendetailseite aus dem gleichen Grund wie auf der Unternehmensseite.
Quellcode-seitig werden mehrere Dateien für die Erstellung des Netzwerks erzeugt. Im Verzeichnis "UI" befindet sich ein Unterordner "utils" mit dem weiteren Unterordner "networkx", in dem die Dateien zu finden sind:
- [network_2d.py ](src/aki_prj23_transparenzregister/utils/networkx/network_2d.py)
- [network_3d.py](src/aki_prj23_transparenzregister/utils/networkx/network_3d.py)
- [network_base.py](src/aki_prj23_transparenzregister/utils/networkx/network_base.py)
- [networkx_data.py](src/aki_prj23_transparenzregister/utils/networkx/networkx_data.py)
Die ersten beiden Dateien enthalten den Quellcode für die Visualisierung des Netzwerks mithilfe eines Scatterplots in 2D oder 3D. Die Datei "base" umfasst die Initialisierung des Netzwerks anhand der ausgewählten Daten. Hierbei werden gleichzeitig die relevanten Metriken gebildet. In der Datei "data" sind verschiedene Funktionen enthalten, die dazu dienen, Daten abzurufen, welche anschließend dem Netzwerk zugeführt werden können.
Die "base"-Datei legt somit den Grundstein für das Netzwerk, indem sie nicht nur die Struktur initialisiert, sondern auch wichtige Metriken generiert. Die "data"-Datei hingegen stellt eine Schnittstelle dar, durch die das Netzwerk mit den benötigten Daten versorgt wird, wobei diverse Funktionen zur Datenabfrage integriert sind.
Der Ablauf gestaltet sich unter anderem wie folgt: Wenn ein Nutzer die Startseite aufruft, werden bestimmte Funktionen in "networkx_data" ausgeführt, um die Datenbank nach den voreingestellten Informationen zu durchsuchen. Diese Daten werden aufbereitet und als Pandas DataFrame an "network_base" übergeben, wo mithilfe des Frameworks NetworkX ein Graph erstellt wird. In diesem Graph-Element sind die Positionen der Nodes enthalten. Zusätzlich werden Methoden bereitgestellt, um Standard-Netzwerkanalysemetriken zu berechnen. Für dieses Projekt handelt es sich dabei um die Metriken "degree", "betweenness", "closeness" und "pagerank".
Anschließend werden das NetzwerkX-Objekt und das DataFrame "Metrics" an die Datei "network_xd" übergeben. Diese Datei liest die Positionen der Nodes aus, berechnet die Edges und erstellt dann mithilfe eines Scatterplots den Graphen in 2D beziehungsweise 3D. Das Ergebnis ist ein Figure-Objekt, das von Dash angezeigt werden kann.
Dieses Figure Objekt wird in den folgenden Pages Dateien eingebunden:
- home.py
- company_elements.py
- person_elements.py
Für das Aktualisieren des Netzwerks nach Benutzereingaben werden Callbacks verwendet. Diese werden durch die Interaktion mit einem HTML-Element wie einem Dropdown oder Radiobuttons ausgelöst. Da dem Benutzer mehrere Auswahlmöglichkeiten für die Betrachtung des Netzwerks gegeben werden, muss der Callback für das Netzwerk mehrere Inputs aufnehmen und verarbeiten. Dies ist erforderlich, da ein Callback immer nur einen Output haben kann und mehrere Callbacks nicht auf dasselbe HTML-Element verweisen sollten. Obwohl es mittlerweile eine Möglichkeit gibt, mehrere Outputs zu nutzen, bietet dies keine signifikanten Vorteile weshalb in diesem Projekt darauf verzichtet wurde.
## Bedienung des Netzwerks auf der Homepage
Die Homepage des Transparenzregisters sieht zur Vollendung des Projektes folgender Maßen aus:
![Abbildung der Homepage](images/Home_page.PNG)
Auf der linken Seite befindet sich eine Tabelle mit den 10 "besten" Nodes aus dem Links abgebildetet Graphen. Mit "besten" sind hier die 10 Nodes mit dem höchsten Wert der oberhalb der Tabelle gewählten Metrik. Je nach gefilterten Daten ändert sich die Tabelle automatisch und zeigt immer den aktuellen Stand zum rechts erstellten Graphen.
Der Graph auf der rechten Seite repräsentiert die Verflechtung von Unternehmen und Akteuren. Dabei stellen orangene Punkte Personen wie Wirtschaftsprüfer oder Geschäftsführer dar, während dunkelgrüne Punkte Unternehmen symbolisieren. Die Bedienung des Graphen erfolgt über die oberen Bedienfelder. Die ersten beiden Dropdown-Menüs ermöglichen die Auswahl der Datenbasis, auf die sich der Graph beziehen soll. Hierbei ist auch eine Mehrfachauswahl möglich. Neben den Daten kann auch das Layout des Graphen eingestellt werden, wobei für die dreidimensionale Darstellung nur begrenzte Optionen verfügbar sind.
Die initiale Erstellung eines Graphen kann mehrere Sekunden in Anspruch nehmen, wird jedoch anschließend zwischengespeichert und kann in den nächsten 30 Minuten schneller durchgeführt werden. Die anfänglichen Leistungsprobleme resultieren aus der Menge an Daten, die abgerufen, verarbeitet und dargestellt werden müssen. Das Zusammenspiel von Datenbank, NetworkX und Plotly beeinflusst hier die Gesamtperformance.
## Bedienung des Netzwerks auf der Company Page
Nach Auswahl eines bestimmten Unternehmens kann auf der Unternehmensseite der Reiter "Verflechtung" gewählt werden, um die Verflechtungen des Unternehmens zu betrachten. Sollte das ausgewählte Unternehmen keine Verflechtungen aufweisen, wird dem Nutzer ein entsprechender Hinweistext angezeigt. Im Bild rot makiert.
<!-- Bild mit Hinweistext -->
![Abbildung eines Hinweis Textes](images/Company_note.PNG)
Bei ausreichend vorhandenen Daten wird ein kompakter Graph erstellt, der die Verflechtungen des betrachteten Unternehmens zeigt. Hierbei werden lediglich die engsten Verbindungen visualisiert. Auf dieser Seite ist keine Interaktion mit dem Graphen vorgesehen, da der Benutzer hier lediglich zusätzliche Informationen zum Unternehmen erhalten soll, ohne den gesamten Graphen durchzugehen. Diese Funktion ist für die Homepage vorgesehen.
<!-- Bild Company Seite -->
![Abbildung der Companypage](images/Company_page.PNG)
## Bedienung des Netzwerks auf der Person Details Page
Die Personen-Detailseite verhält sich in Bezug auf das Netzwerk analog zur Unternehmensseite. Auch hier erhält der Nutzer lediglich zusätzliche Informationen zur Person, kann jedoch nicht weiter mit dem Graphen interagieren.
Auf der Personen-Seite werden die Verflechtungen bis zur dritten Ebene abgebildet, und die jeweilige Anzahl der Verbindungen wird oben in den Containern mit den Kennzahlen dargestellt.
<!-- Bild Personen Seite -->
## Ausgelassene Features
Im Kapitel 3.5 wurden neben den hier beschriebenen Funktionen auch weitere Aspekte betrachtet und evaluiert, insbesondere im Hinblick auf die Darstellung und Analyse unternehmerischer Kennzahlen wie EBIT, Umsatz, Gewinn, Aktienkurs usw. . Leider konnten im Zuge der Datenerfassung die hierfür benötigten Daten nicht zuverlässig und in ausreichender Menge generiert werden. Dies führte dazu, dass diese Funktionen aus dem Verflechtungsscope herausgenommen wurden.
Ein weiterer Herausforderungspunkt war die schwierige und performante Einbindung des Graphen in Plotly Dash. Die Generierung des Graphen bei größeren Datenmengen dauerte mit dem ursprünglich ausgewählten Tool deutlich länger, als es in den Proof-of-Concepts den Anschein erwecken ließ. Daher erfolgte eine Umstellung auf eigens erstellte Scatter Plots, für die ein eigener Algorithmus entwickelt werden musste, der die Positionen aus dem NetworkX ausließt und in Punkte für den Scatter Graphen formatiert. Dies hatte einen deutlichen Einfluss auf die Entwicklung und führte zu Verzögerungen.
Zuletzt ist die Verflechtungsanalyse einer der Bereiche, die abhängig von den Ergebnissen anderer Kategorien ist. Dies bedeutet, dass die Entwicklung mit konkreten Daten erst versetzt beginnen konnte, was den Raum für Fehlerbehebungen und zusätzliche Funktionen zusätzlich begrenzt hat.
## Kurzes Resumee der Verflechtungsanalyse/Netzwerkanalyse
Alles in allem konnte ein Netzwerk auf Basis der Unternehmensdaten erstellt werden, das Einblicke in die Verzweigungen einiger Unternehmensbeziehungen ermöglicht. Die Analyse wird dabei von klassischen Netzwerkanalyse-Kennzahlen unterstützt. Jedoch muss die Analyse weiterhin durch den Benutzer erfolgen und ist nur in einem eingeschränkteren Maße, als ursprünglich angedacht, durchführbar. Die Analyse hinsichtlich unternehmerischer Kennzahlen musste aufgrund mangelnder Daten aus dem Projekt ausgeschlossen werden.
Die Möglichkeit, ein Netzwerk basierend auf den vorliegenden Unternehmensdaten zu generieren, bietet einen Einblick in die Verästelungen bestimmter Unternehmensbeziehungen. Die Analyse wird durch klassische Netzwerkanalyse-Kennzahlen unterstützt. Dennoch ist die Durchführung der Analyse auf eine begrenztere Ebene beschränkt als ursprünglich geplant. Aufgrund unzureichender Daten im Projekt musste die Analyse bezüglich unternehmerischer Kennzahlen ausgeschlossen werden.

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,843 @@
---
author:
- |
Tim Ronneburg
Fachhochschule Südwestfalen\
Schriftliche Ausarbeitung im Modul\
„Projektgruppe"\
im Studiengang\
M.Sc. Angewandte Künstliche Intelligenz\
eingereicht bei\
Prof. Dr.-Ing. Doga Arinir
bibliography:
- literatur/literatur.bib
title: Verflechtungsanalyse des Transparenzregisters
---
# Verflechtungsanalyse des Transparenzregisters
## Erklärung
Ich erkläre hiermit, dass ich die vorliegende Arbeit selbstständig
verfasst und dabei keine anderen als die angegebenen Hilfsmittel benutzt
habe. Sämtliche Stellen der Arbeit, die im Wortlaut oder dem Sinn nach
Werken anderer Autoren entnommen sind, habe ich als solche kenntlich
gemacht. Die Arbeit wurde bisher weder gesamt noch in Teilen einer
anderen Prüfungsbehörde vorgelegt und auch noch nicht veröffentlicht.
2023-10-01
<img src="abbildungen/unterschrift.PNG" alt="image" />
Tim Ronneburg
## Einleitung
In den letzten Jahren mehren sich die großen Wirtschaftsskandale, in
denen Unternehmen Bilanzen fälschen oder untereinander zusammenarbeiten,
um Steuerschlupflöcher auszunutzen, wie bei Wirecard oder der Cum-Ex
Affäre. Ein wichtiger Bestandteil für das Erkennen oder Aufarbeiten
solcher Skandale besteht darin, sich einen Überblick über die
Verflechtungen der beteiligten Akteure zu verschaffen. Diese Arbeit
resultiert aus einem Projekt, welches genau solche Verflechtungen
transparent darzustellen versucht. Bei dem sogenannten
Transparenzregister werden die Beziehungen von Unternehmen und einzelnen
natürlichen Personen wie Wirtschaftsprüfer, Kommanditisten etc. aus
Deutschland und der Europäischen Union dargelegt.
### Problemstellung
Die angesprochenen Verflechtungen sind teilweise komplexe Strukturen von
Unternehmen und anderen Akteuren, die sich erst bei einer umfassenderen
Betrachtung erkennen lassen. Die Analyse solcher Konstrukte ist aus den
reinen Rohdaten, ohne aufwendiger Aufarbeitung und Visualisierung,
nahezu unmöglich. Damit aus den Daten Informationen und Wissen gewonnen
werden können, müssen diese aufbereitet und in einer verständlichen Form
dargestellt werden. Spätestens seit der F8 von Facebook im Jahr 2007 ist
die Nutzung von Graphen für solche Verflechtungen etabliert. Eine solche
Verflechtung nennt man ein *Social Network*, *Social Graph* oder auch
*Sociogram*. Mit diesem Graph können Analysen einfacher durchgeführt
werden, da sowohl das Wissen aus der Graphentheorie zum Tragen kommt als
auch die Beziehungen einzelner Akteure mit dem bloßen Auge ersichtlich
sind.
### Zielsetzung und Aufbau der Arbeit
Ziel dieser Arbeit ist die Vermittlung der Grundlagen für eine solche
Verflechtungsanalyse. Es wird aufgezeigt, wie ein solcher Graph
aufgebaut werden kann, welche Bedingungen gelten und welche Kennzahlen
man berechnen kann.
Das Werk beginnt mit einer Einführung in die Graphentheorie, aus dieser
leiten sich *Social Networks* beziehungsweise *Social Graphs* ab. Neben
der Graphentheorie wird auch auf die Analyse von *Social Networks*
eingegangen. Im Hauptteil werden diese Grundlagen auf das zugrunde
liegende Projekt angewendet. Es wird verdeutlicht, wie ein solcher Graph
für das Projekt aufgebaut wird und welche Bedingungen dafür erfüllt sein
müssen. Des Weiteren werden Kennzahlen vorgestellt und gebildet mithilfe
derer die Analyse einer solchen Verflechtung durchgeführt werden kann.
Abgeschlossen wird diese Arbeit mit einer Handlungsempfehlung für das
Projekt sowie einem Fazit und Ausblick.
## Graphentheorie
In diesem Abschnitt werden die Grundlagen der Graphentheorie erläutert.
Mithilfe dieses Wissen lassen sich Verflechtungen besser verstehen und
analysieren, da auch Verflechtungen Graphen sind.
### Begriffliche Definition
Graphen sind nach der Graphentheorie "Strukturen aus Punkten und
Verbindungen zwischen diesen Punkten" [1]. Die
Punkte werden als **Ecken/Knoten** oder im Englischen ***Nodes*** und
die Verbindungen als **Kanten/Verbindungen**, oder im Englischen
***Edges*** bezeichnet. Dabei liegt der Kern eines Graphen nicht in der
Visualisierung, sondern in dessen mengentheoretischen Eigenschaften.
[1]
Es gibt diverse Arten von Graphen: **Ungerichtete Graphen** und
**gerichtete Graphen** beziehungsweise **Digraphen**. Für ungerichtete
Graphen gilt folgende Definition:
*"Ein ungerichteter Graph $G$ ist ein Paar ($V$, $E$). Hierbei ist $V$
eine endliche Menge, welche die Ecken repräsentiert, und $E$ ist eine
Menge, die aus Mengen der Form $v1$, $v2$ besteht, wobei $v1$, $v2$
$\in$ $V$ gilt. $E$ repräsentiert die Menge der Kanten."*
[1]
Anhand dieser Definition lassen sich Graphen mit beliebig vielen Kanten
zwischen den Ecken bilden. Die Kanten müssen dabei nicht geradlinig
verlaufen, sodass derselbe Graph auf verschiedene Weisen dargestellt
werden kann. [1]
Im Gegensatz dazu besitzen gerichtete Graphen Kanten mit einer
vorgegebenen Richtung. Diese Richtung wird anhand eines Pfeiles auf der
Kante visualisiert. Auch ein gerichteter Graph kann auf verschiedene
Arten dargestellt werden. Eine mögliche Definition von gerichteten
Graphen ist die folgende:
*"Ein gerichteter Graph oder Digraph $D$ ist eine Struktur ($V$, $E$).
Hierbei ist $V$ eine endliche Menge der Ecken und $E$ ist eine Menge,
die aus Paaren der Form ($v1$, $v2$) besteht, wobei $v1$, $v2$ $\in$ $V$
gilt. E repräsentiert die Menge der gerichteten Kanten, welche auch
Bögen genannt werden."* [1]
Die bereits angesprochene Möglichkeit, einen Graphen mit denselben
Eigenschaften auf unterschiedlichste Weise darzustellen, bezeichnet man
als **Isomorphie**. Es ist einfach von einem Graph einen isomorphen
Graphen zu erzeugen, aber deutlich komplexer die Isomorphie von zwei
Graphen festzustellen. [2]
### Sociogram/ Social Network/ Social Graph
Ein Sociogram ist ein Model eines Netzwerks von sozialen Verbindungen
die durch einen Graphen repräsentiert werden. Diese Idee wurde 2007 von
Facebook als Social Graph in der F8 vorgestellt. Diese Art von Graph
basiert auf der Graphentheorie. Die Stärken dieses Graphen liegen in der
Veranschaulichung der sozialen Verflechtungen.
Ein solcher Graph oder ein solches Netz wird aufgebaut, indem jede Ecke
des Graphen einen Akteur (Person oder Unternehmen), jede Kante eine
Verbindung (Beauftragung, Verwandschaft, Arbeitsverhältnis) darstellt.
Die Kanten können mit Gewichten versehen werden. Jede Kante ist dabei
gerichtet.[3]
Das Ergebnis kann als *Social Graph, Social Network* oder *Sociogram*
bezeichnet werden. In dieser Arbeit wird hauptsächlich der Begriff
*Social Network* (SN) genutzt.
Die kleinste Struktur in einem *Social Network* wird als *Dyad*
bezeichnet und ist eine soziale Gruppe bestehend aus zwei Knoten mit
einer gerichteten oder ungerichteten Kante. Die nächst größere Form ist
eine *Triad*, welche offen oder geschlossen sein kann. Offen bedeutet,
dass über einen Knoten die anderen beiden verbunden sind. Hingegen ist
bei einer geschlossenen *Triad* jeder Knoten mit beiden anderen Knoten
über eine Kante verbunden. Die größte soziale Gruppe stellt ein *Quad*
dar und besteht aus vier Ecken.[4]
Die Ansammlung von mehreren Akteuren durch enge Verbindungen wird als
***Cluster*** oder ***Group*** bezeichnet.
### Social Network Analysis (SNA)
Bei der *Social Network Analysis* (SNA) werden die sozialen Strukturen
anhand von Metriken aus der Graphentheorie untersucht.
Eine Analysemöglichkeit ist die Bestimmung der *number of hops*. Diese
gibt an, wie viele Verbindungen benötigt werden, um von einem Punkt zu
einem anderen zu gelangen. Dieser Wert kann auf einen Teil des Graphen
sowie auf den gesamt Graphen gemittelt werden. Ist der Median der
*number of hops* im Gesamtgraphen beispielsweise bei 5, so werden
Verbindungen, die diesen Schwellwert überschreiten, zu einem Cluster
kombiniert. [3]
Weitere Einsichten werden über ein Netzwerk erlangt, in dem man Teile
des Netzwerkes oder das gesamte Netzwerk in drei verschiedene Level
abstrahiert. **Element-Level** ist die Betrachtung der Auswirkungen und
Einflüsse einzelner Ecken und Kanten. **Group-Level** analysiert die
Zusammenhänge und Dichte von Gruppen innerhalb des Netzes.
**Network-Level** ist das Interesse an den topologischen Eigenschaften
des Netzwerks. [5]
#### Element-Level Metriken
Die folgenden Metriken sind aus der Element-Level Analyse und betrachten
die Bedeutung der einzelnen Knoten und Kanten.
Metriken zur Bedeutung von Verbindungen:
**Transitive** beschreibt die Menge an gleichen Kanten zweier Ecken, die
über eine Kante verbunden sind. In einem sozialen Netzwerk für Personen
gibt es die Wahrscheinlichkeit, dass zwei Bekannte einer Person sich
anfreunden.
**Reciprocity** gibt die Wahrscheinlichkeit an, mit der sich eine Ecke
mit sich selbst verbindet.
**Assortativity** drückt aus, wie sehr sich ein Akteur mit anderen
Akteuren verbindet, die ähnlich sind hinsichtlich der Größe des Grades.
**Homophily** ist die Wahrscheinlichkeit von Verbindungen sehr ähnlicher
Akteure untereinander. [5]
Weitere Algorithmen und Kennzahlen sind die Folgenden:
**Degree Centrality** gibt die Anzahl der Kanten je Knoten an. Knoten
mit einer hohen *Degree Centrality* haben die meisten Verbindungen und
können einen hohen Einfluss aufweisen oder gut platziert sein. Es wird
eingesetzt, um gut verbundene, beliebte, informationshaltende oder
Reichweiten starke Akteure zu finden. Die Kennzahl kann bei *directed
graphs* in ***in-degree*** (eingehende) und ***out-degree***
(ausgehende) Kanten aufgeteilt werden.[@NetworksAnIntro vgl. S. 168-169]
Ein Graph kann nach und nach immer weiter nach der *Degree Centrality*
gefiltert werden. Dadurch erhält man die am besten verbundenen Ecken.
Dieses Vorgehen bezeichnet man als ***degenerate graph*** oder
***Degeneracy***.
Um diesen Wert zu berechnen benötigt es lediglich die ausgehenden Kanten
an den jeweiligen Ecken zu berechnen. Um einen standardisierten Wert zu
berechnen, nimmt man die Anzahl an Ecken je Graph (n) und nutzt die
Summe minus 1 (n-1) als Teiler für den Wert der Kanten je Ecke. Wenn ein
Graph 10 Ecken hat und eine Beispiel-Ecke 5 ausgehende Kanten, ergibt
sich daraus eine *Degree Centrality* von 5 und ein standardisierter Wert
von 1/3 (3/(10-1)).
Im Zusammenhang des Transparenzregisters lassen sich mit diesen
Kennzahlen gut vernetzte Unternehmen oder Akteure mit großer Reichweite
ermitteln.
**Betweenness Centrality** hebt Knotenpunkte hervor, welche besonders
oft als Verbindungsknoten zwischen zwei anderen Einheiten dienen. Sie
werden als \"Brücken\" benutzt und können der kürzeste Pfad in einem
Netzwerk sein. Mit dieser Kennzahl werden die Akteure gefunden, die den
Fluss des Netzwerks am meisten beeinflussen. Bei der Interpretation
dieser Kennzahl muss allerdings mit Vorsicht agiert werden.
Eine hohe *Betweeness Centrality* kann ausdrücken, dass ein Akteur einen
großen Einfluss und Autorität über einen Cluster im Netzwerk verfügt, es
kann jedoch auch sein, dass der Akteur nur als Vermittler beider Enden
dient. [7]
Dieser Wert lässt sich dadurch berechnen, indem jedes Ecken-Paar des
Netzwerkes genommen wird und die Anzahl der zwischen ihnen liegenden
Ecken auf dem kürzesten Weg gezählt werden (*geodesic distance*). Man
zählt dann, wie oft ein Knoten als \"Brücke\" fungiert.
Beim Transparenzregister Projekt sind es die Unternehmen und Personen,
die als Vermittler fungieren und zentrale Rollen in Bereichen einnehmen
können. Auf diese wird ein besonderes Augenmerk gelegt, da hier die
meisten Auffälligkeiten vermutet werden.
**Closeness Centrality** hilft dabei, Cluster von Knoten zu finden, die
sehr nahe aneinander sind. Dies geschieht über einen Algorithmus, der
den kürzesten Weg zwischen den Knoten sucht und die Knoten mit einer
Punktzahl aus der Summe aller Pfade versieht. Knoten mit einer hohen
*Closeness Centrality* haben einen kurzen Weg zu allen anderen Knoten.
Diese sind sehr effizient bei der Informationsverteilung - somit sind
Akteure mit einer hohen *Closeness Centrality* in der Lage, schnell das
gesamte Netzwerk zu beeinflussen.
Bei der Interpretation dieser Kennzahl können Informationsverteiler
bestimmt werden, jedoch haben in einem sehr verbundenen Netzwerk die
Ecken meist einen sehr ähnlichen *Closeness Centrality* Wert. Daher ist
es bei diesen Netzwerken sinnvoll, eher Informationsverteiler in den
einzelnen Clustern auszumachen. [7]
Dieser Wert wird berechnet, indem man die Gesamtanzahl an Schritte zu
einer Ecke zählt und diesen Wert invertiert.
Neben der *Betweenness Centrality* ist dies eine der besonderen
Kennzahlen, da hiermit Akteure gefunden werden, die aufgrund ihrer
möglichst geringen direkten Verbindungen gar nicht auffallen würden,
aber durch die kurzen Wege eventuell doch Beziehungen zu vielen Akteuren
besitzen.
**Eigenvector Centrality** gewichtet, anders als bei der *Degree
Centrality*, die Nachbarn unterschiedlich. Dafür werden die Kanten des
Ausgangsknoten gemessen, aber auch die Kanten der Folgeknoten und so
weiter bis der gesamte Graph durchlaufen ist. Nicht jeder Nachbar hat
nach dieser Metrik den gleichen Wert. Dadurch werden Ecken erkannt, die
einen Einfluss durchs gesamte oder einen Großteil des Netzwerkes haben.
Die *Eigenvector Centrality* kann sowohl für gerichtete als auch
ungerichtete Graphen verwendet werden - in der Praxis zeigt sich
allerdings, dass die ungerichteten Graphen deutlich besser
funktionieren. Die Problematiken der *Eigenvector Centrality* bei
gerichteten Graphen kann mittels der *Katz Centrality* behoben werden,
welche aber in dieser Arbeit nicht weiter behandelt wird.
[6]
Berechnet wird die Kennzahl je Knoten durch das Bilden eines
Eigenvektors und Iterieren über jede der Kanten. Wenn die Kennzahl durch
$x$ repräsentiert wird und die Kanten durch $i$, können die Mengen der
Kanten der Nachbarn durch $x$ = $\sum$ Aii Xi, bestimmt werden, wobei
Aii ein Element der Adjacency Matrix ist. Dieser Prozess muss iterativ
durchgeführt werden, womit man $x(t)$ = $A^tx(0)$ erhält.
**PageRank Centrality** ist eine Variante der *Eigenvector Centrality*.
Bei diesem Wert wird jeder Knoten mit einer Punktzahl abhängig der
eingehenden Verbindungen ausgestattet. Die Verbindungen werden dann
abhängig vom ausgehenden Knoten gewichtet. Diese Kennzahl wird genutzt,
um bei *directed Graphs* einflussreiche Akteure auszumachen. Es war
einer der ersten Rangfolgen Algorithmen hinter der Google Search Engine
und wurde nach dem Entwickler und Gründer Larry Page benannt. [7]
Akteure mit einem hohen *PageRank Centrality* Wert können als besonders
einflussreich über ihre direkten Verbindungen hinaus interpretiert
werden.
#### Group-Level Metriken
**K-Cores** ist eine Drill-Down Möglichkeit im Netz. Jeder Knoten erhält
ein k-Wert abhängig von seinem degree. Die Knoten werden dann gruppiert
und gefiltert. Werte mit einem niedrigen k-Wert werden raus genommen.
Somit bleiben nur Werte mit einem hohen k-Wert übrig und es bilden sich
semi-autonome Gruppierungen innerhalb des Netzwerks. [8]
Der k-Wert bietet eine Möglichkeit für das Transparenzregister
verschiedene Zoom Stufen einzubauen, damit gerade bei hohen Mengen an
Daten man noch einen Überblick gewinnt.
**Distance/ shortest path** gibt an, wie viele "hops" benötigt werden,
um von einer Ecke zur anderen zu kommen. Der kürzeste Weg gibt die Route
an, mit der man mit so wenigen "hops" wie möglich durchs Netz kommt. Die
"hops" können auch gewichtet werden, um Distanzen berechnen zu können
oder die Menge an "hops". [8]
Dieser Wert sagt etwas zur Weite des Netzwerks aus. Im Zusammenhang mit
dem Projekt liefert diese Metrik eher unwichtigere Erkenntnisse.
Bahnbrechende Besonderheiten lassen sind im Umfeld von Unternehmen und
Personen Verflechtungen mit dem kürzesten Weg nicht herausfinden.
**Network Diameter** ist die kürzeste Verbindung der beiden am weitesten
entferntesten Ecken. Es zeigt Einblicke über den Weg, der genommen
werden muss, um alle Ecken des Netzes zu erreichen.
Die Aussagekraft dieser Metrik ist vergleichbar zu der des kürzesten
Weges und ist für das Transparenzregister vernachlässigbar. Diese
Metriken können bei zeitlichem Puffer als zusätzliches Feature berechnet
werden.
**Graph density** ist das Verhältnis der Anzahl vorhandener Ecken zu
möglichen Ecken. Die Dichte des Gesamtgraphen ist 1. Bei isolierten
Knoten wäre es 0.
Mit dieser Metriken erhält man einen Einblick über die Dichte und somit
die Stärke und Menge an Kanten im Graph - man kann so erkennen, welche
Gruppe an Unternehmen besser vernetzt sind als andere. Damit sollte
diese Kennzahl, zwar nicht als Top Priorität, aber im späteren Verlauf
des Projektes, mit eingebaut werden.
#### Network-Level Metriken
**Modularity** ist die Aggregation des Netzwerks in Untergruppen
abhängig der Stärke der Verbindungen. Die Untergruppen werden Module
oder *Communities* genannt. Die Modularität gibt die Stärke der
Verbindungen an. Eine hohe Modularität sagt aus, dass eine enge
Verbindung innerhalb der *Community* besteht.
Die meisten reinen Netzwerk-Level Metriken sind generell einfacher zu
berechnen, da sie oft einen konkreten Wert darstellen und nicht pro Ecke
kalkuliert sind. Somit können sie schnell eingebaut werden. Die
Aussagekraft der Modularität steigt mit der Anzahl an Netzwerken, die
man vergleichen möchte. Für das Projekt liegt hier Potential im
Vergleich von Ländern oder Regionen, oder der Vergleich einzelner
Unternehmens-Verflechtungen. Dieses Level ist aber zum aktuellen
Zeitpunkt noch nicht im Betrachtungsraum des Projektes.
**Connected Components** sind Untergruppen von Ecken-Paaren, welche
jeweils über Wege verbunden sind. Bei Graphen mit mehreren Connected
components gibt die Vereinigung dieser die Summe der Kanten des Graphes
wieder.
Mit diesem Wert können zwei Akteure oder Unternehmen gefunden werden,
die mit einander verbunden sind. Auch diese Metrik ist im Vergleich zu
den Element-Level Metriken erst mal der Priorität nach nachgestellt.
**Average Clustering Coefficient** gibt die Wahrscheinlichkeit an, dass
zwei verbundene Ecken einen Cluster bilden.
**Average Path length** ist die durchschnittliche Anzahl an
Verbindungen, um zwei Ecken zu verbinden.
Sowohl *Average Clustering Coefficient* als auch *Average Path length*
können dem Transparenzregister zusätzliche Analyseinformationen liefern,
sind aber wie die anderen Netzwerk-Level Kennzahlen eher in späteren
Iterationen und Funktionen einzubauen.
## Ein Social Graph für das Transparenzregister
In diesem Abschnitt wird der Einstieg in die Verflechtungsanalyse für
das Transparenzregister gegeben. Betrachtet wird, anhand der Ergebnisse
eines Jupyter Notebooks, wie die Metriken auf die ersten Daten des
Projektes angewendet werden können, welche Resultate sich vermuten
lassen und wie das weitere Vorgehen im Projekt sein wird.
### Aufbau des Social Graph/ SNA und Prämissen
Für die Umsetzung des Graphen wird die Programmiersprache Python in der
Version 3.11 mit der freien Bibliothek NetworkX genutzt. NetworkX ist
ein Framework zur Erstellung von Graphen und Netzwerken mit Python
Datenstrukturen. Es können Graphen, Digraphen und Multigraphen damit
erstellt werden. Des Weiteren kann eine NetworkX Struktur auch in
Matplotlib oder PyVis visualisiert werden. Zu den Feinheiten der
Visualisierung wird mehr in der Hausarbeit \"Datenvisualisierung\"
berichtet.
Da zum Entstehungszeitpunkt dieser Arbeit das Projekt noch nicht
vollständig mit Daten versorgt ist, wird an dieser Stelle mit
Mockup-Daten ein Graph erzeugt und gegen Ende dieser Arbeit eine
Vorschau mit den ersten Daten in einem Netzwerk gezeigt.
Für die Erstellung der Daten wird mit der Python Bibliothek Pandas aus
einer Excel Datei Mockup-Daten zu verschiedenen Automobilherstellern
geladen.
``` {.python language="Python" breaklines="true"}
# import pandas
import pandas as pd
# create dataframe based on the sample data
df_nodes = pd.read_csv('companies.csv', sep = ';')
# define shape based on the type
node_shape = {'Company': 'dot', 'Person': 'triangle'}
df_nodes['shape'] = df_nodes['type'].map(node_shape)
# define color based on branche
node_color = {
'Automobilhersteller': ' #729b79ff',
'Automobilzulieferer': '#475b63ff',
'Branche 3': '#f3e8eeff',
'Branche 4': '#bacdb0ff',
'Branche 5': '#2e2c2fff',
}
df_nodes['color'] = df_nodes['branche'].map(node_color)
# add information column that can be used for
the mouse over in the graph
df_nodes = df_nodes.fillna('')
df_nodes['title'] = df_nodes['label'] + '\n' +
df_nodes['branche']
# show first five entries of the dataframe
print(df_nodes.head())
```
Als Ergebnis erhält man ein Dataframe mit den verschiedenen
Automobilherstellern.
| **ID** | **Name** | **Typ** |
|--------|---------------------------|---------|
| 1 | Porsche Automobil Holding | Company |
| 2 | Volkswagen AG | Company |
| 3 | Volkswagen | Company |
*Tabelle 1: Tabelle der Automobilhersteller.*
Neben den Daten zu den Firmen wird noch eine zweite Tabelle
\"relations\" eingelesen, welche die Beziehungen zwischen den Akteuren
beinhaltet. Aus den beiden Tabellen wird ein harmonisiertes Dataframe
erstellt, aus welchem mit der Bibliothek NetworkX ein Graph erstellt
wird. Dafür wird die Methode from_pandas_edgelist genutzt - diese
erstellt aus einem Dataframe einen Graphen.
``` {.python language="Python" breaklines="true"}
# import networkx
import networkx as nx
# create edges from dataframe
graph = nx.from_pandas_edgelist(df_edges, source="from",
target="to", edge_attr="label")
```
Anschließend wird der erzeugte Graph mit PyVis visualisiert.
``` {.python language="Python" breaklines="true"}
# visualize using pyvis
from pyvis.network import Network
net = Network(
directed=False, neighborhood_highlight=True,
bgcolor="white", font_color="black")
# pass networkx graph to pyvis
net.from_nx(graph)
# set edge options
net.inherit_edge_colors(False)
net.set_edge_smooth("dynamic")
adj_list = net.get_adj_list()
# calculate and update size of the nodes
depending on their number of edges
for node_id, neighbors in adj_list.items():
df["edges"] = measure_vector.values()
size = 10 # len(neighbors)*5
next(
(node.update({"size": size}) for node in net.nodes if node["id"] == node_id),
None,)
# set the node distance and spring lenght using repulsion
net.repulsion(node_distance=150, spring_length=50)
# activate physics buttons to further explore the available solvers:
# barnesHut, forceAtlas2Based, repulsion, hierarchicalRepulsion
net.show_buttons(filter_=["physics"])
# save graph as HTML
net.save_graph("./metrics/test.html")
```
Das Resultat ist ein vollständiger Graph, welcher als HTML gespeichert
ist. Öffnet man den Graphen in einem Browser, kann man die einzelnen
Knoten auswählen und bekommt ein Highlighting der verknüpften Knoten.
Des Weiteren können Einstellungen an der Physik vorgenommen werden, um
die Ansicht des Graphen zu verändern, beispielsweise die Knoten
auseinander zu ziehen.
![Abbildung eines Graphens mit Mockdaten](abbildungen/Graph.PNG)
### Anwendung der Social Network Analysis (SNA)
Der Graph kann nun mit den verschiedenen Metriken bestückt werden,
sodass die SNA vollzogen werden kann.
#### Erstellen eines Graphen mit der Element-Level Metriken
Über das Framework NetworkX besteht die Möglichkeit, die Metriken direkt
berechnen zu lassen. Dazu wird die Methode *eigenvector_centrality* auf
den Graphen angewandt. Als Rückgabewert gibt es ein Dictionary mit den
Eigenvector Werten. Das Dictionary muss mit einem Faktor multipliziert
werden, um einen sichtlichen Unterschied bei den Größen der Ecken zu
erhalten. Über die adjacency list des Netzwerks kann auf die Ecken des
Netzwerks zugegriffen werden. Diese wird zu Nutze gemacht, um in einer
for-Schleife die Größe der Ecken neu zu setzen. Der Quellcodes sieht wie
folgt aus:
``` {.python language="Python" breaklines="true"}
adj_list = net.get_adj_list()
measure_vector = {}
if measure_type == "eigenvector":
measure_vector = nx.eigenvector_centrality(graph)
df["eigenvector"] = measure_vector.values()
if measure_type == "degree":
measure_vector = nx.degree_centrality(graph)
df["degree"] = measure_vector.values()
if measure_type == "betweeness":
measure_vector = nx.betweenness_centrality(graph)
df["betweeness"] = measure_vector.values()
if measure_type == "closeness":
measure_vector = nx.closeness_centrality(graph)
df["closeness"] = measure_vector.values()
# calculate and update size of the nodes depending on their number of edges
for node_id, neighbors in adj_list.items():
# df["edges"] = measure_vector.values()
if measure_type == "edges":
size = 10 # len(neighbors)*5
else:
size = measure_vector[node_id] * 50
next(
(
node.update({"size": size})
for node in net.nodes if node["id"] == node_id),
None,
)
```
Selbiges wird mit den Kennzahlen *degree_centrality,
betweennes_centrality* und *closeness_centrality* durchgeführt. Über die
*save_graph* Methode kann das Netzwerk dank des Pyvis Frameworks als
HTML gespeichert und das fertige Netz im Browser betrachtet werden.
![Netzwerk mit der Metrik eigenvector centrality.](abbildungen/Eigenvector.PNG)
Anhand der Veränderung des Netzwerks kann man sehen, wie die
Auswirkungen der Kennzahlen sind. Der Eigenvector misst, wie viele
Verbindungen von einem Knoten ausgehen und wie viele vom nächsten
Nachbarn aus weitergehen, bis das Netz durchdrungen ist. Daher sticht
vor allem die Porsche AG in diesem Beispiel deutlich hervor, da diese
viele direkt Verbindungen hat und mit dem Audi Knoten verbunden ist, der
wiederum die zweit meisten Verknüpfungen besitzt.
![Netzwerk mit der Metrik degree centrality.](abbildungen/Degree.PNG)
Die *Degree Centrality* zeigt hingegen ein etwas anderes Bild. Hier sind
die Hauptakteure noch einmal deutlich größer im Verhältnis zu den
Blättern des Netzes. Es wurden hierfür nur die direkten Verbindungen der
Knoten gezählt, deswegen ist "Hella" auch genau gleich groß wie "Seat"
und "Skoda Auto", da alle nur eine direkte Verbindung besitzen. Beim
vorherigen Graphen war "Seat" größer, da es mit einem einflussreichen
Knoten verbunden war und Hella nicht.
![Netzwerk mit der Metrik betweenness centrality.](abbildungen/Betweenness.PNG)
Im dritten Graphen mit der *betweenness centrality* sieht man, dass die
Blätter keinen Knoten mehr haben, da dieser nicht als \"Brücke\"
fungiert. In einem sehr großen Netzwerk könnte man solche Knoten
wegfallen lassen, um ein genaueren Überblick der wichtigen Akteure zu
erhalten.
![Netzwerk mit der Metrik closeness centrality.](abbildungen/Closeness.PNG)
Die letzte Metrik der Element-Level Metriken zeigt ein eher homogenes
Bild. Die Knoten sind generell größer, was daran liegt, dass es hier
keine unterschiedlichen Subnetze gibt.
#### Berechnung, Aufbau und Interpretationsmöglichkeiten der Group-Level Metriken
Für die Group-Level Metriken wurden zwei Metriken an den Testdaten
ausprobiert:
- Distance/shortest path
- Network Diameter
Da in den Testdaten keine klare Gruppierung festgestellt wurde, können
die Kennzahlen lediglich auf das gesamte Netzwerk angewendet werden. Die
ermittelten Ergebnisse zeigen, dass die Distanz einen Wert von 3 und der
sogenannte *Network Diameter* einen Wert von 4 aufweisen. Dies bedeutet,
dass die kürzeste Verbindung im Netzwerk lediglich 3 Verbindungen
erfordert, während die längste Verbindung maximal 4 Verbindungen
benötigt. Diese Werte deuten darauf hin, dass das Netzwerk insgesamt
recht effizient in Bezug auf die Verbindungen zwischen seinen
Knotenpunkten ist, wobei die meisten Verbindungen relativ kurz sind und
es nur wenige längere Verbindungen gibt, was bei einem so kleinen
Netzwerk nicht verwunderlich ist.
#### Berechnung, Aufbau und Interpretationsmöglichkeiten der Network-Level Metriken
In der Analyse der Netzwerkkennzahlen wurden ausschließlich die
Testdaten verwendet, um die durchschnittliche Pfadlänge zu berechnen.
Diese beträgt 2,3 Verbindungen. Dies bedeutet, dass es im Schnitt nur
etwa 2,3 Schritte braucht, um von einem Punkt zum anderen im Netzwerk zu
gelangen. Eine niedrige durchschnittliche Pfadlänge zeigt an, dass das
Netzwerk effizient und gut miteinander verbunden ist.
### Weitere Analysen
Neben den bereits besprochenen Analysemetriken aus der Graphentheorie
werden für das Transparenzregister weitere Elemente benötigt, um mehr
Informationen aus den Daten zu gewinnen. Dazu wird ein Ausgangsgraph
erstellt, welcher alle Akteure als uneingefärbte Knoten darstellt und
diese miteinander nach den Beziehungen aus der Datenbank über Kanten
verbindet. Die Beziehung in der Datenbank sind gerichtete Werte zwischen
zwei Akteuren. Es können mehrere Beziehungen in dieselbe Richtung gehen.
Beispielsweise kann zwischen Unternehmen A und Unternehmen B die
Verbindung ist_Teil_von, teilen_x_Mitarbeiter und beauftragt bestehen.
Damit diese in einem *Social Network* lesbar dargestellt werden können,
müssen die Kanten ungerichtet und unterschiedlicher Länge sein. Dafür
werden die Beziehungen ähnlich wie bei der *Degree Centrallity*
gemittelt und gewichtet. Die Länge der Kanten ist disproportional
abhängig von der Gewichtung und liegt zwischen 1 und 10. Eine hohe
Gewichtung resultiert also in einer Kantenlänge nahe 1, eine niedrige
nahe 10. Damit wird eine Federkraft generiert, die Knoten mit starken
Verbindungen anzieht. Des Weiteren ist eine Mindestdistanz zwischen
Knoten festgelegt, damit sich beim Generieren des Netzes keine Knoten
überlappen. Somit ergibt sich ein Ausgangsgraph, welcher über die
folgenden Anpassungen verändert werden kann, um weitere Erkenntnisse zu
gewinnen.
Die erste Option besteht im Verändern der Größe der einzelnen Knoten.
Analog zur Größenveränderung bei den Metriken aus der Graphentheorie ist
es bei diesem Graphen machbar, eine der folgenden Metriken aus dem
Transparenzregister Projekt als Ausgangspunkt für den Radius der Knoten
zu wählen und/oder zu filtern:
EBIT, Umsatz, Aktienkurs, Mitarbeiteranzahl und die Wachstumsrate
Personen werden bei dieser Betrachtung weiterhin mit der Einheitsgröße 1
dargestellt oder raus gefiltert. Der Filter kann mit einem Schwellenwert
versehen werden, um nur Unternehmen in bestimmter Größenordnung zu
vergleichen. Damit wird dem Betrachter ersichtlich, welche Unternehmen
beispielsweise einen hohen Umsatz erzielen im Vergleich zum Rest.
Neben dieser Funktionalität können die Knoten nach den folgenden
Kategorien eingefärbt oder gefiltert werden:
Branche, Heimatland, Mutterkonzern, Wachstumsrate, und
Positiver/Negativer Berichtserstattung
In Kombination mit dem vorherigen Ansatz sind Analysen hinsichtlich der
Unternehmen mit den größten Umsätze nach Ländern denkbar.
Abgesehen von diesen eher visuellen Betrachtungen besteht die letzte
Konfiguration im Verändern der Kanten. Darüber hinaus kann das Netzwerk
nach den einzelnen Beziehungstypen aufgebaut werden.
Weiterhin ist eine Variante auswählbar, bei der die Formel der
Federspannung über eine Gewichtung der Akteure erweitert wird.
Verbindungen zu Wirtschaftsprüfern werden zum Beispiel geringer bewertet
als geteilte Vorstandspersonen. Somit wird der Fokus auf
Unternehmensgruppen gelegt, bei denen die priorisierten Merkmale
deutlich herausstechen.
Die letzte Selektion ermöglicht es, Verbindungen zur selben Holding oder
Mutterkonzern heraus zu nehmen. Nach diesem Filter sind nur noch externe
Verbindungen ersichtlich und der Graph wird deutlich übersichtlicher. Es
wird einfacher erkennbar, welche Unternehmen mit welchen Akteuren
zusammenarbeiten.
Diese eigenen Analyseoptionen können nach Belieben miteinander
kombiniert werden, um verschiedene Sichtweise auf des Netzgefüge zu
erhalten.
### Perspektive auf einen Graphen mit Unternehmens und Personen Daten
Gegen ende dieser Arbeit ist hier noch ein Graph mit einem Auszug an
realen Daten aus dem Transparenzregister zu sehen. Bereits ohne die
Verwendung von Metriken sind Gruppenbildungen deutlich zu erkennen.
Insgesamt wurden 1000 Verflechtungen hier dargestellt. Als Knoten
dienten Personen und Firmen. Die Unternehmen wurden grün dargestellt,
sind aber aufgrund der Größe eher schlecht von den Personen mit blauen
Punkten zu unterscheiden.
![Graph mit Unternehmens- und Personendaten](abbildungen/Transparenzregister_Graph.PNG)
### Handlungsempfehlung
Für das Projekt Transparenzregister leitet sich aus dieser Arbeit
folgende Empfehlung für die SNA ab:
Für die Umsetzung der SNA eignet sich die Bibliothek NetworkX sehr gut,
da sie anhand eines Pandas Dataframe bereits ein gutes Netzwerk aufbauen
kann. Des Weiteren können eine Vielzahl an Metriken effizient mit dem
Netzwerk berechnet werden und benötigen keine extra Implementierung von
Algorithmen. Zusätzlich wird empfohlen, das Framework PyVis zur
Visualisierung zu nutzen, da es das Netzwerk gut aufbereitet und zur
Laufzeit Anpassungen an der Grafik zulässt.
Bezogen auf die Auswahl der Metriken wird auf den Nutzen vor allem von
Element-Level Metriken verwiesen. Spezifisch sollten folgende Metriken
betrachtet werden:
- Degree Centrality
- Betweenes Centrality
- Closeness Centrality
- Eigenvector Centrality
Für die Group-Level Metriken werden folgende Metriken angeraten:
- Distance/shortest path
- Network Diameter
Zuletzt werden für die Netzwerk-Level Metriken die nachstehenden
Metriken befürwortet:
- Average Path length
- Modularity
Natürlich können auch die anderen Metriken genutzt werden. Die hier
vorgeschlagene Auswahl ergeben nach der Einschätzung und Erläuterungen
dieser Arbeit den größtmöglichen Nutzen für das Projekt und sollten aus
den genannten Gründen priorisiert werden.
## Zusammenfassung
In diesem Abschnitt wird die Arbeit rückwirkend kritisch betrachtet,
zusammengefasst und ein Fazit gezogen. Zum Schluss wird ein kleiner
Ausblick auf die nächsten Fragestellungen gegeben, die es im genannten
Projekt zu beantworten gilt.
### Kritische Reflexion
Zielsetzung war es, im Rahmen dieser Arbeit sich mit der
Verflechtungsanalyse auseinanderzusetzen. Die grundlegenden Metriken und
Vorgehensweisen wurden aufgezeigt, eine intensivere Betrachtung der
Metriken und Anwendung auf größere Daten sollte in einer erneuten
Betrachtung als Ziel gesetzt werden. Mit einem Ausschnitt an realen
Daten sind bessere und deutlichere Einschätzung der Metriken und Treffen
von Aussagen denkbar. Leider bestand der Zugriff auf echte Daten erst
gegen Ende der Bearbeitungszeit und konnte deshalb nicht für das Testen
der Analyse genutzt werden.
### Fazit
In der vorliegenden Untersuchung wird die Bedeutung und Anwendung einer
Verflechtungsanalyse verdeutlicht. Die Verflechtungsanalyse findet in
Rahmen eines Transparenzregister Projektes für
Unternehmensverflechtungen statt und soll im späteren Verlauf des
Projektes angewendet werden. Diese Arbeit dient als Grundlage für das
Projekt. Deshalb wurde aufgezeigt, auf welcher Grundlage ein Netzwerk an
Verflechtungen basiert und wie es mithilfe von NetworkX und PyVis
aufgebaut werden kann.
Zusätzliche wurde ein Einblick in die Social Network Analyse gegeben und
eine größere Anzahl an Metriken vorgestellt. Die Metriken wurden anhand
des Projektes bewertet und eingeordnet, sodass mittels dieser
Einschätzung eine Auswahl an Metriken stattfinden kann.
Im Hauptteil des Werks wurde dann auf die konkrete Umsetzung mit Python
eingegangen. Es wurde gezeigt, wie ein Netzwerk mit Beispieldaten
aufgebaut wird und wie dazu die benötigten Metriken angewandt werden.
Ein weiterer Aspekt bestand in der Veranschaulichung, wie die Metriken
in das Netz eingebaut werden können, um eine visuelle Analyse
durchzuführen.
Neben den Metriken wurde auch auf weitere Analysemöglichkeiten durch
Farbgestaltung oder Größenveränderung der Knoten eingegangen. Zu guter
Letzt gab es einen Ausblick auf einen Prototypen Graph mit realen
Unternehmensdaten, aber ohne weitere Analysen.
### Ausblick
Der nächste logische Schritt besteht in der konkreten Ausarbeitung der
Verflechtungsanalyse mit realen Daten. Dort gilt es zu überprüfen,
welche Erkenntnisse mithilfe der SNA auf den Daten gewonnen werden
können. Liefern die vorgeschlagenen Metriken die zu erwartenden
Resultate? Sind alle Metriken überhaupt mit den Daten anwendbar? Diese
Fragestellungen werden im Rahmen des Projektes angegangen.
### Literaturverzeichnis
- **[1]** S. Iwanowski und R. Lang, „Graphentheorie,“ ger, in Diskrete Mathematik mit
Grundlagen, Wiesbaden: Springer Fachmedien Wiesbaden, 2020, S. 257320, isbn:
3658327596.
- **[2]** P. Hartmann, „Graphentheorie,“ ger, in Mathematik für Informatiker, Wiesbaden:
Springer Fachmedien Wiesbaden, 2020, S. 269300, isbn: 9783658265236.
- **[3]** I. Pitas, Graph-Based Social Media Analysis. CRC Press, 2016, isbn: 9780429162602.
- **[4]** X. Fu, Social Network Analysis. CRC Press, 2017, isbn: 9781315369594.
- **[5]** E. Yüksel, https://medium.com/@emreeyukseel/a-brief-introduction-to-social-network-analysis-2d13427f5189.
- **[6]** M. Newmans, Networks An introduction. Oxford University Press Inc., 2010, isbn:
9780199206650.
- **[7]** A. Disney,https://cambridge-intelligence.com/keylines-faqs-social-network-analysis/.
- **[8]** C. Intelligence, https://cambridge-intelligence.com/social-network-analysis/.

View File

@ -21,6 +21,7 @@ Diese sind, um Industriestandards zu entsprechen, auf Englisch gehalten.
Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops
Ergebnisse/Zwischenbericht_und_Praesentation/TrNo/Ausarbeitung.md Ergebnisse/Zwischenbericht_und_Praesentation/TrNo/Ausarbeitung.md
seminararbeiten/Datenspeicherung/00_Datenspeicherung seminararbeiten/Datenspeicherung/00_Datenspeicherung
Ergebnisse/Zwischenbericht_und_Praesentation/TiRo/verflechtungsanalyse.md
.. toctree:: .. toctree::
@ -28,12 +29,15 @@ Diese sind, um Industriestandards zu entsprechen, auf Englisch gehalten.
:caption: Abschlussberichte :caption: Abschlussberichte
:numbered: :numbered:
Ergebnisse/Abschlussbericht_und_Praesentation/TiRo/S2.md
Ergebnisse/Abschlussbericht_und_Praesentation/TiRo/S3.md
Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/05-DEV-OPS Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/05-DEV-OPS
Ergebnisse/Abschlussbericht_und_Praesentation/TrNo/S4-2.md Ergebnisse/Abschlussbericht_und_Praesentation/TrNo/S4-2.md
Ergebnisse/Abschlussbericht_und_Praesentation/TrNo/S4-3-1.md Ergebnisse/Abschlussbericht_und_Praesentation/TrNo/S4-3-1.md
Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/4-4-2-database-generator Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/4-4-2-database-generator
Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/04-data-visualisation-container.md Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/04-data-visualisation-container.md
Ergebnisse/Abschlussbericht_und_Praesentation/TiRo/S4-5-2.md
.. toctree:: .. toctree::
:glob: :glob:

310
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]] [[package]]
name = "accelerate" name = "accelerate"
@ -1641,22 +1641,23 @@ files = [
[[package]] [[package]]
name = "dnspython" name = "dnspython"
version = "2.4.2" version = "2.5.0"
description = "DNS toolkit" description = "DNS toolkit"
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = ">=3.8"
files = [ files = [
{file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, {file = "dnspython-2.5.0-py3-none-any.whl", hash = "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6"},
{file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, {file = "dnspython-2.5.0.tar.gz", hash = "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15"},
] ]
[package.extras] [package.extras]
dnssec = ["cryptography (>=2.6,<42.0)"] dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=5.0.3)", "mypy (>=1.0.1)", "pylint (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "sphinx (>=7.0.0)", "twine (>=4.0.0)", "wheel (>=0.41.0)"]
doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"] dnssec = ["cryptography (>=41)"]
doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.25.1)"]
doq = ["aioquic (>=0.9.20)"] doq = ["aioquic (>=0.9.20)"]
idna = ["idna (>=2.1,<4.0)"] idna = ["idna (>=2.1)"]
trio = ["trio (>=0.14,<0.23)"] trio = ["trio (>=0.14)"]
wmi = ["wmi (>=1.5.1,<2.0.0)"] wmi = ["wmi (>=1.5.1)"]
[[package]] [[package]]
name = "docutils" name = "docutils"
@ -2427,13 +2428,13 @@ files = [
[[package]] [[package]]
name = "jsonschema" name = "jsonschema"
version = "4.21.0" version = "4.21.1"
description = "An implementation of JSON Schema validation for Python" description = "An implementation of JSON Schema validation for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "jsonschema-4.21.0-py3-none-any.whl", hash = "sha256:70a09719d375c0a2874571b363c8a24be7df8071b80c9aa76bc4551e7297c63c"}, {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"},
{file = "jsonschema-4.21.0.tar.gz", hash = "sha256:3ba18e27f7491ea4a1b22edce00fb820eec968d397feb3f9cb61d5894bb38167"}, {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"},
] ]
[package.dependencies] [package.dependencies]
@ -2939,6 +2940,7 @@ files = [
{file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"},
{file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"},
{file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"},
{file = "lxml-4.9.4-cp312-cp312-win32.whl", hash = "sha256:266f655d1baff9c47b52f529b5f6bec33f66042f65f7c56adde3fcf2ed62ae8b"},
{file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"},
{file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"},
{file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"},
@ -3052,71 +3054,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]] [[package]]
name = "markupsafe" name = "markupsafe"
version = "2.1.3" version = "2.1.4"
description = "Safely add untrusted strings to HTML/XML markup." description = "Safely add untrusted strings to HTML/XML markup."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"},
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"},
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"},
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"},
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"},
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"},
{file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"},
{file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"},
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"},
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"},
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"},
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"},
{file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"},
{file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"},
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"},
] ]
[[package]] [[package]]
@ -3794,13 +3796,13 @@ attrs = ">=19.2.0"
[[package]] [[package]]
name = "overrides" name = "overrides"
version = "7.4.0" version = "7.6.0"
description = "A decorator to automatically detect mismatch when overriding a method." description = "A decorator to automatically detect mismatch when overriding a method."
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "overrides-7.4.0-py3-none-any.whl", hash = "sha256:3ad24583f86d6d7a49049695efe9933e67ba62f0c7625d53c59fa832ce4b8b7d"}, {file = "overrides-7.6.0-py3-none-any.whl", hash = "sha256:c36e6635519ea9c5b043b65c36d4b886aee8bd45b7d4681d2a6df0898df4b654"},
{file = "overrides-7.4.0.tar.gz", hash = "sha256:9502a3cca51f4fac40b5feca985b6703a5c1f6ad815588a7ca9e285b9dca6757"}, {file = "overrides-7.6.0.tar.gz", hash = "sha256:01e15bbbf15b766f0675c275baa1878bd1c7dc9bc7b9ee13e677cdba93dc1bd9"},
] ]
[[package]] [[package]]
@ -3913,6 +3915,20 @@ files = [
numpy = {version = ">=1.26.0", markers = "python_version < \"3.13\""} numpy = {version = ">=1.26.0", markers = "python_version < \"3.13\""}
types-pytz = ">=2022.1.1" types-pytz = ">=2022.1.1"
[[package]]
name = "pandoc"
version = "2.3"
description = "Pandoc Documents for Python"
optional = false
python-versions = "*"
files = [
{file = "pandoc-2.3.tar.gz", hash = "sha256:e772c2c6d871146894579828dbaf1efd538eb64fc7e71d4a6b3a11a18baef90d"},
]
[package.dependencies]
plumbum = "*"
ply = "*"
[[package]] [[package]]
name = "pandocfilters" name = "pandocfilters"
version = "1.5.1" version = "1.5.1"
@ -4123,13 +4139,13 @@ test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"
[[package]] [[package]]
name = "pip-licenses" name = "pip-licenses"
version = "4.3.3" version = "4.3.4"
description = "Dump the software license list of Python packages installed with pip." description = "Dump the software license list of Python packages installed with pip."
optional = false optional = false
python-versions = "~=3.8" python-versions = "~=3.8"
files = [ files = [
{file = "pip-licenses-4.3.3.tar.gz", hash = "sha256:d14447094135eb5e43e4d9e1e3bcdb17a05751a9199df2d07f043a542c241c7a"}, {file = "pip-licenses-4.3.4.tar.gz", hash = "sha256:9c6c9c3252b976d08735bdffb0eb4c5eaa50dfd46f5e075532c0248ffe94fed1"},
{file = "pip_licenses-4.3.3-py3-none-any.whl", hash = "sha256:1b697cace3149d7d380307bb1f1e0505f0db98f25fada64d32b7e6240f37f72c"}, {file = "pip_licenses-4.3.4-py3-none-any.whl", hash = "sha256:85706ec30781076eb611fed3934f27a1f18437d3211f747567cd3c4e943fce1b"},
] ]
[package.dependencies] [package.dependencies]
@ -4202,6 +4218,36 @@ files = [
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"] testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "plumbum"
version = "1.8.2"
description = "Plumbum: shell combinators library"
optional = false
python-versions = ">=3.6"
files = [
{file = "plumbum-1.8.2-py3-none-any.whl", hash = "sha256:3ad9e5f56c6ec98f6f7988f7ea8b52159662ea9e915868d369dbccbfca0e367e"},
{file = "plumbum-1.8.2.tar.gz", hash = "sha256:9e6dc032f4af952665f32f3206567bc23b7858b1413611afe603a3f8ad9bfd75"},
]
[package.dependencies]
pywin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""}
[package.extras]
dev = ["paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"]
docs = ["sphinx (>=4.0.0)", "sphinx-rtd-theme (>=1.0.0)"]
ssh = ["paramiko"]
[[package]]
name = "ply"
version = "3.11"
description = "Python Lex & Yacc"
optional = false
python-versions = "*"
files = [
{file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
{file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"},
]
[[package]] [[package]]
name = "pockets" name = "pockets"
version = "0.9.1" version = "0.9.1"
@ -4385,27 +4431,27 @@ files = [
[[package]] [[package]]
name = "psutil" name = "psutil"
version = "5.9.7" version = "5.9.8"
description = "Cross-platform lib for process and system monitoring in Python." description = "Cross-platform lib for process and system monitoring in Python."
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [ files = [
{file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"}, {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
{file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"}, {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
{file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"}, {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
{file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"}, {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
{file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"}, {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
{file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"}, {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
{file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"}, {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
{file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"}, {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
{file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"}, {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
{file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"}, {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
{file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"}, {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
{file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"}, {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
{file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"}, {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
{file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"}, {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
{file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"}, {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
{file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"}, {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
] ]
[package.extras] [package.extras]
@ -5119,7 +5165,6 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@ -5127,15 +5172,8 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@ -5152,7 +5190,6 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@ -5160,7 +5197,6 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@ -5962,45 +5998,45 @@ tests = ["black (>=23.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.3)", "numpydoc (
[[package]] [[package]]
name = "scipy" name = "scipy"
version = "1.11.4" version = "1.12.0"
description = "Fundamental algorithms for scientific computing in Python" description = "Fundamental algorithms for scientific computing in Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710"}, {file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"},
{file = "scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41"}, {file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"},
{file = "scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4"}, {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"},
{file = "scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56"}, {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"},
{file = "scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446"}, {file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"},
{file = "scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3"}, {file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"},
{file = "scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be"}, {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"},
{file = "scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8"}, {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"},
{file = "scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c"}, {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"},
{file = "scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff"}, {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"},
{file = "scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993"}, {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"},
{file = "scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd"}, {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"},
{file = "scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6"}, {file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"},
{file = "scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d"}, {file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"},
{file = "scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4"}, {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"},
{file = "scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79"}, {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"},
{file = "scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660"}, {file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"},
{file = "scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97"}, {file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"},
{file = "scipy-1.11.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e619aba2df228a9b34718efb023966da781e89dd3d21637b27f2e54db0410d7"}, {file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"},
{file = "scipy-1.11.4-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:f3cd9e7b3c2c1ec26364856f9fbe78695fe631150f94cd1c22228456404cf1ec"}, {file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"},
{file = "scipy-1.11.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d10e45a6c50211fe256da61a11c34927c68f277e03138777bdebedd933712fea"}, {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"},
{file = "scipy-1.11.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91af76a68eeae0064887a48e25c4e616fa519fa0d38602eda7e0f97d65d57937"}, {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"},
{file = "scipy-1.11.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6df1468153a31cf55ed5ed39647279beb9cfb5d3f84369453b49e4b8502394fd"}, {file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"},
{file = "scipy-1.11.4-cp39-cp39-win_amd64.whl", hash = "sha256:ee410e6de8f88fd5cf6eadd73c135020bfbbbdfcd0f6162c36a7638a1ea8cc65"}, {file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"},
{file = "scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa"}, {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"},
] ]
[package.dependencies] [package.dependencies]
numpy = ">=1.21.6,<1.28.0" numpy = ">=1.22.4,<1.29.0"
[package.extras] [package.extras]
dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"]
doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"]
test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
[[package]] [[package]]
name = "seaborn" name = "seaborn"
@ -6719,7 +6755,7 @@ files = [
] ]
[package.dependencies] [package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"}
mypy = {version = ">=0.910", optional = true, markers = "python_version >= \"3\" and extra == \"mypy\""} mypy = {version = ">=0.910", optional = true, markers = "python_version >= \"3\" and extra == \"mypy\""}
sqlalchemy2-stubs = {version = "*", optional = true, markers = "extra == \"mypy\""} sqlalchemy2-stubs = {version = "*", optional = true, markers = "extra == \"mypy\""}
@ -7807,4 +7843,4 @@ web-server = ["dash", "dash-auth", "dash-bootstrap-components", "dash-daq", "net
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.11,<3.13" python-versions = ">=3.11,<3.13"
content-hash = "d6be01698b1a28f1f259bf849d1dded95dfca23c6c86933b9becc5f4518a46f6" content-hash = "ec642e35625eea4833635ed0d7083623ff04730f997b82cb212b2e8a23649265"

View File

@ -62,7 +62,7 @@ frozendict = "^2.3.9"
html5lib = "^1.1" html5lib = "^1.1"
loguru = "^0.7.0" loguru = "^0.7.0"
networkx = "^3.2.1" networkx = "^3.2.1"
pandas = "^2.1.4" pandas = "2.1.4"
pgeocode = "^0.4.1" pgeocode = "^0.4.1"
psycopg2-binary = "^2.9.7" psycopg2-binary = "^2.9.7"
pymongo = "^4.6.0" pymongo = "^4.6.0"
@ -101,6 +101,7 @@ seaborn = "^0.13.0"
jupyter = "^1.0.0" jupyter = "^1.0.0"
myst-parser = "^1.0.0" myst-parser = "^1.0.0"
nbsphinx = "^0.9.2" nbsphinx = "^0.9.2"
pandoc = "^2.3"
sphinx = "*" sphinx = "*"
sphinx-copybutton = "^0.5.2" sphinx-copybutton = "^0.5.2"
sphinx-git = "^11.0.0" sphinx-git = "^11.0.0"