R-Tipps: Dplyr in Funktionen nutzen

Seit es das R-Package dplyr gibt, sind viele Aufgaben bei der Datenbearbeitung einfacher geworden. Soll diesselbe Umformung für viele Variablen durchgeführt werden, kann es eine Menge Tipparbeit sparen, wenn man den Code in eine Funktion packt.

Bei der Arbeit mit dplyr ist es wichtig, dass man plyr – sofern man es benötigt – grundsätzlich als erstes läd. Andernfalls treten mit Sicherheit an irgendeinem Punkt der Arbeit Fehlermeldungen auf. Summarise() gibt es beispielsweise in plyr, aber auch in dplyr, was zu Konflikten wie diesem führt: „Error in n() : This function should not be called directly„.

# Installieren der nötigen Pakete
install.packages("plyr")
install.packages("dplyr")
install.packages("tidyr")
install.packages("lazyeval") #oder: devtools::install_github("hadley/lazyeval")
library("plyr")
library("dplyr")
library("tidyR")
library("lazyeval")

Was macht dplyr so besonders?
Dplyr-Code ist einfach lesbar, logisch und arbeitet sequenziell, da es den Pipe-Operator %>% aus aus dem MagrittR-Package nutzt.
Durch „%>%“ (sprich: then) können Manipulationen in einer intuitiven Reihenfolge ausgeführt werden. Während früher eine Funktion umständlich mit verschachtelten Klammern zu schreiben war, kann man mit dplyr einfach der Intuition folgen und einen Schritt nach dem nächsten machen.

Ein einfaches Beispiel: Ich möchte alle Personen eines Datensatzes entsprechend der Kategorien einer Variable in Gruppen einteilen (z.B. Geschlecht, Einkommensstufe, Postleitzahl) und dann die Anzahl der Personen je Kategorie bestimmen. Ich wähle den dataframe aus und gebe ihn mit dem Pipe-Operator (%>%) an die group_by() Funktion von dplyr weiter. Anschließend wird der gruppierten Datensatz an die summarise()-Funktion weitergegeben, die dann mit n() die Fälle zählt. Das Ergebnis wird als dataframe-Objekt „Beispiel“ abgelegt.

Durch group_by() werden Gruppen erzeugt und mittels summarise() werden die Daten je Gruppe so aggregiert, wie ich angebe. Hier wird nur n() je Gruppe gezählt.

Beispiel<-dataframe %>% 
    group_by(variable) %>% 
    summarise(
      freq = n()
     )

Dies ist natürlich nur Minimalbeispiel. Um einen Eindruck davon zu bekommen, was dplyr alles kann, empfehle ich folgenden Überblick:
Data Wrangling with dplyr und tidyr Cheat Sheet.
95% aller Aufgaben, die das Umformen von Daten, erstellen neuer Variablen, Gruppieren von Daten, Auswählen von Fällen, Variablen, Werten, oder Zusammenfügen von Datensätzen erfordern, können damit erledigt werden. Und: Alle diese Schritte können miteinander kombiniert werden.

dplyr in Funktionen
Versucht man den obigen dplyr-code in eine Funktion zu schreiben, stößt man auf eine Fehlermeldung. Warum? Innerhalb von Funktionen muss eine andere Version der dplyr-Funktionen verwendet werden: Standard evaluation (SE)

  • Anstatt summarise() ==> summarise_()
  • Anstatt mutate() ==> mutate_()
  • Anstatt filter() ==> filter_()
  • usw.

Zusätzlich zu diesen SE-Versionen von dplyr-Funktionen ist die Übergabe der Input-Objekte an die Funktion leicht unterschiedlich. Entweder müssen die Objekte

  • als Formel „~ Objekt“
  • als „quote(Objekt)“
  • oder als String mit Anführungszeichen “ ‚Objekt‘ „

eingefügt werden. Hadley Wickham, der Programmierer des Pakets, empfiehlt die erste Möglichkeit.

Als Funktion sieht der obige dplyr-code so aus:

test.function <- function(dataframe, variable){
  dataframe %>% 
    group_by_(variable) %>% 
    summarise_(
      freq = ~n()
)
}
#Aufrufen der Funktion für den test_dataframe und die Variable gender
test<-test.function(test_dataframe, ~gender)

