Library

Teile diesen Beitrag:

Der t-Test – Vergleich von Mittelwerten

Teile diesen Beitrag:Hej Leute,
heute geht es um Statistik und zwar um den t-Test! Jetzt ist es raus, kreisch, schrei, Hilfe, renn weg. Ruhig Blut, so schwierig ist der Test gar nicht. Ich erkläre euch ganz in Ruhe, wie er funktioniert und worauf es ankommt.
Der t-Test ist einer der bekanntesten und häufigsten verwendeten Hypothesentest. Er dient dazu, statistisch zu prüfen, ob der Mittelwert einer Stichprobe einem vorgegebenen Wert entspricht. Ich könnte zum Beispiel prüfen, ob das mittlere Alter der Personen, die ich befragt habe, dem deutschen Altersdurchschnitt von 44,3 (im Jahr 2010) entspricht.
Das kann man auch auf zwei Stichproben erweitern. D.h. man prüft, ob die Mittelwerte zweier Stichproben übereinstimmen. So könnte man testen, ob in einer Stichprobe das mittlere Alter der befragten Frauen dem mittleren Alter der befragten Männern entspricht.
Die Geschichte des t-Tests
Der t-Test wurde ursprünglich von William Sealy Gosset, besser bekannt als Student, im Jahr 1908 entwickelt. Gosset arbeitete bei der Guiness-Brauerei, befasste sich dort mit Gerstenqualität und entdeckte dabei die t-Verteilung. Er veröffentlichte seine Erkenntnisse unter dem Pseudonym Student, da sein Arbeitgeber nicht erlaubte, wissenschaftliche Arbeiten zu veröffentlichen. Daher der Name Studentsche t-Verteilung. Ronald Aylmer Fisher erkannte die Bedeutung von Gossets Arbeit und entwickelte die heute gebräuchliche t-Prüfgröße.
Gosset hat herausgefunden, dass der Mittelwert einer normalverteilten Stichprobe nicht mehr normalverteilt ist, wenn man die Varianz schätzen muss. Aber nochmal ganz langsam.
Wie funktioniert ein Hypothesentest
Ich versuche in diesem Abschnitt ganz grob zu erklären, wie ein Hypothesentest funktioniert. Bald sollte ein eigener, ausführlicheren Artikel dazu folgen, den verlinke ich dann natürlich hier.
Zuerst brauchen wir eine Nullhypothese, also z.B. die Annahme, dass der Mittelwert des Alters der Grundgesamtheit, aus der wir die Stichprobe gezogen haben, 44,3 Jahre beträgt. Im Endeffekt will man das Gegenteil zeigen. Man sagt, dass man die Nullhypothese ablehnt. Man lehnt ab, wenn es sehr unwahrscheinlich ist, die Daten – gegeben die Nullhypothese – zu beobachten. Wir gehen aber erstmal von der Nullhypothese aus. Nun wird eine Prüfgröße, auch Test-Statistik genannt, berechnet. Unter der Annahme, dass die Nullhypothese stimmt, sollte die Prüfgröße einer bekannten Verteilung gehorchen, z.B. normal- oder t-verteilt sein.
Was heißt das nun wieder? Die Prüfgröße ist doch nur eine Zahl. Das ist so gemeint, dass wenn wir das Experiment (also die Auswahl einer Stichprobe und Berechnung der Prüfgröße) vielfach wiederholen, die Prüfgrößen der ganzen Experimente sich einer bekannten Verteilung nach verteilen.

Nun können wir berechnen, ob es (unter der angenommenen Nullhypothese) sehr unwahrscheinlich ist, die Stichprobe zu beobachten. Hier kommt das Signifikanzniveau ins Spiel. Wählen wir (ein typischer Wert), dann prüfen wir, ob wir diese Teststatistik in weniger als 5% der Fälle bekommen, wenn wir immer wieder eine Stichprobe ziehen würden. Ist das so, dann lehnen wir die Nullhypothese ab.
Mathematisch wird das über die Quantilsfunktion der bekannten Verteilung gemacht, weil die Quantilsfunktion die Umkehrfunktion zur Verteilungsfunktion ist. Es wird berechnet, ob die Prüfgröße Z am Rande der Verteilung liegt. Also bei einem einseitigem Test mit , ob Z im 5%- bzw. 95%-Quantil liegt (je nach Richtung).
Der Z-Test – wenn es doch so einfach wäre
So, dieser Teil hier ist tatsächlich mathematisch, kommen doch ein paar Formeln vor. Danach wird es aber wieder besser.
Der Mittelwert einer Stichprobe wird über das arithmetische Mittel berechnet, also die Summe aller Werte, geteilt durch die Anzahl
   
Stammt die Stichprobe aus einer Normalverteilung mit Mittelwert mu und Varianz sigma², dann ist das arithmetische Mittel wieder normalverteilt mit Mittelwert mu, aber mit Varianz sigma²/n.
Würden wir die Varianz kennen, dann könnten wir statt des t-Tests einen Gauß-Test, auch Z-Test genannt, machen, um zu prüfen, ob der Mittelwert einem vorgegebenen Wert mu_0 entspricht. Unter der Hypothese, dass der wahre Mittelwert mu_0 ist, können wir folgende Transformation machen, damit das Ergebnis standardnormalverteilt ist. Also Mittelwert = 0 und Varianz = 1
   
Nun können wir berechnen, ob es (unter der Nullhypothese) sehr unwahrscheinlich ist, die Prüfgröße Z zu beobachten.
 
Die Teststatistik ist t-verteilt
Da aber in der Praxis die Varianz nicht bekannt ist, müssen wir diese schätzen
   
Nun macht uns das aber unsere schöne Normalverteilung von Z kaputt, denn statt durch Konstante sigma teilen wir nun durch S. ist -verteilt. Und so kann man die t-Verteilung definieren, als Standardnormalverteilung durch Wurzel aus -Verteilung. Die -Verteilung hat einen Freiheitsgrad, also
   
mit n = Stichprobengröße
Jetzt kennen wir die Verteilung von T und können die übliche Transformation mit Quantilsfunktion machen.
Wir bekommen nur eine t-Verteilung, wenn die ursprüngliche Verteilung eine Normalverteilung ist. Ansonsten kommt der zentrale Grenzwertsatz ins Spiel, der besagt, dass X_quer einer Normalverteilung annähert. Typischerweise stellt man die Bedingung, dass die Stichprobengröße n>30 ist.
Voraussetzungen zur Anwendung vom t-test
Der vorherige Abschnitt gibt uns schon die Antwort
X ist normalverteilt oder
Der zentrale Grenzwertsatz wird erfüllt und die Stichprobengröße > 30
Einseitiger vs. zweiseitiger t-Test
Bei einem zweiseitigen Test lautet die Nullhypothese und die Alternative . Haben wir vorher schon eine Idee zur Richtung, dann können wir auch einen einseitigen Test machen. Da haben wir dann die Nullhypothese und als Alternative . Das ganze geht natürlich auch umgekehrt
Einstichproben- vs. Zweistichproben-t-Test
Beim Einstichproben-t-Test haben wir eine Stichprobe, deren Mittelwert wir mit einem vorgegebenen Wert vergleichen wollen.
Beim Zweistichproben-t-Test haben wir zwei Stichproben, deren Mittelwerte wir miteinander vergleichen wollen. Das macht die Sache leider etwas komplizierter. Wir unterscheiden drei Fälle:
zwei unabhängige Stichproben mit gleicher Varianz
zwei verbundene/abhängige Stichproben
zwei unabhängige Stichproben mit ungleicher Varianz
Unabhängige Stichproben mit gleicher Varianz
Wir betrachten die Differenz der beiden Mittelwerte und schauen, ob diese gleich ist. Für die Teststatistik schätzen wir noch die gemeinsame Varianz der beiden Stichproben.
   
mit
   
Voraussetzungen: Stichproben normalverteilt oder beide so groß, dass Mittelwert durch Normalverteilung approximiert wird
Verbundene/Abhängige Stichproben
Unter diesem Fall versteht man zwei Beobachtungen der gleichen Gruppe. Also zum Beispiel vor und nach einer Behandlung oder auch unter verschiedenen Bedingungen.
Man will nun wissen, ob sich die zwei Mittelwerte voneinander unterscheiden. Anders ausgedrückt, ob die Differenz gleich null ist. Und genauso macht man es dann auch, d.h. einen Einstichproben-t-Test, der auf die Differenz angewandt wird.
Hier muss natürlich wieder geschaut werden, dass die Differenz die Voraussetzungen erfüllt. Im Wesentlichen heißt das, dass die Stichprobe groß genug ist.
Unabhängige Stichproben mit ungleicher Varianz
Problem ist die Voraussetzung, dass Varianz beider Stichproben gleich ist. Das ist in der Realität meist nicht gegeben, daher verwendet man den Welch-Test, der diese Bedingung nicht stellt. Streng genommen ist die Teststatistik nicht t-verteilt, aber sie nähert sich einer t-Verteilung an.
   
Man kann auch die Freiheitsgrade mit folgender Formel bestimmen:
   
Anwendungen vom t-Test
Prüfverfahren
Wie oben geschrieben ist war die Untersuchung von Gerstenqualität tatsächlich der Ursprung der t-Verteilung. Das lässt sich natürlich auf alle möglichen Situationen verallgemeinern, ob Agrarwissenschaft, Biologie, Pharma oder Maschinenbau. Immer dann, wenn die Mittelwerte zweier Gruppen verglichen werden sollen, kommt der t-Test ins Spiel.
Befragungen
Alle Arten von Befragungen, bei denen ein Mittelwert sinnvoll ist. Aber Achtung: Verwendung von Mittelwerten bei Likert-Skalen sind umstritten. Der Modus ist hier die bessere Wahl.
A/B-Test
A/B-Tests sind im Online-Marketing in aller Munde. Dazu werden zwei Varianten gebildet, z.B. Buttonplatzierung oder –farbe oder zwei verschiedene Facebook-Werbeanzeigen. Diese werden nun den Nutzern gezeigt und das Verhalten gemessen. Dann können die beiden Varianten miteinander verglichen werden, um dann die bessere, d.h. meistens profitablere, Variante zu verwenden.
So könnte man zum Beispiel die Conversion-Rate zweier Anzeigen untersuchen, indem man mittels t-Test prüft, ob sie gleich sind oder eine doch signifikant höher ist.
Regression
In den Statistiken einer linearen Regression wird normalerweise für jeden Koeffizienten mittels t-Test geprüft, ob dieser sich signifikant von 0 unterscheidet.
So, habt ihr euch wirklich den ganzen Artikel durchgelesen? Jetzt seid ihr hoffentlich mit dem Grundlagenwissen ausgestattet, um t-Tests anwenden zu können. Schreibt einen Kommentar, in welchem Zusammenhang ihr schon einen t-Test gemacht habt.
Happy testing,
Euer Holger
Teile diesen Beitrag:

Strings in Python

Teile diesen Beitrag:Hej Leute,
Zu Beginn ein kleiner Hinweis: Dieser Beitrag ist ein Auszug aus dem neuen Python Komplettkurs von Edley. Falls ihr innerhalb von 30 Tagen Python lernen wollt und ein übersichtliches Nachschlagewerk mit allen wichtigen Themen braucht, ist das eine empfehlenswerte Ressource.
Heute lernt ihr, was Strings sind und wie man mit Ihnen arbeitet:
Strings
Im Beitrag über Datentypen wurden die String-Variablen ja schon kurz vorgestellt. Diese dienen dazu, Zeichenketten aufzunehmen und wiederzugeben. Strings werden in Anführungszeichen (“ oder ‚) gestellt. Um String-Variablen zu bearbeiten bzw. zu verändern, kommen 2 Operatoren besonders häufig zum Einsatz:
Das Stern-Symbol (*). Dieses kommt bei Zahlen für die Multiplikation zum Einsatz. Ihr könnt jedoch auch eine Zeichenkette mit einer Zahl “multiplizieren”, in diesem Fall erhaltet ihr bei der Ausgabe eine Wiederholung der entsprechenden Zeichen in der vorgegebenen Anzahl.
Außerdem kennt ihr aus dem Beitrag über Operatoren bereits das Pluszeichen (+). Dieses hat, neben der mathematischen Addition von Zahlen, zwei Zeichenketten miteinander verbunden.
In den folgenden Abschnitten lernt ihr weitere Befehle kennen, mit denen es möglich ist, String-Variablen zu bearbeiten.
Wichtig ist es zunächst, auf einen einzelnen Teil der Zeichenkette zugreifen zu können. Das ist möglich, indem man hinter den Variablennamen in eine eckige Klammer ([] bei Windows mit Alt Gr + 8 und Alt Gr + 9, bei macOS mit alt + 5 und alt + 6) die Position des entsprechenden Buchstabens schreibt. Dabei müsst ihr lediglich berücksichtigen, dass der erste Buchstabe den Index 0 hat. Daher muss die entsprechende Zahl immer um 1 kleiner sein, als die eigentliche Position.
Will man beispielsweise auf den Buchstaben auf Position 4 zugreifen, muss man diesen Buchstaben über den Index 3 ansteuern.

Häufig ist es nicht nur notwendig, einen einzelnen Buchstaben aus der Zeichenkette herauszulösen, sondern einen größeren Bereich. Auch das ist möglich. Hierfür müsst ihr ebenfalls eine eckige Klammer hinter den Variablennamen schreiben. Darin stehen der Start- und der Endpunkt mit einem Doppelpunkt  voneinander getrennt. Dabei müsst ihr berücksichtigen, dass das Zeichen, das ihr als Endpunkt angebt, nicht mehr ausgegeben wird, sondern nur alle Zeichen, die vor dieser Position stehen. Wenn ihr am Anfang der Zeichenkette beginnen wollt, dann könnt ihr dafür den Index 0 eingeben. Alternativ dazu ist es möglich, die Zahl vor dem Doppelpunkt wegzulassen. Dann beginnt das Programm automatisch am Anfang der Zeichenkette. Wenn man die Zahl nach dem Doppelpunkt weglässt, beginnt das Programm am angegebenen Startpunkt und gibt alle Zeichen bis zum Ende der Zeichenkette wieder.
Das folgende Beispielprogramm zeigt, wie man Teile aus einer String-Variablen herauslösen kann:

Python

variable = "Herzlich Willkommen!"

print (variable[0])
print (variable[5])
print (variable[3:7])
print (variable[:8])
print (variable[9:])
print (variable[-1])

12345678

variable = "Herzlich Willkommen!" print (variable[0])print (variable[5])print (variable[3:7])print (variable[:8])print (variable[9:])print (variable[-1])

Wenn ihr dieses Programm ausführt, seht ihr genau, wie der Zugriff auf einzelne Teile der Zeichenkette erfolgt.
Interessant ist dabei der letzte Befehl. Man könnte meinen, dass es keine negativen Positionen geben kann. Das Minuszeichen steht jedoch dafür, dass das Programm beim Zählen der Position von hinten beginnt. Die Position -1 bezeichnet daher das letzte Zeichen, die Position -2 das vorletzte und so weiter.

Anmerkung: Eigentlich wäre es logisch, für das letzte Zeichen die Position -0 zu verwenden. Das ist mathematisch jedoch gleichbedeutend mit 0, sodass der Interpreter das erste Zeichen ausgibt. Daher muss man immer mit -1 beginnen, wenn man beim letzten Zeichen beginnen will.
 

String-Methoden
Neben diesen einfachen Operationen mit Zeichenketten gibt es noch viele weitere, “schlauere” Möglichkeiten der Bearbeitung: Es handelt sich um Funktionen, die in Python integriert sind und die sich durch einen ganz bestimmten Schlüsselbegriff abrufen lassen. Diese Funktionen werden als String-Methoden bezeichnet. Die Vielfalt ist dabei so groß, dass es in diesem Beitrag nicht sinnvoll wäre, jede einzelne Funktion zu beschreiben. Die wichtigsten sollen jedoch kurz vorgestellt werden.
Um diese Methoden anzuwenden, ist es in der Regel notwendig, die Variable, die die Zeichenkette enthält, dem entsprechenden Schlüsselbegriff durch einen Punkt getrennt voranzustellen. Danach steht eine Klammer, die jedoch in vielen Fällen leer bleiben kann. Manchmal, je nach Methode, ist es allerdings notwendig, hier einen bestimmten Parameter einzufügen, damit die Methode richtig funktioniert. Um sie auszuprobieren, soll ein kleines Programm mit verschiedenen Methoden als Beispiel dienen:

Python

text = "herzlich willkommen!"

print (text.capitalize())
print (text.count("i"))
print (text.endswith("en!"))
print (text.find("e"))
print (text.isdigit())
print (text.upper())
print (text.replace("e","0"))

123456789

text = "herzlich willkommen!" print (text.capitalize())print (text.count("i"))print (text.endswith("en!"))print (text.find("e"))print (text.isdigit())print (text.upper())print (text.replace("e","0"))

 
Führt das Programm durch einen Klick auf Run aus und ihr erhaltet folgendes Ergebnis in der Konsole:

Die Methode capitalize() sorgt dafür, dass der erste Buchstabe der Zeichenkette groß geschrieben wird. Wenn es sich dabei bereits um einen Großbuchstaben handelt, hat sie keine Auswirkungen.
Die Methode count() benötigt als Parameter eine weitere Zeichenkette innerhalb der Klammer. Sie gibt an, wie häufig die in der Klammer angegebene Zeichenkette in der ursprünglichen Variable vertreten ist. Damit lässt sich beispielsweise die Häufigkeit eines Buchstabens oder eines Wortteils bestimmen. In längeren Texten könnt ihr damit auch bestimmte Wörter zählen.
Die Methode endswith() benötigt ebenfalls eine Zeichenkette in der Klammer und gibt eine boolesche Variable zurück. Diese ist True, wenn die ursprüngliche Variable genau mit den angegebenen Zeichen endet. Tut sie das nicht, ist der Wert False.
Die Methode find() sucht nach einzelnen Zeichen oder Zeichenketten innerhalb der Variable und gibt deren Position zurück. Sollten sie mehrfach auftreten, erhält man den Wert, an dem sie das erste Mal erscheint. Sollte das Zeichen überhaupt nicht vorkommen, erhält man den Wert -1 zurück.
Die Methode isdigit() überprüft, ob eine Zeichenkette ausschließlich aus Ziffern besteht. Je nach Ergebnis der Überprüfung gibt sie True oder False zurück.
Die Methode upper() verwandelt alle Kleinbuchstaben in der Zeichenkette in Großbuchstaben. Bei Ziffern, Satzzeichen oder bei Großbuchstaben hat sie keinen Effekt.
Mit der replace()-Methode könnt ihr einen Buchstaben oder eine bestimmte Abfolge durch einen anderen Ausdruck ersetzen. Diese Methode benötigt zwei Parameter. Der erste von ihnen enthält die Buchstabenfolge, die man ersetzen möchte. Der zweite gibt den neuen Wert an.
Hierbei handelt es sich nur um einen kleinen Ausschnitt aus den vielfältigen Methoden, die ihr auf Zeichenketten anwenden könnt. Eine vollständige Übersicht der String-Methoden findet man in der offiziellen Python-Referenz:
https://docs.python.org/3/library/stdtypes.html#string-methods
Probiert an dieser Stelle selbst ein paar Methoden mit eigenem Beispieltext aus, ihr könnt dabei nichts kaputt machen!
(Auszug aus dem Python Komplettkurs von Edley)
 
Happy coding,
Euer Holger
Teile diesen Beitrag:

Operatoren in Python

Teile diesen Beitrag:Hej Leute,
heute geht es um ein wichtiges Thema in Python: Operatoren. Ohne diese geht beim Programmieren gar nichts. Es ist also wichtig, dass Ihr die Funktionsweise von Operatoren verstanden habt, bevor es mit fortgeschrittenen Themen weitergeht. Aber keine Panik, im Endeffekt ist das Konzept ganz simpel, wie ihr gleich sehen werdet.
(Dieser Beitrag ist ein Auszug aus dem Python Komplettkurs* von Edley)
Eine Variable bleibt im Verlauf eines Programms nicht immer gleich. Stattdessen ist es häufig notwendig, ihren Wert zu verändern. Hierzu kommen sogenannte Operatoren zum Einsatz. Für euch sind für den Anfang erstmal 2 Arten von Operatoren wichtig:
Zuweisungsoperator
Der Zuweisungsoperator besteht aus dem Gleichheitszeichen (=).  Er ermöglicht es, einer Variablen einen Wert zuzuweisen. Das ist nicht nur zu Beginn des Programms möglich. Ihr könnt einer Variablen auch im weiteren Verlauf immer wieder einen neuen Wert zuweisen:

name = "Michael"
print ("Hallo, "+name)
name = "Susanne"
print ("Hallo, "+name)

1234

name = "Michael"print ("Hallo, "+name)name = "Susanne"print ("Hallo, "+name)

Dabei wird der alte Wert überschrieben. Bei der zweiten Ausgabe gibt der print-Befehl daher die Begrüßung mit den neuen Namen (hier: Susanne) aus.
Mathematische Operatoren
Hinzu kommen mathematische Operatoren. Dabei handelt es sich um die vier Grundrechenarten, die ihr hoffentlich noch aus der Schule kennt 😉
Die Verwendung von Addition mit Plus (+) und Subtraktion mit Minus (-) funktioniert wie gewohnt. Für Multiplikationen kommt das Sternsymbol (*) und für Divisionen der Schrägstrich (/) zum Einsatz. Außerdem gibt es den Modulo-Operator. Dieser besteht aus dem Prozentzeichen (%) und gibt den Rest der ganzzahligen Division an. Beispiele:

x = 4
y = x + 2
x = y / 3
x = 5 % 2
x = x * 5

12345

x = 4y = x + 2x = y / 3x = 5 % 2x = x * 5

 
Auf diese Weise sind viele verschiedene Rechenoperationen zwischen Zahlen und Variablen möglich. Dabei kann eine Variable auch Bezug auf sich selbst nehmen. Das zeigt das letzte Beispiel. Dieser Befehl weist der Variablen x das Fünffache ihres bisherigen Werts zu. Da solche Ausdrücke in Python recht häufig vorkommen, gibt es dafür auch eine Kurzform: x *= 5
Analog dazu bestehen auch für die anderen Grundrechenarten entsprechende Ausdrücke:
x += y                 entspricht               x = x + y
x -= y                 entspricht               x = x – y
x *= y                 entspricht               x = x * y
x /= y                 entspricht               x = x / y
Wichtig: Mathematischen Operatoren sind teilweise auch auf Zeichenketten anwendbar. Allerdings haben sie dabei einen anderen Effekt:
Das Pluszeichen setzt zwei Zeichenketten zusammen.
Das Sternsymbol wiederholt die Zeichenkette mit der entsprechenden Anzahl.
Es ist wichtig, dass ihr dieses Prinzip versteht, also nichts wie ran an die Tasten!
Neben dem Zuweisungsoperator und den mathematischen Operatoren, die ihr in diesem Beitrag kennengelernt habt,  werden später die Vergleichsoperatoren eine sehr wichtige Rolle spielen.
Wie genau diese funktionieren, wird z. B. im oben erwähnten Python Komplettkurs* gut erklärt.
Happy coding,
Euer Holger
 
P.S.: Die mit Sternchen (*) versehene Links sind sogenannte Affiliate-Links. Du unterstützt mich, wenn Du auf einen solchen Link klickst und das Produkt kaufst, d.h. ich bekomme vom Anbieter eine Provision. Für dich ändert sich der Preis nicht.
Teile diesen Beitrag:

Data Scientist oder Data Engineer – Was ist der Unterschied?

Teile diesen Beitrag:Hej Leute,
ich werde immer mal wieder gefragt, was denn der Unterschied zwischen einem Data Scientist und einem Data Engineer oder zwischen einem Data Analyst und einem Data Scientist sei. In Jobanzeigen sieht man mal den einen, mal den anderen Begriff, aber auch dort scheint es nicht immer klar abgegrenzt zu sein.
Klar ist, dass es viele Überschneidungen zwischen den drei Tätigkeiten Data Engineering, Data Science und Data Analysis gibt. Und natürlich sind die Begriffe nicht scharf getrennt, so dass es in einem Jobprofil der Data Scientist eigentlich eher ein Data Analyst wäre und umgekehrt. Allerdings sind die Fähigkeiten, die benötigt werden, ziemlich unterschiedlich. Und es fördert Frust, wenn man einen Data Scientist als Data Engineer einsetzt und auch umgekehrt.
Zudem gibt es noch einen Haufen anderer Berufsbezeichnungen wie DevOps, BI-Specialist oder Business Analyst, die mehr oder weniger eng mit den drei genannten verwandt sind. In Jobportalen tauchen gerne auch alle möglichen Kombinationen auf.
Fangen wir aber erstmal damit an, was überhaupt die Aufgabenbereiche sind.
 
