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 hierarchischen 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:

Daten importieren von Wikipedia

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)
## 'data.frame':    51 obs. of  11 variables:
##  $ Rang             : num  1 2 2 4 5 6 7 8 9 10 ...
##  $ Forbes2000       : num  21 28 28 50 51 77 89 93 120 178 ...
##  $ 2018             : chr  "22. " "29. " "16. " "51. " ...
##  $ Name             : chr  "Allianz SE" "Daimler AG" "Volkswagen AG" "Siemens AG" ...
##  $ Hauptsitz        : chr  "München" "Stuttgart" "Wolfsburg" "München" ...
##  $ Umsatz(Mrd. $)   : chr  "115,7" "169,5" "240,3" "88,4" ...
##  $ Gewinn(Mrd. $)   : chr  "7,6" "9,4" "5,7" "6,4" ...
##  $ Aktiva(Mrd. $)   : chr  "838,4" "256,3" "458,7" "133,1" ...
##  $ Marktwert(Mrd. $): chr  "83,7" "76,1" "72,9" "109,8" ...
##  $ Mitarbeiter      : num  140 260 627 351 125 ...
##  $ Branche          : chr  "Versicherungen" "Automobile" "Automobile" "Technologie" ...

    
  

Data-Cleaning

Die Daten sind noch nicht in der gewünschten Form. Die Spalten:
  • Umsatz
  • Gewinn
  • Aktiva
  • 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.

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)

    
  

Das 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.
    
# All
GraphHauptsitzBranche <- graph_from_data_frame(d = Kanten, 
                                        vertices = ForbesKnoten)

    
  

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

    
plot(GraphHauptsitzBranche)
    
  

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)
    
  

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 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
  • 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 

    
  

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.

Autorin

Tanja Grotepaß