Netzwerke in R mit SigmaNet

Von der Tabelle zur Netzwerk-Visualisierung


Netzwerke sind überall in unserer Umgebung:

  • Lieferketten

  • N-Gramme in Text-Mining-Projekten

  • Ursache-Wirkungs-Ketten

  • Organigramme von hirarchischen Strukturen

  • Persönliche und geschäftliche Kontakte

Um nur einige zu nennen.


Eine interessante Anwendung von Netzwerken sind Mitbewerber-Analysen. Anhand einer stark vereinfachten Mitbewerber-Analyse wollen wir im Folgenden das Vorgehen erklären, um von einer Tabelle zu einer Netzwerkvisualisierung zu gelangen.


Für die Visualisierung nutzen wir das htmlwidget SigmaNet welches von Ian Kloo entwickelt wurde. Es baut auf sigma.js auf. Dadurch ist es in der Lage große Netzwerke interaktiv darzustellen. Zudem wird iGraph verwendet.


Die Quelle: Liste der größten deutschen Unternehmen (2017)


Startpunkt der Analyse ist die Liste der größten Unternehmen in Deutschland nach Forbes 2000, welche auf Wikipedia zu finden ist. Wir haben diese Tabelle gewählt, da sie neben den Kennzahlen der Unternehmen auch Branche und Hauptsitz beinhaltet. Wir wollen uns ansehen, welche Zusammenhänge zwischen den Unternehmen bezüglich Branche und Unternehmenssitz existieren.


Zunächst laden wir die benötigten Pakete:


# Laden notwendiger Pakete
library(rvest)    # Datenbezug von Wikipedia
library(tidyr)
library(dplyr)
library(igraph)   # Netzwerk generieren
library(sigmaNet) # Netzwerk visualisieren

Daten von Wikipedia importieren

Wir importieren die Tabelle mittels rvest

# Laden der Liste von Wikipedia
url <- "https://de.wikipedia.org/wiki/Liste_der_gr%C3%B6%C3%9Ften_Unternehmen_in_Deutschland_(Forbes_2000)"

ForbesRoh <- read_html(url) %>% html_nodes("table") %>% `[[`(1) %>% html_table()
str(ForbesRoh)

Von Wikipedia bezogener Dataframe

Data Cleaning

Die Daten sind noch nicht in der gewünschten Form. Die Spalten

  • Umsatz,

  • Gewinn,

  • Aktiva und

  • Marktwert

liegen nicht in numerischer Form vor, sondern als String. Die Ursache ist das Dezimaltrennzeichen (, statt .) Auch bei den Mitarbeiter-Zahlen scheint etwas nicht zu stimmen. Hier wird das Tausender-Trennzeichen (.) als Dezimaltrennung erkannt. Wir lösen das Problem mittels gsub-Befehlen.


## Daten vorbereiten
# 1. Als Strings übernommene Zahlen in numerische Werte umwandeln

ForbesDF <- ForbesRoh
ForbesDF[c(6:9)] <- lapply(ForbesDF[c(6:9)], 
 FUN = function(x){
 as.numeric(gsub(pattern = ",", 
 replacement = ".", 
 x= x, 
 fixed = TRUE))})
 
ForbesDF$Mitarbeiter <- as.numeric(gsub(pattern = ".", 
 replacement = "", 
 x= as.character(ForbesDF$Mitarbeiter), 
 fixed = TRUE))
str(ForbesDF)

Struktur des Dataframes nach dem Data Cleaning

summary(ForbesDF)


Deskriptive Statistik zum Dataframe


Netzwerk-Vorüberlegungen


Unser Ziel ist eine Darstellung, auf der Unternehmen mit Branchen und Hauptsitzen verknüpft sind. Unser Hauptaugenmerk gilt der Branche. Es wäre also gut diese zusätzlich hervorzuheben. Zudem wollen wir den Umsatz der Unternehmen in der Grafik ebenfalls unterbringen.