Die Aufgaben eines Data Scientists
Im Englischen kann man die Aufgabe ganz gut mit “Advances Analytics” beschreiben, also komplexe Analysen. Dabei gibt es zwei Anwendungsbereiche:
zum einen geht es um die Gewinnung von Insights mittels Daten, also die Datenanalyse mit dem Ziel, andere Abteilungen zu unterstützen. Typische Beispiele sind die Verteilung von Marketing-Budget, Betrugserkennung im Finanzwesen (fraud detection) oder auch Absatz-/Bestellprognosen.
Der andere Anwendungsbereich ist die (Weiter-) Entwicklung von Produkten, also mit dem Kunden als Anwender, z.B. eine recommendation engine oder welche Beiträge im Feed eines sozialen Netzwerkes landen.
Beide Anwendungsbereiche können je nach Job unterschiedlich stark gewichtet sind bzw. kann ein Job auch nur einen verlangen. Dabei reichen die Ausprägungen von recht einfachen Adhoc-Analysen bis zur Entwicklung von künstlicher Intelligenz. Auch die Datenmenge ist sehr unterschiedlich und eignet sich nicht als Unterscheidungskriterium zwischen Data Analyst und Data Scientist. Manche sagen zwar: Data Analyst=small data, Data Scientist=big data, aber das greift viel zu kurz. Ab welcher Größe spricht man denn von Big Data? Und zudem kann man auch mit Small Data “Advanced Analytics” entwickeln.
 
Im Endeffekt benötigt ein guter Data Scientist die folgenden vier Fähigkeiten:
Mathematische und statistische Methoden
IT-Skills, insbesondere Programmierung in einer Skriptsprache (Python oder R)
Kommunikation inkl. Datenvisualisierung
Domain Knowledge
Die ersten drei kann man lernen, Domain Knowledge – modern für Branchenwissen – bekommt man mit der Erfahrung, die man in einer bestimmten Branche arbeitet, z.B. Gesundheitswesen, Digital Marketing oder Retail).
 
Die Aufgaben eines Data Engineers
Grob gesprochen ist der Data Engineer für die Infrastruktur der Daten zuständig. Insbesondere geht es um die saubere und verlässlichen Aufbau der Datenströme. Man spricht dabei von sogenannten data pipelines.
Das ist vor allem bei Big Data alles andere als trivial. Daher ist die Berufsbezeichnung Data Engineer eng mit Big Data verknüpft. Hat man “nur” normale Datenbanken, würde man vermutlich eher vom BI-Developer oder Data Warehouse-Specialist sprechen.
Der Data Engineer sollte sich also mit den modernen Tools für Big Data auskennen, sprich Hadoop, Hive, Spark, NoSQL-Datenbanken etc. Er sorgt im Endeffekt dafür, dass eine Big Data-Infrastruktur vorhanden ist, auf der ein Data Scientist dann die Analytics, z.B. maschinelles Lernen, durchführen kann.
 
Die Aufgaben eines Data Analysts
Der Data Analyst befindet sich zwischen Data Scientist und Business Analyst und ist häufig nur schwer vom Data Scientist abzugrenzen. Der Data Analyst hat seinen Schwerpunkt im Bereich Reporting mit den Tools Excel und SQL bzw. der BI-Anwendung. Vom Data Analyst wird in der Regel nicht erwartet, dass er komplexe statistische Modelle benutzt und programmiert, was typischerweise der Data Scientist macht.
Der Data Analyst arbeitet häufiger in einer Abteilung und hat dementsprechend großes Fachwissen, z.B. Controlling. Der Data Scientist agiert eher projektbasiert und abteilungsübergreifend.
 

 
Weitere Berufsbezeichnungen
Um Euch die Orientierung zu erleichtern, habe ich noch ein paar weitere Berufsbezeichnungen skizziert, die man im Zusammenhang mit Daten häufiger liest.
DevOp – Development und Operations
DevOps ist ein neuer Begriff und vielen ist gar nicht klar, was dieser denn nun bedeutet. Das liegt daran, das DevOps keine Berufsbezeichnung im eigentlichen Sinn ist, sondern eher eine Beschreibung einer IT-Kultur.
Vom Wort ist es erstmal eine Kombination von Development, also Softwarentwicklung, und Operations, also dem Betreiben der Software. Und das beschreibt es schon ganz gut. In der alten IT-Welt gibt es Softwareentwickler, die sich an Pflichten- und Lastenheften halten, und es gibt die Administratoren, die sich um den laufenden Betrieb kümmern. Eine Softwareentwicklung ist irgendwann abgeschlossen und Änderungen (Change Requests) müssen genau definiert, beurteilt, budgetiert, programmiert und dann produktiv gesetzt werden. Mit anderen Worten ein langwieriger Prozess. In der heutigen Zeit (Stichwort Agilität) geht es aber darum, in kurzer Zeit Änderungen am Softwareprodukt/-dienst vorzunehmen, um auf sich verändernde Anforderungen einzustellen. Denkt zum Beispiel mal an Google oder Facebook mit ihren tausend Tests, was denn nun besser funktioniert. DevOps kann darauf als Antwort gesehen werden, denn dabei geht es um die Reduktion der Zeit zwischen einer Änderungsanfrage und der tatsächlichen Änderung im produktiven System.
 
Systemadministrator – Manager von Servern/Computern
Der Systemadministrator lässt sich gut vom Data Engineer abgrenzen. Der Systemadministrator ist zuständig für Verfügbarkeit (uptime), Performance, Sicherheit und Ressourcen von Servern und Computern.
Dabei kommen im SysAdmin-Alltag meist einige der folgenden Tätigkeiten vor:
Installation und Konfiguration von neuer Hardware und Software
Installation von Updates und Patches des Betriebssystems und vorhandener Software
Management von User Accounts (Anlegen, Löschen, Passwörter zurücksetzen)
System Logs analysieren, um Probleme zu entdecken
Management der Netzwerk-Infrastruktur
Analyse und Behebung von berichteten Problemen
Anfragen von Usern beantworten
Training von Usern
 
Business Analyst – der Geschäftsmodellversteher
Der Business Analyst ist am nächsten am Data Analyst dran, aber die Ausrichtung ist noch mehr in Richtung BWL. Es geht also vor allem um Geschäftsprozesse und Geschäftsentwickung. Dabei sind die IT-Skills meistens begrenzter als die des Data Analysts, sprich es geht vor allem um Excel, Powerpoint und die Anwendung von BI-Software (SAP, Microstrategy, …). Dafür sind Kommunikationsfähigkeiten, Projektmanagement und Domain Knowledge ausgeprägter. Klassischerweise stellt der Business Analyst die Schnittstelle zwischen den Abteilungen und der IT dar und arbeitet projektbasiert als interner Berater. Je nach Größe des Unternehmens macht die Schnittstellenarbeit aber auch ein Data Analyst.
 
Business Intelligence Developer – der BI-System-Spezialist
Der BI Developer ist wie der Business Analyst auch eine Jobbeschreibung, die es schon einige Jahre gibt. Im Wesentlichen ist der BI-Developer für alles zuständig, was mit Reportings aus dem DataWarehouse und BI-System zu tun hat. Je nachdem, wie technisch der Job ausgelegt ist, gehört dazu neben der Arbeit im BI-System auch die Datenbankadministration. Je nach dieser Auslegung ist das Aufgabenprofil recht nacht nah am Data Analyst oder am Data Engineer. In Jobportalen sieht man dementsprechend auch häufiger die Kombination mit einem der beiden Begriffe. Insbesondere wenn es um moderne BI-Tools wie Tableau oder Qlik geht, ist die Nähe zum Data Analysten gegeben.
Datenmodelle, Reports und Dashboards im BI-System enwickeln
Programmierung und Optimierung von Datenbankabfragen (SQL)
Entwicklung und Design des Data Warehouses
Zusammenführung von verschiedenen Datenquellen im Data Warehouse
Administration von Datenbanken
 
Statistiker – der Statistikexperte
Reine Statistikstellen sind selten, daher beschreibe ich hier eher die Abgrenzung vom Data Scientist zum Statistiker. Am häufigsten sind die Statistiker (noch) in Versicherungen oder der Pharma-Branche, aber auch dort wandelt sich das Bild. In der Pharma-Branche geht es um Studiendesign, Stichprobengröße etc., also wie man die Wirksamkeit eines Medikaments nachweisen kann.
Der Statistiker ist weniger Programmierer als der Data Scientist. Das heißt nicht, dass der Statistiker nicht programmieren kann, denn das muss er durchaus. Im Normalfall sind die Programme aber nicht für den produktiven Einsatz bestimmt, sondern einmalige Analysen.
Man kann auch noch anführen, dass der Data Scientist breiteres Mathematikwissen benötigt (Analysis, Lineare Algebra, Optimierungsmethoden), insbesondere für das Verständnis von Neuronalen Netzen.
Dafür kennt sich der Statistiker natürlich am Besten mit den verschiedensten Verfahren der Statistik aus und hier kann ihm keiner das Wasser reichen. Das ist nicht zu unterschätzen, denn das korrekte Anwenden der Statistik-Methoden erfordert Wissen. Insbesondere der Bereich “Small Samplesize” (kleine Stichproben) ist das Wissen um die entsprechenden Verfahren sehr wichtig.
 

Fazit
Ihr habt gemerkt, dass Jobbeschreibungen keine exakte Wissenschaft sind. Neben großen Überschneidungen und zum Teil unscharfen Definitionen spielt auch noch Trends und wie modisch ein Unternehmen erscheinen will, eine Rolle.
Nichtsdestotrotz macht es Sinn, sich mit Anforderungsprofilen auseinanderzusetzen. Denn auf der einen Seite werden Data Scientisten händeringend gesucht. Auf der anderen Seite erfordern nicht alle Stellen, die für einen Data Scientist ausgeschrieben sind, wirklich einen Data Scientisten.
Was sind Eure Erfahrungen und Jobprofile? Schreibt mir einen Kommentar oder besucht die Data Science Deutschland – Facebookgruppe
Euer Holger (Data Scientist & Blogger)
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen

Datensatzformat – Quer vs. Längs – Konvertierung in Excel und R

Teile diesen Beitrag:Hej Leute,
in diesem Artikel geht es um die Struktur von Datentabellen, also wie eine Tabelle aufgebaut ist. Was gibt es denn darüber zu erzählen, denkt ihr euch vielleicht. Eine Tabelle hat Zeilen und Spalten. Das stimmt natürlich, aber dennoch gibt es zwei Varianten, die beide ihre Vor- und Nachteile haben, so dass auch beide ihre Daseins-Berechtigung haben.
Im ersten Teil des Artikels erfahrt ihr also, was ein Quer-Datensatz und was ein Längs-Datensatz ist. Dann gehen wir auf die Vor- und Nachteile der beiden Strukturen ein, welche auch erklären, wann man besser Längs oder Quer verwendet. Nun kommt der Praxisteil: Wie konvertiert man denn nun zwischen den beiden Formaten? Das erkläre ich Euch in einer bebilderten Schritt-für-Schritt-Anleitung in Excel. Für die R-Nutzer unter euch gehe ich auf die Funktionen aus den Packages tidyr, reshape2 und data.table ein, natürlich mit Code-Beispielen, damit ihr sofort loslegen könnt.
Der Quer-Datensatz – viele Spalten
Der Längs-Datensatz – viele Zeilen
Die Vor- und Nachteile der beiden Strukturen
Die Vorteile der Quer-Datenstruktur
Die Nachteile der Quer-Datenstruktur
Die Vorteile der Längs-Datenstrukur
Die Nachteile der Längs-Datenstruktur
Wann benutze ich welche Struktur?

Umwandeln in Excel
Von Längs zu Quer in Excel mittels Pivot
Von Längs zu Quer in Excel mittels Formeln
Von Quer zu Längs in Excel mittels Formeln

Umwandeln in R
spread und gather aus dem Package tidyr
melt und dcast aus dem Package data.table bzw. reshape2
Performance-Unterschiede tidyr vs data.table

 
Der Quer-Datensatz – viele Spalten
Der Quer-Datensatz (im Englischen Wide oder seltener un-stacked) ist die normale Tabelle, wie wir sie kennen. Jede Zeile steht für einen Eintrag, also zum Beispiel einen Kunden oder ein Produkt. Die Spalten entsprechen den Attributen. Bei Kunden könnte das Name, Adresse, Alter, Geschlecht sein, beim Produkt vielleicht Länge, Breite, Höhe, Gewicht, Farbe, Warenkategorie.

Auch bei einer Befragung ist der Quer-Datensatz typisch. Dort entspricht jede Zeile einem ausgefüllten Fragebogen, die Antworten auf die Fragen stehen in den Spalten

Der Längs-Datensatz – viele Zeilen
Der Längs-Datensatz oder auch Kontenmodell (im Englischen Long, Tall oder seltener stacked) hat nur wenige Spalten, dafür aber viele Zeilen. Denn jedes Attribut bekommt eine eigene Zeile. Im Extremfall benötigt man nur drei Spalten:
Die ID-Spalte(n), welche einen Identifikator (z.B. für einen Fragebogen, ein Produkt, einen Kunden …) enthält
Die Variable-Spalte, welche die Bezeichnung des Attributs / der Variablen enthält
Die Value-Spalte, welche die tatsächlichen Werte enthält

Die Vor- und Nachteile der beiden Strukturen
Die Vorteile der Quer-Datenstruktur
Der Quer-Datensatz hat den Vorteil, dass er die leichter lesbare und damit auch verbreitetste Struktur ist. Jede Spalte hat ihr Format (integer, float, string, Datum). Diese lassen sich gut auswerten, denn es ist ja alles schön nach Spalten getrennt.
Die Nachteile der Quer-Datenstruktur
Der Quer-Datensatz hat aber zwei Nachteile. Zum einen ist es nicht sehr speicherschonend, denn für jede ID wird für jedes Attribut ein Platz reserviert. Wenn nun die Zeilen sehr unterschiedlich sind, dann ist es nicht effizient, die Daten so zu speichern. Enthält zum Beispiel eine Tabelle zwei Fragebögen, wobei der eine 5 Fragen und der andere 100 Fragen enthält, dann muss die Tabelle trotzdem mindestens 101 (ID und 100 Variablen) Spalten haben. In den Zeilen mit dem kurzen Fragebögen sind halt nur 6 Spalten befüllt.
Der zweite Nachteil ist die Unflexibilität. Wenn man ein weiteres Attribut benötigt, dann benötigt man also eine neue Spalte. Erstmal kein Problem, wenn man nur für sich arbeitet. Aber wenn dahinter eine große Tabelle auf einer Datenbank liegt, muss diese auch angepasst werden, was Zeit kostet. Wenn nun die Daten über mehrere Systeme fließen, muss alles an die neue Struktur angepasst werden.
Die Vorteile der Längs-Datenstrukur
So kommt die Längs-Datenstruktur ins Spiel, denn diese zwei Nachteile hat sie nicht. Zum einen gibt es nur eine Zeile, wenn das Attribut auch da ist. In dem Fragebogenbeispiel kommen für einen kurzen Fragebogen maximal 5 Zeilen hinzu, für einen langen maximal 100 Zeilen. Maximal, denn werden Fragen nicht beantwortet, tauchen sie im Datensatz auch nicht unbedingt auf.

Der zweite Vorteil ist die Flexibilität, denn kommt noch ein Attribut hinzu, dann kommt eine Zeile hinzu. Fällt eine Variable weg (z.B. neue Version eines Fragebogens), dann ändert sich nichts. Die Struktur der Tabelle bleibt identisch.
Die Nachteile der Längs-Datenstruktur
OK, aber natürlich hat so ein Längs-Datensatz auch ein paar Nachteile. Zum einen gestalten sich die Auswertungen als nicht ganz so intuitiv, insbesondere, wenn man mit fehlenden Werten zu tun hat.
Zum anderen machen verschiedene Datentypen ein Problem. Denn hat man ein numerisches Attribut und ein Text-Attribut, muss man die Value-Spalte als String definieren. Das verlangsamt wiederum die Auswertungen, denn der String muss ja wieder in eine Zahl umgewandelt werden, wenn man zum Beispiel einen Mittelwert berechnen will.
Dieses Problem kann man aber umgehen, indem man für jeden Datentyp eine eigene Value-Spalte definiert, also eine Spalte für numerische Werte, eine für Text und eine für Datumsangaben. Aber auch das Vorgehen hat Nachteile, denn nun sind ja wieder viele Felder leer.

Wann benutze ich welche Struktur?
Eigentlich ergeben sich die Anwendungen automatisch aus den Vor- und Nachteilen. Die erste Regel ist, dass bei der Weitergabe an jemanden nicht IT-affinen die Quer-Struktur (viele Spalten) verwendet werden sollte. Generell ist meistens die Quer-Struktur vorzuziehen, denn diese ist intuitiver und die meisten Programme sind darauf ausgelegt. Ist aber der Inhalt der Daten variabel bzw. unterliegt häufiger mal Änderungen, die Struktur aber nicht ohne weiteres zu ändern (z.B. SQL-Tabellen), dann sollte man die Längs-Struktur verwenden. Auch bei späterer interaktiver Pivotierung, z.B. in Excel, kann man einfacher mit der Längs-Struktur arbeiten.
Zum Glück kann man relativ problemlos, je nach Größe des Datensatzes, einen Quer-Datensatz in einen Längs-Datensatz und umgekehrt konvertieren.
Umwandeln in Excel
Von Längs zu Quer in Excel mittels Pivot
Einen Längs-Datensatz in einen Quer-Datensatz umzuwandeln, ist in Excel ganz einfach. Aber nur, wenn nur numerische Werte (oder Datumswerte) vorhanden sind. Dafür erzeugt man eine Pivot-Tabelle.
Zuerst wählt ihr den Datensatz aus und erzeugt einen PivotTable
Die ID-Variable (hier BogenNr) gehört in die Zeilenbeschreibung, die Variable-Spalte in die Spaltenbeschreibung und die Wert-Spalte in Werte
Nun noch die Aggregation umstellen von Anzahl auf irgendetwas anderes (Summe, Mittelwert, Min, Max). Es sollte eh nur einen Wert pro Zelle geben, deshalb ist es egal. Nur eben nicht die Standard-Einstellung Anzahl.
Jetzt noch das Datumsfeld korrekt formatieren (Shortcut Strg+1)

Von Längs zu Quer in Excel mittels Formeln
Wir sehen, dass die Wörter (Strings) im Pivot nicht korrekt übertragen werden. Es gibt ein paar Varianten, wie man das erreichen kann. Ich zeige euch die einfachste, die aber mit etwas Handarbeit verbunden ist.
Zuerst fügt man eine Spalte an den Längs-Datensatz an, nämlich einen Schlüssel. Das ist einfach die Kombination der zwei Spalten BogenNr und Variable, getrennt durch das Zeichen #. So kann jede Zeile eindeutig identifiziert werden.
Nun bauen wir das Gerüst der Tabelle. Ihr könntet die Ausprägungen der BogenNr und die Ausprägungen der Variable abtippen. Weniger Arbeit ist es allerdings, die Werte zu kopieren und dann die Duplikate zu entfernen. Bei den Spalten kopiert ihr das Ergebnis nochmal, fügt es aber transponiert ein.
Nun bauen wir uns die Formel per INDEX-VERGLEICH. Die Kombination der beiden Befehle funktioniert wie SVERWEIS, ist allerdings etwas flexibler. Beim SVERWEIS hätten wir den Schlüssel in die erste Spalte schreiben müssen.
Wir ergänzen noch eine kleine Abfrage mit WENNFEHLER, damit fehlende Werte leere Felder sind. Dieser Schritt ist nicht unbedingt nötig und eigentlich ist es auch korrekter, die fehlenden Werte mit #NV zu bezeichnen. Aber die Tabelle sieht etwas netter aus.
Letzter Schritt: Noch das Datumsfeld wieder vernünftig formatieren, also auf Datumsformat umstellen.

 
Man kann nun noch den Prozess, die Quer-Tabelle aufzubauen, auch in Formeln packen. Meiner Meinung nach ist es den Aufwand aber nicht wert. Denn wenn ich etwas automatisieren möchte, dann benutze ich direkt R oder Python.
Von Quer zu Längs in Excel mittels Formeln
Die umgekehrte Richtung, also von Querformat zu Längsformat können wir auch mit ein bisschen Formelarbeit in Excel realisieren. Allerdings ist es nicht die platzsparende Version, in der nicht ausgefüllte Felder gar nicht erst auftauchen. Aber das kann man im Nachhinein durch Sortierung und dann Löschen schnell erreichen.
Zuerst benötigen wir eine Durchnummerierung der Zeilen. Der (vollständige) Längsdatensatz hat genau (Anzahl Zeilen) * (Anzahl Spalten-1) des Querdatensatzes an Zeilen.
In der zweiten Spalte kommt der Identifikator (hier BogenNr). Hier werden alle BogenNr einfach wiederholt, bis die Tabelle voll ist. Ich habe das durch Modulo-Rechnung (Excel-Befehl REST) gelöst.
Nun kommt die Spalte mit der Variablenbezeichnung. Dazu zählen wir, wie häufig die BogenNr schon vorgekommen ist (Excel-Befehl ZAEHLENWENN) und picken dementsprechend die entsprechenden Spaltennamen aus dem Querdatensatz raus.
Als letzter Schritt kommt noch der tatsächliche Zelleneintrag in die Werte-Spalte. Das lässt sich einfach per INDEX-VERGLEICH lösen. Wir brauchen eben zwei Vergleiche, einen für die Zeile, einen für die Spalte.

 
Wir sehen, auch diese Umwandlung ist nicht perfekt, denn das Datum wird nun wieder in der internen Darstellung (als ganze Zahl der Tage seit dem Ursprungsdatum, 01.01.1900 entspricht der 1) angezeigt.
 
Umwandeln in R
In R gibt es mehrere Funktionen, um aus einem Längs- ein Querdatensatz und umgekehrt zu machen, da mehrere Packages entsprechende Funktionen bereitstellen.
Also soweit man das nachvollziehen kann, war reshape2 von Hadley Wickam das erste Package, welches die Konvertierung längs-quer und quer-längs (Befehle melt und dcast) implementiert hat. Wickam hat aber im Package tidyr andere Funktionen (spread und gather) entwickelt, welche mehr zur Syntax des tidyverse passen. Insofern dürfte reshape2 nicht weiterentwickelt werden. Die Konvertierungsfunktionen in data.table (auch melt und dcast) haben die gleiche Syntax wie in reshape2 und rufen die Funktionen aus reshape2 auf, sofern die Daten nur als data.frame und nicht als data.table vorliegen. Sind die Daten allerdings ein data.table, dann werden eigene, für große Datenmengen optimierte, Funktionen aufgerufen.
spread und gather aus dem Package tidyr
Im tidyverse, genauer im R-Package tidyr, gibt es die beiden Funktionen spread und gather.  spread breitet die Daten aus, also von längs zu quer. gather sammelt die Daten ein, also von quer zu längs.
Bei spread gibt man als Parameter neben dem Datensatz noch key und value an. Key ist die Spalte im Längsdatensatz, welche den Variablen-/Attributnamen enthält. Value ist die Spalte, welche die tatsächlichen Werte enthält.
Bei gather gibt man auch key und value an, hier sind es aber die Namen, welche der konvertierte Datensatz haben soll. Dann übergibt man noch die Spalten, welche zusammengefasst werden sollen. In unserem Beispiel sind das Frage 1 bis Frage 5. Alternativ und deutlich kürzer schließt man mit Minus die Spalten aus, die erhalten bleiben sollen, in unserem Fall BogenNr.

library(tidyr)

Laengs <- data.frame(stringsAsFactors=FALSE,
BogenNr = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5),
Variable = c("Frage 1", "Frage 2", "Frage 3", "Frage 4", "Frage 5",
"Frage 1", "Frage 2", "Frage 1", "Frage 4", "Frage 5",
"Frage 1", "Frage 2", "Frage 3", "Frage 4", "Frage 1"),
Wert = c("20", "gut", "22.05.2019", "ja", "immer", "25", "schlecht",
"12", "nein", "selten", "18", "teils/teils", "06.12.2018",
"ja", "27")
)

# Umwandeln in Querdatensatz mittels der Funktion spread
# Parameter sind key: Spalte mit Attribut-/Variablenname und value: die Spalte mit den Werten
Quer <- spread(Laengs, key = Variable, value = Wert)
# Alternativ mit Pipe-Schreibweise
Quer <- Laengs %>% spread(key = Variable, value = Wert)

# Umwandeln von Quer nach Längs
gather(Quer, key = Variable, value = Wert, -BogenNr)
# Alternativ mit Pipe-Schreibweise
Quer %>% gather(key = Variable, value = Wert, -BogenNr)

12345678910111213141516171819202122

library(tidyr) Laengs <- data.frame(stringsAsFactors=FALSE,     BogenNr = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5),    Variable = c("Frage 1", "Frage 2", "Frage 3", "Frage 4", "Frage 5",                 "Frage 1", "Frage 2", "Frage 1", "Frage 4", "Frage 5",                 "Frage 1", "Frage 2", "Frage 3", "Frage 4", "Frage 1"),        Wert = c("20", "gut", "22.05.2019", "ja", "immer", "25", "schlecht",                 "12", "nein", "selten", "18", "teils/teils", "06.12.2018",                 "ja", "27")) # Umwandeln in Querdatensatz mittels der Funktion spread# Parameter sind key: Spalte mit Attribut-/Variablenname und value: die Spalte mit den WertenQuer <- spread(Laengs, key = Variable, value = Wert)# Alternativ mit Pipe-SchreibweiseQuer <- Laengs %>% spread(key = Variable, value = Wert) # Umwandeln von Quer nach Längsgather(Quer, key = Variable, value = Wert, -BogenNr)# Alternativ mit Pipe-SchreibweiseQuer %>% gather(key = Variable, value = Wert, -BogenNr)

