Geschriebene Artikel über Big Data Analytics

R Data Frames meistern mit dplyr – Teil 1

Dieser Artikel ist Teil 1 von 2 aus der Artikelserie R Data Frames meistern mit dplyr.

Data Frames sind das Arbeitspferd von R, wenn Daten in eine Struktur gepackt werden sollen, um sie einzulesen, zu säubern, zu transformieren, zu analysieren und zu visualisieren. Abstrakt gesprochen sind Data Frames nichts anderes als Relationen, also Mengen von Tupels, gebildet aus Elementen von geeigneten Mengen.

Dieses Konzept hat sich auch außerhalb des R-Universums bestens bewährt, umzusammengesetzte Daten, Beobachtungen oder Geschäftsobjekte zu repräsentieren. Der beste Beleg für diese Aussage sind die allgegenwärtigen Relationalen Datenbanksysteme (RDBMS). Dort werden Relationen als Tabellen (Tables) oder Sichten (Views) bezeichnet, und darauf wirkt eine mächtige, imperative Abfrage- und Manipulationssprache namens Structured Query Language, kurz:
SQL.

SQL ist in meiner Wahrnehmung die Lingua Franca der Datenverarbeitung, da sie im Kern über sehr viele Softwareprodukte gleich ist und nach erstaunlich geringem Lernaufwand mächtige Auswerte- und Manipulationsoperationen an den Daten ermöglicht. Hier eine SQL-Anweisung, um eine fiktive Tabelle aller Verkäufe (SALES) nach den Top-10-Kunden in diesem Jahr zu untersuchen:

SELECT customer_id, SUM(sales_amt) AS amount,
COUNT(*) AS n_sales, COUNT(DISTINCT product_id) AS n_products
FROM sales
WHERE sales_date LIKE ’2016-%’
GROUP BY customer_id
ORDER BY SUM(sales_amt) DESC
LIMIT 10

Dieser selbsterklärliche Code aus sieben Zeilen hat einen enormen Effekt: Er fast alle Verkäufe des Jahres 2016 auf Basis der Kundennummer zusammen, berechnet dabei die Summe aller Verkaufsbeträge, zählt die Anzahl der Transaktionen und der verschiedenen vom Kunden gekauften Produkte. Nach Sortierung gemäß absteigenden Umsatzes schneidet der Code nach dem 10. Kunden ab.

SQL kann aber mit der gleichen Eleganz noch viel mehr: Beispielsweise verbinden Joins die Daten mehrerer Tabellen über Fremschlüsselbeziehungen oder analytische Funktionen bestimmen Rankings und laufende Summen. Wäre es nicht toll, wenn R ähnlich effektiv mit Data Frames analoger Struktur umgehen könnte? Natürlich! Aber schon der Versuch, obige SQL-Query auf einem R Data Frame mit den althergebrachten Bordmitteln umzusetzen (subset, aggregate, merge, …), führt zu einem unleserlichen, uneleganten Stück Code.

Genau in diese Bresche springt der von vielen anderen Bibliotheken bekannte Entwickler Hadley Wickham mit seiner Bibliothek dplyr: Sie standardisiert Operationen auf Data Frames analog zu SQL-Operationen und führt zu einer wirklich selbsterklärlichen Syntax, die noch dazu sehr performant abgearbeitet wird. Ganz analog zu ggplot2, das sich an der Grammar of Graphics orientiert, spricht Wickham bei dplyr von einer Grammar of Data Manipulation. Die Funktionen zur Manipulation nennt er folgerichtig Verben.

Dabei treten naturgemäß eine Reihe von Analogien zwischen den Teilen eines SELECT-Statements und dplyr-Funktionen auf:

SELECT-Operation dplyr-Funktion
Bildung der Spaltenliste select()
Bildung eines Ausdrucks mutate()
WHERE-Klausel filter()
GROUP BY Spaltenliste group_by()
Bildung von Aggregaten wie sum() etc. summarise()
HAVING-Klausel filter()
ORDER BY Spaltenliste arrange()
LIMIT-Klausel slice()

Die ersten Schritte

Ich möchte die Anwendung von dplyr mithilfe des Standard-Datensatzes Cars93
aus dem Paket MASS demonstrieren:

> install.packages("dplyr")
> library(dplyr)
> cars <- MASS::Cars93
> str(cars)
’data.frame’: 93 obs. of 27 variables:
$ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 ...
$ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 ...

Die erste Aufgabe soll darin bestehen, aus dem Data Frame alle Autos zu selektieren, die vom Hersteller “Audi” stammen und nur Model und Anzahl Passagiere auszugeben. Hier die Lösung in Standard-R und mit dplyr:

> # Standard R: Zu allen Audis das Model, Anzahl Insassen
> subset(cars,Manufacturer=="Audi")[,c("Model","Passengers")]
Model Passengers
3 90 5
4 100 6
>
> # Das gleiche mit dplyr
> select(filter(cars,Manufacturer=="Audi"),Model,Passengers)
Model Passengers
1 90 5
2 100 6

Man sieht, dass die neue Funktion filter() der Zeilenselektion, also der Funktion subset() entspricht. Und die Auswahl der Ergebnisspalten, die in Standard-R durch Angabe einer Spaltenliste zwischen [ und ] erfolgt, hat in dplyr das Pendant in der Funktion select().

select() ist sehr mächtig in seinen Möglichkeiten, die Spaltenliste anzugeben. Beispielsweise funktioniert dies über Positionslisten, Namensmuster und ggf. das auch noch negiert:

select(cars, -starts_with("L")) 

Die obige Abfrage projiziert aus dem Data Frame sämtliche Spalten, die nicht mit “L” beginnen. Das scheint zunächst ein unscheinbares Feature zu sein, zahlt sich aber aus, wenn analytische Data Frames Dutzende oder Hunderte von Spalten haben, deren Bezeichnung sich nach einem logischen Namensschema richtet.
Soweit ist das noch nicht spektakulär. dplyr hilft uns in obigem Beispiel, als erstes bestimmte Datensätze zu selektieren und als zweites die interessierenden Spalten zu projizieren. dplyr ist aber bezüglich der Verarbeitung von Data Frames sehr intuitiv und funktional, sodass wir früher oder später viele Operationen auf unserem Data Frame verketten werden. So erreichen wir die Mächtigkeit von SQL und mehr. Die funktionale Syntax aus dem letzten Beispiel wird dann ganz schnell unleserlich, da die Verabeitungsreihenfolge (zuerst filter(), dann select()) nur durch Lesen des Codes von innen nach außen und von rechts nach links ersichtlich wird.

Daher geht dplyr einen Schritt weiter, indem es den eleganten Verkettungsoperator %>% aus dem magrittr-Paket importiert und zur Verfügung stellt. Dadurch werden die verschachtelten Ausdrücke in Sequenzen von Operationen gewandelt und somit sehr viel lesbarer und wartbarer:

select(filter(cars,Manufacturer=="Audi"),Model,Passengers)
Model Passengers
1 90 5
2 100 6

> cars %>%
+ filter(Manufacturer=="Audi") %>%
+ select(Model,Passengers)
Model Passengers
1 90 5
2 100 6

Diese in meinen Augen geniale Syntax durch den neuen Operator %>% erlaubt einen sequenziellen Aufbau der Operationen auf einem Data Frame. Benutzer der Unix-Kommandozeile werden hier leicht die Analogie zu Pipes erkennen. Ganz abstrakt kann man sagen, dass damit folgende Operationen äquivalent sind:

Traditioneller Funktionsaufruf Verkettung mit %>%
f(a,b) a %>% f(b)
f(a,b,c) a %>% f(b,c)
g(f(a,b),c) a %>% f(b) %>% g(c)

Weiteres erklärt die Dokumentation zum %>%-Operator im Paket magrittr mithilfe
des Befehls ?magrittr::‘%>%‘.

Neue Variablen

Durch die Funtionen select() und filter() können wir aus Data Frames Spalten projizieren und Zeilen selektieren. Ergebnisse neuer Ausdrücke entstehen hingegen mit dem Verb mutate():

> # Mit Umrechnung der Mileage in Verbrauch und des
> # Kaufpreises von USD in EUR
> cars2 <-
+ cars %>%
+ filter(Manufacturer=="Audi") %>%
+ mutate(l_100km = 235 / MPG.city,
+ eur = Min.Price * 1000 / 1.1) %>%
+ select(Model,Passengers,l_100km,eur)
> cars2
Model Passengers l_100km eur
1 90 5 11.75000 23545.45
2 100 6 12.36842 28000.00
> class(cars2)
[1] "data.frame"

Im obigen Beispiel wird zunächst auf den Hersteller Audi selektiert und danach auf einen Streich zwei neue Spalten eingeführt, l_100km und eur. Durch Zuweisen auf eine neue Variable wird das fertige Ergebnis dauerhaft gespeichert. Hierbei handelt es sich wieder um ein natives Data Frame-Objekt. Die Operation transmute() arbeitet analog zu mutate(), verwirft aber nach Bildung der Ausdrücke alle nicht genannten Spalten. Somit können wir obiges Beispiel auch wie folgt schreiben:

cars3 <-
+ cars %>%
+ filter(Manufacturer=="Audi") %>%
+ transmute(Model = Model, Passengers = Passengers,
+ l_100km = 235 / MPG.city,
+ eur = Min.Price * 1000 / 1.1)
cars3
Model Passengers l_100km eur
1 90 5 11.75000 23545.45
2 100 6 12.36842 28000.00

Aggregate

Neben der Selektion von Zeilen und Spalten sowie der Bildung abgeleiteter Ausdrücke ist bei Datenbanktabellen die Gruppierung und Aggregation mit GROUP BY eine sehr wichtige Operation. Dies gilt auch für Data Frames in R, wenngleich hier der Funktionsumfang über diverse Funktionen wie table() oder aggregate() verteilt ist und wenig intuitiv ist.

Hier bringt dplyr ebenfalls eine großartige Verbesserung mit. Das entsprechende Verb heißt group_by(). Diese Operation wird zusammen mit einer Spaltenliste auf ein Data Frame angewendet:

grp_cars <- cars %>% group_by(Manufacturer)
> class(grp_cars)
[1] "grouped_df" "tbl_df" "tbl" "data.frame"

Das Ergebnis von group_by() ist ein Objekt, das “mehr” ist als ein Data Frame, sondern auch noch einige spezifische Strukturinformationen von dplyr enthält. In unserem Beispiel sind dies Indizes von Zeilen, die zum gleichen Hersteller gehören. Das ursprüngliche Data Frame wird hierbei nicht kopiert, sondern nur eingebettet.

Nach Anwenden einer group_by()-Operation ist das Data Frame optimal vorbereitet für die eigentliche Aggregation mit summarise():

> sum_cars <- cars %>%
+ group_by(Manufacturer) %>%
+ summarise(nCars = n(), avg_price = mean(Min.Price))
> str(sum_cars)
Classes ‘tbl_df’, ‘tbl’ and ’data.frame’: 32 obs. of 3 variables:
$ Manufacturer: Factor w/ 32 levels "Acura","Audi",..: 1 2 3 4 5 6 7 8 9 10 ...
$ nCars : int 2 2 1 4 2 8 1 2 6 2 ...
$ avg_price : num 21.1 28.4 23.7 20.8 35.2 ...

Das Resultat von summarise() ist wieder ein Data Frame, das neben den ursprünglichen Gruppierungskriterien nur noch die Aggregate enthält.

Daten in Reih’ und Glied

Zwischen Relationalen Datenbanken und R-Data Frames besteht ein wesentlicher konzeptioneller Unterschied: Die Ergebnisse eines SELECT-Befehls haben keine definierte Reihenfolge, so lange die Zeilen nicht mit der Klausel ORDER BY festgelegt wird. Im Gegensatz dazu haben die Zeilen von Data Frames eine konstante Reihenfolge, die sich aus der Anordnung derWerte in den Spaltenvektoren ergibt.

Dennoch ist es manchmal wünschenswert, Data Frames umzusortieren, um eine fachliche Reihenfolge abzubilden. Hierzu dient in dplyr das Verb arrange(), das im Standard-R weitgehend der Indizierung eines Data Frames mit Ergebnissen der order()-Funktion entspricht, aber syntaktisch eleganter ist:

cars %>%
+ arrange(desc(Horsepower),Manufacturer) %>%
+ select(Manufacturer, Model, Horsepower,Min.Price) %>%
+ slice(1:5)
Manufacturer Model Horsepower Min.Price
1 Chevrolet Corvette 300 34.6
2 Dodge Stealth 300 18.5
3 Cadillac Seville 295 37.5
4 Infiniti Q45 278 45.4
5 Mazda RX-7 255 32.5

