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() 

Twitter-mining mit R – Teil 5 – Followermap

Mit nur wenigen Zeilen R-Code ist es möglich, die globale Verteilung der Follower eines Accounts zu Visualisieren. Dies geht besonders einfach, wenn man eine Funktion zur Hilfe nimmt, die auf folgender Homepage veröffentlicht wurde http://simplystatistics.org/2011/12/21/an-r-function-to-map-your-twitter-followers/.

Zwei Dinge sollten bedacht werden:

  • Viele Twitter-User tragen aus Datenschutzgründen keinen, oder einen falschen Standort ein
  • Visualisierungen wie diese Followermap bilden häufig nur die variiernde Bevölkerungsdichte bzw. die variierende Dichte des Internetzugangs über die Weltregionen ab

Randell Munroe von XKCD bringt das Problem mit einem Comic treffend auf den Punkt:
XKCD.com

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(twitteR)
# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)

#Followermap-Funktion laden
source("http://biostat.jhsph.edu/~jleek/code/twitterMap.R")

#Followermap-Funktion ausführen (erstellt ein PDF)
twitterMap("OfficialSanta",nMax=2000,plotType="followers",userLocation="Anchorage",fileName="Weihnachtsmann-Follower.pdf")

Das Ausführen dieser Funktion kann etwas länger dauern (durchaus einige Minuten). Es müssen schließlich die Koordinaten von bis zu 2000 Accounts abgefragt werden. Nach einiger Wartezeit erhält man eine solche Grafik:

Als Wohnsitz von Santa Claus wurde der North Pole in Alaska bei Fairbanks gewählt bzw. die nächstgrößte Stadt Anchorage.

Als Wohnsitz von Santa Claus wurde der North Pole in Alaska bei Fairbanks gewählt bzw. die nächstgrößte Stadt Anchorage.

Twitter-mining mit R – Teil 3 – Erweiterte Wordclouds

Im letzten Blogeintrag (Teil 2) wurden die Ergebnisse einer Twitter-Suche als Wordcloud visualisiert. Man kann das Potential von Wordclouds noch etwas weiter ausschöpfen, wenn man Wordclouds miteinander vergleicht. Das  folgende Beispiel orientiert sich an dem code von dieser Seite.

Wie immer, brauchen wir zuerst die Verbindung zwischen R und der Twitter-API.

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(twitteR)
# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)

#--- Suchabfrage: 450 tweets mit dem Hashtag #rstats ---
tweets<-searchTwitter("christmas",n=450))
tweets

Comparison Wordcloud
Als Beispiel werde ich die Tweets der Bundestagsfraktionen von CDU, SPD und den Grünen abgreifen und jeweils einen Textkorpus erstellen, der dann zusammengefügt in einer einzelnen Wordcloud visualisiert wird. Jede Partei wird eine Wordcloud ihrer Farbe erhalten. Dadurch kann vielleicht ein Einblick erhalten werden, über welche inhaltlichen Schwerpunkte die Parteien jeweils am häufigsten twittern.

#Twitter Accounts der Bundestagsfraktionen CDU/CSU, SPD, Gruene(wordcloud)

library(tm)
library(wordcloud)
cdu.tweets<-userTimeline("CDUCSUBT",n=500)
spd.tweets<-userTimeline("SPDBT",n=500)
gruene.tweets<-userTimeline("GrueneBundestag",n=500)

# get text
cdu_txt = sapply(cdu.tweets, function(x) x$getText())
spd_txt = sapply(spd.tweets, function(x) x$getText())
gruene_txt = sapply(gruene.tweets, function(x) x$getText())

#text cleanings
clean.text = function(x)
{
# tolower
x = tolower(x)
# remove rt
x = gsub("rt", "", x)
# remove at
x = gsub("@\\w+", "", x)
# remove punctuation
x = gsub("[[:punct:]]", "", x)
# remove numbers
x = gsub("[[:digit:]]", "", x)
# remove links http
x = gsub("http\\w+", "", x)
# remove tabs
x = gsub("[ |\t]{2,}", "", x)
# remove blank spaces at the beginning
x = gsub("^ ", "", x)
# remove blank spaces at the end
x = gsub(" $", "", x)
return(x)
}

cdu_clean = clean.text(cdu_txt)
spd_clean = clean.text(spd_txt)
gruene_clean = clean.text(gruene_txt)

#join text
cdu = paste(cdu_clean, collapse=" ")
spd = paste(spd_clean, collapse=" ")
gruen = paste(gruene_clean, collapse=" ")
# put everything in a single vector
all = c(cdu, spd, gruen)

Die Daten sind nun vorbereitet und können geplottet werden.
An dem Punkt, an dem die TermDocumentMatrix erstellt wird, werden mittels „stopwords(„german“) häufig genannte deutsche Wörter („der“, „die“, „das“, „und“…) von der Auswertung ausgeklammert.

#WORDCLOUD COMPARISON
# create corpus
corpus = Corpus(VectorSource(all))
# create term-document matrix 
tdm = TermDocumentMatrix(corpus, control = list(removePunctuation = TRUE, stopwords = stopwords("german"),removeNumbers = TRUE))
# convert as matrix
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("CDU", "SPD", "Grüne")

#PLOTTING
# comparison cloud
comparison.cloud(tdm, random.order=FALSE, colors = c("black", "red", "green"),max.words=100)

Die untenstehende Grafik ist das Ergebnis. Während bei der CDU/CSU-Fraktion häufig der Fraktionsvorsitzende Kauder erwähnt wird sowie der Nachruf auf „Schockenhoff“ viel Raum einnimmt, werden auch „Europa“, „Erbschaftssteuer“, „Ukraine“ und „Europa“ genannt. Auch bei den Grünen wird der Fraktionsvorsitzende (Hofreiter) am häufigsten genannt. Ansonsten ist die Klimakonferenz in Lima scheinbar ein Hauptthema. Der Twitteraccount der SPD-Bundestagsfraktion scheint ein breites Themenrepertoire zu haben, da sich keine einzelnen thematischen Schwerpunkte abheben. Jedoch zeigt sich wie bei der CDU und den Grünen, dass die Fraktionsvorsitzende Lambrecht häufig genannt wird.

Comparison Wordcloud. Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion (je 450 tweets)

Comparison Wordcloud. Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion (je 450 tweets)

Commonality Cloud
Eine Alternative zur Comparison Wordcloud ist die Commonality Cloud. Sie wird ebenfalls mit der wordcloud-library erstellt.

# commonality cloud
commonality.cloud(tdm, random.order=FALSE, colors = brewer.pal(8, "Dark2"),title.size=1.5)

Leider kristalliert sich in der Commonality Cloud kaum Übereinstimmung ab. Eventuell sollten mehr Tweets abgegriffen und häufige Wörter wie „die, der, das“ aus der Analyse ausgeschlossen werden.

Jeweils 450 letzte Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion als Commonality Cloud

Jeweils 450 letzte Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion als Commonality Cloud

Im nächsten Blogeintrag (Teil 4) zeige ich, wie eine einfache Sentiment Analysis mit Tweets durchgeführt werden kann.