Mehr Informationen zum Package tidyr findet ihr unter https://tidyr.tidyverse.org.
melt und dcast aus dem Package data.table
Im Package data.table gibt es die Funktionen dcast und melt. dcast konvertiert einen data.frame oder data.table von längs zu quer. melt ist die Umkehrung, konvertiert als von quer zu längs.
Sind die Daten in einem data.frame, dann ruft auch data.table die entsprechenden Funktionen aus reshape2 auf. Sind die Daten aber ein data.table (mit setDT einfach umzuwandeln), dann werden die performanteren dcast- und melt-Funktionen aus data.table verwendet. Zudem gibt es bei diesen die Möglichkeit, die Variablen-Spalte nicht als Faktor zurückzubekommen.
Bei melt werden als Parameter id.vars die ID-Variable(n) übergeben. Alle anderen Spalten werden zusammen konvertiert. Zudem können die Spaltennamen der Variablenspalte und der Werte-Spalte übergeben werden. Wird der Parameter variable.factor auf FALSE gesetzt, ist die Variablenspalte vom Typ chr (String), andernfalls vom Typ factor. Achtung: Der Parameter variable.factor funktioniert nur, wenn tatsächlich ein data.table vorliegt. Bei einem data.frame geht das nicht.
Bei dcast wird eine Formel übergeben, die die Konvertierung definiert. Auf der linken Seite ist die ID-Variable, rechts von der Tilde steht die Spalte mit den Variablen-/Attributnamen. Zusätzlich kann man mit dem Parameter value.var bestimmen, welche Spalte(n) die Werte enthalten. Ist dieser Parameter nicht angegeben, dann werden alle verbleibenden Spalten verwendet.

# melt und dcast vom Package data.table
library(data.table)

# Umwandeln von Quer zu Längs mittels melt
Laengs <- melt(Quer, id.vars = "BogenNr", variable.name = "Variable",
value.name = "Wert")

# damit die performante melt-Funktion zum Einsatz kommt, muss ein data.table vorliegen
QuerDT <- setDT(Quer)
LaengsDT <- melt(QuerDT, id.vars = "BogenNr",variable.name = "Variable",
value.name = "Wert", variable.factor = FALSE)
# explizite Angabe der umzuwandelnden Spalten (measure.vars)
LaengsDT <- melt(QuerDT, id.vars = "BogenNr",
measure.vars = c("Frage 1","Frage 2", "Frage 3", "Frage 4", "Frage 5"),
variable.name = "Variable", value.name = "Wert")

# Umwandeln von Längs zu Quer mittels dcast
QuerDT <- dcast(LaengsDT, BogenNr~Variable, value.var = "Wert")

123456789101112131415161718

# melt und dcast vom Package data.tablelibrary(data.table) # Umwandeln von Quer zu Längs mittels meltLaengs <- melt(Quer, id.vars = "BogenNr", variable.name = "Variable",                value.name = "Wert") # damit die performante melt-Funktion zum Einsatz kommt, muss ein data.table vorliegenQuerDT <- setDT(Quer)LaengsDT <- melt(QuerDT, id.vars = "BogenNr",variable.name = "Variable",                  value.name = "Wert", variable.factor = FALSE)# explizite Angabe der umzuwandelnden Spalten (measure.vars)LaengsDT <- melt(QuerDT, id.vars = "BogenNr",                  measure.vars = c("Frage 1","Frage 2", "Frage 3", "Frage 4", "Frage 5"),                 variable.name = "Variable", value.name = "Wert") # Umwandeln von Längs zu Quer mittels dcastQuerDT <- dcast(LaengsDT, BogenNr~Variable, value.var = "Wert")

Mehr Informationen zum Package data.table, welches viele Funktionen und Strukturen bereitstellt, um super schnell mit großen Datenmengen umzugehen, findet ihr unter https://github.com/Rdatatable/data.table/wiki/Getting-started.
Speziell zur Umwandlung der beiden Datenformate gibt es auch noch genaueres unter https://cran.r-project.org/web/packages/data.table/vignettes/datatable-reshape.html.
Performance-Unterschiede tidyr vs data.table
In unserem kleinen Beispiel haben beide bei jeweils einer Aufgabe die Nase vorn: spread war schneller als dcast und melt war schneller als gather. Der Fairness halber muss man aber sagen, dass dcast mehr Möglichkeiten bietet, so kann gleichzeitig eine Aggregation/Gruppierung stattfinden.

library(rbenchmark)
benchmark(
spread = spread(Laengs, key = Variable, value = Wert),
dcast = dcast(LaengsDT, BogenNr~Variable, value.var = "Wert"),
replications = 1000
)
benchmark(
gather = gather(Quer, key = Variable, value = Wert, -BogenNr),
melt = melt(QuerDT, id.vars = "BogenNr", variable.name = "Variable",
value.name = "Wert", variable.factor = FALSE),
replications=1000
)

123456789101112

library(rbenchmark)benchmark(  spread = spread(Laengs, key = Variable, value = Wert),  dcast = dcast(LaengsDT, BogenNr~Variable, value.var = "Wert"),  replications = 1000  )benchmark(  gather = gather(Quer, key = Variable, value = Wert, -BogenNr),  melt = melt(QuerDT, id.vars = "BogenNr", variable.name = "Variable",               value.name = "Wert", variable.factor = FALSE),  replications=1000)

Auch in einem größeren Beispiel zeigen sich die gleichen Unterschiede. Allerdings wird dcast immer besser, je größer der Datensatz ist. Bei 100.000 Zeilen ist dcast schneller. Interessanterweise sind die ursprünglichen melt- und dcast-Funktionen aus reshape2 deutlich schneller als die aus dem Package data.table. Das wundert mich doch sehr. Seht ihr, warum das so ist?

library(rbenchmark)
library(data.table)
library(tibble)
library(tidyr)
n = 1000000
set.seed(93)
BspQuer <- data.frame(ID=1:n,x = rnorm(n), y = runif(n), z = rbinom(n,1,0.3))
BspQuerDT <- setDT(BspQuer)
BspQuerTibble <- as_tibble(BspQuer)
BspLaengsDT <- melt(BspQuer, id.vars = "ID",variable.name = "Variable",
value.name = "Wert", variable.factor = FALSE)
BspLaengsTibble <- gather(BspQuer, key = Variable, value = Wert, -ID)

benchmark(
spread = spread(BspLaengsTibble, key = Variable, value = Wert),
dcastDT = dcast(BspLaengsDT, ID~Variable, value.var = "Wert"),
dcastReshape2 = dcast(BspLaengs, ID~Variable, value.var = "Wert"),
replications = 100
)

benchmark(
gather = gather(BspQuerTibble, key = Variable, value = Wert, -ID),
meltReshape2 = melt(BspQuer, id.vars = "ID",variable.name = "Variable",
value.name = "Wert"),
meltDT = melt(BspQuerDT, id.vars = "ID",variable.name = "Variable",
value.name = "Wert", variable.factor = FALSE),
replications = 500
)

12345678910111213141516171819202122232425262728

library(rbenchmark)library(data.table)library(tibble)library(tidyr)n = 1000000set.seed(93)BspQuer <- data.frame(ID=1:n,x = rnorm(n), y = runif(n), z = rbinom(n,1,0.3))BspQuerDT <- setDT(BspQuer)BspQuerTibble <- as_tibble(BspQuer)BspLaengsDT <- melt(BspQuer, id.vars = "ID",variable.name = "Variable",                     value.name = "Wert", variable.factor = FALSE)BspLaengsTibble <- gather(BspQuer, key = Variable, value = Wert, -ID) benchmark(  spread = spread(BspLaengsTibble, key = Variable, value = Wert),  dcastDT = dcast(BspLaengsDT, ID~Variable, value.var = "Wert"),  dcastReshape2 = dcast(BspLaengs, ID~Variable, value.var = "Wert"),  replications = 100) benchmark(  gather = gather(BspQuerTibble, key = Variable, value = Wert, -ID),  meltReshape2 = melt(BspQuer, id.vars = "ID",variable.name = "Variable",                       value.name = "Wert"),  meltDT = melt(BspQuerDT, id.vars = "ID",variable.name = "Variable",               value.name = "Wert", variable.factor = FALSE),  replications = 500)

 
Fazit
Habt ihr bis zum Ende durchgehalten? Perfekt! Ich hoffe, die Unterschiede in der Struktur zwischen einem Datensatz im Quer- oder Längs-Format sind nun klar. Ihr versteht, wann ihr welche Struktur einsetzt, weil ihr die Vor- und Nachteile kennt. Und die Schritt-für-Schritt-Anleitung in Excel und die R-Befehle waren hilfreich, so dass ihr einsatzbereit seid.
Happy Coding,
Euer Holger
 
 
 
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm

Das R-Package dplyr: Eine ausführliche Anleitung (mit vielen Beispielen)

Teile diesen Beitrag:Hej Leute,
heute stelle ich Euch ein super nützliches R-Package namens dplyr vor. Dieses dient der sogenannten Datenmanipulation. Damit ist aber nicht die negative Bedeutung von Manipulation, also Fälschung gemeint, sondern einfach häufige Aufgaben wie neue Spalten zu einer Tabelle hinzufügen, eine Tabelle nach bestimmten Werten zu filtern (wie der Filter in Excel) oder auch nach Klassen zu gruppieren.
Keine Angst, wir gehen Schritt für Schritt vor. Ich erkläre, wie ihr das Package installiert und dann schauen wir uns die wichtigsten R-Befehle von dplyr an, natürlich alle mit Beispielen versehen. Und am Ende kommen wir dann zu JOINs, also dem Verbinden von zwei data.frames. Das ist ein ganz wichtiges Konzept beim Arbeiten mit Datenbanken. Dazu gibt es noch ein praktisches Cheat Sheet, also eine Übersichtsseite zum Nachschlagen. Die könnt ihr kostenlos herunterladen und ausdrucken.
Der Artikel ist doch ziemlich lang geworden, ich will euch ja nichts vorenthalten. Wer es eilig hat und einfach nur einen der dplyr-Funktionen anwenden will, springt einfach zum entsprechenden Abschnitt:
Was ist das R-Package dplyr
dplyr vs. data.table

Installation von dplyr
Was bedeutet der R-Code „%>%“? Die Pipe aus magrittr
Die wichtigsten Funktionen von dplyr – R
Die Funktion dplyr::mutate und dplyr::transmute – Spalten ergänzen
Die Funktionen dplyr::select und dplyr::rename – Spalten auswählen und umbenennen
Die Funktion dplyr::filter – Zeilen auswählen
Die Funktionen dplyr::summarise und dplyr::group_by – Aggregation
Die Funktion dplyr:: arrange – Sortieren