Die Funktion „test.function(x,y) kann zwei Input-Objekte annehmen, für die sie den dplyr-code durchführt und das Ergebnis als „test“-dataframe abspeichert.

Komplexeres Beispiel
Es ist möglich, ganze Auswertungsprozeduren als Funktion zusammenzufassen. Der folgende Code führt eine Gewichtung durch, bildet Anteilswerte der Antworten und generiert einen Plot der Daten.

#Funktion zur Berechnung der gewichteten Anteilswerte
weighting.function <- function(dataframe, variable){
  dataframe %>% 
    group_by_(variable) %>% 
    summarise_(
      freq = ~n(),
      freq_weighted = ~sum(weight)                    
    ) %>%
    mutate_(
      perc=~freq/sum(freq)*100,
      perc_weighted=~freq_weighted/sum(freq_weighted)*100
    ) %>%
    gather(key=Gewichtung,value=Wert,perc:perc_weighted)
}

#Ausführen der Funktion
gender_w<-weighting.function(datensatz, ~gender)

# Plotten der Daten mit ggplot2
library("ggplot2")
ggplot(gender_w) + geom_bar(aes(x=gender,y=Wert), stat="identity") + facet_grid(~Gewichtung) +scale_x_discrete(labels=c("männlich","weiblich","NA"))+ ggtitle("Anteilswerte Geschlecht - gewichtet und ungewichtet")

Das Ergebnis:Vergleich Gewichtung
An diesem Plot sieht man, dass vor der Gewichtung der Anteil der Männer etwas höher war, während bei den gewichteten Daten der Frauenanteil etwas erhöht wird. Da es in der Sozialstruktur Deutschlands etwas mehr Frauen als Männer gibt (hauptsächlich wegen der höheren Lebenserwartung), spiegeln die gewichteten Daten die Grundgesamtheit etwas besser wider.

Hier gibt es Weitere Informationen zu standard evaluation in dplyr
Und über die R-Console gelangt man an die vignette: vignette(„tidy-data“).

Ideen für neue Raspberry-Pi Projekte

Bisher habe ich meinen Raspberry-Pi für 3 Zwecke verwendet: Als Pi musicbox, als Mediacenter mit Raspbmc und für mein Webscraping Projekt.

Mit der Pi musicbox kann ich den Raspberry Pi an meine Stereoanlage anschließen und Musik über Spotify-Premium hören. Gesteuert wird das ganze über den Browser eines Smartphones oder PCs im gleichen Wlan-Netzwerk. Diesen Job hat jetzt ein ausgemustertes Smartphone mit installierter Spotify-App übernommen.
Raspbmc macht aus dem Raspberry Pi ein Media-Center, das an den Fernseher angeschlossen werden kann. Es ermöglicht den Zugriff auf alle möglichen Medien (Musik, Filme, Fotos) z.B. von Netzwerkfestplatten und wird ebenfalls per Smartphone oder Tablet PC gesteuert. Um Urlaubsfotos von der Netzwerkfestplatte zu gucken, brauche ich jedoch keinen Raspberry Pi – das kann mein SmartTV schon selbst.
Das dritte Projekt (Radiosong-Webscraping Projekt mit dem Raspberry Pi) ist jetzt beendet. Daher suche ich neue Aufgaben für den kleinen PC.

Momentan reizt mich folgende Idee: Ein Info-Center für das Haus.

Der Raspberry Pi wird an ein Display angeschlossen. Hier werden die Abfahrtszeiten der nächsten Bus- und Straßenbahnverbindungen angezeigt und eine „Ampel“, ob man sie noch erreicht, wenn man jetzt losgeht. Außerdem wird der Raspberry Pi mit einem Temperatur- und einem Luftfreuchtigkeitsfühler ausgestattet. Diese Informationen werden visualisiert. Hier soll ebenfalls eine „Ampel“ angezeigt werden, ob die Luftfeuchtigkeit zu hoch ist und gelüftet werden muss. Außerdem könnte noch angezeigt werden, welcher Müll als nächstes abgeholt wird. Eventuell können auch Daten vom Smart-Meter (Stromzähler) abgegriffen werden. Dann wäre der aktuelle Stromverbrauch im Haus immer offensichtlich. Das könnte beim Stromsparen helfen.