Für das Netzwerk bedeutet dies:

  • Kanten sind Verbindungen zwischen Unternehmen und Branchen bzw. Städten

  • Knoten können Branchen, Städte und Unternehmen sein

  • Knotenattribute sollen der Umsatz, der Unternehmensname und die Branche sein


Wir werden zur Netzwerk-Erzeugung iGraph verwenden. Die Funktion graph_from_data_frame() benötigt einen Dataframe mit Kanten-Informationen. Optional kann ein Dataframe mit Knoten-Informationen angereichert werden.


Knoten- Informationen als Dataframe vorbereiten


Der Knoten-Dataframe beinhaltet eine ID und ein Label (die Unternehmens-/ Branchen-/ und Stadt-Namen). Als zusätzliche Informationen sollen die Aktiva, sowie Gewinn, Umsatz und Anzahl der Mitarbeiter je Unternehmen hinterlegt werden.


Da wir Unternehmen mit Städten und Branchen verknüpfen wollen, werden wir zunächst einen Vektor mit allen Unternehmen, Branchen und Städten anlegen. Diesen reichern wir um eine ID an. Hierbei wird der Vektor zudem in einen Dataframe gewandelt. Den Dataframe verknüpfen wir mittels merge mit den gewünschten zusätzlichen Knoten-Informationen.


Im letzten Schritt ersetzen wir die nicht vorhandenen Werte durch 0.01. Dies geschieht, um die Knoten der Städte und Branchen später darzustellen.


# 2. Knoten-Datensatz vorbereiten
# Knoten sollen die Merkmale aufweisen
# * Name inkl. Branche und Hauptsitz

# * Mitarbeiter
# * Marktwert
# * Aktiva
# * Gewinn
# * Umsatz
ForbesListe <- ForbesDF
ForbesListe[] <- lapply(ForbesDF[c(11, 5, 4)], as.character)
ForbesListe <- unique(unlist(ForbesListe))

ForbesKnoten <- data.frame(id = 1:length(ForbesListe))
ForbesKnoten$label <- ForbesListe
ForbesKnoten <- base::merge(x = ForbesKnoten, y = ForbesDF[4:11], by.x = "label", by.y = "Name", all.x = TRUE)
ForbesKnoten <- ForbesKnoten[c(2, 1, 3:ncol(ForbesKnoten))]
ForbesKnoten[is.na(ForbesKnoten)] <- 0.01 # Min innerhalb des DF ist 0.6

Kanten-Datensatz erstellen


Im Dataframe für die Kanten müssen die Spalten “to” und “from” an erster und zweiter Stelle stehen. Diese Spalten beinhalten die ID’s der zu verknüpfenden Knoten. Zudem können weitere Informationen übergeben werden. Wir wollen ein Attribut “Type” hinzufügen. Dieses gibt an, ob die Kante zwischen einer Stadt und einem Unternehmen (“Hauptsitz”) oder einer Branche und einem Unternehmen (“Branche”) besteht.


# 3. Unternehmen mit Branchen und Städten vernüpfen
# Branche und Name aus ForbesDF herausziehen
BranchenNamen <- ForbesDF[c("Branche", "Name")]
names(BranchenNamen) <- c("to", "from")
# Namen und Branchen durch ID ersetzen
BranchenKanten<- BranchenNamen
BranchenKanten[] <- ForbesKnoten$id[match(unlist(BranchenNamen), ForbesKnoten$label)]
BranchenKanten$Typ <- "Branche"
 
# Branche und Name aus ForbesDF herausziehen
StadtNamen <- ForbesDF[c("Hauptsitz", "Name")]
names(StadtNamen) <- c("from", "to")
# Namen und Branchen durch ID ersetzen
StadtKanten<- StadtNamen
StadtKanten[] <- ForbesKnoten$id[match(unlist(StadtNamen), ForbesKnoten$label)]
StadtKanten$Typ <- "Hauptsitz"

# Einen Dataframe erstellen mit KantenInfos
Kanten <- rbind(BranchenKanten, StadtKanten)

Netzwerk erzeugen