Zwei Datensätze miteinander verbinden – joins
Für die vier join-Varianten von dplyr habe ich für euch eine Übersichtsseite zusammenstellt, die hoffentlich nützlich ist. Der Link ist im letzten Abschnitt Zwei Datensätze miteinander verbinden.
Was ist das R-Package dplyr?
Dplyr wurde 2014 von Hadley Wickham entwickelt (https://blog.rstudio.com/2014/01/17/introducing-dplyr/) und hat sich seitdem rasant verbreitet. Wie oben schon geschrieben erleichtert das Package die Aufbereitung von Datensätzen, indem es einfach zu nutzende Funktionen für die üblichen Aufgaben bereitstellt wie z.B. für die Auswahl von Spalten (select), nach gewissen Kriterien die Zeilen filtern (filter) oder Werte zu aggregieren (summarise). Der zu bearbeitende Datensatz muss als data.frame oder tibble (die data.frame-Variante im tidyverse) vorliegen, also einer Tabelle mit mehreren Spalten und vielen Zeilen.
Im Prinzip sind viele diese Aufgaben vergleichbar mit dem SQL-Befehl select. Ist ja auch logisch, in SQL geht es schließlich auch um die Verarbeitung von Tabellen. Man könnte also für viele Befehle auch das Package sqldf nehmen, welches es erlaubt, SQL-Befehle auf data.frames loszulassen. Macht natürlich nur Sinn, wenn man sich ein wenig mit SQL auskennt. Ein ausführlicher Artikel ist in Planung, aktuell müsst ihr euch noch mit einem R-Bite, also nur einem Mini-Happen, zu SQL-Befehlen in R mit sqldf begnügen.
Die Stärke von dplyr liegt im klar strukturierten Aufbau: Die Befehle sind als Verb benannt. Das erste Argument ist immer die Datentabelle (data.frame), die weiteren Argumente beschreiben, was genau zu tun ist und als Rückgabe gibt es wieder einen data.frame.
dplyr vs. data.table
Als Alternative möchte ich noch das Package data.table nennen. Mittlerweile ist ein regelrechter Kampf entstanden, welches Package denn besser geeignet sei. Die Syntax ist jedenfalls grundlegend verschieden. Tendenziell wird dplyr als etwas einfacher in der Anwendung beschrieben (was Anwender von data.table verneinen), dafür ist data.table insbesondere bei großen Datensätzen schneller. Es muss aber jeder selber entscheiden, welches Package er bevorzugt. Ich nutze einfach beide abhängig von der Anwendung.
Installation von dplyr
dplyr ist ein ganz normales Package in R, d.h. ihr müsst es einmalig mit install.packages(„dplyr“)  herunterladen und installieren. Im Anschluss genügt es dann, das Package mittels library(dplyr) einzubinden.
Da dplyr ein Teil des tidyverse ist, funktioniert alternativ die Installation auch mit install.packages(„tidyverse“), womit ihr alle Packages, die im tidyverse enthalten sind, installiert.
Wenn dplyr einmal heruntergeladen und installiert ist, wird es einfach mit library(dplyr) oder require(dplyr) eingebunden. Dabei spuckt R folgende Warnung heraus:

Was bedeuten diese dplyr-Warnungen? R macht nur darauf aufmerksam, dass einige Funktionen aus dem Package dplyr genauso heißen wie welche aus den Package stats und base, welche standardmäßig in R geladen werden. D.h. wenn wir nun filter, lag, intersect, setdiff, setequal oder union verwenden, wird die Funktion dieses Namens von dplyr aufgerufen und nicht mehr die „Standard“-Funktion. Wenn man den Package-Namen gefolgt von zwei Doppelpunkten voranstellt, kann man aber dennoch darauf zugreifen. Also stats::filter, stats::lag, base::intersect, base::setdiff, base::setequal und base::union, falls man das möchte.
 
Was bedeutet der R-Code „%>%“? Die Pipe aus magrittr
Dieses komische Zeichen „Prozent – größer – Prozent“ nennt sich Forward-Pipe und ist ein Konzept, welches die Lesbarkeit von R-Code erhöhen soll. Prinzipiell braucht man sie nicht, die Pipe hat sich aber als so praktisch erwiesen, dass sie heute vielfach verwendet wird. Insbesondere im tidyverse, also dplyr, ggplot2 etc., ist sie nicht mehr wegzudenken.
Aber was macht sie denn nun. Ganz einfach eigentlich: Statt f(x, …) schreibt man nun x %>% f(…). Warum ist das so nützlich? Weil damit der verschachtelte Aufruf von Funktionen viel lesbarer wird. Stellt euch vor, wir wollen auf einen Datensatz df zuerst die Funktion f anwenden und auf das Ergebnis die Funktion g. Dann wäre der normale Code g(f(df)). Mit der magrittr-Pipe wäre dass df  %>% f %>% g und entspricht der Leserichtung von links nach rechts. Kommen nun noch Parameter in Spiel, z.B. p1 und p2 für f sowie p3 für g, dann wäre der konventionelle R-Code g(f(df,p1,p2),p3). Mit der Pipe df %>% f(p1,p2) %>% g(p3).
Diese Pipe wurde 2014 von Stefan Milton Bache mit dem Package magrittr in R (benannt nach dem Bild einer Pfeife von René Magritte) umgesetzt und von Hadley Wickham in das tidyverse integriert. Wenn ihr also ein Paket aus dem tidyverse ladet, bekommt ihr automatisch die Pipe-Funktionalität dazu. Wer sich für die Entstehung interessiert: Ursprünglich kommt die Pipe Forward wohl aus der Programmiersprache F#, dort allerdings mit dem Syntax „|>“. Diese Funktionalität fand Bache so praktisch, dass er sie in R umsetze. Schaut mal auf diesen Blogeintrag von R-Statistics.com.
Übrigens, der Shortcut in R-Studio für die Pipe ist Strg + Shift + M. In meinem Blogartikel über die nützlichsten RStudio Shortcuts findet ihr noch 14 weitere.
 
Die wichtigsten Funktionen von dplyr – R
Die Funktionen dplyr::mutate und transmute
Der R-Befehl mutate ergänzt einen Datensatz um eine oder mehrere Spalten. Der häufigste Fall ist die Berechnung aus anderen Spalten. Aber auch Ergänzung von Konstanten oder das Löschen einer Spalte kann man mit mutate erreichen. Als Rückgabewert wird der „mutierte“ Datensatz zurückgegeben.
Eine Variante ist die dplyr-Funktion transmute. Das funktioniert ganz genauso, nur der Rückgabewert ist nicht der komplette Datensatz, sondern nur die „mutierten“ Spalten.

###############################################################################
#
# Beispiele für dplyr::mutate
#
###############################################################################

# Im Package mlbench sind viele Standard-Datensätze enthalten
# In diesem Beispiel verwenden wir BostonHousing
install.packages("mlbench")
# AUflistung der enthaltenen Datensätze
library(help = "mlbench")

library(mlbench)
library(dplyr)
data(BostonHousing)

## Eine Spalte mit dem quadrierten Alter hinzufügen
# in Pipe-Schreibweise
BostonHousing <- BostonHousing %>% mutate(age2 = age*age)
# klassische Schreibweise
BostonHousing <- mutate(BostonHousing, age2 = age*age)
# Varianten ohne dplyr (Base-R)
BostonHousing$age2 <- BostonHousing$age * BostonHousing$age
BostonHousing$age2 <- with(BostonHousing, age2 <- age*age)
BostonHousing$age2 <- BostonHousing %>% with(age2 <- age*age)

## eine Spalte löschen
# in pipe-Schreibweise
BostonHousing <- BostonHousing %>% mutate(age2 = NULL)
# klassische Schreibweise
BostonHousing <- mutate(BostonHousing, age2 = NULL)
# ohne dplyr (Base-R)
BostonHousing$age2 <- NULL
BostonHousing <- BostonHousing[,colnames(BostonHousing)!="age2"]

## transmute gibt einen data.frame nur mit den neu berechneten Spalten zurück
BostonHousing$ID <- 1:nrow(BostonHousing)
# in Pipe-Schreibweise
mittlererAbstand <- BostonHousing %>% transmute(ID, avgDis = dis/mean(dis))
# klassische Schreibweise
mittlererAbstand <- transmute(BostonHousing, ID, avgDis = dis/mean(dis))

## Sind die zu verrechnende Variablen als Strings vorhanden,
# wird die Syntax etwas kompliziert
a = "crim"
b = "nox"
BostonHousing <- BostonHousing %>% mutate(Summe = .data[[a]] + .data[[b]])
# Varianten ohne dply
BostonHousing$Summe <- BostonHousing[[a]] + BostonHousing[[b]]
BostonHousing$Summe <- BostonHousing$a + BostonHousing$b

## Der Vorteil von dplyr zeigt sich bei der Bearbeitung von mehreren Spalten
BostonHousing <- BostonHousing %>%
mutate(
age2 = age * age,
rad = NULL,
v1 = dis / mean(dis),
v2 = tax / dis,
v3 = 100
)

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768

################################################################################# Beispiele für dplyr::mutate################################################################################  # Im Package mlbench sind viele Standard-Datensätze enthalten# In diesem Beispiel verwenden wir BostonHousinginstall.packages("mlbench")# AUflistung der enthaltenen Datensätzelibrary(help = "mlbench")  library(mlbench)library(dplyr)data(BostonHousing)  ## Eine Spalte mit dem quadrierten Alter hinzufügen# in Pipe-SchreibweiseBostonHousing <- BostonHousing %>% mutate(age2 = age*age)# klassische SchreibweiseBostonHousing <- mutate(BostonHousing, age2 = age*age)# Varianten ohne dplyr (Base-R)BostonHousing$age2 <- BostonHousing$age * BostonHousing$ageBostonHousing$age2 <- with(BostonHousing, age2 <- age*age)BostonHousing$age2 <- BostonHousing %>% with(age2 <- age*age)  ## eine Spalte löschen # in pipe-SchreibweiseBostonHousing <- BostonHousing %>% mutate(age2 = NULL)# klassische SchreibweiseBostonHousing <- mutate(BostonHousing, age2 = NULL)# ohne dplyr (Base-R)BostonHousing$age2 <- NULLBostonHousing <- BostonHousing[,colnames(BostonHousing)!="age2"]  ## transmute gibt einen data.frame nur mit den neu berechneten Spalten zurückBostonHousing$ID <- 1:nrow(BostonHousing)# in Pipe-SchreibweisemittlererAbstand <- BostonHousing %>% transmute(ID, avgDis = dis/mean(dis))# klassische SchreibweisemittlererAbstand <- transmute(BostonHousing, ID, avgDis = dis/mean(dis))  ## Sind die zu verrechnende Variablen als Strings vorhanden, # wird die Syntax etwas komplizierta = "crim"b = "nox"BostonHousing <- BostonHousing %>% mutate(Summe = .data[[a]] + .data[[b]])# Varianten ohne dplyBostonHousing$Summe <- BostonHousing[[a]] + BostonHousing[[b]]BostonHousing$Summe <- BostonHousing$a + BostonHousing$b   ## Der Vorteil von dplyr zeigt sich bei der Bearbeitung von mehreren SpaltenBostonHousing <- BostonHousing %>%   mutate(    age2 = age * age,    rad = NULL,    v1 = dis / mean(dis),    v2 = tax / dis,    v3 = 100  )

Die Funktion dplyr::select und rename
Der R-Befehl select wählt eine oder mehrere Spalten aus. Nicht mehr und nicht weniger. Klingt eigentlich überflüssig, denn dafür gibt es schon mehrere Varianten im Standard-R (Base-R). Macht aber doch Sinn, denn select hat mehrere entscheidende Vorteile:
es erzeugt sehr gut lesbaren Code
man kann auf die Anführungszeichen verzichten
es funktioniert mit der Pipe, also dem Hintereinander Ausführen mehrerer Schritte
die Auswahl bzw. Abwahl von Spalten ist flexibel und hat einige nützliche Kniffe (siehe Code-Beispiel)
Während select nur einen data.frame mit den ausgewählten Spalten zurückgibt, gibt die dplyr-Funktion rename den ganzen data.frame zurück. Diese Funktion ist, wie der Name schon sagt, zum Umbenennen von Spalten da.
Wenn Du statt Spalten auszuwählen Zeilen auswählen willst, dann ist der Befehl filter der richtige.

###############################################################################
#
# Beispiele für dplyr::select
#
###############################################################################

library(dplyr)
library(mlbench)

data(BostonHousing)

# Auswahl von zwei Spalten
Boston2 <- BostonHousing %>% select(age, nox)
# Auswahl von allen Spalten zwischen "nox" und "age"
Boston2 <- BostonHousing %>% select(nox:age)
# Auswahl der ersten 5 Spalten
Boston2 <- BostonHousing %>% select(1:5)
# Auswahl aller Spalten außer der ersten
Boston2 <- BostonHousing %>% select(-1)
# Auswahl aller Spalten außer der ersten fünf
Boston2 <- BostonHousing %>% select(-1:-5)
# Auswahl aller Spalten außer "tax"
Boston2 <- BostonHousing %>% select(-"tax")
# Reihenfolge umdrehen
Boston2 <- BostonHousing %>% select(ncol(BostonHousing):1)
# Auswahl aller Spalten, die auf s enden
Boston2 <- BostonHousing %>% select(ends_with("s"))

# Mit rename werden Spalten umbenannt
BostonHousing <- BostonHousing %>% rename(CrimeRate = crim)
BostonHousing <- BostonHousing %>%
rename(CrimeRate = crim,
Distance = dis,
Steuer = tax)

# im Gegensatz zu den anderen dplyr-Funktionen unterstützen select auch
# Strings und Vektoren
Spaltennamen <- c(Stickoxid="nox",Autobahn="rad",Schule="ptratio")
Boston2 <- BostonHousing %>% select(Spaltennamen)
# bei rename funktioniert das nur, wenn alle Spalten benannte werden
# Fehler: Boston2 <- BostonHousing %>% rename(Spaltennamen)
# Um das zu umgehen, benutzt man die "richtige" Schreibweise mit doppeltem
# Ausrufezeichen
Boston2 <- BostonHousing %>% rename(!!Spaltennamen)

Spaltennamen <- c("nox","rad")
Boston2 <- BostonHousing %>% select(one_of(Spaltennamen))

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849

################################################################################# Beispiele für dplyr::select################################################################################  library(dplyr)library(mlbench) data(BostonHousing) # Auswahl von zwei SpaltenBoston2 <- BostonHousing %>% select(age, nox)# Auswahl von allen Spalten zwischen "nox" und "age"Boston2 <- BostonHousing %>% select(nox:age)# Auswahl der ersten 5 SpaltenBoston2 <- BostonHousing %>% select(1:5)# Auswahl aller Spalten außer der erstenBoston2 <- BostonHousing %>% select(-1)# Auswahl aller Spalten außer der ersten fünfBoston2 <- BostonHousing %>% select(-1:-5)# Auswahl aller Spalten außer "tax"Boston2 <- BostonHousing %>% select(-"tax")# Reihenfolge umdrehenBoston2 <- BostonHousing %>% select(ncol(BostonHousing):1)# Auswahl aller Spalten, die auf s endenBoston2 <- BostonHousing %>% select(ends_with("s")) # Mit rename werden Spalten umbenanntBostonHousing <- BostonHousing %>% rename(CrimeRate = crim)BostonHousing <- BostonHousing %>%   rename(CrimeRate = crim,         Distance = dis,         Steuer = tax) # im Gegensatz zu den anderen dplyr-Funktionen unterstützen select auch # Strings und VektorenSpaltennamen <- c(Stickoxid="nox",Autobahn="rad",Schule="ptratio")Boston2 <- BostonHousing %>% select(Spaltennamen)# bei rename funktioniert das nur, wenn alle Spalten benannte werden# Fehler: Boston2 <- BostonHousing %>% rename(Spaltennamen)# Um das zu umgehen, benutzt man die "richtige" Schreibweise mit doppeltem # AusrufezeichenBoston2 <- BostonHousing %>% rename(!!Spaltennamen)  Spaltennamen <- c("nox","rad")Boston2 <- BostonHousing %>% select(one_of(Spaltennamen))

Die Sache mit den Anführungszeichen – Variablen mit Spaltennamen
Das ist tatsächlich – man glaubt es nicht – ein kompliziertes Thema. Was ist, wenn man einem dplyr-Befehl statt direkt die Spaltennamen (ohne Anführungszeichen) eine Variable mit dem Spaltennamen oder einen Vektor mit Spaltennamen (mit Anführungszeichen) übergeben will?
Im Gegensatz zu den meisten anderen Funktionen verstehen select direkt Vektoren mit den Spaltennamen. Stellt euch vor, ihr wollt am Anfang vom Skript die zu untersuchenden Variablen definieren (übrigens sehr zu empfehlen, sauberer Programmierstil). Oder das Programm entscheidet dynamisch, welche Spalten ausgewählt werden sollen. Dann stehen die Spaltennamen in String-Variablen oder –Vektoren. Die meisten Funktionen aus dplyr können damit nicht ohne weiteres umgehen (das hat durchaus Sinn, denn dadurch werden Doppeldeutigkeiten verhindert), bei select funktioniert es aber problemlos.
Bei rename benötigen wir den !!-Operator (siehe Code-Beispiel)
Wer mehr über non-standard-evaluation und quote/unquote erfahren will, findet in diesem Artikel auf der tidyverse-Seite eine gute Erklärung.
Nützliche Funktionen für select, aber auch andere dplyr-Befehle
dplyr liefert einige hilfreiche Funktionen mit, die die Auswahl mehrerer Spalten mit rename oder select erleichtern. Diese Funktionen können aber auch in den anderen Befehlen wie filter angewendet werden. Wer es genau nimmt: Die Befehle sind im Package tidyselect, welches aber von dplyr geladen wird.
starts_with – Wie der Name schon sagt, liefert starts_with die Spalten zurück, die mit der im Parameter angegebenen Zeichenkette anfangen
ends_with – Wie starts_with, nur eben für die Endung
contains – auch diese Funktion analog zu starts_with und ends_with. Hier muss einfach nur der String im Spaltennamen enthalten sein
matches – Auswahl der Spalten über einen regulären Ausdruck. Mehr über reguläre Ausdrücke und die Base-R-Befehle grepl, gsub, … erfahrt ihr übrigens in diesem R-HowTo zu RegEx
num_range – Auswahl von Spaltennamen, welche durchnummeriert sind. Also zum Beispiel V01, V02, V03, …
one_of – Auswahl der Spalten, deren Namen per String-Vektor angegeben werden. Bei select funktioniert das impliziert (siehe Abschnitt über Anführungszeichen)
everything – eben alle Spalten
last_col – eben die letzte Spalte

Die Funktion dplyr:: filter
Mit dem dplyr-Befehl filter lassen sich Zeilen aus einem data.frame filtern. D.h. der Datensatz wird nach bestimmten Bedingungen reduziert, z.B. Alter > 50. Also ganz simpel, aber mit eine der häufigsten Anwendungen, denn meist wollen wir nur gewisse Daten analysieren oder zum Beispiel zwei Gruppen miteinander vergleichen.

###############################################################################
#
# Beispiele für dplyr::filter
#
###############################################################################

library(mlbench)
library(dplyr)
data(BostonHousing)

# nur Zeilen mit Spalte rad größer als 8
B2 <- BostonHousing %>;% filter(rad<8)
# alternativ ohne Pipe
B2 <- filter(BostonHousing,filter(rad<8))
# in Base-R würde das so aussehen
B2 <- BostonHousing[BostonHousing$rad<8,]

# nur Zeilen mit Kriminalität unterdurchschnittlich und
# Schüler-Lehrer-Ratio größer als 75%-Quantil
B2 <- BostonHousing %>% filter(crim < mean(crim) & ptratio>quantile(ptratio,0.75))

# Vergleich den Mittelwert des Lehrer-Schüler-Ratio zwischen unterdurchschnittlicher
# und überdurchschnittlicher Kriminalität
B1 <- BostonHousing %>% filter(crim > mean(crim))
B2 <- BostonHousing %>% filter(crim > mean(crim))
t.test(B1$ptratio,B2$ptratio)

# alternativ mittels einer gruppierenden Variablen crimHoch
B2 <- BostonHousing %>% mutate(crimHoch=crim < mean(crim))
t.test(ptratio~crimHoch,B2)

12345678910111213141516171819202122232425262728293031

################################################################################# Beispiele für dplyr::filter################################################################################ library(mlbench)library(dplyr)data(BostonHousing)  # nur Zeilen mit Spalte rad größer als 8B2 <- BostonHousing %>;% filter(rad<8)# alternativ ohne PipeB2 <- filter(BostonHousing,filter(rad<8))# in Base-R würde das so aussehenB2 <- BostonHousing[BostonHousing$rad<8,] # nur Zeilen mit Kriminalität unterdurchschnittlich und # Schüler-Lehrer-Ratio größer als 75%-QuantilB2 <- BostonHousing %>% filter(crim < mean(crim) & ptratio>quantile(ptratio,0.75)) # Vergleich den Mittelwert des Lehrer-Schüler-Ratio zwischen unterdurchschnittlicher# und überdurchschnittlicher KriminalitätB1 <- BostonHousing %>% filter(crim > mean(crim))B2 <- BostonHousing %>% filter(crim > mean(crim))t.test(B1$ptratio,B2$ptratio) # alternativ mittels einer gruppierenden Variablen crimHochB2 <- BostonHousing %>% mutate(crimHoch=crim < mean(crim)) t.test(ptratio~crimHoch,B2)

Die Funktion dplyr:: summarise/summarize und dplyr::group_by
Das Package dplyr beinhaltet auch die R-Funktion summarize oder summarise (je nachdem ob man britisches oder amerikanisches Englisch lieber mag). Mit dieser lassen sich Daten aggregieren, also z.B. eine Summe oder ein Mittelwert bilden.
Insbesondere in Kombination mit der dplyr-Funktion group_by entfaltet summarize seine Wirkung. group_by funktioniert wie der SQL-Zusatz group by zum select-Befehl. Man unterteilt damit den Datensatz in verschiedene Gruppen. Kombiniert man nun summarize und group_by, lassen sich bequem Aggregate in diesen Gruppen berechnen. Im Endeffekt ähnlich zur aggregate-Funktion von Base-R, nur komfortabler.

###############################################################################
#
# Beispiele für dplyr::summarize bzw. dplyr::summarise
#
###############################################################################

library(mlbench)
library(dplyr)
data(BostonHousing)

BostonHousing %>% summarise(MW = mean(crim), SD = sd(crim))
BostonHousing %>% group_by(rad) %>% summarise(MW = mean(crim), SD = sd(crim))

123456789101112

################################################################################# Beispiele für dplyr::summarize bzw. dplyr::summarise################################################################################ library(mlbench)library(dplyr)data(BostonHousing) BostonHousing %>% summarise(MW = mean(crim), SD = sd(crim))BostonHousing %>% group_by(rad) %>% summarise(MW = mean(crim), SD = sd(crim))

Die Funktion dplyr:: arrange
Der R-Befehl arrange ordnet einen Datensatz entsprechend der Variablen, die als Parameter übergeben werden. Viel mehr gibt es darüber gar nicht zu sagen, schaut euch das Code-Beispiel an. Um eine umgekehrte Reihenfolge zu erhalten, gibt es die Funktion desc. Bei numerischen Spalten geht natürlich auch einfach das Minus-Zeichen, aber für Strings und Faktoren braucht man desc.

###############################################################################
#
# Beispiele für dplyr::arrange
#
###############################################################################

library(mlbench)
library(dplyr)
data(BostonHousing)

# sortiert nach Spalte crim
BostonHousing <- BostonHousing %>% arrange(crim)
# klassisch
BostonHousing <- BostonHousing[order(BostonHousing$crim),]

# umgekehrte Reihenfolge
BostonHousing <- BostonHousing %>% arrange(desc(crim))
BostonHousing <- BostonHousing %>% arrange(-crim)
# bei Faktoren oder Strings geht - nicht, da braucht man desc
BostonHousing <- BostonHousing %>% arrange(desc(chas))

# klassisch
BostonHousing <- BostonHousing[order(-BostonHousing$crim),]

# sortiert nach Spalte chas und dann crim
BostonHousing <- BostonHousing %>% arrange(chas,crim)

1234567891011121314151617181920212223242526

################################################################################# Beispiele für dplyr::arrange################################################################################ library(mlbench)library(dplyr)data(BostonHousing) # sortiert nach Spalte crimBostonHousing <- BostonHousing %>% arrange(crim)# klassischBostonHousing <- BostonHousing[order(BostonHousing$crim),] # umgekehrte ReihenfolgeBostonHousing <- BostonHousing %>% arrange(desc(crim))BostonHousing <- BostonHousing %>% arrange(-crim)# bei Faktoren oder Strings geht - nicht, da braucht man descBostonHousing <- BostonHousing %>% arrange(desc(chas)) # klassischBostonHousing <- BostonHousing[order(-BostonHousing$crim),] # sortiert nach Spalte chas und dann crimBostonHousing <- BostonHousing %>% arrange(chas,crim)

Zwei data.frames miteinander verbinden – joins
Kommen wir nun zu einem zentralen Bestandteil vom Datenmanagement, dem Verbinden von zwei Tabellen mittels merge oder join. Der R-Befehl merge wird in Base-R bzw. data.table verwendet, join kommt aus SQL und es gibt genau vier Varianten davon.
Wie funktioniert das grundsätzlich? Zum einen können Tabellen ganz einfach aneinander gehangen werden (untereinander oder nebeneinander), zum anderen können Tabellen anhand eines eindeutigen Identifikators (z.B. Kundennummer, Seriennummer, Name) verbunden werden. D.h. die Zeile aus Tabelle 2 wird neben die Zeile aus Tabelle 1, die den gleichen Identifikator hat, gepackt.
Wie oben erwähnt, gibt es vier Varianten mit den Identifikator-Werten umzugehen, welche nicht übereinstimmen
inner join: Es bleiben nur die Zeilen übrig, deren Identifikator in beiden Tabellen vorkommen.
left join: Die zuerst genannte Tabelle (also die linke) bleibt vollständig erhalten. Gibt es kein Pendant in der zweiten Tabelle, wird mit NAs aufgefüllt.
right join: Genau umgekehr zu left join. Die zweite Tabelle bleibt komplett erhalten. Werte, die keine Entsprechung in der ersten Tabelle haben, werden mit NAs aufgefüllt.
full (outer) join: Sowohl die erste als auch die zweite Tabelle bleiben vollständig erhalten.

Das dplyr join-Cheatsheet als pdf zum Nachschlagen könnt ihr gerne kostenlos herunterladen.
Gibt man nichts explizit an, werden alle gleichnamigen Spalten als Identifikatoren verwendet. Man kann aber auch über den Parameter by die Identifikator-Spalten definieren.

###############################################################################
#
# Beispiele für dplyr::inner_join, left_join, right_join, full_join
#
###############################################################################

library(dplyr)

table1 <- data.frame(ID=1:10,x=rnorm(10))
table2 <- data.frame(ID=5:14,y=rnorm(10))

a = inner_join(table1,table2)
b = left_join(table1,table2)
c = right_join(table1,table2)
d = full_join(table1,table2)

# zwei Spalten als Identifikatoren
table1 <- data.frame(ID1=1:10,ID2=c("rot","grün"),x=rnorm(10),stringsAsFactors = FALSE)
table2 <- data.frame(ID1=5:14,ID2=c("rot","schwarz"),y=rnorm(10),stringsAsFactors = FALSE)

a2 = inner_join(table1,table2)
b2 = left_join(table1,table2)
c2 = right_join(table1,table2)
d2 = full_join(table1,table2)

# Identifikatorwerte nicht eindeutig
table1 <- data.frame(ID1=1:10,x=paste("A",1:10),stringsAsFactors = FALSE)
table2 <- data.frame(ID1=rep(1:3,2),y=paste("B",1:6),stringsAsFactors = FALSE)

a3 = inner_join(table1,table2)
b3 = left_join(table1,table2)
c3 = right_join(table1,table2)
d3 = full_join(table1,table2)

1234567891011121314151617181920212223242526272829303132333435

################################################################################# Beispiele für dplyr::inner_join, left_join, right_join, full_join################################################################################ library(dplyr)  table1 <- data.frame(ID=1:10,x=rnorm(10))table2 <- data.frame(ID=5:14,y=rnorm(10)) a = inner_join(table1,table2)b = left_join(table1,table2)c = right_join(table1,table2)d = full_join(table1,table2) # zwei Spalten als Identifikatorentable1 <- data.frame(ID1=1:10,ID2=c("rot","grün"),x=rnorm(10),stringsAsFactors = FALSE)table2 <- data.frame(ID1=5:14,ID2=c("rot","schwarz"),y=rnorm(10),stringsAsFactors = FALSE) a2 = inner_join(table1,table2)b2 = left_join(table1,table2)c2 = right_join(table1,table2)d2 = full_join(table1,table2)  # Identifikatorwerte nicht eindeutigtable1 <- data.frame(ID1=1:10,x=paste("A",1:10),stringsAsFactors = FALSE)table2 <- data.frame(ID1=rep(1:3,2),y=paste("B",1:6),stringsAsFactors = FALSE) a3 = inner_join(table1,table2)b3 = left_join(table1,table2)c3 = right_join(table1,table2)d3 = full_join(table1,table2)

Fazit
Habt ihr euch wirklich alles bis zum Ende durchgelesen? Dann seid ihr nun bestens gerüstet, dplyr einzusetzen. Daneben habt ihr das Konzept der Pipe kennengelernt, welches viele Anwendungen hat, z.B. auch für das Grafik-Package ggplot2. Und ihr versteht nun die verschiedenen join-Arten.
Fun Fact: Pliers heißt Zange auf englisch. Daher kommt die Zange im Logo von dplyr.
Ich bin auf Euren Input angewiesen. Twittert diese Erklärung zu dplyr oder hinterlasst einen Kommentar und schreibt mir, wie ihr dplyr einsetzt.

Happy coding,
Euer Holger
 
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste

Python Datentypen Übersicht

Teile diesen Beitrag:Dieser Blogartikel gibt eine Übersicht über die verschiedenen Basis-Datentypen in Python und richtet sich damit an die Python-Einsteiger. Es gibt gar nicht so viele verschiedene Python-Datentypen und jeder, der in Python programmiert, sollte diese Typen kennen. Je nach genutztem Package gibt es natürlich noch andere. Wer Data Science in Python lernen möchte, kommt an NumPy und dessen schnelles Array nicht herum.
Also los geht’s:
Als erstes brauchen wir natürlich die Möglichkeit, Zahlen und Wörter (Strings, Zeichenketten) in entsprechenden Variablen zu speichern. Dann erweitern wir diese zu Kombinationen, also Listen, Tupel und Mengen von Variablen sowie sogenannten Dictionaries (Lexika), welche man sich tatsächlich wie ein Lexikon oder Index vorstellen kann.
Die wichtigsten Datentypen in Python
Zahlen / numerische Datentypen
Strings / Zeichenketten
Listen
Tupel
Mengen
Dictionary
Und wenn ihr dieses HowTo nützlich findet, dann teilt es bitte bei Twitter. So finden es noch mehr Leute:
 
Benennung von Variablen
Ok, man kann seine Python-Variablen ziemlich beliebig benennen, es gibt aber ein paar Regeln. Die wichtigste Regel ist natürlich, eure Variablen sinnvoll zu benennen, so dass ihr euch noch zurecht findet, wenn ihr den Code in einem Jahr nochmal anschaut.
Ansonsten gelten folgende Einschränkungen:
Es sind nur Buchstaben (a-z, A-Z), Zahlen (0-9) und der Unterstrich _ erlaubt
Variablen dürfen nicht mit einer Zahl anfangen, also 9a ist verboten
Python beachtet Groß- und Kleinschreibung, d.h. Abc und abc sind zwei unterschiedliche Variablen
Es gibt ein paar reservierte Schlüsselwörter, die nicht verwendet werden dürfen, da sie schon für andere Zwecke reserviert sind:
and
continue
except
global
lambda
pass
while
as
def
False
if
None
raise
with
assert
del
finally
import
nonlocal
return
yield
break
elif
for
in
not
True

class
else
from
is
or
try

 
Variablendeklaration in Python
Man muss Variablen in Python nicht deklarieren, also den Datentyp mit angeben, wie das in anderen Programmiersprachen wie C der Fall ist.
In Python schreibt man also einfach a=3. Python erkennt automatisch, dass es sich um eine ganze Zahl handelt und weist a den Variablentyp int zu. In C müsste man den Typ mit angeben also int a = 3;
Die automatische Typendeklaration macht es für den Programmierer einfacher, weil er sich nicht darum kümmern muss. Es hat aber auch Nachteile, denn die explizite Angabe gibt uns mehr Kontrolle. So können in Python zum Beispiel keine Konstanten (also nicht änderbare Variablen) definiert werden. Der Programmierer muss selber darauf achten, den Wert nicht zu verändern.
 
Numerische Datentypen in Python
Es gibt drei Arten von Zahlen in Python
int ist für ganze Zahlen, also z.B. 2, 3, 10, 100, 10000000, -5, -10000
float ist für Dezimalzahlen (Kommazahlen), also z.B. 3.14, 1.9999, -0.96424, 10.23. Beachte, dass als Dezimaltrennzeichen der Punkt statt des Kommas verwendet wird (englische Konvention)
complex ist für komplexe Zahlen, welche aus Real- und Imaginärteil bestehen. Wenn Du nicht weißt, was das ist, macht das nichts. Dann benötigst Du diesen Datentyp vermutlich auch nicht.
Die zulässige Größe ist im Gegensatz zu vielen anderen Programmiersprachen nicht beschränkt, sondern hängt nur vom Speicherplatz ab.
Wie im vorherigen Abschnitt geschrieben, müsst ihr euch in Python nicht explizit um den Typ kümmern, d.h. wenn ihr a = 3 eingebt, dann wird a eine Variable vom Typ int. Wenn ihr a = 3.0 eingebt, dann wird a eine Variable vom Typ float.

############################################
# Zahlen
############################################

# Integer-Variablen (ganze Zahlen) definieren
a = 3
b = -10
c = 100000000000000000000000000000000000000000000000000
d = a
a = -2

# Float-Variablen (Kommazahlen) definieren
fa = 3.14
fb = -0.9999999999999
# Exponentialschreibweise -5e-5 = -5 * 10^3 = -5 * 1000 = -5000
fc = -5e3

# complex-Variablen (komplexe Zahlen) definieren
w = 3 + 2j
z = 1j

# gleichzeitige Zuweisung mehrerer Variablen
i, j, k = 10, 20, 3.14

1234567891011121314151617181920212223

############################################# Zahlen############################################ # Integer-Variablen (ganze Zahlen) definierena = 3b = -10c = 100000000000000000000000000000000000000000000000000d = aa = -2 # Float-Variablen (Kommazahlen) definierenfa = 3.14fb = -0.9999999999999# Exponentialschreibweise -5e-5 = -5 * 10^3 = -5 * 1000 = -5000fc = -5e3 # complex-Variablen (komplexe Zahlen) definierenw = 3 + 2jz = 1j # gleichzeitige Zuweisung mehrerer Variableni, j, k = 10, 20, 3.14

Strings / Zeichenketten
Jede Programmiersprache kann Zeichenketten in Variablen speichern, so auch Python. Und das ist genau simpel wie bei Zahlen, nämlich einfach die Zeichenkette, die ihr speichern wollt in Anführungsstriche, egal ob einfache oder doppelte, verpacken. Also s = „Hallo“ bzw.  s= ‚Hallo‘.
Für die Länge eines Strings s gibt es die Funktion len(s). Einzelne Zeichen eines Strings könnt ihr mit eckigen Klammern ansprechen, so gibt z.B. s[0] das erste Zeichen zurück oder s[0:2] die ersten zwei Zeichen. Achtung: In Python beginnt die Zählweise mit 0 und bei einer Folge wie 0:2 ist das rechte Element nicht mehr dabei, d.h. 0:2 entspricht den Werten 0 und 1, also das 1. und das 2. Zeichen des Strings

############################################
# Strings
############################################

s1 = "Hello"
s2 = "databraineo!"
s2 = s1
s1 = "Ola"
s3 = 'auch ein String'

s2[0]
s2[1:]
len(s2)

#Sonderzeichen
# \ wird als Start für Escape-Sequenz erkannt, \t als Tab
print('C:\temp')
# für den Backslash benötigen wir also \\
print('C:\\temp')
#raw string
s4 = r'C:\\temp'
#
#mehrzeiliger String
s5 = """abc
def
ghi"""
print(s5)

# einen String als Unicode definieren
s6 = u"Das hier ist Unicode"

123456789101112131415161718192021222324252627282930

############################################# Strings############################################ s1 = "Hello"s2 = "databraineo!"s2 = s1s1 = "Ola"s3 = 'auch ein String' s2[0]s2[1:]len(s2) #Sonderzeichen# \ wird als Start für Escape-Sequenz erkannt, \t als Tabprint('C:\temp')# für den Backslash benötigen wir also \\print('C:\\temp')#raw strings4 = r'C:\\temp'##mehrzeiliger Strings5 = """abcdefghi"""print(s5) # einen String als Unicode definierens6 = u"Das hier ist Unicode"

Listen, Tupel, Mengen
Ihr wollt mehrere Elemente abspeichern, dann braucht ihr Listen, Tupel oder Mengen. Zusammengefasst sind Listen eben eine (geordnete) Liste von Elementen. Die einzelnen Elemente lassen sich über einen Index ansprechen. Tupel sind fast das gleiche wie eine Liste, nur lassen sich die einzelnen Elemente nicht mehr verändern. Mengen sind nicht geordnet und haben keine doppelten Elemente.
Listen in Python
Dabei sind Listen sicherlich die wichtigste Struktur. Eine Liste wird mit eckigen Klammern definiert und besteht aus einer Folge von Elementen, die sogar von unterschiedlichen Typen sein können. Also zum Beispiel l1 = [1, 2, 3] oder l2 = [„grün“, 3.14, 2].
Die Elemente der Liste lassen sich über die Indizes ansprechen. Im Prinzip genauso wie die einzelnen Zeichen eines Strings. Und wieder gilt, dass wir bei 0 mit dem Zählen anfangen. Also l1[0] gibt das 1. Element zurück, in dem Fall 1. l2[0] würde „grün“ zurückgeben.
Listen können beliebige Datentypen enthalten, auch wieder Listen. l3 = [[1,2], „grün“, [3.0,3.1,3.2]]
Vorsicht beim Kopieren von Listen: Wenn man mit l2 = l1 eine Liste „kopiert“, wird keine echte Kopie gemacht, sondern nur der sogenannte Zeiger auf die Liste kopiert. Verändere ich die ursprüngliche Liste l1, dann verändert sich damit auch l2. Um eine echte Kopie zu machen, verwendet man l2 = l1.copy().
Tupel in Python
Tupel sind im Gegensatz zu Listen nicht mehr veränderbar, d.h. im Nachhinein können keine Elemente eines Tupels geändert werden. Ansonsten verhalten sie sich genauso wie Listen. Tupel werden mit runden Klammern initialisiert, also z.B. t1 = (1, 2, 3).
Mit t1[1] greift ihr auf das 2. Element zurück, hier also 2. Nicht erlaubt ist t1[1] = 9, da wie eben geschrieben, Tupel nicht veränderbar sind.
Mengen in Python
Vermutlich eher ein selten genutztes Konstrukt. Mengen enthalten eindeutige Elemente, also keine doppelten Einträge, und sind nicht geordnet. D.h. im Gegensatz zu Listen oder Tupel können wir die Elemente nicht mit einem Index abrufen. Mengen werden in Python mit geschweiften Klammern gebildet. also z.B. m1 = { „grün“, „rot“, „blau“}

############################################
# Listen, Tupel, Mengen
############################################
alist = [1,2,3,4]
alist
atuple = (1,2,3,3)
atuple
aset = {'grün','gelb','rot','rot'}
aset

# Elemente einer Liste können geändert werden
alist[2] = 9.34
alist[3] = 'grün'
alist[0] = ['gelb',-2e-2,5]
len(alist)

# Elemente eines Tuples können nicht geändert werden
atuple[0] = 9
btuple = atuple + ('grün',2.1,[-3,-2,0])
btuple[6]
btuple[6][2] = 9
len(btuple)

# Mengen sind nicht geordnet
aset[0]
'grün' in aset
aset.add('lila')
aset.remove('grün')

# Listen werden nicht kopiert, sondern zeigen auf das gleiche Objekt
blist = [1,2,3]
clist = blist
clist[0]
blist[0] = 9
clist[0]
# echte Kopien erstellen
dlist = blist.copy()
dlist = list(blist)

1234567891011121314151617181920212223242526272829303132333435363738

############################################# Listen, Tupel, Mengen############################################alist = [1,2,3,4]alistatuple = (1,2,3,3)atupleaset = {'grün','gelb','rot','rot'}aset # Elemente einer Liste können geändert werdenalist[2] = 9.34alist[3] = 'grün'alist[0] = ['gelb',-2e-2,5]len(alist) # Elemente eines Tuples können nicht geändert werdenatuple[0] = 9btuple = atuple + ('grün',2.1,[-3,-2,0])btuple[6]btuple[6][2] = 9len(btuple) # Mengen sind nicht geordnetaset[0]'grün' in asetaset.add('lila')aset.remove('grün') # Listen werden nicht kopiert, sondern zeigen auf das gleiche Objektblist = [1,2,3]clist = blistclist[0]blist[0] = 9clist[0]# echte Kopien erstellendlist = blist.copy()dlist = list(blist)

 
Python Dictionaries
Ein Dictionary kann man sich, wie der Name sagt, als Lexikon vorstellen, d.h. es gibt einen Schlüsselbegriff (key), dem etwas zugeordnet wird. Beim normalen Lexikon wäre es eine Erklärung des Schlüsselbegriffs, beim dictionary können es aber beliebige Datentypen sein. Wie schon bei Listen, Tupeln oder Mengen können auch dictionaries verschachtelt sein.
Ein Dictionary wird wie eine Menge per geschweiften Klammern definiert, allerdings bestehen die Einträge aus dem Schlüssel, gefolgt von Doppelpunkt und dann dem Wert.
Die einzelnen Einträge werden dann anstatt über einen Index über den Schlüssel angesprochen. So können die Einträge auch geändert werden. Wenn man alle Werte des Dictionaries dic als Liste bekommen will, benutzt man die Funktion values, also list(dic.values()). Beachtet, dass wir noch list davor schreiben müssen, um auch wirklich eine Liste zu bekommen. Genauso funktioniert es mit den Schlüsseln, also list(dic.keys()).
Sehr praktisch sind dictionaries, wenn mehrere Eigenschaften eines Objekts gespeichert werden sollen. Zum Beispiel soll für einen Kunden Name, Vorname und Alter gespeichert werden.

############################################
# Dictionaries
############################################
dic = { 'E1':'Sofa','E2':'Stuhl','E3':'Tisch' }
print(dic)
dic['E1']
dic[0]
dic.values()
dic.keys()

dic = { 1:'Sofa',2:'Stuhl',3:'Tisch' }
dic[1]

Kunde = {'Vorname':'Kevin', 'Name': 'Chalupke', 'Alter': 32}
print(Kunde['Name'])
print(Kunde['Alter'])
Kunde['Alter'] = 33

123456789101112131415161718

############################################# Dictionaries############################################dic = { 'E1':'Sofa','E2':'Stuhl','E3':'Tisch' }print(dic)dic['E1']dic[0]dic.values()dic.keys() dic = { 1:'Sofa',2:'Stuhl',3:'Tisch' }dic[1]  Kunde = {'Vorname':'Kevin', 'Name': 'Chalupke', 'Alter': 32}print(Kunde['Name'])print(Kunde['Alter'])Kunde['Alter'] = 33

 
Den Datentyp einer Variable abfragen
Um zu prüfen, welchen Datentyp eine Variable x hat, gibt es den Befehl type(x). Mit isinstance(x,typ) wird geprüft, ob x vom angegebenen Typ ist. Es wird also True oder False zurückgegeben
Python Datentypen umwandeln
Das Umwandeln von Datentypen in Python ist ziemlich intuitiv. Man benutzt einfach den Datentyp als Funktion und als Parameter kommt die umzuwandelnde Variable.
int zu float: float(3)
float zu int: int(3.14)
int zu string: str(3)
string zu int: int("3")
float zu string: str(3.14)
string zu float: float("3.14")
Auch die drei zusammengesetzten Typen List, Tuple und Set lassen sich so ineinander überführen
list zu tuple: tuple(l)
tuple zu list: list(t)
list zu set: set(l)
set zu list: list(s)
tuple zu set: set(t)
set zu tuple: tuple(s)
Nur das Dictionary fällt aus der Reihe, denn Python kann ja nicht wissen, wie es z.B. eine Liste in ein Dictionary umwandeln soll, denn es fehlen die Schlüssel. Andersherum funktioniert es: aus einem Dictionary kann man eine Liste/Tupel/Menge machen, allerdings werden nur die Schlüssel verwendet.

############################################
# Datentypen umwandeln
############################################

# int schneidet die Dezimalstellen ab
x = 3.14
int(x)
int(3.6)
int(-0.9)

float(3)
float("3.14")

int("3")
# ein String einer Dezimalzahl kann nicht in einen int umgewandelt werden
int("3.14")
int(float("3.14"))

str(10)
str(10.34)

# Listen, Tupel und Mengen lassen sich ineinander überführen
tuple(alist)
list(atuple)
set(alist)
set(atuple)
list(aset)
tuple(aset)

# Wenn man ein Dictionary in eine Liste/Tupel/Menge umwandelt, wird
# der Schlüssel (Key) in das entsprechende Objekt umgewandelt
list(dic)
tuple(dic)
set(dic)
list(Kunde)

12345678910111213141516171819202122232425262728293031323334353637

############################################# Datentypen umwandeln ############################################ # int schneidet die Dezimalstellen abx = 3.14int(x)int(3.6)int(-0.9)  float(3)float("3.14") int("3")# ein String einer Dezimalzahl kann nicht in einen int umgewandelt werdenint("3.14")int(float("3.14")) str(10)str(10.34) # Listen, Tupel und Mengen lassen sich ineinander überführentuple(alist)list(atuple)set(alist)set(atuple)list(aset)tuple(aset)  # Wenn man ein Dictionary in eine Liste/Tupel/Menge umwandelt, wird# der Schlüssel (Key) in das entsprechende Objekt umgewandeltlist(dic)tuple(dic)set(dic)list(Kunde)

 
So, das war es mit den wichtigsten Datentypen in Python. Eigentlich nicht so schwierig, oder?
Hat Euch der Beitrag gefallen? Dann schreibt mir einen Kommentar oder teilt den Artikel bei Twitter, damit andere auch etwas davon haben.

Happy coding,
Euer Holger
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste

Kopieren, Umbennen und Löschen von Dateien in R

Teile diesen Beitrag:Hej Leute,
wie der Titel schon sagt, geht es in diesem Artikel darum, die R-Befehle kennenzulernen, mit denen man Dateien in einem Verzeichnis auflistet, Dateien umbenennt, kopiert oder löscht. Und das alles in R mit einfachen Funktionen. Das ist super praktisch für viele Automatisierungs-Aufgaben.
Ein Szenario besteht darin, alle Excel- oder pdf-Dateien eines Verzeichnisses in R einzulesen. Oder man will die neueste Datei. die einem gewissen Namensaufbau hat, z.B. „Quartalsreporting 20XX.xlsx“ in einem Verzeichnis zu finden. Ein weiteres Beispiel aus meiner Arbeitspraxis ist das automatische Erstellen eines Backups, bevor ich eine Datei mit neuen Daten aktualisiere.
Natürlich geht das alles auch über Skripte (bash, Powershell) und für komplexere Aufgaben ist das vermutlich auch die bessere Wahl. Der Vorteil, in R zu bleiben, ist aber groß: Der Code ist an einem Ort und für jeden R-Programmierer gut lesbar.
 
Das Arbeitsverzeichnis in R wechseln
Die am meisten verwendeten Befehle sind wohl getwd() und setwd(Verzeichnis). getwd steht für get working directory, der Befehl gibt also das aktuelle Arbeitsverzeichnis zurück. Mit setwd kann man es dementsprechend verändern. R benutzt immer ein Verzeichnis als Arbeitsverzeichnis.
Der Backslash
Für Windows-Nutzer: R benutzt, wie viele andere Programmiersprachen auch, den Backslash \ als sogenannte Escape-Sequenz, d.h. als Startzeichen für ein Sonderzeichen, z.B. eine neue Zeile wird in einem String mit \n dargestellt. Um einen einfachen Backslash in einem String darzugestellen, muss man zwei Backslashes schreiben, also \\. Für Verzeichnisse geht aber auch der Forward-Slash /. Man muss also “C:/temp” oder “C:\temp” benutzen.
Achtung: Fehleranfälligkeit bei absoluten Pfaden
Hier ein Wort der Warnung zu absoluten Pfaden, die als schlechter Stil angesehen werden. Insbesondere, wenn dann auch noch mitten im Code ein setwd verwendet wird. Warum ist das so? Es geht dabei um Wartbarkeit und Portabilität. Solange ein R-Skript nur auf meinem PC vom gleichen Verzeichnis gestartet wird, habe ich kein Problem. Wenn ich es aber verschiebe oder auf einem anderen Rechner starte (Stichwort: produktiv setze), funktionieren die Pfade eventuell nicht. Auch sollten abhängige Dateien, die im Skript eingelesen werden, nicht quer über die Festplatten verstreut liegen und mit absoluten Pfaden darauf zugegriffen werden.
Eine Möglichkeit ist, in RStudio ein Projekt anzulegen. Wenn ich das Projekt öffne, wird das Arbeitsverzeichnis auf den Projektordner gesetzt. Alle Dateien, die benötigt werden, sollten sich in dem Ordner oder Unterordnern befinden.
Möchte man auf Projekte verzichtet, dann empfielt sich das setwd, welches in das Verzeichnis des Skripts wechselt, ganz am Anfang vom Skript, also gut sichtbar, zu platzieren. Dann nur noch mit relativen Pfaden auf Unterordner zugreifen.
Insbesondere das Vorhalten aller Daten in Unterverzeichnissen ist aber nicht immer praktikabel. Bei meiner Arbeit haben wir zum Beispiel mehrere zentrales Verzeichnisse, in denen z.B. die SAP-Daten liegen. Hier empfielt es sich, die benötigten Verzeichnisse am Anfang vom R-Skript als Variablen zu definieren, die bei Bedarf angepasst werden können. Wir sind sogar noch einen Schritt weitergegangen und ich habe ein Package geschrieben, dass die Verzeichnisse per get-Funktionen, also z.B. getSAPDirectory(), zurückgibt. Sollten sich die Verzeichnisse oder Netzwerkpfade einmal ändern, gibt es eine neue Version des Packages.
R-Beispielcode für getwd und setwd

# ein Verzeichnis als String speichern, beachte den Forward-Slash
Verzeichnis &lt;- "C:/"

# speichere aktuelles Arbeitsverzeichnis in einer Variable
oldwd &lt;- getwd()

# Wechsle in ein anderes Verzeichnis
setwd(Verzeichnis)

# mache hier irgendetwas ...
list.files()

# Wechsle wieder in das alte Verzeichnis
setwd(oldwd)

# Variable oldwd löschen
rm(oldwd)

1234567891011121314151617

# ein Verzeichnis als String speichern, beachte den Forward-SlashVerzeichnis &lt;- "C:/" # speichere aktuelles Arbeitsverzeichnis in einer Variableoldwd &lt;- getwd() # Wechsle in ein anderes Verzeichnissetwd(Verzeichnis) # mache hier irgendetwas ...list.files() # Wechsle wieder in das alte Verzeichnissetwd(oldwd) # Variable oldwd löschenrm(oldwd)

Ein Verzeichnis durchsuchen
Eine Liste (technisch: ein Vektor) mit den Dateinamen eines Verzeichnisses zu bekommen, ist in R total simpel. Es genügt der Befehl list.files, der verschiedene nützliche Parameter bietet. Die drei wichtigsten habe ich hier aufgelistet, insbesondere die Angabe eines regulären Ausdrucks mittels pattern ist sehr hilfreich, sofern man Dateien systematisch benannt hat. Man denke an alle Exceldateien, die mit “Quartalsreporting” beginnen und 2019 enthalten.
path gibt den zu durchsuchenden Pfad an. Ist kein Pfad angegeben, wird das aktuelle Arbeitsverzeichnis (siehe getwd und setwd) genommen.
pattern ermöglicht es, die Dateiliste mittels regulärer Ausdrücke zu filtern. Reguläre Ausdrücke sind eine in der IT übliche und sehr flexible Methode, Mengen von Strings zu beschreiben. Reguläre Ausdrücke können recht komplex sein, häufig kommt man aber mit einem einfachen Ausdruck zu Rande. Mehr über reguläre Ausdrücke findet ihr im Kapitel Strings.
recursive bezieht die Unterordner in die Suche mit ein, d.h. alle Dateien aus den Unterordnern werden als relativer Pfad (von der Form Unterordner/Dateiname oder Unterordner/Unterordner/Dateiname) mit ausgegeben.
Alle Parameter zum Befehl list.files findet ihr in meinem kostenlosen R-Glossar, in dem eine ständig wachsende Zahl R-Befehle mit Erklärung und Beispielen aufgelistet sind.

# gibt einen Vektor mit allen Dateien im aktuellen Arbeitsverzeichnis zurück
Dateien &lt;- list.files()

# gibt einen Vektor mit allen Dateien in einem bestimmten Verzeichnis zurück
list.files (path = "C:/Ordner/Unterordner")

# bezieht die Unterordner in die Suche mit ein
list.files(recursive = TRUE)

# auch Ordner werden mit zurückgegeben, allerdings kann man nicht mehr zwischen
# einem Ordner und einer Datei ohne Endung (ja, die gibt es, wenn auch selten)
# unterscheiden
list.files(include.dirs = TRUE)

#liefert einen Vektor mit allen Dateien, welche die Endlung xlsx haben. Das $-Zeichen in einem regulären Ausdruck bedeutet das Ende des Strings. Würden wir es weglassen, dann würden alle Dateien, welche irgendwo xlsx im Namen haben, zurückgegeben.
list.files(pattern = "xlsx$")

#alle Dateien, die mit Report anfangen und die Endung .csv haben
list.files(pattern = "^Report(.*)\\.csv$", ignore.case=TRUE)

12345678910111213141516171819

# gibt einen Vektor mit allen Dateien im aktuellen Arbeitsverzeichnis zurückDateien &lt;- list.files() # gibt einen Vektor mit allen Dateien in einem bestimmten Verzeichnis zurücklist.files (path = "C:/Ordner/Unterordner") # bezieht die Unterordner in die Suche mit einlist.files(recursive = TRUE) # auch Ordner werden mit zurückgegeben, allerdings kann man nicht mehr zwischen# einem Ordner und einer Datei ohne Endung (ja, die gibt es, wenn auch selten) # unterscheidenlist.files(include.dirs = TRUE) #liefert einen Vektor mit allen Dateien, welche die Endlung xlsx haben. Das $-Zeichen in einem regulären Ausdruck bedeutet das Ende des Strings. Würden wir es weglassen, dann würden alle Dateien, welche irgendwo xlsx im Namen haben, zurückgegeben.list.files(pattern = "xlsx$") #alle Dateien, die mit Report anfangen und die Endung .csv habenlist.files(pattern = "^Report(.*)\\.csv$", ignore.case=TRUE)

Es gibt übrigens auch den Befehl list.dirs, der entsprechend nur die Verzeichnisse zurückgibt. Allerdings gibt es weniger Parameter, nur path, full.names (default=TRUE) und recursive (default=TRUE) sind möglich. So werden z.B. immer die versteckten Ordner angezeigt, bei list.files gibt es dafür den Parameter all.files (default = FALSE).
 
Verzeichnisse prüfen, erstellen und löschen
Um zu prüfen, ob ein Verzeichnis existiert, gibt es den Befehl dir.exists. Um ein Verzeichnis anzulegen, benutzt man dir.create.
Nur der Lösch-Befehl fällt etwas aus dem Rahmen und heißt unlink. Wichtig bei unlink ist, dass der Parameter recursive=TRUE gesetzt wird. Das hängt damit zusammen, dass unlink auch für Dateien verwendet werden kann. Auch ein leeres Verzeichnis kann nicht gelöscht werden, wenn recursive=FALSE. Der Rückgabewert, der zwar nicht wiedergegeben wird, aber per Variable abgefangen werden kann (siehe Skript), ist bei Erfolg 0, bei Fehler 1. Allerdings gilt das Fehlen des Verzeichnisses nicht als Fehler. Konnte das Verzeichnis hingegen nicht gelöscht werden, weil die Berechtigung fehlt oder es aktuell in Verwendung ist (z.B. wenn eine Datei aus dem Verzeichnis durch ein Programm geöffnet ist), dann gibt unlink 1 als Wert zurück.

# prüft, ob das Verzeichnis temp exisitert
dir.exists("temp")
# erstelle das Verzeichnis temp
dir.create("temp")
# jetzt, da wir es angelegt haben, gibt die Funktion TRUE zurück
dir.exists("temp")
# auch per list.files kann man es sehen
list.files(pattern="temp", include.dirs=TRUE)
#häufig wird dir.exists und dir.create kombiniert
if (!dir.exists("temp")) dir.create("temp")
# Das Verzeichnis temp wieder löschen, inklusive aller darin enthaltenen Dateien. Dabei ist wichtig, recursive=TRUE zu setzen
a &lt;- unlink("temp", recursive=TRUE)
print(a)

12345678910111213

# prüft, ob das Verzeichnis temp exisitertdir.exists("temp")# erstelle das Verzeichnis tempdir.create("temp")# jetzt, da wir es angelegt haben, gibt die Funktion TRUE zurück dir.exists("temp")# auch per list.files kann man es sehenlist.files(pattern="temp", include.dirs=TRUE)#häufig wird dir.exists und dir.create kombiniertif (!dir.exists("temp")) dir.create("temp")# Das Verzeichnis temp wieder löschen, inklusive aller darin enthaltenen Dateien. Dabei ist wichtig, recursive=TRUE zu setzena &lt;- unlink("temp", recursive=TRUE)print(a)

Dateien in R erstellen, kopieren, umbennen und löschen
Was können wir mit Dateien anstellen? Na ja, prüfen, ob sie existieren, erstellen, kopieren, umbenennen oder löschen. Wobei das Erstellen ohne Zusammenhang eher selten vorkommt, meist schreibt man dann doch direkt eine csv- oder xlsx-Datei oder wenigstens Text in die Datei. Wie man letzteres macht, erfahrt ihr weiter unten im nächsten Abschnitt.
An sich sind die Befehle ziemlich klar aufgebaut, nämlich file.xxx. Alle Befehle nehmen nicht nur einzelne Strings mit einem Dateinamen entgegen sondern auch Vektoren von Dateinamen und führen den Befehl für alle darin enthaltenen Dateien aus.
file.exists prüft, ob eine oder mehrere Dateien bereits existieren und gibt dementsprechend einen Vektor mit TRUE oder FALSE zurück
file.create erzeugt eine leere Datei bzw. überschreibt eine existierende, sofern der Parameter overwrite=TRUE gesetzt wird.
file.rename benennt Dateien um
file.remove entfernt eine oder mehrere Dateien. Als Rückgabewert wird TRUE oder FALSE zurückgegeben, je nachdem ob eben das Löschen geklappt hat oder nicht
file.copy kopiert Dateien. Dabei gibt es die Parameter overwrite, copy.mode und copy.date. Overwrite sorgt dafür, dass eine schon existierende Datei überschrieben wird, mit copy.mode=TRUE kopiert R die Berechtigungen mit (Lese-/Schreib-Einschränkungen) und mit copy.date=TRUE wird das Erstellungsdatum der ursprünglichen Datei kopiert.

R

# prüft, ob eine Datei existiert
file.exists("abc.txt")
# erzeugt eine leere Datei
file.create("abc.txt")
# die Datei abc.txt in cde.txt umbenennen
file.rename("abc.txt", "cde.txt")
# Versucht, die Datei abc.txt zu löschen. Diese existiert aber nicht mehr, da wir sie ja
# umbenannt habe. Daher wird eine Meldung und FALSE zurückgegeben.
file.remove("abc.txt")
# Erzeugt den Ordner Backup und kopiert die Datei cde.txt dorthin
dir.create("Backup")
file.copy("cde.txt","Backup/cde.txt",overwrite=TRUE,copy.date=TRUE)
# file.remove nimmt auch einen Vektor mit den Dateinamen entgegen und erzeugt dann
# einen Ausgabevektor mit TRUE/FALSE
file.remove(c("cde.txt","abc.txt"))
# Das Backup-Verzeichnis wieder löschen
unlink("Backup",recursive=TRUE)

1234567891011121314151617

# prüft, ob eine Datei existiertfile.exists("abc.txt")# erzeugt eine leere Dateifile.create("abc.txt")# die Datei abc.txt in cde.txt umbenennenfile.rename("abc.txt", "cde.txt")# Versucht, die Datei abc.txt zu löschen. Diese existiert aber nicht mehr, da wir sie ja # umbenannt habe. Daher wird eine Meldung und FALSE zurückgegeben.file.remove("abc.txt")# Erzeugt den Ordner Backup und kopiert die Datei cde.txt dorthindir.create("Backup")file.copy("cde.txt","Backup/cde.txt",overwrite=TRUE,copy.date=TRUE)# file.remove nimmt auch einen Vektor mit den Dateinamen entgegen und erzeugt dann # einen Ausgabevektor mit TRUE/FALSEfile.remove(c("cde.txt","abc.txt"))# Das Backup-Verzeichnis wieder löschenunlink("Backup",recursive=TRUE)

Datei-Informationen wie Datum oder Größe in R
Als letztes zeige ich euch noch, wie ihr Informationen zu Dateien in R auslesen könnt. Die wichtigsten sind vermutlich Dateigröße oder Änderungsdatum. So könnte man das Änderungsdatum checken, um zu sehen, ob es ein Update der Datei gab und es sich lohnt, den Inhalt einzulesen. Die R-Funktionen dafür sind überschaubar denn eigentlich gibt es nur file.info. Die weiteren hier aufgezählten Funktionen rufen file.info auf, sind aber eventuell bequemer in der Anwendung.
Ein bisschen tricky sind die Berechtigungen (Spalte mode), da diese kodiert sind und man sich die Werte herauspulen muss. Andererseits benötigt man diese Details eher selten.

file.info liefert einen data.frame mit 7 Spalten, jede Zeile entspricht einer angegeben Datei.
size: Die Dateigröße in Bytes
isdir: Handelt es sich um ein Verzeichnis
mode: gibt eine dreistellige Oktalzahl mit den Rechten zurück. Das Ganze ist ein bisschen kompliziert. Die drei Ziffern stehen für den Besitzer der Datei, die Gruppe, der der Besitzer angehört und jeder. Eine Ziffer setzt sich zusammen aus Lesen (Wert 4), Schreiben (Wert 2) und Ausführen (Wert 1), also z.B. 6 für Lese- und Schreibzugriff.
mtime: Zeitstempel, wann die Datei das letzte Mal geändert wurde
ctime: Zeitstempel, wann der Status der Datei das letzte Mal geändert wurde, also z.B. durch chmod auf Unix. Unter Windows entspricht das dem Erstellungsdatum.
atime: Zeitstempel, wann das letzte Mal auf die Datei zugegriffen wurde
exe: Die Ausführbarkeit auf Windows-PCs. Mögliche Werte sind „no“, „msdos“,“win16″, „win32“, „win64“ und „unknown“

file.access testet eine Datei auf die Zugriffsmöglichkeiten, welche man mit dem Parameter mode definiert. Dabei wird 0 für Erfolg und -1 für Misserfolg zurückgegeben
0: Existenz (default)
1: Ausfürbarkeit
2: Schreibrechte
3: Leserechte

file.mtime gibt einen Zeitstempel (POSIXct) zurück, wann die Datei zum letzten Mal modifiziert wurde
file.size gibt die Größe der Datei in Bytes zurück. Um zur nächst größeren Einheit (Kilobyte, Megabyte, Gigabyte, …) zu kommen, muss man die Zahl durch 1024 teilen. Zur Illustration habe ich unten im Beispiel eine Funktion geschrieben, die einen Parameter für die Größeneinheit entgegennimmt.
 

R

# gibt einen data.frame mit vielen Informationen (Größe, Datum, ...) zu den übergebenen Dateien zurück
file.info("cde.txt")

# Eistiert die Datei
file.access("cde.txt")
file.access("cde.txt", mode=1)
file.access("cde.txt", mode=2)
file.access("cde.txt", mode=3)

# Wann wurde die Datei zum letzten Mal modifiziert
file.mtime("cde.txt")

# die Größe der Datei in Bytes
file.size("cde.txt")

# Eine Funktion, um die Dateigröße auch in anderen Einheiten wiederzugeben
Dateigroesse &lt;- function(Dateien, Einheit="MB") {
# Einheit in Großbuchstaben umwandeln
Einheit &lt;- toupper(Einheit)
# Fehlermeldung bei nicht unterstützter Einheit
if(!(Einheit %in% c("B","KB","MB","GB"))) {
print("Die Funktion unterstützt nur die Einheiten B, KB, MB und GB")
return()
}
# Faktor für Einheit
Faktor &lt;- ifelse(Einheit=="B",1,
ifelse(Einheit=="KB",1024,
ifelse(Einheit=="MB",1024^2,
ifelse(Einheit=="GB",1024^3,
NA))))
# Rückgabe von Bytegröße/Faktor, gerundet auf 2 Stellen
return(round(file.size(Dateien)/Faktor,2))
}
Dateigroesse("Dateisystem_in_R.html","KB")

12345678910111213141516171819202122232425262728293031323334

# gibt einen data.frame mit vielen Informationen (Größe, Datum, ...) zu den übergebenen Dateien zurück file.info("cde.txt") # Eistiert die Dateifile.access("cde.txt")file.access("cde.txt", mode=1)file.access("cde.txt", mode=2)file.access("cde.txt", mode=3) # Wann wurde die Datei zum letzten Mal modifiziertfile.mtime("cde.txt") # die Größe der Datei in Bytesfile.size("cde.txt") # Eine Funktion, um die Dateigröße auch in anderen Einheiten wiederzugebenDateigroesse &lt;- function(Dateien, Einheit="MB") {  # Einheit in Großbuchstaben umwandeln  Einheit &lt;- toupper(Einheit)  # Fehlermeldung bei nicht unterstützter Einheit  if(!(Einheit %in% c("B","KB","MB","GB"))) {    print("Die Funktion unterstützt nur die Einheiten B, KB, MB und GB")    return()  }  # Faktor für Einheit   Faktor &lt;- ifelse(Einheit=="B",1,            ifelse(Einheit=="KB",1024,            ifelse(Einheit=="MB",1024^2,            ifelse(Einheit=="GB",1024^3,            NA))))  # Rückgabe von Bytegröße/Faktor, gerundet auf 2 Stellen  return(round(file.size(Dateien)/Faktor,2))}Dateigroesse("Dateisystem_in_R.html","KB")

 
So, nun seid ihr Experten in Sachen Verzeichnisse und Dateien. Ich bin gespannt, welche tollen Projekte und Automatisierungen ihr damit umsetzt.
Happy coding,
Euer Holger
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files

Buchreview: „Deep Learning Kochbuch“

Teile diesen Beitrag:In diesem Blogartikel geht es um das „Deep Learning Kochbuch – Praxisrezepte für einen schnellen Einstieg“ von Douwe Osinga. Die erste Auflage ist im Februar 2019 im O’Reilly Verlag erschienen, ist also noch ganz frisch. Die gedruckte Version kostet 34,99€, als eBook zahlt man 27,99€ und bekommt dafür 262 Seiten.
Neuronale Netze mit Python und Keras in der Praxis
So könnte man den Inhalt zusammenfassen. Nach einigen allgemeineren Kapiteln geht es um viele verschiedene KI-Projekte. Abschließend gibt es noch ein Kapitel, welches sich mit der Produktivsetzung beschäftigt. Sehr erfreulich ist, dass alle Projekte als Jupyter Notebooks auf Github unter https://github.com/DOsinga/deep_learning_cookbook  zur Verfügung gestellt sind.
Das Konzept ist anders als bei vielen anderen Büchern, da weniger die Theorie dahinter als die praktische Anwendung im Vordergrund steht. Dafür ist jeder Abschnitt in die drei Bereiche Problem – Lösung – Diskussion gegliedert. Problem beschreibt kurz die Fragestellung. Das ist häufig nur eine einzige Frage wie z.B. „Wie kann man einen großen Korpus aus Dialogen gewinnen?“.  In der Lösung wird der Lösungsweg mit ein paar Codezeilen skizziert. Die Diskussion gibt dann ein paar Hintergrundinformationen. So ein Abschnitt umfasst meist ca. zwei Seiten, die Inhalte sind also stark komprimiert.
Die 13 Deep Learning Projekte im Einzelnen
Die Ähnlichkeit von Texten mithilfe von Worteinbettungen berechnen
Ein Empfehlungssystem anhand ausgehender Wikipedia-Links erstellen
Text im Stil eines Beispieltexts generieren
Übereinstimmende Fragen
Emojis vorschlagen
Sequenz-zu-Sequenz-Mapping
Ein vortrainiertes Netzwerk zur Bilderkennung verwenden
Eine Revere-Image-Suchmaschine erstellen
Mehrere Bildinhalte erkennen
Mit Bildstilen arbeiten
Bilder mit Autoencodern erzegen
Piktogramme mithilfe von neuronalen Netzwerken erzeugen
Musik und Deep Learning
Das gesamte Inhaltsverzeichnis und das erste Projekt als Leseprobe ist unter diesem Link https://www.oreilly.de/buecher/13301/9783960090977-deep-learning-kochbuch.html zu finden.
Der Autor Douwe Osinga
Douwe Osinga wurde in den Niederlanden geboren, hat an mehreren Standorten von Google gearbeitet und StartUps gegründet. Zum einen die niederländische Firma Oberon, welche Online-Plattformen entwickelt. Zum anderen Triposo, ein KI-gesteuerten Reiseführer. Das klingt ziemlich spannend: Der Algorithmus crawlt durch verschiedene Internet-Seiten wie Wikipedia, OpenStreetMap etc. und stellt dann mittels maschinellem Lernen die Informationen zu einer Location zusammen.
Mehr über den Autor gibt es auf seiner Seite https://douwe.com zu lesen. Sein Blog scheint er jedoch aktuell nicht mehr mit neuen Texten zu füttern, der letzte Eintrag ist von Mai 2017, aber er hatte ja in letzter Zeit ja auch genug mit dem Buch zu tun. Daneben stellt er aber einige interessante Projekte vor. Es lohnt sich also, sich dort umzusehen.
Für wen ist das Buch
Das Buch geht schon ans Eingemachte, d.h. man muss einige Vorkenntnisse mitbringen, um etwas mit den Projektbeschreibungen anfangen zu können. Also ich würde sagen, als Grundvoraussetzung sollte man sich ein bisschen mit Python und Keras auskennen. Besonders wichtig ist aber das Wissen um die verschiedenen Konzepte der Neuronalen Netze. Wer also mit Begriffen wie CNN, seq2seq, oder Corpora nichts anfangen kann, wird wenig Freude an dem Buch haben. Man kann das Buch natürlich als Struktur hernehmen und dann die Themen und Begriffe, die in einem Projekt auftauchen, im Internet recherchieren, bis man sie verstanden hat.
Fazit
Ich bin hin- und hergerissen. Prinzipiell finde ich den strukturierten Aufbau mit Problem – Lösung – Diskussion sehr gut und übersichtlich. Auch die Konzentration auf Rezepte aus der Deep-Learning-Praxis, wie der Titel ja schon sagt, ist super. Denn nur so lernt man, wie es wirklich geht.
Auf der anderen Seite stellt das Buch hohe Anforderungen an bereits vorhandenes Wissen und stellt die einzelnen Projekte ziemlich knapp dar. Vielleicht hätte eine Konzentration auf weniger Projekt es erleichtert, mehr in die Tiefe gehen zu können.
Das Buch ist einfach kein normales Lesebuch, welches man von vorne bis hinten durchliest. Vielmehr ist es ein Arbeitsbuch. Man nimmt sich also ein (Unter-)Kapitel vor, liest über den Text, arbeitet das Jupyter Notebook, welches auf Github zur Verfügung steht, durch und recherchiert über die Terminologie und Deep-Learning-Strukturen im Netz. Wenn man sich diese Mühe macht, lernt man sehr viel und vor allem ganz konkret die Anwendung in der Praxis.
 

 
 
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files

Was ist der Naive Bayes-Algorithmus?

Teile diesen Beitrag:
Der Naive Bayes-Algorithmus ist ein probabilistischer Klassifikationsalgorithmus. Puh, schon ein schwieriger Ausdruck. Klassifikationsalgorithmus heißt aber nur, dass der Algorithmus Beobachtungen verschiedenen Klassen zuordnet. Und probabilistisch, dass es mit Wahrscheinlichkeiten zu tun hat. Denn der Naive Bayes-Algorithmus gibt uns für jede Klasse eine Wahrscheinlichkeit, dass die Beobachtung (x1,…,xn) zu dieser Klasse Ki gehört.

Mathematisch ausgedrückt:

Wenn wir genau sind, dann gibt es nicht den Naive Bayes-Algorithmus, sondern das beschreibt eine Klasse von Algorithmen, die alle auf dem gleichen Prinzip beruhen.

So, aber wie funktionieren die Algorithmen nun? Wie der Name vermuten lässt, hat es mit Bayes Theorem zu tun. Ich hole jetzt etwas aus, aber der Satz von Bayes ist wirklich wichtig und nicht so schwer zu verstehen.

Der Satz von Bayes

Bei dem Satz von Bayes geht es um bedingte Wahrscheinlichkeiten, also die Wahrscheinlichkeit eines Ereignisses A, wenn ein Ereignis B eingetreten ist. Man schreibt die Wahrscheinlichkeit dann als P( A | B ).

Der Satz von Bayes sagt:

Schauen wir uns das mal an einem Beispiel an. Nehmen wir an, wir machen einen medizinischen Test auf eine Krankheit. Der Test hat eine Sensitivität von 98%, d.h. die Wahrscheinlichkeit für ein positives Testergebnis gegeben die Krankheit beträgt 98%. Man sagt dazu auch die True-Positive-Rate. Zudem hat der Test eine Spezifität von 97%, d.h. die Wahrscheinlichkeit für ein negatives Testergebnis unter der Voraussetzung, dass man die Krankheit nicht hat, beträgt 97%. Das nennt man auch die True-Negative-Rate.

Wenn wir jetzt noch wissen, dass die Krankheit bei 1% der Bevölkerung vorkommt, können wir mit Hilfe des Satzes von Bayes ausrechnen, wie wahrscheinlich es ist, dass jemand tatsächlich die Krankheit hat, wenn der Test positiv ausfällt.

Die Wahrscheinlichkeit, tatsächlich krank zu sein, auch wenn der Test positiv ist, beträgt also nur knapp 25%. Das liegt an der geringen Wahrscheinlichkeit, überhaupt krank zu sein (nur 1%). Diese hebelt einen vermeintlich zuverlässigen Test (98% True-Positive, 97% True-Negative) komplett aus. Daher benötigt man bei medizinischen Tests bzw. überall, wo die Ausprägungen (ja/nein) so ungleich verteilt sind, deutlich bessere Tests.

 Jetzt kennen wir den Satz von Bayes. Und den wollen wir nutzen. Oben steht ja, dass der Algorithmus P(Ki | x1,…,xn) ausspucken soll. Das könnten wir doch nach dem Satz von Bayes umstellen.

Im Endeffekt interessiert uns nur der Zähler, da der Nenner P(x) gar nicht von der Klasse abhängt. P(Ki) sind die a priori Wahrscheinlichkeiten, also eine Annahme, wie häufig die einzelnen Klassen vorkommt. Man kann diese a priori Wahrscheinlichkeiten auch als das Vorwissen bezeichnen. Gibt es keine theoretische Annahmen, diese Wahrscheinlichkeiten zu setzen, dann zählt man für gewöhnlich das Auftreten der einzelnen Klassen in einem Test-Datensatz. Man kann aber auch eine Gleichverteilung annehmen, d.h. erstmal davon ausgehen, dass jede Klasse gleich wahrscheinlich ist.

Für den Naive Bayes-Algorithmus brauchen wir jetzt aber noch die zweite Zutat, die Naivität, damit wir weiterkommen.

Warum heißt der Naive Bayes-Algorithmus naiv?

Nein, der Algorithmus ist nicht simpel. Er benutzt einfach nur die „naive“ Annahme der bedingten Unabhängigkeit. Was heißt das nun?

Wir hatten ja weiter oben die Gleichung P(Ki | x) = P(x| Ki) * P(Ki) ) / P(x). Der Zähler auf der rechten Seite entspricht der gemeinsamen Verteilung, also P(Ki, x1, …, xn). Und jetzt kommt die bedingte Unabhängigkeit ins Spiel: Wir treffen die Annahme, dass xj und xk gegeben Ki unabhängig sind. Das bedeutet, dass sich die beiden Ereignisse nicht beeinflussen. Mathematisch drückt man es so aus, dass die Wahrscheinlichkeit, dass das Ereignis xj eintritt, sich nicht durch das Eintreten von Ereignis xk ändert. Alles unter der Bedingung, dass Ki gegeben ist. Daher spricht man von bedingter Unabhängigkeit.
Nehmen wir zum Beispiel einen Würfel, der zweimal geworfen wird. Das Ereignis, dass ich im ersten Wurf eine 1 würfel, gegeben dass der erste Wurf ungerade ist, ist unabhängig vom Ereignis, dass der zweite Wurf eine 3 würfelt, gegeben dass der erste Wurf ungerade ist.
Diese „naive“ Annahme ist in der Realität zwar meistens nicht richtig, aber in der Praxis hat das erstaunlich wenig Einfluß. Damit wird P(Ki, x1, …, xn) zum Produkt der einzelnen Wahrscheinlichkeiten P(xj | Ki), j = 1, …, n