Haus-Automatisierung
Eine weitere Anwendungsmöglichkeit ist Haus-Automatisierung. Steckdosen und evtl. auch Leuchten könnten z.B. via smartphone auf dem Raspberry-Pi (Web-interface) gesteuert werden. Hierfür werden entsprechend steuerbare Steckdosen mit DIP Schalter(!) benötigt, die es aber schon günstig zu kaufen gibt: Brennenstuhl Comfort RCS 1000N Funkschalt Se. Für die Heizkörperregelung wäre die Funksteuerung ebenfalls möglich, allerdings sind entsprechende Thermostate am Heizkörper etwas teurer und zudem ist unsere Zentralheizung selbst schon auf bestimmte Uhrzeiten programmiert. Die Notwendigkeit für eine weitere Steuerungsmöglichkeit ist also fraglich. Man kann mit dem raspberry pi auch eigene Alarmanlagen realisieren, wobei ich darauf nicht allzu sehr vertrauen würde.

Das Info-Center könnte man als „Spiegel“ konstruieren: Raspberry Pi Mirror. Diese Lösung ist mir aber etwas zu kostspielig, da hier ein anständiger Monitor für zerlegt wird. Interessanter wäre das Recycling eines alten Kindle-E-Readers von amazon, der per Jailbreak als Monitor umfunktionalisiert werden kann. Der Kindle hat den Vorteil, auch bei Sonneneinstrahlung ablesbar zu sein (vor allem der Paperwhite) und lange Zeit ohne Stromversorgung auszukommen.

Webscraping mit Raspberry Pi – Teil 3 – Datenauswertung

Ich habe mich entschieden, dieses kleine Webscraping-Projekt mit dem Raspberry-Pi durchzuführen, weil der Stromverbrauch des kleinen Rechners bei nur 3,5 Watt liegt. So habe ich kein schlechtes Gewissen, wenn ich den Raspberry-Pi 2-3 Wochen am Stück laufen lasse, um eine Datenbasis für meine Radiosong-Auswertung zu generieren.

Datenauswertung
Bei einem Blick auf die gewonnenen Rohdaten, fiel mir auf, dass ab und zu Duplikate von Einträgen entstanden sind. Das heißt, dass derselbe Song direkt hintereinander zweimal in der Liste gelandet ist. Das hatte ich durch einen Abgleich in der Schleife meines Python-Codes eigentlich vermeiden wollen und es hat auch überwiegend funktioniert. Die Ursache hierfür muss ich noch herausfinden.

MacDonald,Amy This is the life 07:22 14-12-13
MacDonald,Amy This is the life 07:22 14-12-13
Foster The People Pumped up kicks 07:25 14-12-13
Foster The People Pumped up kicks 07:25 14-12-13
Farin Urlaub Racing Team Herz? Verloren 07:32 14-12-13
Farin Urlaub Racing Team Herz? Verloren 07:32 14-12-13
FUN. We are young 07:36 14-12-13
FUN. We are young 07:36 14-12-13

Für die weitere Arbeit mit dem Datensatz muss ich also diese unmittelbar aufeinanderfolgenden Duplikate entfernen. Dies mache ich natürlich mit R, denn dort brauche ich nur eine kleine Zeile Code für diese Aufgabe.

#Daten einlesen
radio<-read.csv2(file="C:/Beispieldaten/radio.csv",sep=";",header=FALSE)
names(radio)<-c("Kuenstler","Titel","Uhrzeit","Datum")

#Doppelte Einträge löschen
radio<-unique(radio)
[/code]
Und schon sieht die Datenbank so aus:
<code>
MacDonald,Amy	This is the life	07:22	14-12-13
Foster The People	Pumped up kicks	07:25	14-12-13
Farin Urlaub Racing Team	Herz? Verloren	07:32	14-12-13
FUN.	We are young	07:36	14-12-13

Datenauswertung
Als erstes interessiert mich: Welche Künstler werden am häufigsten gespielt?
Hierfür werde ich erstmal eine Wordcloud für den groben Eindruck machen, anschließend ein normales Balkendiagramm, da man hier deutlich besser vergleichen kann.

#Wordcloud
library("tm")
library("wordcloud")