Dieses Beispiel hat zum Ziel, die fünf PS-stärksten Autos zu selektieren. Die arrange()-Funktion sortiert hier zunächst absteigend nach der PS-Stärke, dann aufsteigend nach Herstellername. Die Selektion der 5 ersten Zeilen erfolgt mit der hilfreichen Funktion slice(), die aus einem Data Frame Zeilen anhand ihrer Reihenfolge selektiert.

Fazit und Ausblick

Mit dplyr wird die Arbeit mit Data Frames stark verbessert: Im Vergleich zu “nacktem” R bringt das Paket eine klarere Syntax, abgerundete Funktionalität und bessere Performance. In der Kürze dieses Artikels konnte ich dies nur oberflächlich anreissen. Daher verweise ich auf die vielen Hilfe-Seiten, Vignetten und Internet-Videos zum Paket. Im zweiten Teil dieses Artikels werde ich auf einige fortgeschrittene Features von dplyr eingehen, z.B. die Verknüpfung von Data Frames mit Joins, die Window-Funktionen und die Verwendung von Datenbanken als Backend.

Weiter zu R Data Frames meistern mit dplyr – Teil 2.

Interview – Mit Data Science Kundenverhalten vorhersagen

Frau Dr. Eva-Marie Müller-Stüler ist Associate Director in Decision Science der KPMG LLP in London. Sie absolvierte zur Diplom-Mathematikerin an der Technischen Universität München, mit einem einjährigen Auslandssemester in Tokyo, und promovierte an der Philipp Universität in Marburg.

linkedin-button xing-button

english-flagRead this article in English:
“Interview – Using Decision Science to forecast customer behaviour”

Data Science Blog: Frau Dr. Müller-Stüler, welcher Weg hat Sie bis an die Analytics-Spitze der KPMG geführt?

Ich hatte schon immer viel Spaß an analytischen Fragestellungen, aber auch ein großes Interesse an Menschen und Finance. Die Frage wie Menschen ticken und Entscheidungen treffen finde ich unglaublich spannend. Im Mathematikstudium und auch bei der Doktorarbeit kamen dann das Auswerten von großen Datenmengen und das Programmieren von Algorithmen hinzu. Die solide mathematische Ausbildung kombiniert mit dem spezifischen Branchen- und Finanzverständnis ermöglicht es mir das Geschäftsmodell meiner Kunden zu verstehen und Methoden zu entwickeln, die den Markt verändern und neue Wege finden.

Data Science Blog: Welche Analysen führen Sie für Ihre Kundenaufträge durch? Welche Vorteile generieren Sie für Ihre Kunden?

Unser Team beschäftigt sich hauptsächlich mit Behaviour und Customer Science. Daher auch der Slogan „We understand human behaviour and we change it“. Unser Focus ist der Mensch (z.B. Kunde oder der Mitarbeiter) und die Frage, wie wir ihn durch das Verständnis seiner Datenartefakte im Verhalten ändern bzw. zukünftiges Verhalten vorhersagen können. Auf dieser Basis entwickeln wir Always-on forecasting Modelle, die es dem Mandanten ermöglichen, bereits im Vorfeld zu agieren. Das kann z.B. bedeuten, durch ortgenaue Informationen spezifische Kundennachfrage an einem bestimmten Standort vorherzusagen, wie sie verbessert oder in die gewünschte Richtung beeinflusst werden kann oder durch welche Maßnahmen bzw. Promotions welcher Kundentyp optimal erreicht wird. Oder auch die Frage wo und mit welcher Produktmischung am besten ein neues Geschäft eröffnet werden soll, ist mit Predictive Analytics viel genauer vorherzusagen als durch herkömmliche Methoden.

Data Science Blog: Welche Voraussetzungen müssen erfüllt sein, damit prädiktive Analysen für Kundenverhalten adäquat funktionieren?

Die Daten müssen natürlich eine gewisse Qualität und Historie haben um z. B. auch Trends und Zyklen zu erkennen. Oft kann man sich aber auch über die Einbindung neuer Datenquellen einen Vorteil erschaffen. Dabei ist Erfahrung und Kreativität enorm wichtig, um zu verstehen was möglich ist und die Qualität verbessert oder ob etwas nur für mehr Rauschen sorgt.

Data Science Blog: Welche externen Datenquellen müssen Sie dafür einbinden? Wie behandeln Sie unstrukturierte Daten?

Hier in England ist man – was externe Datenquellen angeht – schon sehr verwöhnt. Wir benutzen im Schnitt an die 10.000 verschiedene Signale, die je nach Fragestellung unterschiedlich seien können: z. B. die Zusammensetzung der Bevölkerung, Nahverkehrsinformationen, die Nähe von Sehenswürdigkeiten, Krankenhäusern, Schulen, Kriminalitätsraten und vieles mehr. Der Einfluss eines Signals ist bei jedem Problem unterschiedlich. So kann eine hohe Anzahl an Taschendiebstählen ein Zeichen dafür sein, dass in der Gegend viel los ist und die Menschen im Schnitt viel Bargeld bei sich tragen. Das kann z. B. für einen Fast Food-Retailer in der Innenstadt durchaus einen positiven Einfluss auf sein Geschäft haben in einer anderen Gegend aber das Gegenteil bedeuten.

Data Science Blog: Welche Möglichkeiten bietet Data Science für die Forensik bzw. zur Betrugserkennung?

Da jeden Kunden tausende Datensignale umgeben und er durch sein Verhalten weitere produziert und aussendet, kann man gerade beim Online-Geschäft schon ein ziemlich gutes Bild über die Person bekommen. Jede Art von Mensch hat ein gewisses Verhaltensmuster und das gilt auch für Betrüger. Diese Muster muss man nur rechtzeitig erkennen oder vorherzusagen lernen.

Data Science Blog: Welche Tools verwenden Sie bei Ihrer Arbeit? In welchen Fällen setzten Sie auf proprietäre Software, wann hingegen auf Open Source?

Das hängt vom Arbeitsschritt und dem definierten Ziel ab. Wir unterscheiden unser Team in unterschiedliche Gruppen: Unsere Data Wrangler (die für das Extrahieren, Erzeugen und Aufbereiten der Daten zuständig sind) arbeiten mit anderen Tools als z. B. unsere Data Modeller. Im Grunde umfasst es die gesamte Palette von SQL Server, R, Python, manchmal aber auch Matlab oder SAS. Immer häufiger arbeiten wir auch mit auf Cloud-Technologie basierenden Lösungen. Data Visualisation und Dashboards in Qlik, Tableau oder Alteryx geben wir in der Regel jedoch an andere Teams weiter.

Data Science Blog: Wie sieht Ihrer Erfahrung nach der Arbeitsalltag als Data Scientist nach dem morgendlichen Café bis zum Feierabend aus?

Meine Rolle ist vielleicht am besten zu beschreiben als der Player-Coach. Da läuft von allem etwas mit ein. Am Anfang eines Projektes geht es vor Allem darum, mit den Mandaten die Fragestellung zu erarbeiten und das Projekt zu gewinnen. Teil dessen ist auch neue Ideen und Methoden zu entwickeln.  Während eines Projektes sind das Team Management, der Wissenstransfer im Team, der Review und das Hinterfragen der Modelle meine Hauptaufgaben. Am Schluss kommt dann der endgültige Sign-off des Projektes. Da ich oft mehrere Projekte in unterschiedlichen Stadien gleichzeitig leite, wird es garantiert nie langweilig.

Data Science Blog: Sind gute Data Scientists Ihrer Erfahrung nach tendenziell eher Beratertypen oder introvertierte Nerds?

Das hängt so ein bisschen davon ab wo man seinen Schwerpunkt sieht. Als Data Visualizer oder Data Artist geht es darum die Informationen auf das wesentlich zu reduzieren und toll und verständlich darzustellen. Dafür braucht man Kreativität und ein gutes Verständnis für das Geschäft und einen sicheren Umgang mit den Tools.

Der Data Analyst beschäftigt sich vor Allem mit dem „Slice and Dice“ von Data. Ziel ist es, die Vergangenheit zu analysieren und Zusammenhänge zu erkennen. Es ist wichtig zusätzlich zu dem finanziellen Wissen auch gute mathematische Fähigkeiten zu haben.

Der Data Scientist ist der mathematischste von allen. Er beschäftigt sich damit aus den Daten tiefere Zusammenhänge zu erkennen und Vorhersagen zu treffen. Dabei geht es um die Entwicklung von komplizierten Modellen oder auch Machine Learning Algorithmen. Ohne eine gute mathematische Ausbildung und Programmierkenntnisse ist es leider nicht möglich die Sachen in voller Tiefe zu verstehen. Die Gefahr falsche Schlüsse zu ziehen oder Korrelationen zu interpretieren, die sich aber nicht bedingen ist sehr groß. Ein einfaches Beispiel hierfür ist, dass im Sommer, wenn das Wetter schön ist, mehr Menschen Eis essen und in Seen baden gehen. Daher lässt sich eine eindeutige Korrelation zwischen Eis essen und der Anzahl an Ertrunkenen zeigen, obwohl nicht das Eis essen zum Ertrinken führt sondern die beeinflussende Variable die Temperatur ist. Daher ist ein Doktor in einem mathematiknahen Fach schon wichtig.

Genauso ist aber für den Data Scientist auch das entsprechende Finanz- und Branchenwissen wichtig, denn seine Erkenntnisse und Lösung müssen relevant für den Kunden sein und deren Probleme lösen oder Prozesse verbessern. Die tollste AI Maschine bringt keiner Bank einen Wettbewerbsvorteil, wenn sie den Eisverkauf auf Basis des Wetters vorhersagt. Das kann zwar rechnerisch 100% richtig sein, hat aber keine Relevanz für den Kunden.

Es ist im Grunde wie in anderen Bereichen (z. B. der Medizin) auch. Es gibt viele verschiedene Schwerpunkte und für ernsthafte Probleme wendet man sich am besten an einen Spezialisten, damit man keine falschen Schlüsse zieht.

Data Science Blog: Für alle Studenten, die demnächst ihren Bachelor, beispielsweise in Informatik, Mathematik oder Wirtschaftslehre, abgeschlossen haben, was würden sie diesen jungen Damen und Herren raten, wie sie gute Data Scientists werden können?

Nie aufhören mit dem Lernen!  Der Markt entwickelt sich derzeit unglaublich schnell und hat so viele tolle Seiten. Man sollte einfach mit Leidenschaft, Begeisterung und Kreativität dabei sein und Spaß an der Erkennung von Mustern und Zusammenhängen haben. Wenn man sich dann noch mit interessanten und inspirierenden Menschen umgibt, von denen man noch mehr lernen kann, bin ich zuversichtlich, dass man eine tolle Arbeitszeit haben wird.

Interview – Data Science im Online Marketing

Interview mit Thomas Otzasek, Head of Data Science bei der Smarter Ecommerce GmbH

Thomas Otzasek ist Head of Data Science bei der Smarter Ecommerce GmbH in Linz, ein Unternehmen für die Automatisierung des professionellen Suchmaschinen Marketings. Herr Otzasek leitet das Data Science Team zur Automatisierung von operativen Prozessen im Suchmaschinen Marketing mit Machine Learning. Weitere interessante Blogposts von Thomas Otzasek zum Thema Suchmaschinen Marketing und Data Science finden Sie im Whoop! Blog.

Data Science Blog: Herr Otzasek, welcher Weg hat Sie zum Data Science für das Suchmaschinen Marketing geführt?

Ich war schon immer an Zahlen interessiert und begann daher im Jahr 2002 ein Masterstudium der Statistik an der Johannes Kepler Universität in Linz. Im Jahr 2006 wurde an dieser Uni dann erstmalig der Studiengang Bioinformatik mit Schwerpunkt Machine Learning angeboten, der mich ebenfalls angesprochen hat. Im Jahr 2009 habe ich beide Masterstudien erfolgreich abgeschlossen.

Nachdem ich in diversen Branchen u.a. als Business Analyst oder Software-Entwickler gearbeitet habe, überzeugte mich im Jahr 2015 die Firma Smarter Ecommerce mit einer innovativen Produktidee, für die ich den fehlenden Data Science Puzzleteil ideal ausfüllen konnte. Seitdem sind wir auf Wachstumskurs und konnten unsere Mitarbeiterzahl innerhalb von 15 Monaten auf derzeit 85 Mitarbeiter mehr als verdoppeln.