Nun fügen wir alles zusammen:

wobei nur ein Skalierungsfaktor ist.

Der Naive Bayes Algorithmus wählt nun die Klasse Ki, die gegeben die Beobachtung x1, .., xn am wahrscheinlichsten ist. Statistisch sagt man dazu die maximale a-posteriori-Wahrscheinlichkeit. Das bedeutet, dass wir das i wählen, für das die Wahrscheinlichkeit maximal wird. Mathematisch ausgedrückt:

   

Finally: Das Event Model

Eine Sache fehlt noch. Wir müssen uns Gedanken machen, wie wir P(xj | Ki) bestimmen. Dazu gibt es verschiedene Annahmen über die Verteilung der Features

Gaussian Naive Bayes

Die zugrunde liegende Annahme ist, dass die x, die zu einer Klasse Ki gehören, normalverteilt sind. Damit das so sein kann, sollten die x natürlich erstmal metrisch sein.

Bestimmen wir also den Mittelwert und die Standardabweichung , dann können wir die Wahrscheinlichkeit einer bestimmten Ausprägung von x wie folgt berechnen:

   

Multinomial Naive Bayes

In diesem Fall ist die Annahme, dass eine multinomiale Verteilung zugrunde liegt, wie sie beim mehrmaligen Werfen eines k-seitigen Würfels. Eine Ausprägung des Features xj entspricht zum Beispiel der Häufigkeit des Auftretens eines Ereignisses j

   