test<-as.character(radio$Kuenstler)
tweet.corpus<-Corpus(VectorSource(enc2utf8(test)))
tdm = TermDocumentMatrix(tweet.corpus)

m = as.matrix(tdm)
word_freqs = sort(rowSums(m), decreasing=TRUE) 
dm = data.frame(word=names(word_freqs), freq=word_freqs)

#Plotten
wordcloud(dm$word, dm$freq, random.order=FALSE, colors=brewer.pal(8, "Dark2"))

Das Ergebnis sieht wie folgt aus. Leider werden Künstlernamen mit dem obigen Code leider bei Leerzeichen getrennt. Daher sind „one“ und „republic“ an zwei Stellen in der Grafik vorhanden und nicht zusammengeschrieben an einer. Hier muss ich nochmal nachforschen.
Es fällt ausserdem auf, dass ich die Musik im Dezember gespeichert habe, da Wham und Melanie Thornton sowie Band Aid in der Liste der meistgespielten Künstler vorkommen.
radio

Top 20 Songs auf Bremenvier
Leider habe ich aktuell keine Zeit, die Daten weiter auszuwerten. Als Appetitanreger gibt es eine Grafik vorweg:
radiosongs

Weitere Auswertungen
In Kürze wird die Auswertung noch ergänzt.
– Häufigkeit von Künstlern (Barchart, Bubble/Balloon-chart)
– Zeitpunkte von Liedern (Uhrzeiten, Wochentage mittels Calender Heatmap)

Webscraping mit Raspberry Pi – Teil 2 – Beautifulsoup

Von jeder Website, die aufgerufen werden kann, können auch Daten extrahiert werden. Manche Websiten bieten APIs an, um eine direkte Schnittstelle zu haben. Wenn so etwas nicht existiert, kommt „Webscraping“ zum Zuge. Webscraping heisst, dass Daten direkt aus dem Quelltext einer HTML-Seite extrahiert werden.

Da in diesem Tutorial mit Beautifulsoup gearbeitet werden soll, muss eine HTML-Website angesteuert werden. Javascript kann mit BeautifulSoup4 nicht verwendet werden. Hierfür gibt es andere Möglichkeiten (Selenium).

Welche Daten möchte ich haben?

Meine Frau und ich haben schon lange die Vermutung, dass wir morgens im Bad immer wieder dieselben Lieder im Radio hören. Ich möchte herausfinden, ob dies nur eine subjektive Empfindung ist, oder sogar objektiv feststellbar ist.

BeiRadio Bremenvier gibt es eine Tabelle, auf der die zuletzt gespielten Lieder vom Radiosender Bremen4 angezeigt und ständig aktualisiert werden. Die Tabelle ist im HTML code recht einfach zu finden.

  • Das Element der Tabelle, dessen Informationen ich abspeichern möchte, markieren und Rechtsklick für „Element untersuchen (z.B. im Browser Chrome)
  • Tabellen auf Websites werden mit dem <table> – Tag eingeleitet und mit </table> geschlossen. Innerhalb der Tabellen wird mittels <tr> die Reihe und mit <td> die Spalte bzw. Zelle der Reihe definiert. Mich interessiert nur der erste Song-Eintrag in der Tabelle. Dieser ist innterhalb der zweiten Reihe der Tabelle zu finden. In der ersten Reihe stehen die Überschriften der Spalte.
<tr class="top44_table_line1">
		<td class="top44_table_zelle left bottom">14:23</td>
		<td class="top44_table_zelle  bottom">Smith,Sam</td>
		<td class="top44_table_zelle right bottom">I'm not the only one</td>
	</tr>

An dem Quellcode-Ausschnitt wird deutlich, dass die interessierende Tabellenreihe das Attribut class=“top44_table_line1“ enhält. Alle Informationen die innerhalb dieses <tr>-Tags sind, will ich haben.