Data Science Blog: Welche Bedeutung hat Big Data und Data Science für Ihre Branche?

Im Suchmaschinen Marketing gibt es sehr viel manuelle Arbeit. Mit dem Einsatz von Data Science können wir diese manuelle Arbeit unterstützen oder automatisieren. Ist das Produktsortiment entsprechend groß, können wir die Platzierung in Online-Anzeigen soweit optimieren, wie es selbst dem besten Mitarbeiter ohne entsprechende Tools niemals möglich wäre.

Wir übernehmen das Aussteuern von Google Shopping, für welche Produkte wo genau Anzeigen zu welchen Konditionen geschaltet werden. Wir haben dafür Machine Learning Modelle entwickelt, die diese Anzeigenschaltung optimieren. Der dafür von meinem Data Science Team entwickelte Prototyp ist seit über einem Jahr produktiv im Einsatz.

Data Science Blog: Was optimieren diese Algorithmen des maschinellen Lernens?

Der vollautomatisierte Ansatz kommt bei unserem Produkt Whoop! für Google Shopping zum Einsatz. Google Shopping ist ein Teil von Google AdWords. Wir verwenden den Produkt-Datenfeed des Kunden, die Performance-Historie von Google AdWords, unsere jahrelange Google Shopping Erfahrung sowie die Ziele des Kunden bezüglich der Anzeigen um z. B. die Kosten-Umsatz-Relation oder die Kosten pro Akquisition zu optimieren.

Die Herausforderung ist, das richtige Gebot für das jeweilige Produkt zu wählen. Wenn Sie eine ganze Reihe von verschiedenen oder auch ähnlichen Produkten haben (z. B. verschiedene Farben oder Größen), müssen wir diese Gebote so tunen, dass die Reichweite und Zielgruppe ideal ist, ohne dass die Kosten explodieren.

Wird ein Produkt zu hoch geboten, sind nicht nur die Kosten für das bewerbende Unternehmen zu hoch, auch die Platzierung ist dann meistens nicht optimal. Google, unser Anzeigenpartner, verallgemeinert die Suchanfragen im hochpreisigen Segment tendenziell zu sehr, darunter leidet dann die Relevanz. Wird für die Anzeige zu niedrig geboten, wird sie hingegen gar nicht erst angezeigt. Neben der Conversion Rate spielt für unsere Kunden hauptsächlich die Kosten-Umsatz-Relation eine Rolle. Ein Mitarbeiter im Online Marketing könnte diese Optimierung für mehr als eine Hand voll Produkte nicht vornehmen. Denken Sie z. B. an die Mode-Branche, die ein sich schnell umschlagendes Produktsortiment mit vielen Produkten hat.

Data Science Blog: Welche datenwissenschaftlichen Herausforderungen spielen dabei eine Rolle?

Die Produktdaten sind sehr umfangreich, der Anzeigenmarkt und die Produkttrends extrem dynamisch. Außerdem gibt es für viele Produkte nur wenige Klicks, so dass wir ausgeklügelte Algorithmen brauchen, um trotzdem statistisch valide Aussagen treffen zu können.

Für die manuelle Aussteuerung ist die Produktanzahl meist zu groß um produktgenaue Gebote abgeben zu können. Bei einem großen und/oder schnell umschlagenden Produktsortiment haben wir es mit komplexen Strukturen zu tun, die wir in diesen Modellen berücksichtigen müssen, um stets die optimalen Gebote zu setzen.

Das Modell muss dabei jederzeit berücksichtigen, welche Produkte bzw. Anzeigen performen bzw. nicht performen, um jene entsprechend hoch- oder runter zu regeln. Eine einfache Regressionsanalyse reicht da nicht aus. Auch Änderungen des Kunden in den Einstellungen sowie externe Faktoren wie z. B. das Wetter müssen sofort berücksichtigt werden.

Data Science Blog: Welche Methoden des Data Science sind aktuell im Trend und spielen demnächst eine Rolle?

Aus meiner Sicht ist Deep Learning mit neuronalen Netzen der Trend. Vermutlich werden sie sich weiter durchsetzen, denn sie können noch komplexere Aufgaben bewältigen. Aktuell gibt es allerdings teilweise noch Akzeptanzprobleme, da neuronale Netze mit vielen versteckten Schichten eine Blackbox darstellen. Die Ergebnisse sind also im Gegensatz zu weniger komplexen Methoden nicht nachvollziehbar.

Data Science Blog: Auf welche Tools setzen Sie bei Ihrer Arbeit? Bevorzugen Sie Open Source oder proprietäre Lösungen?

Ich habe viel mit proprietären Lösungen gearbeitet, beispielsweise mit SAS oder IBM SPSS. Wir setzen derzeit allerdings auf Open Source, vor allem auf die Programmiersprache R. Neue Mitarbeiter im Data Science Bereich sollten daher zumindest über Grundkenntnisse in R verfügen und die Lust haben, sich tiefer mit dieser Programmiersprache zu befassen.

Wir verwenden unter anderem die Pakete ggplot und Shiny. Mit Shiny erstellen wir interne Web-Applikationen, um Kollegen Analysen zur Verfügung zu stellen. Für Eigenentwicklungen komplexer Visualisierungen ist ggplot perfekt geeignet.

Mit R können wir außerdem selbst eigene Packages erstellen um den Funktionsumfang nach unseren Wünschen zu erweitern. Wir haben daher keinen Grund, auf kostenintensive Lösungen zu setzen.

Data Science Blog: Was macht Ihrer Erfahrung nach einen guten Data Scientist aus?

Aus meiner Sicht sollte man ein Zahlenfreak sein und niemals aufhören Fragen zu stellen, denn darum geht es im Data Science. Gute Data Scientists sind meiner Meinung nach interdisziplinär ausgebildet, kommen also nicht nur aus einer Ecke, sondern besser aus zwei oder drei Fachbereichen. Man benötigt verschiedene Sichtweisen.

Aus welchem Fachbereich man ursprünglich kommt, ist dabei gar nicht so wichtig. Es muss also nicht unbedingt ein Mathematiker oder Statistiker sein.

Data Science Blog: Gibt es eigentlich aus Ihrer Erfahrung heraus einen Unterschied zwischen Mathematikern und Statistikern?

Ja. Mathematiker denken meiner Meinung nach sehr exakt und beweisorientiert. Statistik ist zwar ein Teilbereich der Mathematik, aber für einen Statistiker steht das Schätzen im Vordergrund. Statistiker denken in Verteilungen, Wahrscheinlichkeiten und Intervallen und können gut mit einer gewissen Unsicherheit leben, die reine Mathematiker manchmal unbefriedigt lässt.

Data Science Blog: Für alle diejenigen, die gerade ihr Studium der Statistik, Ingenieurwissenschaft oder was auch immer abschließen. Welchen Rat haben Sie, wie diese Menschen einen Schritt näher ans Data Science herankommen?

Ich würde empfehlen, einfach ein eigenes kleines Projekt zu starten – „Learning by doing“! Ob das Projekt um die eigenen Stromverbrauchsdaten, eine Wettervorhersage oder Fantasy-Football geht ist nicht wichtig. Man stößt dann zwangsläufig auf die verschiedenen Arbeitsschritte und Herausforderungen. Ein empfehlenswerter Workflow ist der Cross Industry Standard Process for Data Mining, kurz CRISP-DM.

Zuerst muss man ein Geschäftsverständnis aufbauen. Weiter geht es mit der Datensammlung und Datenintegration, danach folgt die Datenaufbereitung. Diese Schritte benötigen bereits ca. 80% der Projektzeit. Erst dann können explorative Analysen, Hypothesentests oder Modellierung aufgesetzt werden. Am Ende des Prozesses erfolgt das Deployment.

 

ABC-XYZ-Analyse

Die ABC-XYZ-Analyse ist eine aussagekräftige Analyse für die Strategiefindung in der Warenwirtschaft und Logistik bzw. im Supply Chain Management. Die Analyse basiert auf der Vorstellung einer Pareto-Verteilung, die darauf hindeutet, dass oftmals eine kleine Menge eines großen Ganzen einen unverhältnismäßig großen Einfluss auf eben dieses große Ganze hat.

Die ABC-XYZ-Analyse beinhaltet im ersten Schritt eine ABC- und im zweiten Schritt eine XYZ-Analyse. Im dritten Schritt werden die Ergebnisse in einer Matrix zusammengeführt. In diesem Artikel erläutere ich nicht, wofür eine ABC-XYZ-Analyse dient und wie die Ergebnisse zu interpretieren sind, hier kann ich jedoch auf einen älteren Artikel “ABC-XYZ-Analyse” – www.der-wirtschaftsingenieur.de vom 3. Mai 2011 von mir verweisen, der vorher lesenswert ist, wenn kein Vorwissen zur ABC-XYZ-Analyse vorhanden ist.

Die Vorarbeit

Für die ABC- und XYZ-Analyse benötigen wir folgende Python-Bibliotheken:

import pandas as pd
import numpy as np
import random as random
import matplotlib.pyplot as pyplot

Wir laden die EKPO-Tabelle in ein DataFrame (Datenstruktur der Pandas-Bibliothek):

EKPO = pd.read_csv("[PFAD]EKPO.csv", delimiter=';', thousands='.', decimal=',')

Die Datei stammt aus einem SAP-Testsystem und steht hier zum Download bereit:

csv-icon

SAP.EKPO

Wir benötigen daraus nur folgende Zeilen:

EKPO_X = EKPO[['MATNR', 'MATKL', 'MENGE', 'PEINH', 'NETPR', 'NETWR']].copy()

Jetzt kommt der erste Kniff: Das Feld “MENGE” im SAP beschreibt die Menge in der jeweiligen Mengeneinheit (z. B. Stück, Meter oder Liter). Da wir hier jedoch nicht den genauen Verbrauch vorliegen haben, sondern nur die Einkaufsmenge (indirekt gemessener Verbrauch), sollten wir die Menge pro Preiseinheit “PEINH” berücksichtigen, denn nach dieser Preiseinheitsmenge erfolgt der Einkauf.

EKPO_X['Preiseinheitsmenge'] = EKPO_X['MENGE'] / EKPO_X['PEINH']

Für die Preiseinheitsmenge ein Beispiel:
Sie kaufen sicherlich pro Einkauf keine 3 Rollen Toilettenpapier, sondern eine oder mehrere Packungen Toilettenpapier. Wenn Sie zwei Packung Toilettenpapier für jeweils 2 Euro kaufen, die jeweils 10 Rollen beinhalten, ist die Preiseinheit = 10 und die Preiseinheitsmenge => 20 gekaufte Toilettenrollen / 10 Rollen pro Packung = 2 Packungen Toilettenpapier.

Nun haben wir also unsere für den Einkauf relevante Mengeneinheit. Jetzt sortieren wir diese Materialeinkäufe primär nach dem Umsatzvolumen “NETWR” absteigend (und sekundär nach der Preiseinheitsmenge aufsteigend, allerdings spielt das keine große Rolle):

EKPO_X = EKPO_X.sort_values(by = ['NETWR', 'Preiseinheitsmenge'], ascending=[False, True]) # Sortierung nach Umsatzvolumen pro Bestellung absteigend

Einige Störfaktoren müssen noch bereinigt werden. Erstens sollen Einträge mit Preisen oder Umsätzen in Höhe von 0,00 Euro nicht mehr auftauchen:

EKPO_X = EKPO_X[(EKPO_X.NETPR != 0) & (EKPO_X.NETWR != 0)]

Zweitens gibt es Einkäufe, die ein Material ohne Materialnummer und/oder ohne Materialklasse haben. Bei einer Zusammenfassung (Aggregation) über die Materialnummer oder die Materialklasse würden sich diese “leeren” Einträge als NULL-Eintrag bündeln. Das wollen wir vermeiden, indem wir alle NULL-Einträge mit jeweils unterschiedlichen Zufallszahlen auffüllen.

EKPO_X.MATNR[EKPO_X.MATNR.isnull() == True] = EKPO_X.MATNR[EKPO_X.MATNR.isnull() == True].apply(lambda x: random.random()) # Manche MATNR fehlen (NULL), diese füllen wir mit zufälligen Werten auf. Dabei ist es natürlich wichtig, dass die Zufallszahl für jede Zeile neu generiert wird! EKPO_X.MATNR.fillna(random.random()) funktioniert nicht, denn hier würde ein gleicher Wert alle NaN-Werte ersetzen

ABC – Analyse:

Nun geht es an die eigentliche ABC-Analyse, dafür müssen wir die Gruppierung der Materialien vornehmen. Gleich vorweg: Dies sollte man eigentlich über die einzelnen Materialnummern machen, da dies jedoch in der Visualisierung (auf Grund der hohen Anzahl und Vielfältigkeit) etwas aufwändiger ist, machen wir es über die Materialklassen. Wir gehen dabei einfach davon aus, dass die Materialklassen relativ homogene Materialien zusammenfassen und somit auch das Verbrauchs-/Einkaufverhalten innerhalb einer Gruppe nicht sonderlich viel Abweichung aufweist.

# Aggregation über die Materialklasse, Aufsummierung der Umsätze, Mengen und Volumen 
MATKL_MENGEN = (EKPO_X.MENGE.groupby(EKPO_X.MATKL).sum()).to_frame()
MATKL_PREISEINHEIT_MENGE = (EKPO_X.Preiseinheitsmenge.groupby(EKPO_X.MATKL).sum()).to_frame()
MATKL_VOLUMEN = (EKPO_X.NETWR.groupby(EKPO_X.MATKL).sum()).to_frame()

# Aggregation über die Materialklasse, Berechnung des Durchschnittpreises (ist bei einer Materialklasse, allerdings wenig sinnvoll!)
MATKL_Preise = (EKPO_X.NETPR.groupby(EKPO_X.MATKL).mean()).to_frame()EKPO_G = MATKL_MENGEN.join(MATKL_PREISEINHEIT_MENGE, how='left')

# Zusammenfügen der Ergebnisse (Left-Join)
EKPO_G = EKPO_G.join(MATKL_Preise, how='left')
EKPO_G = EKPO_G.join(MATKL_VOLUMEN, how='left')
EKPO_G = EKPO_G.sort_values(['NETWR'], ascending=False)

# Berechnung der kumulierten Umsätze und Mengen (Beachte: Vorher muss nach Umsätzen absteigend sortiert worden sein! (siehe oben)
EKPO_G['Volumen_kumuliert'] = EKPO_G.NETWR.cumsum()
EKPO_G['Menge_kumuliert'] = EKPO_G.MENGE.cumsum()

Nun können wir uns ganz im Sinne der ABC-Analyse die typische Pareto-Verteilung der kumulierten Umsätze (Umsatzgrößen absteigend sortiert) ansehen:

EKPO_G[['Menge_kumuliert','Volumen_kumuliert']].plot([EKPO_G.Menge_kumuliert, EKPO_G.Volumen_kumuliert], color=['red','pink'], figsize=[20,10], fontsize=8, title='Kumulierte Werte - Sortierung nach Materialklassen-Volumen')

abc_analyse_sap_netwr_menge_kumulierte_kurve_pareto

Die X-Achse zeigt die Materialklassen von links nach rechts in der Sortierung nach dem Umsatzvolumen (größester Umsatz links, kleinster Umsatz rechts). Die Y-Achse zeigt den Betrag der Umsatzhöhe (Euro) bzw. der Menge (Preiseinheitsmenge). Die Kurve der Menge ist mit Vorsicht zu bewerten, da primär nach dem Umsatz und nicht nach der Menge sortiert wurde.

Klassifikation:

Nun kommen wir zur Klassifikation. Hier machen wir es uns sehr einfach: Wir gehen einfach davon aus, dass 80% des Wertbeitrages aller Umsätze von etwa 20% der Materialien (hier: Materialklassen) umfassen und klassifizieren daher über feste relative Größen:

EKPO_G['ABC_Gruppe'] = "C" # Erstmal sind alle Materialien der C-Gruppe zugeordnet
EKPO_G['ABC_Gruppe'][EKPO_G.Volumen_kumuliert <= EKPO_G.NETWR.sum() / 100 * 95] = 'B' # Materialien, deren kumuliertes Volumen maximal 95% des Gesamtvolumens umfassen, sind Gruppe B
EKPO_G['ABC_Gruppe'][EKPO_G.Volumen_kumuliert <= EKPO_G.NETWR.sum() / 100 * 80] = 'A' # Materialien, deren kumuliertes Volumen maximal 80% des Gesamtvolumens umfassen, sind Gruppe A

Hinweis:
Intelligenter wird so eine Klassifikation, wenn wir den steilsten Anstieg innerhalb der kumulierten Volumen (die zuvor gezeigte Kurve) ermitteln und danach die Grenzen für die A-, B-, C-Klassen festlegen.

Optional: Farben für die Klassen festlegen (für die nachfolgende Visualisierung)

EKPO_G['Color'] = 'red'
EKPO_G['Color'][EKPO_G['ABC_Gruppe'] == 'B'] = 'orange'
EKPO_G['Color'][EKPO_G['ABC_Gruppe'] == 'C'] = 'green'

Jetzt Aggregieren wir über die ABC-Gruppe:

GruppenWerte = EKPO_G.groupby(['ABC_Gruppe'])
GruppenVolumen = (GruppenWerte.NETWR.sum()).to_frame()
GruppenMengen = (GruppenWerte.Preiseinheitsmenge.sum()).to_frame()

# Wieder zusammenfügen
GruppenVolumenMengen = GruppenVolumen.join(GruppenMengen)

Das Ergebnis:

GruppenVolumenMengen

Out:
NETWR Preiseinheitsmenge
ABC_Gruppe
A 6190725.01 175748.29
B 1231070.86 199599.24
C 408128.45 99745.63

Schauen wir uns nun die Verteilung der Werte und Mengen zwischen den Klassen A, B und C an:

GruppenVolumenMengen.plot(kind='bar', width=0.90, xlim=[0,1000], figsize=[10,5], yticks=GruppenVolumenMengen.NETWR)

 

abc_analyse_gruppen_vergleich

Es ist recht gut erkennbar, dass die Gruppe A deutlich mehr Umsatzvolumen (also Wertbeitrag) als die Gruppen B und C hat. Allerdings hat sie auch eine höhere Bestellmenge, wie jedoch nicht proportional von C über B zu A ansteigt wie das Umsatzvolumen.

Nachfolgend sehen wir die Klassifikation nochmal nicht kumuliert über die Umsatzvolumen der Materialien (Materialklassen):

EKPO_G[['NETWR']].plot(kind='bar', figsize=[20,10], legend = True, color=EKPO_G.Color, alpha=0.65, title='ABC - Analyse')

abc_analyse_sap_netwr

XYZ – Analyse

Für die XYZ-Analyse berechnen wir den arithmetischen Mittelwert, die Standardabweichung und die Summe aller Mengen pro Materialklasse [‘MATKL’] (oder alternativ, der einzelnen Materialnummern [‘MATNR’]) über eine Aggregation: 

Material_Menge = EKPO_X.Preiseinheitsmenge.groupby(EKPO_X.MATKL).agg({'mean', 'std', 'sum'})
#Oder mit dem Material: Material_Menge = EKPO_X.Preiseinheitsmenge.groupby(EKPO_X.MATNR) .agg({'mean', 'std', 'sum'})

#Leider ergeben sich einige NaNs bei der Standardabweichung, da ein Material oder eine Materialklasse nur eine einzige Buchung haben kann, diese müssen wir bereinigen (hier: mit Nullen auffüllen):
Material_Menge = Material_Menge.fillna(0)

Die XYZ-Analyse soll aufzeigen, welche Materialien (hier: Materialklassen) in stabilen Mengen verbraucht (hier: eingekauft) werden und welche größere Schwankungen hinsichtlich der Verbrauchsmenge (hier: Einkaufsmenge) aufweisen. Dazu berechnen wir den Variationskoeffizienten:

Variationskoeffizient = frac{Standardabweichung}{Mittelwert}

Wir berechnen diesen Variationskoeffizienten und sortieren das DataFrame nach diesem aufsteigend:

Material_Menge['Variationskoeffizient'] = Material_Menge['std'] / Material_Menge['mean']
Material_Menge = Material_Menge.sort_values(['Variationskoeffizient'], ascending = True)

Klassifikation:

Nun klassifizieren wir die Materialien (Materialklassen) über den Variationskoeffizienten in XYZ-Klassen. Dabei gehen wir davon aus, dass Materialien/Materialklassen, die einen Variationskoeffizienten von bis zu 70% des Maximalwertes aufweisen, in die Y-Klasse fallen. Solche, die nur maximal 20% des Maximalwertes aufweisen, fallen in die X-Klasse:

Material_Menge['XYZ_Gruppe'] = 'Z'
Material_Menge['XYZ_Gruppe'][Material_Menge.Variationskoeffizient <= Material_Menge.Variationskoeffizient.max() / 100 * 70] = 'Y'
Material_Menge['XYZ_Gruppe'][Material_Menge.Variationskoeffizient <= Material_Menge.Variationskoeffizient.max() / 100 * 20] = 'X'

Auch hier gilt analog zur ABC-Analyse: Intelligente Klassifikation erfolgt über die Analyse der Kurve der kumulierten Variationskoeffizienten. Die Grenzen der Klassen sollten idealerweise zwischen den steilsten Anstiegen (bzw. die größten Wertedifferenzen) zwischen den Werten der kumulierten Variationskoeffizienten-Liste gezogen werden.

Optional: Farben fürs Plotten setzen.

Material_Menge['Color'] = 'red'
Material_Menge['Color'][Material_Menge.XYZ_Gruppe == 'Y'] = 'orange'
Material_Menge['Color'][Material_Menge.XYZ_Gruppe == 'X'] = 'green'

Jetzt schauen wir uns mal die Verteilung der Materialien hinsichtlich des Variationskoeffizienten an:

Material_Menge.Variationskoeffizient.plot(kind='bar', width=0.90, xlim=[0,1000], figsize=[20,5], rot=90, color=Material_Menge.Color, title='XYZ - Analyse')

xyz_analyse_sap_matkl_menge

Die meisten Materialklassen haben einen recht niedrigen Variationskoeffizienten, sind im Einkauf (und daher vermutlich auch im Verbrauch) recht stabil. Die Materialklasse 0004 hingegen ist einigen Mengenschwankungen unterworfen. In der ABC-Analyse ist diese Materialklasse 0004 als B-Gruppe klassifiziert.

ABC-XYZ-Analyse

Nun möchten wir also die zuvor erstellte ABC-Klassifikation mit der XYZ-Klassifikation zusammen bringen.

Dafür fügen wir die beiden Pandas.DataFrame über den Index (hier die Materialklasse ‘MATKL’, im anderen Fall das Material ‘MATNR’) zusammen:

XYZ_ABC = pd.merge(EKPO_G, Material_Menge, left_index = True, right_index = True, how='left')

Die Zusammenfassung als Kreuztabelle:

pd.crosstab(XYZ_ABC.ABC_Gruppe, XYZ_ABC.XYZ_Gruppe, margins=True)

Out:

  X Y Z All

A 17 1 0 18

B 19 1 1 21

C 69 2 0 71

All 105 4 1 110

Für die Interpretation dieser Ergebnisse verweise ich erneut auf den Artikel bei der-wirtschaftsingenieur.de.

Warenkorbanalyse in R

Was ist die Warenkorbanalyse?

Die Warenkorbanalyse ist eine Sammlung von Methoden, die die beim Einkauf gemeinsam gekauften Produkte oder Produktkategorien aus einem Handelssortiment untersucht. Ziel der explorativen Warenkorbanalyse ist es, Strukturen in den Daten zu finden, so genannte Regeln, die beschreiben, welche Produkte oder Produktkategorien gemeinsam oder eben nicht gemeinsam gekauft werden.

Beispiel: Wenn ein Kunde Windeln und Bier kauft, kauft er auch Chips.

Werden solche Regeln gefunden, kann das Ergebnis beispielsweise für Verbundplatzierungen im Verkaufsraum oder in der Werbung verwendet werden.

Datenaufbau

Die Daten, die für diese Analyse untersucht werden, sind Transaktionsdaten des Einzelhandels. Meist sind diese sehr umfangreich und formal folgendermaßen aufgebaut:

data-bsp

Ausschnitt eines Beispieldatensatzes: Jede Transaktion (= Warenkorb = Einkauf) hat mehrere Zeilen, die mit der selben Transaktionsnummer (Spalte Transaction) gekennzeichnet sind. In den einzelnen Zeilen der Transaktion stehen dann alle Produkte, die sich in dem Warenkorb befanden. In dem Beispiel sind zudem noch zwei Ebenen von Produktkategorien als zusätzliche Informationen enthalten.