Bernoulli Naive Bayes

In diesem Fall ist die Annahme, dass die Features x unabhängige binäre Variablen (ja/nein) sind, also zum Beispiel das Auftauchen einer bestimmten Eigenschaft

   

Beispiele für Naive Bayes in R

In R gibt es meines Wissens zwei Packages, die Naive Bayes implementiert haben. Das ist zum einen das Package e1071, welches eine ganze Reihe statistische Methoden wie support vector machines und eben auch Naive Bayes implementiert hat. Zum anderen gibt es das Package NaiveBayes, welches ich hier benutze.

Die Anwendung des Naive Bayes-Algorithmus ist ganz einfach in einer Zeile gemacht. Man braucht aber natürlich ein bisschen Code drumherum.

Im ersten Beispiel ziehen wir 1000x aus der Standardnormalverteilung und ordnen den Wert einer Klasse (TRUE) zu, wenn der Wert größer als 0 ist. Andernfalls gehört er zur anderen Klasse (FALSE).

Den Datensatz splitten wir zufällig in einen Trainingsdatensatz und einen Testdatensatz, wobei der Trainingsdatensatz 80% des gesamten Datensatzes umfasst.

#sofern noch nicht installiert, einmalig das Package herunterladen und installieren
#install.packages("naivebayes")

library(naivebayes)

## 1. Beispiel: Normalverteilung positiv ##############################
n &lt;- 1000
trainsize &lt;- 0.8 * n
set.seed(1)
df &lt;- data.frame(ID=1:n,x=rnorm(n))
df$Klasse &lt;- ifelse(df$x &gt; 0, TRUE, FALSE)

#Split in Trainings- und Testdatensatz
trainRows &lt;- sample(n, trainsize, replace = FALSE)
train &lt;- df[trainRows,]
test &lt;- df[setdiff(1:n,trainRows),]

12345678910111213141516

#sofern noch nicht installiert, einmalig das Package herunterladen und installieren#install.packages("naivebayes") library(naivebayes) ## 1. Beispiel: Normalverteilung positiv ##############################n &lt;- 1000trainsize &lt;- 0.8 * nset.seed(1)df &lt;- data.frame(ID=1:n,x=rnorm(n))df$Klasse &lt;- ifelse(df$x &gt; 0, TRUE, FALSE)  #Split in Trainings- und TestdatensatztrainRows &lt;- sample(n, trainsize, replace = FALSE)train &lt;- df[trainRows,]test &lt;- df[setdiff(1:n,trainRows),]

Nachdem wir dann den Naive Bayes-Algorithmus darauf losgelassen haben, überprüfen wir am Testdatensatz, wie gut er funktioniert hat. Dazu lassen wir den Testdatensatz anhand predict klassifizieren und schauen uns die Kontingenztafel an und berechnen die Accuracy, also den Anteil an richtig zugeordneten Klassen.

# Naive Bayes
nb &lt;- naive_bayes(Klasse ~ x, data = train)

# Vorhersage auf Testdatensatz
test$predicted &lt;- predict(nb,test)
Kontingenztafel &lt;- table(test[,c("Klasse","predicted")])
Kontingenztafel
Accuracy &lt;- (Kontingenztafel[1,1] + Kontingenztafel[2,2])/sum(Kontingenztafel)
sprintf("Accuracy: %1.1f%%", 100*Accuracy)

## 2. Beispiel: Klassifikation des Iris-Datensatzes ###################
trainRows &lt;- sample(nrow(iris), nrow(iris)*0.8, replace = FALSE)
train &lt;- iris[trainRows,]
test &lt;- iris[setdiff(1:nrow(iris),trainRows),]

# Naive Bayes
nb &lt;- naive_bayes(Species ~ ., train)

# Vorhersage auf Testdatensatz
test$predicted &lt;- predict(nb,test)
Kontingenztafel &lt;- table(test[,c("Species","predicted")])
Kontingenztafel
Accuracy &lt;- sum(diag(Kontingenztafel))/sum(Kontingenztafel)
sprintf("Accuracy: %1.1f%%", 100*Accuracy)

123456789101112131415161718192021222324

# Naive Bayesnb &lt;- naive_bayes(Klasse ~ x, data = train) # Vorhersage auf Testdatensatztest$predicted &lt;- predict(nb,test)Kontingenztafel &lt;- table(test[,c("Klasse","predicted")])KontingenztafelAccuracy &lt;- (Kontingenztafel[1,1] + Kontingenztafel[2,2])/sum(Kontingenztafel)sprintf("Accuracy: %1.1f%%", 100*Accuracy) ## 2. Beispiel: Klassifikation des Iris-Datensatzes ###################trainRows &lt;- sample(nrow(iris), nrow(iris)*0.8, replace = FALSE)train &lt;- iris[trainRows,]test &lt;- iris[setdiff(1:nrow(iris),trainRows),]    # Naive Bayesnb &lt;- naive_bayes(Species ~ ., train) # Vorhersage auf Testdatensatztest$predicted &lt;- predict(nb,test)Kontingenztafel &lt;- table(test[,c("Species","predicted")])KontingenztafelAccuracy &lt;- sum(diag(Kontingenztafel))/sum(Kontingenztafel)sprintf("Accuracy: %1.1f%%", 100*Accuracy)

 

Wir sehen, dass Naive Bayes dieses Beispiel sehr gut vorhersagt. Die Accuracy liegt bei 98,5%

Vorhersage (predict)
TRUEFALSE
Test-DatensatzTRUE1010
FALSE396
Im zweiten Beispiel benutzen wir den Iris-Datensatz, der mit R mitgeliefert wird und ein beliebter Anschauungsdatensatz für zahlreiche Beispiele ist.

Wie im ersten Beispiel splitten wir wieder zufällig in Trainings- und Testdatensatz. Dann wollen wir die drei Arten (Species) anhand der anderen Variablen mittels Naive Bayes klassifizieren. Das wenden wir dann auf den Testdatensatz an und schauen uns an, wie gut der Algorithmus war

## 2. Beispiel: Klassifikation des Iris-Datensatzes ###################
trainRows &lt;- sample(nrow(iris), nrow(iris)*0.8, replace = FALSE)
train &lt;- iris[trainRows,]
test &lt;- iris[setdiff(1:nrow(iris),trainRows),]

# Naive Bayes
nb &lt;- naive_bayes(Species ~ ., train)

# Vorhersage auf Testdatensatz
test$predicted &lt;- predict(nb,test)
Kontingenztafel &lt;- table(test[,c("Species","predicted")])
Kontingenztafel
Accuracy &lt;- sum(diag(Kontingenztafel))/sum(Kontingenztafel)
sprintf("Accuracy: %1.1f%%", 100*Accuracy)

1234567891011121314

## 2. Beispiel: Klassifikation des Iris-Datensatzes ###################trainRows &lt;- sample(nrow(iris), nrow(iris)*0.8, replace = FALSE)train &lt;- iris[trainRows,]test &lt;- iris[setdiff(1:nrow(iris),trainRows),]    # Naive Bayesnb &lt;- naive_bayes(Species ~ ., train) # Vorhersage auf Testdatensatztest$predicted &lt;- predict(nb,test)Kontingenztafel &lt;- table(test[,c("Species","predicted")])KontingenztafelAccuracy &lt;- sum(diag(Kontingenztafel))/sum(Kontingenztafel)sprintf("Accuracy: %1.1f%%", 100*Accuracy)

Hier ist Naive Bayes sogar perfekt. Die Accuracy liegt bei 100%, es wurden alle Arten korrekt klassifiziert.

Vorhersage (predict)
SetosaVersicolorVirginica
Test-DatensatzSetosa1000
Versicolor0120
Virginica008
So, jetzt könnt ihr selber loslegen. Schnappt euch einen interessanten Datensatz, schmeißt den Naive Bayes-Algorithmus drauf und schaut, wie gut er abschneidet. So bekommt ihr auch ein Gefühl dafür, wann er gut geeignet ist und wann eher nicht so.
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files

Master of Time: Datum und Zeitstempel in R

Teile diesen Beitrag:
In diesem R-Artikel geht es darum, wie
ihr in R mit Datums- und Zeitangaben umgeht. Dafür hat R drei Datentypen
vorgesehen, nämlich Date für ein Datum und POSIXct bzw. POSIXlt für
Zeitstempel, also Datum plus Zeitangabe. Reine Zeitangaben, also nur die Uhrzeit
ohne Datum, sind eher selten und nicht sehr praktikabel.

Ich zeige euch, wie ihr Strings oder
Zahlen in einen der Datentypen überführt und damit rechnen könnt. Für ein
besseres Handling ist das Package “lubridate” aus dem tidyverse super geeignet.
Dazu aber weiter unten mehr, wir fangen mit Standard-R an.

Der Datumstyp in R

heute <- Sys.Date()
print(heute)
## [1] "2019-02-23"
jetzt <- Sys.time()
print(jetzt)
## [1] "2019-02-23 08:34:00 CET"

typeof(heute)
## [1] "double"
typeof(jetzt)
## [1] "double"

str(heute)
## Date[1:1],
format: "2019-02-23"
str(jetzt)
## POSIXct[1:1],
format: "2019-02-23 08:34:00"

123456789101112131415161718

heute <- Sys.Date()print(heute)## [1] "2019-02-23"jetzt <- Sys.time()print(jetzt)## [1] "2019-02-23 08:34:00 CET" typeof(heute)## [1] "double"typeof(jetzt)## [1] "double" str(heute)## Date[1:1],format: "2019-02-23"str(jetzt)## POSIXct[1:1],format: "2019-02-23 08:34:00"

Wir sehen also, dass sowohl Datum als
auch Zeitstempel intern als Zahl gespeichert werden, nämlich als Anzahl Tage
(Date) oder Sekunden (POSIX) seit einem festgelegten Startdatum, nämlich seit
dem 1. Januar 1970 00:00 UTC. Als Zusatz wird aber doch noch abgespeichert,
dass es sich um ein Datum oder Zeitstempel handelt. Das sehen wir an der
Struktur (str). Sonst würde die print-Funktion ja auch nur eine Zahl ausgeben
und nicht automatisch in einen String umwandeln.

print(as.numeric(heute))
## [1] 17950
print(as.numeric(jetzt))
## [1] 1550907241

# ein Datum kann als Anzahl Tage seit einem
Upsprungsdatum angegeben werden
d <- as.Date(1,origin="1970-01-01")
print(as.numeric(d))
## [1] 1

12345678910

print(as.numeric(heute))## [1] 17950print(as.numeric(jetzt))## [1] 1550907241 # ein Datum kann als Anzahl Tage seit einemUpsprungsdatum angegeben werdend <- as.Date(1,origin="1970-01-01")print(as.numeric(d))## [1] 1

Datum aus String

Wollen wir ein Datum aus einem String
erzeugen, können wir entweder die Standardformate verwenden, die in R
YYYY-MM-DD und YYYY/MM/DD sind. Ansonsten müssen wir mit dem Parameter format angeben, welches Format wir
übergeben. Dabei gibt es einige Kürzel, die alle mit % anfangen. In der Liste
unten findet ihr sie. Um aus einem Datum wieder einen String zu machen, könnt
ihr die Funktion format benutzen.