Python Code mit Beautifulsoup
Untenstehend ist der Python-Code, der genutzt wird, um die Radiosongs (d.h. Uhrzeit, Künstler, Titel) von der Website zu scrapen. Da die Uhrzeit von der Website kein Datum enthält, ergänze ich Jahr, Monat und Tag über ein Python-Package.
Ich beschreibe kurz, was der untere Code macht. Als erstes wird die Arbeitsumgebung geschaffen, indem die Module geladen werden. Dann wird ein leeres Objekt erstellt, dass ich später benötige.
Anschließend wird eine Endlosschleife gestartet, die alle zwei Minuten durchläuft. Zuerst wird die Website abgefragt und als Beautifulsoup-Objekt abgelegt. Beautifulsoup wird nun angewiesen in den festgelegten Zellen der HTML-Tabelle nach Informationen zu suchen und diese in eine Datei abzulegen. Jedoch nur, wenn der neue Eintrag nicht derselbe wie der alte ist. Anschließend wartet das Programm zwei Minuten, um anschließend von vorne loszulegen. Die „Else“ Bedingung tritt z.B. dann in Kraft, wenn keine Information auf der HTML-Website zu finden war. Dies ist – wie ich herausfand – Nachts von 0:00 bis 6:00 morgens der Fall, da Bremenvier um diese Zeit keine eigenen Lieder sendet, sondern von einem anderen Sender der RadioBremen-Gruppe übernimmt.

Den Code habe ich übrigens während einer „ONOC – Open Night of Coding“ an der Uni-Bremen geschrieben. Wie ihr seht, ist der Code allerdings nicht besonders lang, weshalb meine ONOC eher eine „OHOC – Open Hour of Coding“ war 😉

#Importieren der Module
import requests
import time
import datetime
from bs4 import BeautifulSoup

# leeren Wert fuer den Titelvergleich erstellen, da dieser beim ersten Durchlauf noch nicht existiert
last=""

#Endlosschleife starten
while (True):
    try:
        #Website aufrufen und als soup-objekt nutzbar machen
        fobj = open("daten.dat","a")
        r  = requests.get("http://www.radiobremen.de/bremenvier/musik/titelsuche/index.html")
        data = r.text
        soup = BeautifulSoup(data)
        
        #Gesuchte Informationen werden als Objekt gespeichert          
        line1= soup.select('tr.top44_table_line1 > td')[0]
        line2= soup.select('tr.top44_table_line1 > td')[1]
        line3= soup.select('tr.top44_table_line1 > td')[2]
        now = datetime.datetime.now().strftime("%y-%m-%d")            
        entry="{Artist:s}\t{Song:s}\t{Zeit:s}\t{Datum}".format(Zeit=line1.text,Artist=line2.text,Song=line3.text,Datum=now)
        print(entry)
        
        #Abgleich, ob bei der Abfrage noch dasselbe Lied läuft wie bei der letzten Abfrage, wenn nicht wird das neue Lied eingetragen und ein neuer Wert für den Titelvergleich definiert
        if last != entry:
                fobj.write(entry + "\n")
                last=entry
                fobj.close()      
        time.sleep(120)
               
    except:
        print("Did not find any data")

Auf dem Raspberry Pi findet sich anschließend eine Datei namens „daten.dat“, in der eine Tabulatorseparierte Liste von Einträgen steht.

Twitter-mining mit R – Teil 6 – Tweets im Zeitverlauf visualisieren – in Überarbeitung

Der letzte Blogeintrag (Teil 5) zeigte ein einfaches Rezept, um eine Followermap zu erstellen.

Hier möchte ich ein Beispiel zeigen, in dem ich die Tweethäufigkeit zu einem Hashtag über einen Zeitraum visualisiere. Konkret möchte ich die Tweets zum Fußballspiel von Werder-Bremen gegen Hannover96 nutzen. Wann wird am meisten getwittert? Wenn etwas spannendes auf dem Spielfeld passiert (Tor, gelbe Karte, Freistoß)? Oder überwiegend vor und nach dem Spiel beziehungsweise in der Halbzeitpause, wenn das Spiel keine Aufmerksamkeit erfordert?

Folgende Schritte sind nötig:

  • Verbindung zur Twitter-(Streaming)-API herstellen und relevante Tweets speichern.
  • Spielereignisse und ihre Zeitpunkte von Liveticker abgreifen und als Tabelle abspeichern
  • Daten formatieren/bereinigen, aggregieren und für Grafik vorbereiten
  • Daten mit ggplot2 plotten

 