Es gibt mindestens 2 Spalten: Spalte 1 enthält die Transaktionsnummer (oder die Nummer des Kassenbons, im Beispielbild Spalte Transaction), Spalte 2 enthält den Produktnamen. Zusätzlich kann es weitere Spalten mit Infos wie Produktkategorie, eventuell in verschiedenen Ebenen, Preis usw. geben. Sind Kundeninformationen vorhanden, z.B. über Kundenkarten, so können auch diese Informationen enthalten sein und mit ausgewertet werden.

Beschreibende Datenanalyse

Die Daten werden zunächst deskriptiv, also beschreibend, analysiert. Dazu werden z.B. die Anzahl der Transaktionen und die Anzahl der Produkte im Datensatz berechnet. Zudem wird die Länge der Transaktionen, also die Anzahl der Produkte in den einzelnen Transaktionen untersucht. Dies wird mit deskriptiven Maßzahlen wie Minimum, Maximum, Median und Mittelwert in Zahlen berichtet sowie als Histogramm grafisch dargestellt, siehe folgende Abbildung.

hist-sizes
Histogramm der Längenverteilung der Transaktionen.

Die häufigsten Produkte werden ermittelt und können gesondert betrachtet werden. Als Visualisierung kann hier ein Balkendiagramm mit den relativen Häufigkeiten der häufigsten Produkte verwendet werden, wie im folgenden Beispiel.

relfreq-items
Relative Häufigkeiten der häufigsten Produkte, hier nach relativer Häufigkeit größer 0,1 gefiltert.

Ähnliche Analysen können bei Bedarf auch auf Kategorien-Ebene oder nach weiteren erhobenen Merkmalen selektiert durchgeführt werden, je nachdem, welche Informationen in den Daten stecken und welche Fragestellungen für den Anwender interessant sind.

Verbundanalyse

Im nächsten Schritt wird mit statistischen Methoden nach Strukturen in den Daten gesucht, auch Verbundanalyse genannt. Als Grundlage werden Ähnlichkeitsmatrizen erstellt, die für jedes Produktpaar die Häufigkeit des gemeinsamen Vorkommens in Transaktionen bestimmen. Solch eine Ähnlichkeitsmatrix ist zum Beispiel eine Kreuztabelle in der es für jedes Produkt eine Spalte und eine Zeile gobt. In den Zellen in der Tabelle steht jeweils die Häufigkeit, wie oft dieses Produktpaar gemeinsam in Transaktionen in den Daten vorkommt, siehe auch folgendes Beispiel.

screenshot-crosstable-ausschnitt

Ähnlichkeitsmatrix oder Kreuztabelle der Produkte: Frankfurter und Zitrusfrüchte werden in 64 Transaktionen zusammen gekauft, Frankfurter und Berries in 22 usw.

Auf Basis solch einer Ähnlichkeitsmatrix wird dann z.B. mit Mehrdimensionaler Skalierung oder hierarchischen Clusteranalysen nach Strukturen in den Daten gesucht und Gemeinsamkeiten und Gruppierungen gefunden. Die hierarchische Clusteranalyse liefert dann ein Dendrogram, siehe folgende Abbildung, in der ähnliche Produkte miteinander gruppiert werden.

dendrogram

Dendrogram als Visualisierung des Ergebnisses der hierarchischen Clusterananlyse. Ähnliche Produkte (also Produkte, die zusammen gekauft werden) werden zusammen in Gruppen geclustert. Je länger die vertikale Verbindungslinie ist, die zwei Gruppen oder Produkte zusammen fasst, um so unterschiedlicher sind diese Produkte bzw. Gruppen.

Assoziationsregeln

Schließlich sollen neben den Verbundanalysen am Ende in den Daten Assoziationsregeln gefunden werden. Es werden also Regeln gesucht und an den Daten geprüft, die das Kaufverhalten der Kunden beschreiben. Solch eine Regel ist zum Beispiel „Wenn ein Kunde Windeln und Bier kauft, kauft er auch Chips.“ Formal: {Windeln, Bier} → {Chips}

Für diese Regeln lassen sich statistische Maßzahlen berechnen, die die Güte und Bedeutung der Regeln beschreiben. Die wichtigsten Maßzahlen sind Support, Confidence und Lift:

Support ist das Signifikanzmaß der Regel. Es gibt an, wie oft die gefundene Regel in den Daten anzuwenden ist. Wie oft also die in der Regel enthaltenen Produkte gemeinsam in einer Transaktion vorkommen. In dem Beispiel oben: Wie oft kommen Windeln, Bier und Chips in einer Transaktion gemeinsam vor?

Confidence ist das Qualitätsmaß der Regel. Es beschreibt, wie oft die Regel richtig ist. In dem oben genanten Beispiel: Wie oft ist in einer Transaktion Chips enthalten, wenn auch Windeln und Bier enthalten sind?

Lift ist das Maß der Bedeutung der Regel. Es sagt aus wie oft die Confidence den Erwartungswert übersteigt. Wie ist die Häufigkeit des gemeinsamen Vorkommens von Windeln, Bier und Chips im Verhältlnis zur erwarteten Häufigkeit des Vorkommens, wenn die Ereignisse stochastisch unabhängig sind?

Algorithmen

In den Daten werden zunächst alle möglichen Regeln gesammelt, die einen Mindestwert an Support und Confidence haben. Die Mindestwerte werden dabei vom Nutzer vorgegeben. Da es sich bei Transaktionsdaten um große Datenmengen handelt und häufig große Anzahlen von Produkten enthalten sind, wird die Suche nach Regeln zu einem komplexen Problem. Es wurden verschiedene effiziente Algorithmen als Suchstrategien entwickelt, z.B. der APRIORI-Algorithmus von Agrawal und Srikant (1994), der auch im weiter unten vorgestellten Paket arules von R verwendet wird.

Sind die Assoziationsregeln gefunden, können Sie vom Nutzer genauer untersucht werden und z.B. nach den oben genannten Kennzahlen sortiert betrachtet werden, oder es werden die Regeln für spezielle Warenkategorien genauer betrachtet, siehe folgendes Beispiel.

screenshot-rules

Beispielausgabe von Regeln, hier die drei Regeln mit dem besten Lift. In der ersten Regel sieht man: Wenn Bier und Wein gekauft wird, wird auch Likör gekauft. Diese Regel hat einen Support von 0,002. Diese drei Produkte kommen also in 0,2 % der Transaktionen vor. Die Confidence von 0,396 zeigt, dass in 39,6 % der Transaktionen auch Likör gekauft wird, wenn Bier und Wein gekauft wird.

Umsetzung mit R

Die hier vorgestellten Methoden zur Warenkorbanalyse lassen sich mit dem Paket arules der Software R gut umsetzen. Im Folgenden gebe ich eine Liste von nützlichen Befehlen für diese Analysen mit dieser Software. Dabei wird mit data hier durchgehend der Datensatz der Transaktionsdaten bezeichnet.

summary(data)

Zusammenfassung des Datensatzes:

  • Anzahl der Transaktionen und Anzahl der Warengruppen
  • die häufigsten Produkte werden genannt mit Angabe der Häufigkeiten
  • Längenverteilung der Transaktionen (Anzahl der Produkte pro Transaktion): Häufigkeiten, deskriptive Maße wie Quartile
  • Beispiel für die Datenstruktur (Levels)
size(data)

Längen der Transaktionen (Anzahl der Produkte pro Transaktion)

hist(size(data))

Histogramm als grafische Darstellung der Transaktionslängen

itemFrequencyPlot(data, support=0.1)

rel. Häufigkeiten der einzelnen Produkte, hier nur die mit mindestens 10 % Vorkommen

crossTable(data)

Äquivalenzmatrix: Häufigkeiten der gemeinsamen Käufe für Produktpaare

dissJacc <- dissimilarity(data[, itemFrequency(data) > 0.05], method = "Jaccard", which = "items")

Unähnlichkeitsmatrix für die hierarchische Clusteranalyse

hcWard <- hclust(dissJacc, method = "ward.D")

Hierarchische Clusteranalyse

plot(hcWard)

Dendrogram der hierarchischen Clusteranalyse

rules <- apriori(data, parameter = list(support = 0.001, confidence = 0.2), control = list(verbose = FALSE))

Assoziationsregeln finden mit APRIORI-Algorithmus, hier Regeln mit mindestens 1% Support und 20 % Confidence

summary(rules)

Zusammenfassung der oben gefundenen Regeln (Anzahl, Eigenschaften Support, Confidence, Lift)

inspect(SORT(rules,by=“lift“)[1:5])

Einzelne Regeln betrachten, hier die laut Lift besten 5 Regeln

Referenzen:

  • Michael Hahsler, Kurt Hornik, Thomas Reutterer: Warenkorbanalyse mit Hilfe der Statistik-Software R, Innovationen in Marketing, S.144-163, 2006.
  • Michael Hahsler, Bettina Grün, Kurt Hornik, Christian Buchta, Introduciton to arules – A computational environment for mining association rules and frequent item sets. (Link zum PDF)
  • Rakesh Agrawal, Ramakrishnan Srikant, Fast algorithms for mining association rules, Proceedings of the 20th VLDB Conference Santiago, Chile, 1994
  • Software R:  R Core Team (2016). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. Link: R-Project.org.
  • Paket: arules: Mining Association Rules using R.

Beispieldatensatz: Groceries aus dem Paket arules

Benford-Analyse

Das Benfordsche Gesetz beschreibt eine Verteilung der Ziffernstrukturen von Zahlen in empirischen Datensätzen. Dieses Gesetz, welches kein striktes Naturgesetz ist, sondern eher ein Erklärungsversuch in der Natur und in der Gesellschaft vorkommende Zahlenmuster vorherzusagen.

Das Benfordsche Gesetz beruht auf der Tatsache, dass die Ziffern in einem Zahlensystem hierarchisch aufeinander aufbauen: Es beginnt mit der 1, dann folgt die 2, dann die 3 usw. In Kombination mit bestimmten Gesetzen der Natur (der natürliche Wachstumsprozess, dabei möglichst energiesparend wachsen/überleben) oder Ökonomie (so günstig wie möglich einkaufen) ist gemäß des Benfordschen Gesetz zu erwarten, dass die Ziffer 1 häufiger vorkommt als die 2, die wiederum häufiger vorkommt als die 3. Die Ziffer 9 braucht demnach den längsten Weg und kommt entsprechend verhältnismäßig seltener vor.

Dieses Phönomen hilft uns bei echten Zufallszahlen nicht weiter, denn dann sind alle Ziffern nicht aufeinander aufbauend, sondern mehr oder weniger gleichberechtigt in ihrem Auftrauen. Bei der klassischen und axiomatischen Wahrscheinlichkeit kommen wir damit also nicht ans Ziel.

Die Benford-Analyse ist im Grunde eine Ausreißeranalyse: Wir vergleichen Ziffernmuster in Datenbeständen mit der Erwartungshaltung des Benfordschen Gesetzes. Weicht das Muster von dieser Erwartung ab, haben wir Diskussionsbedarf.

Moderne Zahlensysteme sind Stellenwert-Zahlensysteme. Neben den Dual-, Oktal- und Hexadezimalzahlensystemen, mit denen sich eigentlich nur Informatiker befassen, wird unser Alltag vom Dezimalzahlensystem geprägt. In diesem Zahlensystem hat jede Stelle die Basis 10 (“dezi”) und einen Exponenten entsprechend des Stellenwertes, multipliziert mit der Ziffer d. Es ist eine Exponentialfunktion, die den Wert der Ziffern in bestimmter Reihenfolge ermittelt:

Z =sum_{i=-n}^{m}d_{i}cdot10^{i}

Die Benford-Analyse wird meistens nur für die erste Ziffer (also höchster Stellenwert!) durchgeführt. Dies werden wir gleich einmal beispielhaft umsetzen.

Die Wahrscheinlichkeit des Auftretens der ersten “anführenden” Ziffer d ist ein Logarithmus zur Basis B. Da wir im alltäglichen Leben – wie gesagt – nur im Dezimalzahlensystem arbeiten, ist für uns B = 10.

p(d)=log_{B}left( 1 +frac{1}{d} right)

Im Standard-Python lässt sich diese Formel leicht mit einer Schleife umsetzen:

import math

[round(math.log10(1+1/float(i))*100.0, 2) for i in range(1,10)]
Out: [30.1, 17.61, 12.49, 9.69, 7.92, 6.69, 5.8, 5.12, 4.58]

Benford-Algorithmus in Python mit NumPy und Pandas

Nachfolgend setzen wir eine Benford-Analyse als Minimalbeispiel in NumPy und Pandas um.

import numpy as np

x = np.arange(1,10) # NumPy-Array erstellen
x
Out: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9])