d1 <- as.Date("2018-02-22")
d2 <- as.Date("20.02.2018",format="%d.%m.%Y")
d3 <- as.Date("02/18/18",format="%m/%d/%y")

format(d1,"%d.%m.%Y")
## [1] "22.02.2018"
format(d2,"%d.%m.%Y")
## [1] "20.02.2018"

12345678

d1 <- as.Date("2018-02-22")d2 <- as.Date("20.02.2018",format="%d.%m.%Y")d3 <- as.Date("02/18/18",format="%m/%d/%y") format(d1,"%d.%m.%Y")## [1] "22.02.2018"format(d2,"%d.%m.%Y")## [1] "20.02.2018"

Kürzel

Beschreibung

Beispiel

%m

Monat (01-12)

05

%b or %h

3-stelliger Monatsname

Feb

%B

Monatsname

Februar

%d

Tag des Monats (01-31)

27

%j

Tag vom Jahr (001-366)

148

%y

2-stelliges Jahr (00-99)

18

%Y

4-stelliges Jahr

2018

%C

Jahrhundert

20

%x

Datum (System)

 

%D

Datum MM/TT/JJJJ

05/27/84

%U

Kalenderwoche, Sonntag=erster Tag der Woche
(01-53)

22

%W

Kalenderwoche, Montag=erster Tag der Woche
(00-53)

22

%u

Wochentag, Montag=1 (1-7)

7

%w

Wochentag, Sonntag=0 (0-6)

0

%a

3-stelliger Wochentag

So

%A

Wochentag

Donnerstag
Und so sieht das dann im Code aus:

d <- as.Date("2018-02-22")
print(paste("Monat:",format(d,"%m")))
## [1] "Monat: 02"
print(paste("3-stelliger Monatsname:",format(d,"%b")))
## [1] "3-stelliger Monatsname: Feb"
print(paste("Monatsname:",format(d,"%B")))
## [1] "Monatsname: Februar"
print(paste("Tag:",format(d,"%d")))
## [1] "Tag: 22"
print(paste("Tag vom Jahr:",format(d,"%j")))
## [1] "Tag vom Jahr: 053"
print(paste("2-stelliges Jahr:",format(d,"%y")))
## [1] "2-stelliges Jahr: 18"
print(paste("4-stelliges Jahr:",format(d,"%Y")))
## [1] "4-stelliges Jahr: 2018"
print(paste("Jahrhundert:",format(d,"%C")))
## [1] "Jahrhundert: 20"
print(paste("Kalenderwoche (US):",format(d,"%U")))
## [1] "Kalenderwoche (US): 07"
print(paste("Kalenderwoche (DE):",format(d,"%W")))
## [1] "Kalenderwoche (DE): 08"
print(paste("Datum (Systemeinstellung):",format(d,"%x")))
## [1] "Datum (Systemeinstellung): 22.02.2018"
print(paste("Datum MM/TT/JJJJ:",format(d,"%D")))
## [1] "Datum MM/TT/JJJJ: 02/22/18"
print(paste("Wochentag (Montag=1):",format(d,"%u")))
## [1] "Wochentag (Montag=1): 4"
print(paste("Wochentag (Sonntag=0):",format(d,"%w")))
## [1] "Wochentag (Sonntag=0): 4"
print(paste("3-stelliger Tagesname:",format(d,"%a")))
## [1] "3-stelliger Tagesname: Do"
print(paste("Tagesname:",format(d,"%A")))
## [1] "Tagesname: Donnerstag"

123456789101112131415161718192021222324252627282930313233

d <- as.Date("2018-02-22")print(paste("Monat:",format(d,"%m")))## [1] "Monat: 02"print(paste("3-stelliger Monatsname:",format(d,"%b")))## [1] "3-stelliger Monatsname: Feb"print(paste("Monatsname:",format(d,"%B")))## [1] "Monatsname: Februar"print(paste("Tag:",format(d,"%d")))## [1] "Tag: 22"print(paste("Tag vom Jahr:",format(d,"%j")))## [1] "Tag vom Jahr: 053"print(paste("2-stelliges Jahr:",format(d,"%y")))## [1] "2-stelliges Jahr: 18"print(paste("4-stelliges Jahr:",format(d,"%Y")))## [1] "4-stelliges Jahr: 2018"print(paste("Jahrhundert:",format(d,"%C")))## [1] "Jahrhundert: 20"print(paste("Kalenderwoche (US):",format(d,"%U")))## [1] "Kalenderwoche (US): 07"print(paste("Kalenderwoche (DE):",format(d,"%W")))## [1] "Kalenderwoche (DE): 08"print(paste("Datum (Systemeinstellung):",format(d,"%x")))## [1] "Datum (Systemeinstellung): 22.02.2018"print(paste("Datum MM/TT/JJJJ:",format(d,"%D")))## [1] "Datum MM/TT/JJJJ: 02/22/18"print(paste("Wochentag (Montag=1):",format(d,"%u")))## [1] "Wochentag (Montag=1): 4"print(paste("Wochentag (Sonntag=0):",format(d,"%w")))## [1] "Wochentag (Sonntag=0): 4"print(paste("3-stelliger Tagesname:",format(d,"%a")))## [1] "3-stelliger Tagesname: Do"print(paste("Tagesname:",format(d,"%A")))## [1] "Tagesname: Donnerstag"

Rechnen mit Daten

d1 <- as.Date("2018-01-23")
d2 <- as.Date("2018-02-22")
delta <- d2 - d1
delta
## Time difference of 30 days
str(delta)
## 'difftime' num
30
## - attr(*,"units")= chr "days"
attributes(delta)
## $class
## [1] "difftime"
##
## $units
## [1] "days"
difftime(d2,d1,units="weeks")
## Time difference of 4.285714 weeks

#erster Tag des Monats
Tage <- as.numeric(format(d1,"%d")) - 1
as.Date(-Tage,d1)
## [1] "2018-01-01"

ersterTagDesMonats <- function(Datum) {
return(as.Date(-as.numeric(format(Datum,"%d")) + 1,Datum))
}
ersterTagDesMonats(d2)
## [1] "2018-02-01"

12345678910111213141516171819202122232425262728

d1 <- as.Date("2018-01-23")d2 <- as.Date("2018-02-22")delta <- d2 - d1delta## Time difference of 30 daysstr(delta)## 'difftime' num30## - attr(*,"units")= chr "days"attributes(delta)## $class## [1] "difftime"## ## $units## [1] "days"difftime(d2,d1,units="weeks")## Time difference of 4.285714 weeks #erster Tag des MonatsTage <- as.numeric(format(d1,"%d")) - 1as.Date(-Tage,d1)## [1] "2018-01-01" ersterTagDesMonats <- function(Datum) {    return(as.Date(-as.numeric(format(Datum,"%d")) + 1,Datum))}ersterTagDesMonats(d2)## [1] "2018-02-01"

Datum mit dem
Package lubridate

Viel einfacher geht das alles mit dem
Package lubridate. Wie bei jedem
Packages muss es einmal installiert werden install.packages(“lubridate”).
Danach kann es mit library(lubridate) oder require(lubridate) im Skript geladen werden.

Lubridate hat einige Funktionen, um
schnell ein Datum zu erzeugen, an die einzelnen Teile heranzukommen und auch
diese abzuändern

library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date

# Ein Datum erstellen
d <- ymd(20180514)
d2 <- mdy("4/1/17")

# Abruf der einzelnen Teile
year(d)
## [1] 2018
month(d)
## [1] 5
day(d)
## [1] 14
wday(d)
## [1] 2
wday(d,label=TRUE)
## [1] Mo
## Levels: So < Mo < Di < Mi < Do < Fr < Sa
wday(d,label=TRUE,abbr=FALSE)
## [1] Montag
## 7 Levels: Sonntag < Montag < Dienstag < Mittwoch < ... < Samstag
wday(d,label=TRUE,abbr=FALSE,week_start=1)
## [1] Montag
## 7 Levels: Montag < Dienstag < Mittwoch < Donnerstag < ... < Sonntag
yday(d)
## [1] 134

# Modifikation
month(d) <- 2
day(d) <- 5
d
## [1] "2018-02-05"

12345678910111213141516171819202122232425262728293031323334353637

library(lubridate)## ## Attaching package: 'lubridate'## The following object is masked from 'package:base':## ##      date # Ein Datum erstellend <- ymd(20180514)d2 <- mdy("4/1/17") # Abruf der einzelnen Teileyear(d)## [1] 2018month(d)## [1] 5day(d)## [1] 14wday(d)## [1] 2wday(d,label=TRUE)## [1] Mo## Levels: So < Mo < Di < Mi < Do < Fr < Sawday(d,label=TRUE,abbr=FALSE)## [1] Montag## 7 Levels: Sonntag < Montag < Dienstag < Mittwoch < ... < Samstagwday(d,label=TRUE,abbr=FALSE,week_start=1)## [1] Montag## 7 Levels: Montag < Dienstag < Mittwoch < Donnerstag < ... < Sonntagyday(d)## [1] 134 # Modifikationmonth(d) <- 2day(d) <- 5d## [1] "2018-02-05"

Als nützlich haben sich auch die
Rundungsfunktionen in lubridate erwiesen. Mit diesen kann zum Beispiel der
Monatsanfang leicht bestimmt werden.

# springt zum Anfang des Monats
floor_date(d,unit="month")
## [1] "2018-02-01"
# springt zum Anfang des nächsten Monats
ceiling_date(d,unit="month")
## [1] "2018-03-01"
# rundet auf Monate, also bis zum 14. zum Monatsanfang, ab dem 15. zum nächsten Monatsanfang
round_date(d,unit="month")
## [1] "2018-02-01"
round_date(d+1,unit="month")
## [1] "2018-02-01"

1234567891011

# springt zum Anfang des Monatsfloor_date(d,unit="month")## [1] "2018-02-01"# springt zum Anfang des nächsten Monatsceiling_date(d,unit="month")## [1] "2018-03-01"# rundet auf Monate, also bis zum 14. zum Monatsanfang, ab dem 15. zum nächsten Monatsanfanground_date(d,unit="month")## [1] "2018-02-01"round_date(d+1,unit="month")## [1] "2018-02-01"

Zeitstempel

Ein Zeitstempel setzt sich aus Datum
und Uhrzeit zusammen. Für eine richtige Uhrzeit ist noch die Zeitzone
angegeben, damit es eindeutig wird. Der entsprechende Datentyp in R ist POSIXct
bzw. POSIXlt. Diese beiden unterscheiden sich in der internen Darstellung.
POSIXct speichert die Anzahl Sekunden seit dem Origin (UNIX Epoch, nämlich der
01.01.1970 00:00:00), POSIXlt speichert eine Liste mit Tag, Monat, Jahr,
Stunde, Minute, etc.

Damit kann man mittels Attributen auf
Teile eines POSIXlt zugreifen, beim POSIXct muss man über format den entsprechenden Wert extrahieren und dann gegebenenfalls
noch in eine Zahl umwandeln. Mit dem Package lubridate läßt sich das
vereinfachen, in dem per Funktion direkt auf die einzelnen Teile zugreifen
kann, dazu weiter unten mehr.

# die aktuelle Systemzeit wird als POSIXct zurückgegeben
jetzt <- Sys.time()
str(jetzt)
##&nbsp; POSIXct[1:1], format: "2019-02-23 08:34:01"
attributes(jetzt)
## $class
## [1] "POSIXct" "POSIXt"
#gibt die Minuten aus
as.numeric(format(jetzt,"%M"))
## [1] 34

# mit as.POSIXlt kann sie in ein POSIXlt umgewandelt werden
jetzt_lt <- as.POSIXlt(jetzt)
str(jetzt_lt)
## POSIXlt[1:1],format: "2019-02-23 08:34:01"
attributes(jetzt_lt)
## $names
## [1] "sec" "min" "hour" "mday" "mon" "year" "wday"
## [8] "yday" "isdst" "zone" "gmtoff"
##
## $class
## [1] "POSIXlt" "POSIXt"
##
## $tzone
## [1] "" "CET" "CEST"
jetzt_lt$min
## [1] 34

# origin und tz (timezone) sind optional, also nur nötig, wenn nicht der Standardwert genommen werden soll
ts <- as.POSIXct("20190222105519", format="%Y%m%d%H%M%S", origin="1970-01-01", tz="CET")
ts
## [1] "2019-02-22 10:55:19 CET"
ts2 <- as.POSIXct("22.02.2019 10:55:19", format="%d.%m.%Y %H:%M:%S")
ts2
## [1] "2019-02-22 10:55:19 CET"

123456789101112131415161718192021222324252627282930313233343536

# die aktuelle Systemzeit wird als POSIXct zurückgegebenjetzt <- Sys.time()str(jetzt)##&nbsp; POSIXct[1:1], format: "2019-02-23 08:34:01"attributes(jetzt)## $class## [1] "POSIXct" "POSIXt"#gibt die Minuten ausas.numeric(format(jetzt,"%M"))## [1] 34 # mit as.POSIXlt kann sie in ein POSIXlt umgewandelt werdenjetzt_lt <- as.POSIXlt(jetzt)str(jetzt_lt)##  POSIXlt[1:1],format: "2019-02-23 08:34:01"attributes(jetzt_lt)## $names## [1] "sec"   "min"   "hour"  "mday"  "mon"  "year"  "wday"## [8] "yday"  "isdst" "zone"  "gmtoff"## ## $class## [1] "POSIXlt" "POSIXt"## ## $tzone## [1] ""   "CET"   "CEST"jetzt_lt$min## [1] 34  # origin und tz (timezone) sind optional, also nur nötig, wenn nicht der Standardwert genommen werden sollts <- as.POSIXct("20190222105519", format="%Y%m%d%H%M%S", origin="1970-01-01", tz="CET")ts## [1] "2019-02-22 10:55:19 CET"ts2 <- as.POSIXct("22.02.2019 10:55:19", format="%d.%m.%Y %H:%M:%S")ts2## [1] "2019-02-22 10:55:19 CET"

Neben den Datumskürzelns aus der
obigen Tabelle können wir mit den folgenden Kürzeln noch die Zeit definieren.
Das funktioniert sowohl bei der Erzeugung aus einem String als auch der
umgekehrte Weg mittels format.

Kürzel

Beschreibung

Beispiel

%c

Zeitstempel im System-Format

Fr Feb 22 10:55:19 2019

%H

Stunden (00-24)

23

%I

Stunden (00-12)

11

%M

Minuten

55

%p

AM/PM (sofern verwendet)

AM

%S

Decimal second

19

%X

Zeit (Systemeinstellungen)

HH:MM:SS

%z

Abweichung von GMT

+0100

%Z

Zeitzone

CET

format(ts,"%c")
## [1] "Fr Feb 22 10:55:19 2019"
format(ts,"%H")
## [1] "10"
format(ts,"%I")
## [1] "10"
format(ts,"%M")
## [1] "55"
format(ts,"%p")
## [1] ""
format(ts,"%S")
## [1] "19"
format(ts,"%X")
## [1] "10:55:19"
format(ts,"%z")
## [1] "+0100"
format(ts,"%Z")
## [1] "CET"

123456789101112131415161718

format(ts,"%c")## [1] "Fr Feb 22 10:55:19 2019"format(ts,"%H")## [1] "10"format(ts,"%I")## [1] "10"format(ts,"%M")## [1] "55"format(ts,"%p")## [1] ""format(ts,"%S")## [1] "19"format(ts,"%X")## [1] "10:55:19"format(ts,"%z")## [1] "+0100"format(ts,"%Z")## [1] "CET"

Um die Zeitspanne zwischen zwei
Zeitstempeln zu bestimmen, können wir sie einfach voneinander abziehen. Das
Problem ist aber, dass wir dann die Einheit (Tage, Stunden, …) nicht selber
wählen können. Daher ist es besser, dass über difftime zu machen und den
Parameter units explizit zu setzen.

delta <- Sys.time() - ts
str(delta)
## 'difftime' num
21.6450349113676
## - attr(*,"units")= chr "hours"
delta <- difftime(Sys.time() , ts, units="days")

123456

delta <- Sys.time() - tsstr(delta)##  'difftime' num21.6450349113676##  - attr(*,"units")= chr "hours"delta <- difftime(Sys.time() , ts, units="days")

Zeitstempel mit
lubridate

Zeittempel mit lubridate funktionieren
ganz genauso wie wir das oben schon beim Datum gesehen haben. Es gibt
Erzeuger-Funktionen wie ymd_hms und
Funktionen, die die einzelnen Teile des Zeitstempels extrahieren. Ordnet man
diesen Extraktionsfunktionen einen Wert zu, wird der Zeitstempel geändert.

library(lubridate)
ts <- ymd_hms("2011-06-04 12:00:00")
year(ts)
## [1] 2011
hour(ts)
## [1] 12
second(ts)
## [1] 0

second(ts) <- 25
ts
## [1] "2011-06-04 12:00:25 UTC"

123456789101112

library(lubridate)ts <- ymd_hms("2011-06-04 12:00:00")year(ts)## [1] 2011hour(ts)## [1] 12second(ts)## [1] 0 second(ts) <- 25ts## [1] "2011-06-04 12:00:25 UTC"

So, ich hoffe, jetzt seid ihr zum Master of Date and Time geworden.
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files

Kann man eine KI entwickeln, die ohne langes Training etwas lernt?

Teile diesen Beitrag:
Das ist tatsächlich ein sehr spannendes Forschungsfeld, das
viele Leute umtreibt. Die KI-Technik, aus wenigen Beispiel zu lernen, nennt
sich übrigens One-Shot-Learning. Und in dieser Technik ist der Mensch dem
Computer noch haushoch überlegen. Der Mensch kann anscheinend sehr gut
vorheriges Wissen auf neue Kategorien übertragen.

Die Vorteile liegen auf der Hand. Denn mit vielen der
aktuellen Methoden braucht man einen sehr großen Datensatz für das Training.
Diesen Datensatz zu erstellen ist meistens teuer und zeitaufwendig. Zudem wird
dann teure Rechenkapazität benötigt, um diese vielen Daten in das Modell zu
schleusen. Wenn man an seltene Krankheiten denkt, dann gibt es mitunter auch
gar keinen genügend großen Datensatz.

Warum brauchen wir aktuell noch so große Trainingssets?

Die meisten KI-Algorithmen basieren auf statistischen
Verfahren und haben, wenn sie komplex genug sind, einen Haufen Parameter, die
anhand des Trainings optimiert werden. Wer schon mal lineare Regression in der
Praxis eingesetzt hat, weiß, dass die Anzahl Datenpunkte mit der Anzahl
Prädiktoren massiv steigt.

Die aktuell populärste KI-Algorithmenklasse sind künstliche
Neuronale Netze. Ein neuronales Netz besteht aus mehreren Schichten, also einer
Eingangsschicht, gefolgt von mehreren Zwischenschichten (sogenannte hidden
layers) und einer Ausgangsschicht. Jede Schicht besteht aus mehreren Knoten
(den Neuronen). Die Knoten zweier aufeinander folgende Schichten sind alle
miteinander verbunden und jede dieser Verbindungen hat ein Gewicht. Nun werden
diese Gewichte anhand der Trainingsdaten so angepasst, dass das neuronale Netz
die gestellte Aufgabe hoffentlich gut erledigt.

Die Eingangsschicht hat für jedes Feature ein Neuron, also
zum Beispiel die Anzahl Pixel eines Bildes. Die Ausgangsschicht hat so viele
Neuronen wie eben Entscheidungen/Handlungen möglich sind, z.B. zwei bei einer
binären Klassifikation: ja und nein. Über die Anzahl Zwischenschichten und deren
Anzahl Neuronen gibt es keine allgemeingültige Aussage. Es gibt Paper, die
sagen, dass für die meisten Probleme eine Zwischenschicht genügt und dass die
Anzahl Neuronen zwischen der Anzahl Neuronen der Ausgangs- und der
Eingangsschicht liegen.

Nehmen wir das populäre Beispiel der handschriftlichen
Ziffernerkennung anhand der MNIST Datenbank. Ein sogenanntes Convolutional
Neural Network (CNN), das sehr gut abgeschnitten hat (Fehlerrate von 0,27%)
besteht insgesamt aus 7 Schichten mit den folgenden Anzahl Neuronen:
784-50-100-500-1000-10-10. Das sind also gigantische 604.300 Verbindungen, die
einen Gewichtsparamter haben. Die Fehlerrate konnte durch die Kombination von
mehreren CNNs noch reduziert werden.

Es gibt also eine riesige Menge Gewichte, die alle bestimmt
werden wollen. Und dafür braucht man diese riesige Anzahl Trainingsbeispiele

Und was ist jetzt One-Shot-Learning?

Wie der Begriff schon sagt, soll mittels eines Versuchs
gelernt werden. Der Name ist allerdings ein bisschen irreführend, denn beim
One-Shot-Learning geht es nicht zwingend darum, aus nur einem einzigen Beispiel
zu lernen. Das wäre zwar besonders wünschenswert, aber realistischerweise
braucht die KI doch einige wenige Trainingsdaten.

Wie soll das funktionieren? Dazu nehmen wir uns den Menschen
zum Vorbild, der die Fähigkeit besitzt das Lernen zu lernen. In der KI-Welt
bezeichnet man das als Meta-Learning.

Was ist Meta-Learning? Ein Gedächtnis muss her!

Die Idee hinter Meta-Learning ist, dass wir schon gelerntes
auf neue Situationen übertragen, also eine Art Wissenstransfer. Dabei können
wir drei Arten unterscheiden:

Wissenstransfer
durch Modellparameter: Der Startpunkt für das Training einer neuen Klasse
sind die aus früheren Trainings gelernten Modellparameter.Wissenstransfer
durch gemeinsame Features: Die Features, die die neue als auch die alte
Klasse besitzen, werden als Ausgangspunkt genommen. Wenn ein Algorithmus
gelernt hat, eine Kuh zu erkennen, dann kann er davon ausgehend schnell lernen,
auch ein Pferd zu erkennen. Wissenstransfer
durch Kontext: Hierbei geht es darum, das Setting mit einzubeziehen, also
zum Beispiel von dem perfekten Bilder-Datensatz, welcher auf das zu erkennende
Motiv  zugeschnitten und gedreht wurde,
zu realistischeren Bildern überzugehen, welche das Motiv zur enthalten.Alle drei müssen auf bereits Gelerntes zugreifen können.
Dafür ist eine Art Gedächtnis nötig. Man könnte allgemein sagen, dass
Meta-Learning ein langwieriger Prozess ist, der durch Erweiterung des Wissens
immer mehr lernt. One-Shot-Learning ist davon also nur der Teil, neues Wissen
aus bestehendem Wissen und einigen Beispielen zu generieren.

Was sind Memory-Augmented Neural Networks?

Eine Klasse Neuronaler Netze, welche eine Art Meta-Learning
implementieren, sind die sogenannten Memory-Augmented Neural Networks (MANN). Diese
sind eine Weiterentwicklung bzw. eher eine Vereinfachung von Neural Turing
Machines (NTM) und bestehen aus einem Controller, welcher ein Neuronales Netz
ist, sowie einem Speicher besteht. Der Controller muss, wie der Name sagt,
alles koordinieren, also je nach Inputs Inhalte aus dem Speicher auslesen, modifizieren
oder neu schreiben. Und am Ende muss der Controller natürlich zu einem Ergebnis
kommen.

Wer mehr darüber erfahren will, liest sich am besten in das
Paper „One-Shot-Learning with
Memory-Augmented Neural Networks“ ein.

Funktionieren MANNs für One-Shot-Learning?

Die ersten Ergebnisse sehen vielversprechend aus. So wurde
der Omniglot Datensatz verwendet, der aus Buchstaben bzw. Symbolen
verschiedener Sprachen besteht, um zu lernen, das Symbol einer Sprache
zuzuordnen. Dabei gibt es jeweils nur wenige Beispiele einer Sprache.

Dabei schnitt das Memory-Augmented Neural Network mit einem
LSTM Neuronales Netz (Long Short Term Memory) als Controller besser ab als das reine
LSTM Netz.
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz

Wie erklärt man einem Kind, wie maschinelles Lernen funktioniert?

Teile diesen Beitrag:
Es kommt natürlich ein bisschen darauf an, wie alt das Kind
ist. Eine eingängige Beschreibung ist, dass Computerprogramme bestimme Aufgaben
erledigen können, ohne dass ihnen ganz genau gesagt wurde, wie sie die Aufgabe
erledigen. Wie ein Mensch lernt das Computerprogramm durch Wiederholung und Übung.

Aber vermutlich ist es am einfachsten, anhand von Beispielen
zu erklären, was maschinelles Lernen ist:

Beispiel 1: Bilderkennung durch den Computer

Ein Computerprogramm soll erkennen, ob ein Hund auf einem Bild ist. Dazu wurde das Programm trainiert, indem ihm ganz viele Bilder mit und ohne Hunde gezeigt und dabei gesagt wurde, ob ein Hund auf dem Bild war. Dadurch hat das Programm gelernt, Bilder mit und ohne Hunde zu unterscheiden.

Beispiel 2: Schach spielen

Computerprogramme können im Schach die besten
Schach-Großmeister besiegen. Dazu wurde dem Computerprogramm zum einen beigebracht,
welche Züge erlaubt sind. Zum anderen greift das Programm auf eine riesige
Bibliothek von gespielten Schachpartien zurück, um zu entscheiden, was aktuell
der bestmögliche Zug ist. Vor nicht allzu langer Zeit hat man einem
Computerprogramm sogar nur die Regeln einprogrammiert und es dann ganz oft gegen
sich selbst spielen lassen.