Verbindung zur Streaming-API
Wie immer: Zunächst eine Verbindung mit der Twitter-API herstellen. In diesem Fall mit der Streaming-API, was einen etwas anderen Code erfordert, als die Rest-API

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(RCurl)
library(ROAuth)
library(twitteR)
library(streamR)

# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
options(RCurlOptions = list(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl")))#nicht für MAC/Linux-User

my_oauth consumerSecret=api_secret,
requestURL='https://api.twitter.com/oauth/request_token',
accessURL='https://api.twitter.com/oauth/access_token',
authURL='https://api.twitter.com/oauth/authorize')

my_oauth$handshake(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl"))

Mit dem Ausführen der letzten Codezeile öffnet sich ein Browserfenster und zeigt eine PIN an. Diese PIN muss nun in die R-console eingegeben werden, um die Verbindung herzustellen.

Tweets während des Fußballspiels speichern
Nun soll der Twitter-Datenstream zu den Suchbegriffen „Werder“,“H96″ und „SVWH96“ abgespeichert werden. Die ersten beiden Suchbegriffe stehen natürlich für die Mannschaften. Der dritte Begriff ist das Hashtag für das Bundeligaspiel. Es hat sich eingebürgert, beide Kürzel der Mannschaften in einem Hashtag zu kombinieren, wobei die Heimatmannschaft zuerst genannt wird. Die Suchbegriffe werden im untenstehenden Code mit dem Argument „track“ als Komma-separierte Liste festgelegt. So werden Tweets gespeichert, die mindestens einen der Begriffe enthalten. Das Komma entspricht dem logischen „OR“. Lässt man es weg, sind die Begriffe wie beim logischen „AND“ verknüpft.

Ich habe mich für die Streaming-API entschieden, da ich hierdurch wesentlich mehr Tweets erhalte. Die Streaming-API greift die Tweets fortlaufend ab. Deshalb muss ich den Zeitraum festlegen, für den ich Tweets haben möchte. Die Tweets sollen 6 Stunden lang aufgezeichnet werden. Dies lege ich mit dem Wert 21600 im timeout-Argument fest (21600 = 6 x 60min x 60sek).

#Tweets abgreifen
filterStream(file.name="C:/Beispieldaten/svwh96b.json", track=c("svwh96","Werder","H96"), timeout=21600, oauth=my_oauth)

#Tweets in Dataframe packen
tweets_svw <- parseTweets("C:/Beispieldaten/svwh96b.json", verbose = TRUE)

 

Spielereignisse in Tabelle speichern
Nachdem wir die Tweets haben, möchten wir nun eine Tabelle mit den Spielereignissen erstellen. Am einfachsten ist es, eine Liveticker-Tabelle von einer Sport-Homepage zu kopieren, in eine Tabellenkalkulation (Excel, LibreOfficeCalc, Numbers) einzufügen und als .csv-Datei zu speichern.

Es geht natürlich auch komplizierter. Der untenstehende Code zeigt, wie „rvest“ (ein Webscraping-tool von Hadley Wickham) genutzt werden kann, um Daten von Websiten zu holen. Der Code zeigt nur den Ansatz, das Data-cleaning habe ich nicht eingefügt.

#--------------------------------------------------------------------------
# --- Webscraping der Spielverlauf-Tabelle
#--------------------------------------------------------------------------
install.packages("rvest")
library(rvest)

#Website parsen und anschließend, den interessierenden TEXT extrahieren
spielbericht<- html("http://www.weser-kurier.de/werder/werder-bundesliga_artikel,-Unentschieden-im-Weserstadion-_arid,1013156.html")
Spielverlauf1<-as.character(spielbericht %>% html_nodes("p") %>% html_text())
Spielverlauf2<-as.character(spielbericht %>% html_nodes("strong") %>% html_text())

#Vector in Minute und Ereignis trennen und als dataframe speichern
library(reshape2)
Spielverlauf<-colsplit(Spielverlauf," ",c("Minute","Ereignis"))
#hier beginnt dann das Datacleaning. 

 

Daten für Visualisierung vorbereiten
Ich habe oben zwei dataframes erzeugt: Einen dataframe mit den tweets über 6 Stunden, den anderen dataframe mit den wichtigsten Spielereignissen und den entsprechenden Spielminuten.

tweets_svw<-read.csv(file='c:/Beispieldaten/svwh96_streamR.csv', header=T)
Spielverlauf<-read.csv2(file="C:/Beispieldaten/Spielereignisse.csv",sep=";",header=FALSE,col.names=c("Minute","Ereignis"))

Eine schöne Sache von R ist, dass Zeit- und Datumsangaben eine eigene Klasse (class) besitzen. Die Klasse legt fest, wie mit den Daten umgegangen werden soll. Sehr vereinfacht, wissen Pakete wie GGPlot2 durch die Klasse „ah, wenn ich auf 15:30 Uhr 40 Minuten addiere, ist es 16:10 Uhr und nicht 15:70Uhr.“
Konkret nutze ich die Klasse POSIXct. Sie ist hilfreich, wenn Zeiten in dataframes gespeichert werden sollen. POSIXlt wäre die Alternative. Doch sie nutzt man eher, wenn es um Dinge wie Wochentage oder ähnliches geht.
Der Befehl „strptime“ ist nützlich, wenn die Daten noch nicht im Format der entsprechenden Klasse vorliegen (sondern z.B. als character vector) und noch umformatiert werden müssen.

Zeitformat der Tweets anpassen
Im tweets_svw dataframe gibt es eine Spalte namens „created_at“, welche den Zeitpunkt enthält, an dem ein Tweet geschrieben wurde. Diese Einträge sehen so aus: „Sat Dec 13 13:04:34 +0000 2014“. Um diese Daten als Zeitangabe nutzbar zu machen, konvertiere ich sie ins POSIXct-Format.

#Datenformat (musste ich bei mir anpassen)
Sys.setlocale("LC_TIME", "English") 
## Zeitformat der created_at-Variable auf POSIXct ändern
tweets_svw$Zeitformat <- as.POSIXct(tweets_svw$created_at, format = "%a %b %d %H:%M:%S %z %Y", tz="")
head(tweets_svw$Zeitformat)
[/code]
<strong>Zeitformat der Spielereignisse anpassen</strong>
Der Liveticker enthielt nur die Spielereignisse und die entsprechende Spielminute (0-92). Die Zeitangaben sollen aber in beiden dataframes im gleichen Format sein, daher muss ich die Spielminuten in Uhrzeiten umwandeln. Mit der POSIXct Klasse ist dies möglich. Der Anstoß war um 15:30. Hier müssen nun noch die Spielminuten aufaddiert werden. 
[code language="r"]
Spielverlauf$Uhrzeit<-c("2014-12-13 15:30:00")
Spielverlauf$Uhrzeit<- as.POSIXct(Spielverlauf$Uhrzeit,tz="")
Spielverlauf$Minute<- (Spielverlauf$Minute*60)
Spielverlauf$Uhrzeit<-Spielverlauf$Uhrzeit + Spielverlauf$Minute
head(Spielverlauf$Uhrzeit)

Anmerkung: Bei der Berechnung der Zeitpunkte für die Spielereignisse habe ich einen Fehler gemacht, da ich nicht berücksichtigt habe, dass die Halbzeit ebenfalls Zeit verbraucht.

Tweets pro Minute aggregieren
Mich interessiert nicht der Inhalt der Tweets, sondern ihre Häufigkeit vor, während und nach dem Spiel. Daher habe ich mich entschieden, zu zählen, wieviele Tweets pro Minute geschrieben wurden.
Der Code hierfür ist nicht wirklich schön und eventuell werde ich ihn nochmal aktualisieren, aber für den Moment macht er erstmal das, was ich möchte.
In Teil 1.) erzeuge ich einen neuen dataframe, der nur die Uhrzeit des abgesetzten Tweets enthält, sowie eine zweite Spalte mit einer „1“ als Zähler, um im nächsten Schritt die Tweets je Minute zu zählen.
In Teil 2.) zerstückele ich den dataframe nach Minuten, zähle die Tweets je Minute und bekomme am Ende einen neuen dataframe namens plot.df. In plot.df ist eine minutengenaue Zeitangabe (Uhrzeit) und die Anzahl der Tweets (Freq) in dieser Minute enthalten.