Nachdem wir unsere Dataframes vorbereitet haben, können wir nun das Netzwerk erstellen. Dazu nutzen wir graph_from_data_frame() aus dem Paket iGraph. Dem Parameter d wird der Kanten-Dataframe übergeben. Der Parameter vertices erhält den Kanten-Dataframe.


GraphHauptsitzBranche <- graph_from_data_frame(d = Kanten, 
 vertices = ForbesKnoten)

Wir können uns das Netzwerk bereits jetzt mittels plot-Befehl ansehen:


plot(GraphHauptsitzBranche)
Netzwerk mit IGraph


HTML-Netzwerk mit SigmaNet


Das ungeschmückte iGraph-Netzwerk ist unübersichtlich und bietet keine Interaktivität, wie beispielsweise das Einblenden der benachbarten Knoten. Dazu verwenden wir SigmaNet. Ohne weitere Grafik-Optionen zu verwenden erhalten wir folgendes Netzwerk:


 sigmaFromIgraph(GraphHauptsitzBranche)

Rohe Visualisierung mit SigmaNet

Voreingestellt sind die Interaktivitäten:

  • Label on Hover

  • Nachbarschaft bei Klick auf 1 Knoten

  • Zoomen über Doppelklick und Mausrad

Wir können dem Netzwerk auch ein bestimmtes Layout zuweisen. Standardmäßig ist layout_nicely() eingestellt. Es sucht ein optimales Layout heraus.

Wir testen nun das kreisförmige Layout:


 # SigmaNet als Kreis
 Layout <-layout_in_circle(GraphHauptsitzBranche)
 NetzwerkForbes <- sigmaFromIgraph(GraphHauptsitzBranche, Layout)
 NetzwerkForbes

Netzwerk mit SigmaNet im kreisförmigen Layout

Netzwerk mit Grafik-Optionen


Nachdem wir uns für ein Layout (layout_with_kk()) entschieden haben, werden nun die Knoten mit

  • Name als Label

  • Umsatz als Knotengröße und

  • Branche als Knotenfarbe

dargestellt.


Den Kanten weisen wir den Typ (Branche oder Hauptsitz) als Farbattribut zu.


GraphHauptsitzBranche <- graph_from_data_frame(d = Kanten, 
 vertices = ForbesKnoten)
 
Layout <- layout_with_kk(GraphHauptsitzBranche)
NetzwerkBranchenHauptsitz <- sigmaFromIgraph(GraphHauptsitzBranche, layout = Layout)
#  Farbe = Branche, Verknüpfung = Stadt, Größe Knoten = Umsatz, Kantenfarbe = Typ
NodeSize <- ForbesKnoten$`Umsatz(Mrd. $)`
NetzwerkBranchenHauptsitzFormatiert <- NetzwerkBranchenHauptsitz %>% 
    addNodeColors(colorAttr = "Branche", colorPal = "Set2") %>% # Kantenfarbe = Branche
  addNodeLabels(labelAttr = "label") %>% # Namen der Unternehmen
  addNodeSize(sizeVector = NodeSize, minSize = 1, maxSize = 10) %>% # Knotengröße = Umsatz
  addEdgeColors(colorAttr = "Typ", colorPal = "Set3")
 
 NetzwerkBranchenHauptsitzFormatiert  


Abbildung 4: SigmaNet-Netzwerk in finaler Version

Zum Schluss speichern wir das Netzwerk und die zugehörigen Daten.


# Netzwerk als HTML speichern
saveSigma(NetzwerkBranchenHauptsitzFormatiert, "NetzwerkForbes2000.html")
# Daten speichern
save(list = ls(), file = "NetzwerkWorkSpace.RData")

Fazit

Netzwerke zu visualisieren ist eine großartige Möglichkeit um Verbindungen zwischen verschiedenen Objekten zu veranschaulichen. Mit SigmaNet können dabei besonders große Netzwerke visualisiert und interaktiv gestaltet werden. In Verbindung mit iGraph stehen vielfältige Layout-Optionen zur Verfügung. Da die Netzwerke als HTML speicherbar sind, können auch Personen ohne Programmierkenntnisse das Netzwerk für sich entdecken.