benford = np.round(np.log10(1+1/x) * 100.0, decimals=2) # Den Logarithmus auf das NumPy-Array anwenden und runden
Out: array([ 30.1 ,  17.61,  12.49,   9.69,   7.92,   6.69,   5.8 ,   5.12,
         4.58])

Nun möchten wir eine Tabelle erstellen, mit zwei Spalten, eine für die Ziffer (Digit), die andere für die relative Häufigkeit der Ziffer (Benford Law). Dazu nutzen wir das DataFrame aus dem Pandas-Paket. Das DataFrame erstellen wir aus den zwei zuvor erstellten NumPy-Arrays.

import pandas as pd

benfordFrame = pd.DataFrame({'Digit': x, 'Benford Law': benford})

benfordFrame
Out: 
   Benford Law  Digit
0        30.10      1
1        17.61      2
2        12.49      3
3         9.69      4
4         7.92      5
5         6.69      6
6         5.80      7
7         5.12      8
8         4.58      9

Man könnte sicherlich auch den natürlichen Index des DataFrames nutzen, indem wir diesen nur um jeweils 1 erhöhen, aber das verwirrt später nur und tun wir uns jetzt daher lieber nicht an…

benfordFrame.plot('Digit', 'Benford Law', kind='bar', title='Benford', legend=False)

Das Dataframe-Objekt kann direkt plotten (das läuft über die matplotlib, die wir aber nicht direkt einbinden müssen):benfordsches_gesetz_dezimalzahlen_ziffern

Diese neun Balken zeigen die Verteilung der Ziffernhäufigkeit nach dem Benfordschen Gesetz, diese Verteilung ist unsere Erwartungshaltung an andere nummerische Datenbestände, wenn diese einem natürlichen Wachstum unterliegen.

Analyse der Verteilung der ersten Ziffer in Zahlungsdaten

Jetzt brauchen wir Daten mit nummerischen Beträgen, die wir nach Benford testen möchten. Für dieses Beispiel nehme ich aus einem ein SAP-Testdatensatz die Spalte ‘DMBTR’ der Tabelle ‘BSEG’ (SAP FI). Die Spalte ‘DMBTR’ steht für “Betrag in Hauswährung’, die ‘BSEG’ ist die Tabelle für die buchhalterischen Belegsegmente.
Die Datei mit den Testdaten ist über diesen Link zum Download verfügbar (Klick) und enthält 40.000 Beträge.

Wir laden den Inhalt der Datei via NumPy.LoadTxt() und machen aus dem resultierenden NumPy-Array wieder ein Pandas.DataFrame und holen uns die jeweils erste Ziffer für alle Einträge als Liste zurück.

financialTransactions = np.loadtxt("[DEIN LOKALER PFAD]\BSEG_DMBTR.csv", skiprows=1)

financialTransactionsFrame = pd.DataFrame({'Zahlungen':financialTransactions})

firstDigits = [str(value)[0:1] for value in financialTransactionsFrame['Zahlungen']]

Die Einträge der ersten Ziffer in firstDigits nehmen wir uns dann vor und gruppieren diese über die Ziffer und ihrer Anzahl relativ zur Gesamtanzahl an Einträgen.

percentDigits = np.asarray([[i, firstDigits.count(str(i))/float(len(financialTransactionsFrame['Zahlungen']))*100] for i in range(1, 10)])

percentDigits.T[1].sum()
Out: 94.0

Wenn wir die Werte der relativen Anzahl aufsummieren, landen wir bei 94% statt 100%. Dies liegt daran, dass wir die Ziffer 0 ausgelassen haben, diese jedoch tatsächlich vorkommt, jedoch nur bei Beträgen kleiner 1.00. Daher lassen wir die Ziffer 0 außenvor. Wer jedoch mehr als nur die erste Ziffer prüfen möchte, wird die Ziffer 0 wieder mit in die Betrachtung nehmen wollen. Nur zur Probe nocheinmal mit der Ziffer 0, so kommen wir auf die 100% der aufsummierten relativen Häufigkeiten:

percentDigits = np.asarray([[i, firstDigits.count(str(i))/float(len(financialTransactionsFrame['Zahlungen']))*100] for i in range(0, 10)])
percentDigits.T[1].sum()
Out: 100.0

Nun erstellen wir ein weiteres Pandas.DataFrame, mit zwei Spalten: Die Ziffern (Digit) und die tatsächliche Häufigkeit in der Gesamtpopulation (Real Distribution):

percentDigitsFrame = pd.DataFrame({'Digit':percentDigits[:,0], 'Real Distribution':percentDigits[:,1]})

Abgleich der Ziffernhäufigkeit mit der Erwartung

Nun bringen wir die theoretische Verteilung der Ziffern, also nach dem zuvor genannten Logarithmus, und die tatsächliche Verteilung der ersten Ziffern in unseren Zahlungsdaten in einem Plot zusammen:

import matplotlib.pyplot as pyplot 

fig = pyplot.figure()

ax = fig.add_subplot(111)
ax2 = ax.twinx()

percentDigitsFrame.plot('Digit', 'Real Distribution', kind='bar', ax=ax2, width = 0.4, color="green", position=0, legend=False)
benfordFrame.plot('Digit', 'Benford Law', kind='bar', ax=ax, width = 0.4, color="blue", position=1, legend=False)

lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2, loc=0)

ax.set_ylim(1,35)
ax2.set_ylim(1,35)

pyplot.show()

In dem Plot wird deutlich, dass die Verteilung der führenden Ziffer in unseren Zahlungsdaten in ziemlich genau unserer Erwartung nach dem Benfordschen Gesetz entspricht. Es sind keine außerordentlichen Ausreißer erkennbar. Das wäre auch absolut nicht zu erwarten gewesen, denn der Datensatz ist mit 40.000 Einträgen umfassend genug, um dieses Muster gut abbilden zu können und von einer Manipulation dieser Beträge im SAP ist ebenfalls nicht auszugehen.

benford-analyse-python-fuer-sap-bseg-dmbtr

Gegenüberstellung: Computer-generierte Zufallszahlen

Jetzt wollen wir nochmal kurz darauf zurück kommen, dass das Benfordsche Gesetz für Zufallszahlen nicht unwendbar ist. Bei echten Zufallszahlen bin ich mir da auch sehr sicher. Echte Zufallszahlen ergeben sich beispielweise beim Lotto, wenn die Bälle mit Einzel-Ziffern durch eine Drehkugel hüpfen. Die Lottozahl-Ermittlung erfolgt durch die Zusammenstellung von jeweils gleichberechtigt erzeugten Ziffern.
Doch wie ist dies bei vom Computer generierten Zufallszahlen? Immerhin heißt es in der Informatik, dass ein Computer im Grunde keine Zufallszahlen erzeugen kann, sondern diese via Takt und Zeit erzeugt und dann durchmischt. Wir “faken” unsere Zahlungsdaten nun einfach mal via Zufallszahlen. Hierzu erstellen wir in NumPy ein Array mit 2000 Einträgen einer Zufallszahl (NumPy.Random.rand(), erzeugt floats 0.xxxxxxxx) und multiplizieren diese mit einem zufälligen Integer (Random.randint()) zwischen 0 und 1000.

from random import randint

financialTransactions = np.round(np.random.rand(2000) * randint(0,1000),decimals=2) # Zufallszahlen erzeugen, die auf dem ersten Blick als Zahlungsdaten durchgehen :-)

Erzeugen wir die obigen Datenstrukturen erneut, zeigt sich, dass die Verteilung der Zufallszahlen ganz anders aussieht: (vier unterschiedliche Durchläufe)

benford-analyse-python-numpy-pseudo-zufallszahlen

Anwendung in der Praxis

Data Scientists machen sich das Benfordsche Gesetz zu Nutze, um Auffälligkeiten in Zahlen aufzuspüren. In der Wirtschaftsprüfung und Forensik ist diese Analyse-Methode recht beliebt, um sich einen Eindruck von nummerischen Daten zu verschaffen, insbesondere von Finanztransaktionen. Die Auffälligkeit durch Abweichung vom Benfordchen Gesetz entsteht z. B. dadurch, dass Menschen eine unbewusste Vorliebe für bestimmte Ziffern oder Zahlen haben. Greifen Menschen also in “natürliche” Daten massenhaft (z. B. durch Copy&Paste) ein, ist es wahrscheinlich, dass sie damit auch vom Muster des Benfordschen Gesetzes abweichen. Weicht das Muster in Zahlungsströmen vom Bendfordsche Erwartungsmuster für bestimmte Ziffern signifikant ab, könnte dies auf Fälle von unnatürlichen Eingriffen hindeuten.

Die Benford-Analyse wird auch gerne eingesetzt, um Datenfälschungen in wissenschaftlichen Arbeiten oder Bilanzfälschungen aufzudecken. Die Benford-Analyse ist dabei jedoch kein Beweis, sondern liefert nur die Indizien, die Detailanalysen nach sich ziehen können/müssen.

Die Rastrigin-Funktion

Jeder Data Scientist kommt hin und wieder mal in die Situation, einen Algorithmus trainieren bzw. optimieren zu wollen oder zu müssen, ohne jedoch, dass passende Trainingsdaten unmittelbar verfügbar wären. Zum einen kann man in solchen Fällen auf Beispieldaten zugreifen, die mit vielen Analysetools mitgeliefert werden, oder aber man generiert sich seine Daten via mathematischer Modelle selbst, die für bestimmte Eigenschaften bekannt sind, die gute Bedingungen für das Optimierungstraining liefern. 

Ein solches Modell, das man als Machine Learning Entwickler kennen sollte, ist die Rastrigin-Funktion, die laut Wikipedia von Leonard A. Rastrigin erstmalig beschrieben wurde. Dabei handelt es sich um eine Häufigkeites-/Wahrscheinlichkeitsverteilung, deren Dichte mehrere lokale Modi (Gipfel) aufweist. Ein Modus (oder Modalwert) ist in einer Häufigkeitsverteilung der häufigste Wert (“Bergspitze”) bzw. der Wert mit der höchsten Wahrscheinlichkeit.

Anmerkung des Autors: Dieser Artikel stellt zum einen die Rastrigin-Funktion und ihre Bedeutung für die Optimierungsrechnung vor, ist zum anderen aber auch eine Einführung in den Umgang mit NumPy-Matrizen (die eine Menge For-Schleifen ersparen können).

Die Rastrigin-Funktion

Mathematisch beschrieben wird die Rastrigin-Funktion wie folgt:

f(x_1 cdots x_n) = An + sum_{i=1}^n (x_i^2 -10cos(2pi x_i))

-5.12 leq x_i leq 5.12

Wobei für das globale Minimum gilt: f(0) = 0
Außerdem ist zu beachten, dass A=10 eine Konstante ist.

Die Rastrigin-Funktion im Standard-Python umsetzen und visualisieren

Die Formel lässt sich in Python (wie natürlich in jeder anderen Programmiersprache auch) einfach umsetzen:

value = 10 + x**2 - 10 * math.cos(2 * math.pi * x)

Nun können wir über den klassischen Weg der Programmierung einfach eine For-Schleife verwenden, um die Rastrigin-Funktionswerte in eine Liste zu packen und mit einem Plot zu visualsieren, dabei bin ich leider doch nicht ganz um die Verwendung des NumPy-Pakets nicht herumgekommen:

import matplotlib.pyplot as pyplot
import numpy as np # NumPy hat die Matrizen-Datenstruktur, die wir benötigen 
import math as math # Grundlegende mathematische Funktionen (hier benötigt: Kreiszahl Pi und Cosinus-Funktion)

rastriginValues = []
i = 0


for x in np.arange(-5.12, 5.12, 0.01): # Die Python-eigene range()-Funktion kann leider keine Floats, sondern nur Integer erzeugen :-/
    value = 10 + x**2 - 10 * math.cos(2 * math.pi * x)
    i += 1
    print(i, x, value)
    rastriginValues.append(value)
    
pyplot.plot(rastriginValues)
pyplot.ylim(0,50)
pyplot.xlim(0,1024)
pyplot.show()

rastrigin-line-chart

Die grafische Darstellung zeigt, dass es sich tatsächlich um eine symmetrische multimodalen Verteilung handelt.

Die Rastrigin-Funktion mehrdimensional umsetzen, mit NumPy-Matrizen-Funktionen