# 1.) 
tweets.df<-as.data.frame(tweets_svw$Zeitformat) #neuer dataframe
tweets.df$freq<-1 #jeder Tweet erhält einen Häufigkeitszähler
colnames(tweets.df)<-c("time","freq")
tweets.df$time <- as.POSIXct(tweets.df$time)

# 2.)
#Tweets pro Minute aggregieren und als Dataframe abspeichern
by.mins <- cut.POSIXt(tweets.df$time,"mins")
tweets.mins <- split(tweets.df, by.mins)
plot.df<-sapply(tweets.mins,function(x)sum(as.integer(x$freq)))
plot.df<-as.data.frame(plot.df)
plot.df<-cbind(Uhrzeit = rownames(plot.df), plot.df)
plot.df$Uhrzeit <- as.POSIXct(plot.df$Uhrzeit)
plot.df<-as.data.frame(plot.df)
names(plot.df)[names(plot.df) == 'plot.df'] <- 'Freq'

Die Vorbereitungen sind jetzt fast abgeschlossen. Wir müssen nur noch eine unnütze Spalte aus den dataframes loswerden und vorsichtshalber nochmal das Datumsformat festlegen.

library(dplyr)
plot.df<- plot.df %>% select(Uhrzeit,Freq)
Spielverlauf<- Spielverlauf %>% select(Uhrzeit,Ereignis)
plot.df$Uhrzeit<-as.POSIXct(plot.df$Uhrzeit)
Spielverlauf$Uhrzeit<-as.POSIXct(Spielverlauf$Uhrzeit)