Das funktioniert auch für andere Spiele. Es gibt sogar
Computerprogramme, die Computerspiele besser als Menschen spielen können.

Beispiel 3: Sprachsteuerung durch maschinelles Lernen

Damit man dem Computer nicht mit Maus, Tastatur oder dem
Touchscreen befehlen muss, was er tun soll, erkennen Computer mittlerweile
unsere Sprache. Das ist zum Beispiel im Auto sehr nützlich, wenn man ein Lied
hören möchte, aber die Hände besser am Lenkrad behält. Aber auch sonst ist es
viel bequemer, dem Computer nur zu sagen „Computer, spiele das Lied xy“ anstatt
den Liedtitel eintippen zu müssen.

Der Computer muss nicht nur verschiedene Stimmen und Sprachen erkennen, sondern auch noch verstehen, dass er ein Lied abspielen soll. Er muss also den Satz in einen Computerbefehl übersetzen. Auch hier funktioniert es dadurch, dass das Computerprogramm mit einer riesigen Anzahl Beispielsätze gefüttert wurde.

Und wie funktioniert Maschinelles Lernen denn nun genau?

Es gibt zwei verschiedene Verfahren. Das erste nennt man den
„symbolischen Ansatz“. Dabei geht es darum, Regeln zu erlernen. Zum Beispiel
wenn ein Mensch größer als 1,8m ist, dann ist er groß. Der zweite Ansatz heißt
einfach „nicht-symbolisch“. Hier ist das Gelernte indirekt gespeichert, so wie wir
auch einfach Fahrrad fahren können, ohne genaue Regeln zu befolgen. Das wäre ja
komisch, wenn wir die Regel hätten: „Linkes Bein strecken, wenn die linke
Pedale oben ist“.

Eine Möglichkeit des nicht-symbolischen maschinellen Lernens ist ein sogenanntes künstliches neuronales Netz. Dieses wurde unserem Gehirn nachempfunden, aber natürlich ganz stark vereinfacht. Es besteht aus Punkten, welche miteinander verbunden sind. Um einem neuronalen Netz etwas beizubringen, werden ihm ganz viele Beispiele gezeigt, so dass es lernt, wann es richtig und wann falsch liegt. Im Inneren des neuronalen Netzes wird dadurch die Stärke der Verbindungen geändert.

Ein einfaches neuronales Netz mit 3 Schichten und 11 Knoten. In der Praxis haben neuronale Netze hunderte von Schichten und tausende von KnotenTeile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz

Was ist Keras?

Teile diesen Beitrag:
Hej Leute,

vielleicht habt ihr schon mal von Keras im Zusammenhang mit
Neuronalen Netzen gehört. Nun fragt ihr euch, was das denn genau ist.

Ganz kurz: Keras ist
eine Schnittstelle zu mehreren Deep Learning Frameworks.

Aber das ist natürlich ein bisschen zu kurz. Also hier eine
etwas längere Erklärung.

Was ist ein Deep Learning Framework?

Ein Deep Learning Framework ist eine Programmbibliothek,
welche das Erstellen, Trainieren und Anwenden von Neuronalen Netzen relativ
komfortabel ermöglicht, ohne das Neuronale Netz komplett selber programmieren
zu müssen. In der Praxis wird nämlich nicht jedes Mal von Null begonnen,
sondern man möchte ja schnell zu Resultaten kommen und nicht das Rad nochmal
neu erfinden. Dafür gibt es mehrere Deep Learning Bibliotheken für verschiedene
Programmiersprachen.

Die bekanntesten sind (ohne Gewähr)

TensorFlow (Python, C/C++, Java, Go, JavaScript,
R, Julia, Swift)PyTorch (Python)Caffe (Python, Matlab, C++)Theano (Python)MXNet (C++, Python, Julia, Matlab, JavaScript, Go,
R, Scala, Perl)Microsoft Cognitive Toolkit / CNTK (Python, C++,
BrainScript)Keras (Python, R) Aber ist Keras dann so was wie TensorFlow?

Keras hat eine Sonderrolle, denn es ist eigentlich ein
Interface und setzt auf andere Frameworks auf. Damit können TensorFlow,
Microsoft Cognitive Toolkit und Theano als Backend verwendet werden, die
Befehle in Keras sind immer die gleichen. Keras wurde mit dem Ziel entwickelt, Experimente
schnell realisieren zu können. D.h. eine nutzerfreundliche API mit wenig
Overhead.

Kann ich Keras auch in R nutzen?

Ja, Keras ist neben MXNet eine der wenigen Bibliotheken,
welche auch für R vorhanden ist. Und da Keras als Backends ja TensorFlow,
Theano und Microsoft Cognitive Toolkit unterstützt, können wir diese also
ziemlich bequem aus R heraus steuern.

Der Befehl zum Installieren des Packages in R ist ziemlich
simpel, jedenfalls mit CPU-basiertem TensorFlow im Hintergrund.

install.packages("keras") <br>library(keras) <br>install_keras()

1

install.packages("keras") <br>library(keras) <br>install_keras()

Also, viel Spaß mit Keras!
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Was ist Julia? Eine tolle Programmiersprache!

Teile diesen Beitrag:
Julia ist eine junge Programmiersprache, die aktuell immer
mal wieder als tolle Alternative im Bereich Data Science genannt wird. Julia
wurde am MIT entwickelt, 2012 erschien die Open-Source-Version.

Julia ist eine General Purpose Language, wurde aber vor
allem für die Datenanalyse und das numerische Rechnen entwickelt. Julia benutzt
eine just-in-time compilation, welche zwischen einer klassischen
Compiler-Sprache (AOT = ahead of time) und einem Interpreter sitzt. Der Code
wird während der Ausführung kompiliert.

Der große Vorteil von Julia ist die Geschwindigkeit bei einem
dynamischen Typsystem, d.h. der Typ einer Variable (z.B. int) wird erst zu Laufzeit
überprüft. Andere Skriptsprachen, die ein dynamisches Typsystem haben, sind normalerweise
langsamer. Zudem kann R- und Python-Code mit den entsprechenden Bibliotheken
ausführen.

Die Geschwindigkeit von Julia ist bemerkenswert, da andere
Skriptsprachen schon über Jahre optimiert wurden. Dies kann durch die
Typ-Stabilität erklärt werden, so wirft z.B. 2^-5 einen Error, da 2 und -5
Integer sind, das Ergebnis aber nicht. R würde das Ergebnis einfach in eine
Kommazahl umwandeln. Die Typstabilität sorgt dafür, dass direkt der optimierte
C/FORTRAN-Code aufgerufen werden kann. Gleichzeitig sind die Funktionen
überladen, so dass es für viele Kombinationen von Typen im Hintergrund eine
eigene Funktion aufgerufen wird.

Es scheint so, dass Julia eine Konkurrenz für R oder Python im Bereich Data Science werden könnte. Zudem besteht ja die Möglichkeit, Code verschiedenster Programmiersprachen einzubinden.

Julia-Links

Die offizielle Website von Julia findest Du unter julialang.orgEine gute englische Einführung gibt es unter A Deep Introduction to Julia for Data Science and Scientif ComputingTeile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

GPU-Prozessoren für Künstliche Intelligenz

Teile diesen Beitrag:
Hej Leute,

heute geht es um die Unterschiede zwischen CPU und GPU, denn
aktuell ist die Verwendung von GPUs für Maschinelles Lernen in aller Munde.
Aber was ist eigentlich eine GPU und warum sind die GPUs für Deep Learning so
gut geeignet?

Dazu klären wir erstmal, was eine CPU, eine GPU und auch
eine TPU sind. Danach ist klarer, warum die GPUs einen so großen Vorteil in der
KI haben.

Was ist der Unterschied zwischen CPU, GPU und TPU?

Die CPU ist der Hauptprozessor eines Rechners und für allgemeine
Aufgaben ausgelegt. Die GPU ist ein für 3D-Grafik optimierter Grafikprozessor.
Und die TPU ist ein Spezialprozessor von Google, der für Tensorflow optimiert
wurde.

Die CPU im Detail

CPU ist der Hauptprozessor eines Computers (CPU = central
processing unit = zentrale Verarbeitungseinheit). Die CPU besteht heutzutage
aus mehreren Prozessorkernen, die im Wesentlichen eigenständige CPUs sind.

Eine CPU setzt sich hauptsächlich aus einem Steuerwerk und
einem Rechenwerk zusammen. Daneben sind üblicherweise noch weitere Teile wie
zum Beispiel eine Gleitkomma-Recheneinheit vorhanden. Zudem ist die CPU über
einen Daten-Bus mit den anderen Komponenten des Rechners verbunden

Das Steuerwerk (engl. control unit) ist für die Befehlsverarbeitung
zuständig. Dazu holt es sich einen Befehl einen Befehl aus dem Speicher,
dekodiert ihn, führt ihn aus und schreibt das Ergebnis in den Speicher.

Das Rechenwerk besteht aus einer oder mehreren ALUs (arithmetic-logic
unit) und mehreren Registern. Die ALU kann zwei Binärwerte miteinander verrechnen.
Dabei sind mindestens die Basisbefehle ADD (Addition), NOT (Invertieren) und
AND (logische Und-Verknüpfung) vorhanden, meist aber noch weitere wie OR, XOR,
MUL, SUB, die aber auch durch die ersten drei abgebildet werden können.

Die GPU besticht durch Matrixberechnungen

Mit GPU bezeichnet man einen Grafikprozessor (graphical
processing unit), also einen Prozessor, der auf die Berechnung von Befehlen,
die für die Grafikdarstellung benötigt werden, optimiert ist. Eine oder mehrere
GPUs können in der CPU, onboard (auf der Hauptplatine), auf einer internen Grafikkarte
oder in externen Erweiterungsboxen (eGPU) sein.

Die GPU ist für 3D-Berechnungen ausgelegt. Typische Aufgaben
sind geometrische Berechnungen (Transformation, Rotation), Texture Mapping,
Shading oder Interpolations-Techniken. Dafür sind vor allem Matrix- und
Vektorberechnungen (Lineare Algebra) notwendig. Zudem sind GPUs hochgradig parallelisiert,
sie können also mehrere Datenblöcke gleichzeitig verarbeiten.

Die Kombination aus Parallelisierung und Matrix-Rechnung
machen GPUs so effektiv für moderne KI-Methoden wie Neuronale Netze. Dazu
weiter unten mehr.

Die TPU ist ein Spezialprozessor

TPU steht für Tensor Processing Unit und ist ein Prozessor,
der von Google speziell für die Berechnungen von Neuronalen Netzen entwickelt
wurde. Genauer gesagt für das Neuronale Netze-Framework Tensorflow. Im
Gegensatz zu GPUs sind die TPUs auf ein hohes Volumen von Berechnungen mit
niedriger Genauigkeit, z.B. 8-Bit. GPUs hingegen sind auf
32-/64-Bit-Operationen optimiert.

Warum brauchen wir GPUs in der KI?

Abstrakt gesehen brauchen wir die GPUs nicht, denn natürlich
kann auch das Rechenwerk einer CPU die entsprechenden Berechnungen ausführen.
Aber praktisch gesehen brauchen wir die GPUs aus Geschwindigkeitsgründen. Denn
die GPU ist optimiert auf Vektor- und Matrixberechnungen. Und die Architektur
der GPU ist auf parallele Verarbeitung ausgelegt. Beides sind Eigenschaften,
die für KI-Algorithmen, insbesondere für Neuronale Netze von enormer Bedeutung
sind.

Was haben Neuronale Netze mit Matrixrechnung zu tun?

Neuronale Netze basieren ganz Wesentlich auf Matrixberechnungen.
Zum Beispiel brauchen wir diese, um den Output eines Netzwerks schnell zu
berechnen. Achtung, jetzt wird es mathematisch:

bezeichnet das Gewicht der Verbindung vom k-ten Neurons der Schicht (l-1) zum j-ten Neuron in der Schicht l, den Bias des j-ten Neurons in der Schicht l, die Aktivierung des j-ten Neurons in der Schicht l die AktivierungsfunktionDann wird die Aktivierung der l-ten Schicht so berechnet

In Matrixschreibweise sieht das dann so aus:

Optimierung der Gewichte mittels Gradient Descent und Backpropagation

Um ein neuronales Netz zu trainieren, füttern wir es mit
einer Unzahl an Beispielen, so dass es die Gewichte der Verbindungen und den
Bias optimiert. Dafür wird meistens eine Variation des sogenannten Gradient
Descent-Algorithmus verwendet. Grob gesprochen geht es darum, dass die
Fehlerfunktion (loss function) möglichst klein. Es werden in jedem Schritt die
Gewichte also so angepasst, dass die Verbesserung der Fehlerfunktion maximal
wird. Dafür müssen wir wissen, wo die Funktion am steilsten bergab geht
(descent = Abstieg). Das wiederum hängt an der Ableitung (gradient), wissen die
Mathematiker.

Ein Spezialfall des Gradient Descent ist Backpropagation,
welches auf dem mittleren quadratischen Fehler basiert. Den mittleren
quadratischen Fehler kennen viele vermutlich schon von der linearen Regression.
Backpropagation ist kurz für „backward propagation of errors“ und das backward
kommt daher, dass das Neuronale Netzwerk rückwärts durchlaufen wird, also vom Output
Layer her.

Die genauen Formeln würden hier etwas zu weit führen, aber
im Wesentlichen sind es jede Menge Matrixmultiplikationen. Und dafür sind
wieder besonders geeignet: GPUs.

Parallelisierung in Neuronalen Netzen

Daten-Parallelisierung

Wir gruppieren die (Trainings-)Daten in sogenannte Mini-Batches. Ein Mini-Batch wird dann parallel in der GPU optimiert (gradient descent) und anschließend werden die Ergebnisse gemittelt.

Modell-Parallelisierung

Hier wird die Gewichtsmatrix in mehrere Untermatrizen
aufgeteilt, so dass diese dann parallel optimiert werden können. Haben wir zum
Beispiel eine Matrix mit 1000×1000 Einträgen, können wir diese in vier 1000×250
Matrizen splitten und diese dann an vier GPUs gleichzeitig zur Optimierung schicken.

Happy Coding, Euer Holger
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Buchreview: Datenanalyse mit Python – McKinney

Teile diesen Beitrag:
Das Buch „Datenanalyse mit
Python“ von Wes McKinney ist bei O’Reilly erschienen und kostet im Handel
44,90€. Die zweite Auflage, auf Deutsch erschienen im Oktober 2018, ist mit 522
Seiten ganz schön umfangreich, man bekommt also schon mal einige Seiten für
sein Geld. Die erste Auflage ist übrigens von 2012. Der Untertitel „Auswertung
von Daten mit Pandas, Numpy und IPython“ sagt eigentlich schon, dass es im
Wesentlichen um die Aufbereitung und Manipulation von strukturierten Daten
(Tabellen, Matrizen, Zeitreihen) – englisch data munging –  mittels Pandas und Numpy geht.
Datenvisualisierung mittels Matplotlib wird in einem Kapitel beschrieben.
ML-Algorithmen und statistische Auswertungen per scikit-learn und statsmodels
werden im vorletzten Kapitel angekratzt.

Über den Autor

Der Amerikaner Wes
McKinney hat Mathematik am MIT studiert und entwickelt seit 2007
Datenanalyse-Software, hauptsächlich für Python. So ist er der Hauptautor der Python-Bibliothek
pandas, um die kein Data Scientist
herumkommt. Zudem ist er in Apache Arrow
involviert, einer Plattform für In-Memory-Daten, welche mehrere Programmiersprachen
unterstützt. In 2018 hat McKinney Ursa Labs
zusammen mit Hadley Wickham, den jeder R-Programmierer kennt, gegründet. Ursa
Labs ist eine Non-Profit-Organisation, die sich open-source, cross-language
Software für Data Science auf die Fahne geschrieben haben.

Die deutsche Übersetzung wurde von Christian Tismer,
Kristian Rother und Kathrin Lichtenberg angefertigt.

Inhaltsverzeichnis

EinleitungGrundlagen von Python, IPython und
Jupyter-NotebooksIn Python integrierte Datenstrukturen,
Funktionen und DateienGrundlagen von NumPy: Arrays und vektorisierte
BerechnungErste Schritte mit pandasLaden und Speichern von Daten sowie DateiformateDaten bereinigen und vorbereitenDatenaufbereitung: Verknüpfung, Kombinieren und
UmformenPlotten und VisualisierenAggregation von Daten und GruppenoperationenZeitreihenPandas für FortgeschrittenenEinführung in Modellierungsbibliotheken in
PythonBeispiele aus der DatenanalyseAnhang A: NumPy für Fortgeschrittene

Anhang B: Mehr zum IPython-System

Für wen ist „Datenanalyse mit Python“ geeignet?

Prinzipiell ist das Buch für jeden Python-Programmierer
geeignet, der sich mit Datenaufbereitung und –manipulation herumschlagen muss.
Das sind vermutlich weniger die reinen Software-Entwickler, sondern eher die
Data Engineers und Data Scientists, die zwar meistens noch tiefergehende
Analysen machen, aber das sogenannte Data Munging nimmt eben einen großen Teil
der Arbeit in Anspruch. Da schadet es nichts, sich damit auszukennen, um
effizient und schnell zu programmieren.

Die ersten Kapitel sind sehr elementar und richten sich an
diejenigen, die noch nie mit Python, numpy und pandas zu tun hatten. Aber es
ist ja ein dickes Buch. Es macht also nichts, wenn man die ersten 100 Seiten
überspringen kann. Los geht es also eigentlich in Kapitel 4, in dem die
Grundlagen von Numpy behandelt werden.

Fazit

Wer Daten mit Python analysieren will, kommt um die
Bibliotheken numpy und pandas nicht herum und das ist genau der Schwerpunkt
dieses Buchs. Es beschreibt die Datenstrukturen und Funktionsweisen mit
präziser Sprache. Das macht es zwar nicht gerade zu einem einfach zu lesendem
Buch, dafür hat es die benötigte Genauigkeit, die man als Programmierer
benötigt.

An der ein oder anderen Stelle hätte ich mir praxisnähere
Beispiele gewünscht. Hier wurde auf Kürze und Konzentration auf das Wesentliche
mehr Wert gelegt. In Kapitel 14 kommen dann aber doch noch einige spannende
Beispiele aus der Praxis.

Insgesamt kann ich dieses Buch allen angehenden Data Scientisten, die Python lernen wollen, empfehlen. Es ist nicht mal eben am Wochenende durchgearbeitet, enthält dafür umfangreiches Wissen zu numpy und pandas. So eignet es sich auch später noch als Nachschlagewerk.

Auf der Website von O’Reilly gibt es eine Leseprobe, das gesamte Kapitel 4

Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Eine neue Art im Neuronale Netze-Zoo?

Teile diesen Beitrag:
Spektrum titelt recht reißerisch: „Eine neue Form von KI?„, immerhin mit Fragezeichen. Worum geht es denn überhaupt? Das Paper „Neuronal Ordinary Differential Equations“ wurde auf im Rahmen der NeurIPS 2018 mit dem Best Paper Award ausgezeichnet.

Was sind ODE-Netze?

Die Idee, die in dem Paper beschrieben wird, hört sich interessant an. Diese wurde übrigens schon 2017 von anderen Autoren veröffentlicht. Grundsätzlich besteht ein Neuronales Netz ja aus einer Eingangsschicht, mehrerer versteckter Schichten (hidden layers) und einer Ausgangsschicht. Um diese sogenannten hidden layers geht es. Anstatt diese als diskrete Schichten zu betrachten, benutzen die Wissenschaftler ein kontinuierliches Modell, indem sie Ableitung des hidden states parametrisieren. Das wird mittels eines Differentialgleichungen-Solver (ODE = ordinary differential equations) gelöst. Im Endeffekt ist das Konstrukt kein Neuronales Netz mehr, denn die ganzen Knoten und Verbindungen sind ja nicht mehr da, sondern sind durch ein „Feld“ ersetzt.

Noch ist die Entwicklung in einem frühen Stadium. Das Paper gibt einen proof of concept, mehr aber noch nicht. Wie nützlich das Konstrukt ist, wird sich herausstellen. Aber super spannend, denn jetzt kommen Analysis-Methoden zum Einsatz und auf diesem Gebiet kennen sich die Mathematiker sehr gut aus.

Links

Das Paper „Neuronal Ordinary Differential Equations“ ist auf arxiv.org zu finden. Hier ist der Link dazu. Nicht zu vergessen das erste Paper zu Neuronalen ODEs von 2017: Reversible Architectures for Arbitrarily Deep Residual Neural Networks

Der zugehörige Artikel auf Spektrum.de findet Ihr hier.

Und hier ist noch ein besserer Artikel dazu auf MIT Technological Review
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Maschinelles Lernen und die französische Revolution

Teile diesen Beitrag:
Letztes Jahr wurde ein Paper in „Proceedings of the National Academy of Sciences of the United States of America“ (PNAS) veröffentlicht, welches die Rhetorik der ersten Jahre der französischen Revolution untersucht. Das interessante daran aus Data Science Sicht ist die Nutzung von ML-Algorithmen, um eine große Menge an Reden (40.000) zu analysieren. Soweit ich weiß, ist das eine der ersten Forschungen, welche Maschinelles Lernen verwendet, um historische Daten dieser Größenordnung auszuwerten. Besonders toll finde ich, dass die NLP-Methoden (NLP = Natural Language Processing) eine praktische Anwendung finden.

Ich bin gespannt, ob durch die fortschreitende Digitalisierung und Veröffentlichung historischer Daten die quantitativen Analysen in den Geisteswissenschaften zunehmen. Wie jeder, der mit Datensätzen zu tun hat, sind aber ebenso wichtig wie neue Methoden die richtigen Fragestellen. Daher ist die Zusammenarbeit zwischen Fachexperten (Domain Owner) und Data Scientisten so wertvoll.

Vorgehen

Nach der typischen Aufbereitung, sprich Stop-Word-Removal, wurden die Reden mittels Latent Dirichlet Allocation (LDA) in 100 „Themen“ einsortiert. Das passiert anhand von gemeinsam auftauchenden Wortpaaren. Die Themen wurden dann im zeitlichen Verlauf analysiert und dabei Kennzahlen für Novelty (Abweichung von vorherigen Reden), Transience (Abweichung von folgenden Reden) und Resonance (die Differenz der beiden) quantifiziert.

Links

Hier geht es zu einem Überblicks-Artikel über das Paper des Santa Fe Institute. Das Paper „Individuals, institutions, and innovation in the debates of the French Revolution„ selbst ist bei PNAS zu finden.
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Buchtipp: Bayesian Methods for Hackers

Teile diesen Beitrag:
Das kostenlose Ebook „Bayesian Methods for Hackers“ von Cameron Davidson-Pilon erklärt die Bayeschen Algorithmen in Python mittels der Python-Library PyMC. Dabei richtet sich das Buch an „Hacker“. Damit ist gemeint, dass es in erster Linie um die praktische Umsetzung und nicht um die mathematischen Grundlagen geht. Nichtsdestotrotz wird einiges an Mathematik verlangt, das liegt aber in der Natur der Sache.

Bei der Veröffentlichung geht Davidson-Pilon dankenswerterweise den Open-Source-Weg, d.h. das gesamte Buch ist als Jupyter Notebooks aufgebaut, welche auf Github zur Verfügung stehen. Eine gedruckte Version gibt es auch auf Amazon zu kaufen. Zudem gibt es eine Adaption an Tensorflow Probability, wenn man die Tensorflow-Umgebung und TPU/GPUs nutzen will.

Inhalt

Das Buch ist in 7 Kapitel unterteilt, welche jeweils als Jupyter Notebook (ipynp-Datei) zur Verfügung stehen. Am besten klont ihr euch das gesamte Github-Repository und öffnet es mit Jupyter Notebook oder einem anderen Editor, der Notebooks anzeigen kann. So könnt ihr lesen und die Beispiele ausführen und modifizieren. Alternativ könnt ihr euch die Notebooks auch im Browser mit dem nbviewer ansehen. Die einzelnen Kapitel habe ich verlinkt.

Introduction to Bayesian MethodsA little more on PyMCOpening the black box of MCMCThe greatest theorem never toldWould you rather lose an arm or a legGetting our prior-ities rightBayesian Machine Learning (in Arbeit)Das Buch als gedruckte Version

Das Buch gibt es auch in einer gedruckten Version vom Addison-Wesley Verlag (2015). Durch Klick auf das Bild gelangt Ihr zu Amazon.
Hier ist der Link zur Website des Buches und hier der Link zur zugehörigen Github-Seite.

Viel Spaß beim Lesen.
Teile diesen Beitrag:Glossar: R (Programmiersprache) Maschinelles Lernen min rnorm mean merge paste getwd setwd RStudio list.files Neuronales Netz Tensorflow

Wird geladen
Teile diesen Beitrag:

Gratis für Dich

Gratis für Dich

Ich habe für Dich die wichtigsten Machine-Learning Algorithmen übersichtlich zusammengefasst.

Trage jetzt Deine Email ein und Du kannst sofort das kostenlose Ebook downloaden.

Du bist noch einen Schritt vom Ebook entfernt. Ich habe Dir soeben eine Email geschickt. Bitte klicke auf den dort hinterlegten Link.