Die obige Umsetzung der Rastrigin-Funktion ist eindimensional (eine Variable), braucht für die Darstellung allerdings zwei Dimensionen (f(x) und die Durchlaufanzahl bzw. Zeitachse). Nun könnten wir die Zahl der Variablen von 1 (x) auf 2 (x und y) erhöhen und eine dreidimensionale Darstellung erzeugen. Eine ähnliche dreidimensionale Darstellung gab es bereits in meiner Vorstellung des k-nearest-Neighbour-Algorithmus nachzuvollziehen. Dabei müssten wir die Konstante A=10 auf A=20 verdoppeln:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as pyplot
import numpy as np

figure = pyplot.figure()
axe = figure.add_subplot(111, projection='3d')

x = np.linspace(-5.12, 5.12, 100) # unterteilt den Bereich in 100 Schnitte, ähnlich: np.arange(-5.12, 5.12, 0.1)
y = np.linspace(-5.12, 5.12, 100)
x, y = np.meshgrid(x, y) # erzeugt ein Koordinatensystem

# Nun ohne Schleifen: Wir wenden die NumPy-Funktionen (np.cos statt math.cos und np.pi statt math.pi) 
# auf die NumPy-Arrays an (x und y) und erhalten ein NumPy-Array z zurück
z = 20 + x**2 - 10 * np.cos(2 * np.pi * x) + y**2 - 10 * np.cos(2* np.pi * y)

# Plotte die drei Variablen (x, y, z) im dreidimensionalen Raum
axe.plot_surface(x, y, z, rstride=1, cstride=1, cmap="jet", linewidth=0, antialiased=False)

pyplot.title('Rastrigin-Map')
pyplot.grid(True)
axes = pyplot.gca()
axes.set_xlim([-5.12,5.12])
axes.set_ylim([-5.12,5.12])
pyplot.show()

rastrigin-funktion-python-3d-plot

Die Rastrigin-Funktion wird gerne für Optimierungsalgorithmen eingesetzt, wofür sie wegen des großen Suchraums und der hohen Anzahl lokaler Modi ein herausforderndes Umfeld bietet. Beispielsweise wird – meines Erachtens nach – das wohl beliebteste Optimierungsverfahren im maschinellen Lernen, das Gradientenverfahren, hier keine guten Ergebnisse liefern, denn es gibt einfach zu viele lokale Minima.

 

 

Python vs R Statistics

Immer wieder wird mir von Einsteigern die Frage gestellt, ob sich der Einstieg und die Einarbeitung in die Programmiersprache Python eher lohnen würde als in R Statistics. Nun gibt es in den englischsprachigen Portalen bereits viele Diskussionen und Glaubenskriege zu diesem Vergleich – diese habe ich mir mit Absicht nicht weiter durchgelesen, sondern ich versuche hier meine Erfahrung aufs Blog zu bringen und bin auf Eure Meinungen/Erfahrungen gespannt!

Mit weniger R-Code schneller zum Ziel, und mit Python darüber hinaus

Was mir beim Einstieg in R gleich auffiel: Nach der Installation kann man sofort loslegen! Ein Plot oder eine Regressionsanalyse ist binnen weniger Code-Zeilen erledigt, denn die Sprache bringt diese Funktionen von Haus aus mit. In Python ist das Ziel auch nicht weit weg, allerdings müssen für die Plots erst die MatplotLib installiert werden, für Matrizenberechnung die Numpy-Bibliothek und um eine, mit der R-Datenstruktur Data.Frame vergleichbare Datenstruktur in Python zu erhalten, die Pandas-Bibliothek. Diese Python-Bibliotheken kann man zwar mit Fug und Recht als Bestandteil des Python-Universums ansehen, standardmäßig ausgeliefert werden sie aber nicht und auch sollten sie streng vom Standardpython in der Anwendung getrennt werden, im Klartext: Die Bibliotheken erfordern extra Einarbeitung und machen die Handhabung komplizierter, das einfache Python verliert ein Stück weit seine Einfachheit.

Auch die beliebte Entwicklungsumgebung R-Studio sucht seinesgleichen und ist IPython meiner Meinung nach hinsichtlich der Usability absolut überlegen. R ist einfach darauf ausgerichtet, Daten zu analysieren und zu visualisieren, aber beschränkt sich eben auch darauf.

“R is more about sketching, and not building,” says Michael Driscoll, CEO of Metamarkets. “You won’t find R at the core of Google’s page rank or Facebook’s friend suggestion algorithms. Engineers will prototype in R, then hand off the model to be written in Java or Python.”

Im Gegenzug ist Python eine Programmiersprache, die nicht nur an den einen Zweck gebunden ist. Mit Python können ebenfalls (Web-)Server- oder Desktop-Anwendungen und somit ohne Technologiebruch analytische Anwendungen komplett in Python entwickelt werden. Und auch wenn R ebenfalls unüberschaubar viele Packages mitbringt, bietet Python noch einiges mehr, beispielsweise zur dreidimensionalen Darstellung von Graphen.

Software-Entwickler lieben Python, Mathematiker eher R

Data Science ist ein äußerst interdisziplinäres Fachgebiet und Data Scientists können Mathematiker, Physiker, Informatiker, Ingenieure oder (wenn auch etwas seltener) Wirtschafts- oder auch Geisteswissenschaftler sein. Ein Großteil kommt aus der Mathematik oder äußerst mathematischer Fachgebiete wie der Physiker oder der Elektroingenieurwissenschaft. In diesen Studiengängen wird überwiegend mit Programmiersprachen gearbeitet, die von Mathematikern für Mathematiker entwickelt wurden, also R Statistics, MATLAB oder Octave. Beispielsweise ist meine Frau studierte Elektotechnikingenieurin und setzte alle ihre Prototypen des maschinellen Lernens in MATLAB um, sie findet sich aber auch in R gut zurecht.

Wer aus der Software-Entwicklung kommt, findet sich in Python vermutlich sehr viel schneller zurecht als in R. In meiner subjektiven Wahrnehmung stelle ich tatsächlich fest, dass diejenigen Data Scientists, die aus der Mathematik zum Data Science gekommen sind, meistens R präferieren und diejenigen, die aus der Anwendungsentwicklung kommen, eher mit Python arbeiten.

datascience

Python kollaboriert besser

Ein Data Scientist kommt selten allein, denn Data Science ist Teamarbeit. Und wo Teams ein gemeinsames Ziel erreicht sollen, werden besondere Anforderungen an die Arbeitsumgebung gestellt. Python gilt als eine syntaktisch leicht verständliche Programmiersprache, die manchmal sogar als “executable Pseudocode” bezeichnet wird (was allerdings dann doch leicht übertrieben ist…). Es ist also für alle Teammitglieder eine relativ einfach zu erlernende Sprache. Dabei muss Python nicht von allen Teammitgliedern favorisiert werden, denn eigene lokale Prototypen können in R, Octave oder was auch immer erstellt werden, lassen sich dann aber auch einfach in Python integrieren. Für richtig schnelle Anwendungen sind Python und R als Interpretersprachen sowieso zu langsam, solche Anwendungen werden am Ende in C/C++ umgesetzt werden müssen, aber selbst dann bietet Python nicht zu unterschätzende Vorteile: Der Erfolg von Python im wissenschaftlichen Rechnen beruht nämlich auch auf der unkomplizierten Integration von Quellcode der Programmiersprachen C, C++ und Fortran.

Neue Spieler auf dem Feld: Scala und Julia

Leider kann ich zu den beiden Programmiersprachen Scala und Julia (noch) nicht viel sagen. Scala scheint sich meiner Einschätzung nach als eine neue Alternative für Python zu entwickeln. Scala ist ein Produkt aus dem Java-Universum und war als eine Programmiersprache für unterschiedlichste Zwecke gedacht. Die Sprache setzt sich im Big Data Science immer weiter durch, einige Tools für Big Data Analytics (Apache Spark, Apache Flink) sind auf Scala ausgelegt und basieren selbst auf dieser Programmiersprache. Was Scala als eine stark von Java inspirierte Sprache sehr sympathisch macht, ist der enorm kompakte Code. Ein MapReduce-Algorithmus lässt sich in Scala mit einem Bruchteil an Code erstellen, als es in Java der Fall wäre, wie es auch die Code-Beispiele der Spark-Webseite eindrücklich zeigen: (Was ist eigentlich Apache Spark?)

Text Search in Python (Apache Spark)

textFile = sc.textFile("hdfs://...")

# Creates a DataFrame having a single column named "line"
df = textFile.map(lambda r: Row(r)).toDF(["line"])
errors = df.filter(col("line").like("%ERROR%"))
# Counts all the errors
errors.count()
# Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count()
# Fetches the MySQL errors as an array of strings
errors.filter(col("line").like("%MySQL%")).collect()
Text Search in Scala (Apache Spark)

val textFile = sc.textFile("hdfs://...")

// Creates a DataFrame having a single column named "line"
val df = textFile.toDF("line")
val errors = df.filter(col("line").like("%ERROR%"))
// Counts all the errors
errors.count()
// Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count()
// Fetches the MySQL errors as an array of strings
errors.filter(col("line").like("%MySQL%")).collect()
Text Search in Java (Apache Spark)

// Creates a DataFrame having a single column named "line"
JavaRDD<String> textFile = sc.textFile("hdfs://...");
JavaRDD<Row> rowRDD = textFile.map(
  new Function<String, Row>() {
    public Row call(String line) throws Exception {
      return RowFactory.create(line);
    }
  });
List<StructField> fields = new ArrayList<StructField>();
fields.add(DataTypes.createStructField("line", DataTypes.StringType, true));
StructType schema = DataTypes.createStructType(fields);
DataFrame df = sqlContext.createDataFrame(rowRDD, schema);

DataFrame errors = df.filter(col("line").like("%ERROR%"));
// Counts all the errors
errors.count();
// Counts errors mentioning MySQL
errors.filter(col("line").like("%MySQL%")).count();
// Fetches the MySQL errors as an array of strings
errors.filter(col("line").like("%MySQL%")).collect();

Julia wurde (ähnlich wie R) explizit für den Zweck der statistischen Datenanalyse entwickelt, wird auf Grund des aktuellen Beta-Status noch kaum produktiv eingesetzt. Da Julia auf sehr schnelle Anwendungen ausgerichtet ist, liegt in Julia die neue Hoffnung für jene, für die R und Python zu langsame Interpretersprachen sind.

Buchempfehlungen zum Einstieg in R oder Python

Es versteht sich von selbst, dass ich alle Bücher auch selbst besitze und mehr als nur das Vorwort gelesen habe…

Was ist Eure Erfahrung? Ihr seid gefragt!

Schreibt Eure Meinung einfach als Kommentar zu diesem Artikel! Wer meint, den Vergleich logischer, “richtiger” und nachvollziehbarer aufs digitale Papier bringen zu können, darf einen Artikelvorschlag übrigens gerne an redaktion@data-science-blog.com senden!

Was ist eigentlich Apache Spark?

Viele Technologieanbieter versprechen schlüsselfertige Lösungen für Big Data Analytics, dabei kann keine proprietäre Software-Lösung an den Umfang und die Mächtigkeit einiger Open Source Projekten heranreichen.

Seit etwa 2010 steht das Open Source Projekt Hadoop, ein Top-Level-Produkt der Apache Foundation, als einzige durch Hardware skalierbare Lösung zur Analyse von strukturierten und auch unstrukturierten Daten. Traditionell im Geschäftsbereich eingesetzte Datenbanken speichern Daten in einem festen Schema ab, das bereits vor dem Laden der Daten definiert sein muss. Dieses Schema-on-Write-Prinzip stellt zwar sicher, dass Datenformate bekannt und –konflikte vermieden werden. Es bedeutet jedoch auch, dass bereits vor dem Abspeichern bekannt sein muss, um welche Daten es sich handelt und ob diese relevant sind. Im Hadoop File System (HDFS) wird ein Schema für erst bei lesenden Zugriff erstellt.

Apache Spark ist, ähnlich wie Hadoop, dank Parallelisierung sehr leistungsfähig und umfangreich mit Bibliotheken (z. B. für Machine Learning) und Schnittstellen (z. B. HDFS) ausgestattet. Allerdings ist Apache Spark nicht für jede Big Data Analytics Aufgabe die beste Lösung, Als Einstiegslektüre empfiehlt sich das kostenlose Ebook Getting Started with Spark: From Inception to Production. Wer jedoch erstmal wissen möchte, erfährt nachfolgend die wichtigsten Infos, die es über Apache Spark zu wissen gilt.

Was ist Apache Spark?

Apache Spark ist eine Allzweck-Tool zur Datenverarbeitung, eine sogenannte Data Processing Engine. Data Engineers und Data Scientists setzen Spark ein, um äußerst schnelle Datenabfragen (Queries) auf große Datenmengen im Terabyte-Bereich ausführen zu können.