Tweet-Daten und Spielereignis-Daten zusammenfügen
Mittels „plyr“ werden die dataframes anhand der gemeinsamen Uhrzeiten in einen gemeinsamen dataframe überführt. Das all.x=TRUE Argument ist wichtig, da somit alle Einträge aus dem plot.df dataframe übernommen werden. Dies entspricht einem „left join“. Das Bild hinter diesem Link verdeutlicht das.

library(plyr)
daten<- merge(plot.df,Spielverlauf,by="Uhrzeit", all.x=TRUE) 

Wenn wir den Text zu jedem Spielereignis plotten würden, würde man von der eigentlichen Grafik nichts mehr sehen. Daher filtere ich alle Spielereignisse raus, die nicht während der 10% der stärksten Tweetminuten geschrieben wurden.

daten$Ereignis<-as.character(daten$Ereignis)
daten3 <- daten %>% mutate(cumdist = cume_dist(desc(Freq)), 
                         top = as.character(ifelse(cumdist < 0.1, Ereignis, NA))) %>%
                         select(Uhrzeit,Freq, Ereignis, top)

Visualisieren
Endlich können wir die Daten visualisieren. Der dataframe „daten“ enthält die Spalten „Uhrzeit“, „Freq“,“Ereignis“ und „Top“. „Top“ enthält dasselbe wie „Ereignis“, allerdings nur für die 10% der Minuten mit der größten Tweetproduktion.

Grafik 1
Grafik1

ggplot(daten3,aes(Uhrzeit,Freq)) +geom_line()+ geom_area(fill="darkgreen",alpha=.8)+theme_bw()

Grafik 2
Grafik2

ggplot(daten3) + geom_bar(aes(Uhrzeit,Freq),stat="identity",colour="darkgreen") + geom_text(aes(Uhrzeit,Freq,label=top),size=4)+ theme_bw()

Grafik 3
Grafik3

xmin<-as.POSIXct(strptime("2014-12-13 15:30:00", "%Y-%m-%d %H:%M:%S"))
xmax<-as.POSIXct(strptime("2014-12-13 17:02:00", "%Y-%m-%d %H:%M:%S"))
Halbzeit<-as.POSIXct(strptime("2014-12-13 16:15:00", "%Y-%m-%d %H:%M:%S"))
ggplot(daten3,aes(Uhrzeit,Freq))+geom_rect(data=NULL,aes(xmin=xmin,xmax=xmax,ymin=0,ymax=Inf),alpha=0.9,fill="lightgrey")+geom_rect(data=NULL,aes(xmin=Halbzeit,xmax=Halbzeit,ymin=0,ymax=Inf),fill="black")+geom_line(colour="black") + geom_area(fill="darkgreen",alpha=.8) +theme_bw()