Spark wurde 2013 zum Incubator-Projekt der Apache Software Foundation, eine der weltweit wichtigsten Organisationen für Open Source. Bereits 2014 es wie Hadoop zum Top-Level-Produkt. Aktuell ist Spark eines der bedeutensten Produkte der Apache Software Foundation mit viel Unterstützung von Unternehmen wie etwa Databricks, IBM und Huawei.

Was ist das Besondere an Spark?

Mit Spark können Daten transformiert, zu fusioniert und auch sehr mathematische Analysen unterzogen werden.
Typische Anwendungsszenarien sind interactive Datenabfragen aus verteilten Datenbeständen und Verarbeitung von fließenden Daten (Streaming) von Sensoren oder aus dem Finanzbereich. Die besondere Stärke von Spark ist jedoch das maschinelle Lernen (Machine Learning) mit den Zusätzen MLib (Machine Learning Bibliothek) oder SparkR (R-Bibliotheken direkt unter Spark verwenden), denn im Gegensatz zum MapReduce-Algorithmus von Hadoop, der einen Batch-Prozess darstellt, kann Spark sehr gut iterative Schleifen verarbeiten, die für Machine Learning Algorithmen, z. B. der K-Nearest Neighbor Algorithmus, so wichtig sind.spark-stack

Spark war von Beginn an darauf ausgelegt, Daten dynamisch im RAM (Arbeitsspeicher) des Server-Clusters zu halten und dort zu verarbeiten. Diese sogenannte In-Memory-Technologie ermöglicht die besonders schnelle Auswertung von Daten. Auch andere Datenbanken, beispielsweise SAP Hana, arbeiten In-Memory, doch Apache Spark kombiniert diese Technik sehr gut mit der Parallelisierung von Arbeitsschritten über ein Cluster und setzt sich somit deutlich von anderen Datenbanken ab. Hadoop ermöglicht über MapReduce zwar ebenfalls eine Prallelisierung, allerdings werden bei jedem Arbeitsschrit Daten von einer Festplatte zu einer anderen Festplatte geschrieben. Im Big Data Umfeld kommen aus Kostengründen überwiegend noch mechanisch arbeitende Magnet-Festplatten zum Einsatz, aber selbst mit zunehmender Verbreitung von sehr viel schnelleren SSD-Festplatten, ist der Arbeitsspeicher hinsichtlich der Zeiten für Zugriff auf und Schreiben von Daten unschlagbar. So berichten Unternehmen, die Spark bereits intensiv einsetzen, von einem 100fachen Geschwindigkeitsvorteil gegenüber Hadoop MapReduce.

Spark kann nicht nur Daten im Terabyte, sondern auch im Petabyte-Bereich analysieren, ein entsprechend großes Cluster, bestehend aus tausenden physikalischer oder virtueller Server, vorausgesetzt. Ähnlich wie auch bei Hadoop, skaliert ein Spark-Cluster mit seiner Größe linear in seiner Leistungsfähigkeit. Spark ist neben Hadoop ein echtes Big Data Framework.
Spark bringt sehr viele Bibliotheken und APIs mit, ist ferner über die Programmiersprachen Java, Python, R und Scala ansprechbar – das sind ohne Zweifel die im Data Science verbreitetsten Sprachen. Diese Flexibilität und geringe Rüstzeit rechtfertigt den Einsatz von Spark in vielen Projekten. Es kann sehr herausfordernd sein, ein Data Science Team mit den gleichen Programmiersprachen-Skills aufzubauen. In Spark kann mit mehreren Programmiersprachen gearbeitet werden, so dass dieses Problem teilweise umgangen werden kann.spark-runs-everywhere

In der Szene wird Spark oftmals als Erweiterung für Apache Hadoop betrachtet, denn es greift nahtlos an HDFS an, das Hadoop Distributed File System. Dank der APIs von Spark, können jedoch auch Daten anderer Systeme abgegriffen werden, z. B. von HBase, Cassandra oder MongoDB.

Was sind gängige Anwendungsbeispiele für Spark?

  • ETL / Datenintegration: Spark und Hadoop eignen sich sehr gut, um Daten aus unterschiedlichen Systemen zu filtern, zu bereinigen und zusammenzuführen.
  • Interaktive Analyse: Spark eignet sich mit seinen Abfragesystemen fantastisch zur interaktiven Analyse von großen Datenmengen. Typische Fragestellungen kommen aus dem Business Analytics und lauten beispielsweise, welche Quartalszahlen für bestimmte Vertriebsregionen vorliegen, wie hoch die Produktionskapazitäten sind oder welche Lagerreichweite vorhanden ist. Hier muss der Data Scientist nur die richtigen Fragen stellen und Spark liefert die passenden Antworten.
  • Echtzeit-Analyse von Datenströmen: Anfangs vor allem zur Analyse von Server-Logs eingesetzt, werden mit Spark heute auch Massen von Maschinen- und Finanzdaten im Sekundentakt ausgewertet. Während Data Stream Processing für Hadoop noch kaum möglich war, ist dies für Spark ein gängiges Einsatzgebiet. Daten, die simultan von mehreren Systemen generiert werden, können mit Spark problemlos in hoher Geschwindigkeit zusammengeführt und analysiert werden.
    In der Finanzwelt setzen beispielsweise Kreditkarten-Unternehmen Spark ein, um Finanztransaktionen in (nahezu) Echtzeit zu analysieren und als potenziellen Kreditkartenmissbrauch zu erkennen.
  • Maschinelles Lernen: Maschinelles Lernen (ML – Machine Learning) funktioniert desto besser, je mehr Daten in die ML-Algorithmen einbezogen werden. ML-Algorithmen haben in der Regel jedoch eine intensive, vom Data Scientist betreute, Trainingsphase, die dem Cluster viele Iterationen an Arbeitsschritten auf die großen Datenmengen abverlangen. Die Fähigkeit, Iterationen auf Daten im Arbeitsspeicher, parallelisiert in einem Cluster, durchführen zu können, macht Spark zurzeit zu dem wichtigsten Machine Learning Framework überhaupt.
    Konkret laufen die meisten Empfehlungssysteme (beispielsweise von Amazon) auf Apache Spark.

 

Einführung in WEKA

Waikato Environment for Knowledge Analysis, kurz WEKA, ist ein quelloffenes, umfangreiches, plattformunabhängiges Data Mining Softwarepaket. WEKA ist in Java geschrieben und wurde an der WAIKATO Iniversität entwickelt. In WEKA sind viele wichtige Data Mining/Machine Learning Algorithmen implementiert und es gibt extra Pakete, wie z. B. LibSVM für Support Vector Machines, welches nicht in WEKA direkt implementiert wurde. Alle Einzelheiten zum Installieren und entsprechende Download-Links findet man unter auf der Webseite der Waikato Universität. Zusammen mit der Software wird ein Manual und ein Ordner mit Beispiel-Datensätzen ausgeliefert. WEKA arbeitet mit Datensätzen im sogenannten attribute-relation file format, abgekürzt arff. Das CSV-Format wird aber ebenfalls unterstützt. Eine Datei im arff-Format ist eine ASCI-Textdatei, welche aus einem Header- und einem Datateil besteht. Im Header muss der Name der Relation und der Attribute zusammen mit dem Typ stehen, der Datenteil beginnt mit einem @data-Schlüsselwort. Als Beispiel sei hier ein Datensatz mit zwei Attributen und nur zwei Instanzen gegeben.

@relation my_relation
@attribute first_attribute numeric
@attribute second_attribute numeric
@attribute class {-1,1}
@data
2.5 3.8 1
1.2 1.5 -1

WEKA unterstützt auch direktes Einlesen von Daten aus einer Datenbank (mit JDBC) oder URL. Sobald das Tool installiert und gestartet ist, landet man im Hauptmenü von WEKA – WEKA GUI Chooser 1.

Abbildung 1: WEKA GUI Chooser

Abbildung 1: WEKA GUI Chooser

Der GUI Chooser bietet den Einstieg in WEKA Interfaces Explorer, Experimenter, KnowledgeFlow und simple CLI an. Der Explorer ist ein graphisches Interface zum Bearbeiten von Datensätzen, Ausführen von Algorithmen und Visualisieren von den Resultaten. Es ist ratsam, dieses Interface als Erstes zu betrachten, wenn man in WEKA einsteigen möchte. Beispielhaft führen wir jetzt ein paar Algorithmen im Explorer durch.

Der Explorer bietet mehrere Tabs an: Preprocess, Classify, Cluster, Associate, Select attributes und Visualize. Im Preprocess Tab hat man die Möglichkeit Datensätze vorzubereiten. Hier sind zahlreiche Filter zum Präprozessieren von Datensätzen enthalten. Alle Filter sind in supervised und unsupervised unterteilt, je nachdem, ob das Klassenattribut mitbetrachtet werden soll oder nicht. Außerdem kann man entweder Attribute oder Instanzen betrachten, mit Attributen lässt man Filter spaltenweise arbeiten und bei Instanzen reihenweise. Die Auswahl der Filter ist groß, man kann den ausgewählten Datensatz diskretisieren, normalisieren, Rauschen hinzufügen etc. Unter Visualize können z. B. die geladenen Datensätze visualisert werden. Mit Select attributes kann man mithilfe von Attribut Evaluator und Search Method ein genaueres Ergebnis erzielen. Wenn man im Preprocess den Datensatz lädt, erhält man einen Überblick über den Datensatz und dessen Visualisierung. Als Beispiel wird hier der Datensatz diabetes.arff genommen, welcher mit WEKA zusammen ausgeliefert wird. Dieser Datensatz enthält 768 Instanzen mit je 9 Attributen, wobei ein Attribut das Klassenattribut ist. Die Attribute enthalten z. B. Informationen über die Anzahl der Schwangerschaften, diastolischer Blutdruck, BMI usw. Alle Attribute, außer dem Klassenattribut, sind numerisch. Es gibt zwei Klassen tested negativ und tested positiv, welche das Resultat des Testens auf diabetes mellitus darstellen. über Preprocess -> Open File lädt man den Datensatz in WEKA und sieht alle relevanten Informationen wie z. B. Anzahl und Name der Attribute. Nach dem Laden kann der Datensatz klassifiziert werden.

Abbildung 2: Diabetes.arff Datensatz geladen in WEKA

Abbildung 2: Diabetes.arff Datensatz geladen in WEKA

Hierzu einfach auf Classify klicken und unter Choose den gewünschten Algorithmus auswählen. Für diesen Datensatz wählen wir jetzt den Algorithmus kNN (k-Nearest Neighbour). Der Algorithmus klassifiziert das Testobjekt anhand der Klassenzugehörigkeit von den k Nachbarobjekten, die am nähsten zu dem Testobjekt liegen. Die Distanz zwischen den Objekten und dem Testobjekt wird mit einer Ähnlichkeitsmetrik bestimmt, meistens als euklidische oder Manhattan-Distanz. In WEKA ist der Algorithmus unter lazy iBk zu finden. Wenn man auf das Feld neben dem Algorithmusnamen in WEKA mit rechter Maustaste klickt, kann man unter show properties die Werte für den ausgewählten Algorithmus ändern, bei iBk kann man u.A. den Wert für k ändern. Für den ausgewählten Datensatz diabetes.arff stellen wir beispielsweise k = 3 ein und führen die 10-fache Kreuzvalidierung durch, indem wir unter Test Options die Cross Validation auswählen. Nach der Klassifikation werden die Ergebnisse in einer Warhheitsmatrix präsentiert. In unserem Fall sieht diese wie folgt aus:

a b <-- classified as
410 90 | a = tested_negative
120 148 | b = tested_positive

Die Anzahl der richtig klassifizierten Instanzen beträgt 72.6563 %. Wenn man in der Result list auf den entsprechenden Algorithmus einen Rechtsklick macht, kann man z. B. noch den Fehler der Klassifizierung visualisieren. Entsprechend lassen sich im Explorer unter Cluster Clustering-Algorithmen und unter Associate Assoziationsalgorithmen auf einen ausgewählten Datensatz anwenden. Die restlichen Interfaces von WEKA bieten z. T. die gleiche Funktionalität oder erweitern die Möglichkeiten des Experimentierens, fordern aber mehr Erfahrung und Wissen von dem User. Das Experimenter Interface dient dazu, mehrere Datensätze mit mehreren Algorithmen zu analysieren. Mit diesem Interface kann man groß-skalierte Experimente durchführen. Simple CLI bietet dem User eine Kommandozeile, statt einem graphischen Interface, an.