# ***************************** # Forschungsseminar Mastertrack # Politischer Datenjournalismus # # 3. Blog Post: Alternative # # Sina Blassnig # Mai 2014 # **************************** # Voreinstellungen rm(list=ls(all=TRUE)) # Alles bisherige im Arbeitssprecher löschen setwd("/home/ddj/sina_b/") # Arbeitsordner bestimmen options(stringsAsFactors=F) # Das automatische bilden von Faktoren in Data frames verhindern set.seed(123) # Für die Reproduzierbarkeit bei Operationen mit Zufallszahlen # Packete laden library(ROAuth) # R-Anbindung für die Registrierung bei Twitter (siehe https://dev.twitter.com/docs/faq) library(twitteR) # R-Anbindung für die Datenbeschaffung bei Twitter library(ggplot2) library(grid) # Für Anpassung des Hintergrunds mit ggplot require(scales) # Für Anpassung der Skalen mit ggplot library(RColorBrewer) # Für Anpassung der Farbenschemas mit ggplot #### Datenbeschaffung über Twitter-API (mit twitteR und ROAuth) # URLs der Twitter Registrierungs-API definieren requestURL <- "https://api.twitter.com/oauth/request_token" accessURL <- "https://api.twitter.com/oauth/access_token" authURL <- "https://api.twitter.com/oauth/authorize" # Informationen zur Identifikation definieren (siehe https://dev.twitter.com) consumerKey = "xxx" secretKey = "xxx" # Alle Angaben zum Autorisierungsobjekt (OAuth) zusammenfügen cred <- OAuthFactory$new(consumerKey = consumerKey, consumerSecret = secretKey, requestURL = requestURL, accessURL = accessURL, authURL = authURL) # Den Handschlag mit der Twitter-Registrierung ausführen. cred$handshake() # Als Antwort erscheint ein Link, den man öffnen muss: den erscheinenden Code wieder in Konsole eingeben. # Registrierung vornehmen save(cred, file="twitter-cred.rdata") # OAuth in eine Datei speichern, um die Registrierung nicht jedesmal neu machen zu m\"ussen load("twitter-cred.rdata") # laden der OAuth # Neue Registrierung consumer_key = "xxx" consumer_secret = "xxx" access_token = "xxx" access_secret = "xxx" setup_twitter_oauth(consumer_key, consumer_secret, access_token, access_secret, credentials_file = "twitter-cred.rdata") # Userliste von phpMyAdmin laden library(RMySQL) # Für Anknüpfung an Datenbank v_db <- dbConnect(MySQL(), # Mit Datenbank verbinden user="xxx", password="xxx", dbname="xxx", host="xxx") on.exit(dbDisconnect(v_db)) # Userliste von Datenbank laden rs <- dbSendQuery(v_db, "SELECT * FROM `user`") user <- fetch(rs, n=-1) userID <- user$TwitterID # Liste von Twitter ID's erstellen, da Twitternames teilweise falsch/nicht vorhanden userNames <- lookupUsers(userID) # Twitternames für ID's suchen userDF <- twListToDF(userNames) # Liste in dataframe umwandeln für zusätzliche Informationen userDF <- subset(userDF, statusesCount>=1) # subset der user, die mind. einen Tweet abgesetzt haben userNames <- userDF$screenName write.csv(userDF, file = "Post3/userDF_12-5-14.csv", row.names = FALSE) # Resultate als CSV speichern # Download der Tweets für alle Politiker results <- data.frame() # Zunächst einen leeren Data frame definieren # Loop, um für jeden User die letzten 600 Tweets herunterzuladen (Aus Kapazitätsgründen und aufgrund des gewählten Zeitraums nicht 3200) # ACHTUNG: Loop dauert ca. 6-8h bei dieser Anzahl User und Tweets! for (user in userNames) { err <- try(userTimeline(user, n=600, includeRts=TRUE), silent = T) # Userabfrage testen wegen rate limit if (class(err) == "try-error") { Sys.sleep(900) # Anzahl sekunden warten, falls rate limit erreicht ist (Twitter: 15 times in 15 minutes) err <- try(userTimeline(user, n=600, includeRts=TRUE), silent = T) # Userabfrage nochmals testen, um versch. Errors aufzufangen if (class(err) == "try-error") { next # Falls nochmals ein Error auftaucht (weil Account gesperrt, keine Tweets usw.) zum nächsten User auf der Liste springen } else { tweets <- twListToDF(err) # Liste in Dataframe umwandeln results <- rbind(results, tweets) # Alles zusammenfügen } } else { tweets <- err # Wenn class(err) ≠ "try-error", dann ist err = tweets tweets <- twListToDF(tweets) # Liste in Dataframe umwandeln # Alles zusammenfügen results <- rbind(results, tweets) } } write.csv(results, file = "Post3/results_Twitter_12-5-14.csv", row.names = FALSE) # Resultate als CSV speichern # Resultate auf phpMyAdmin schreiben dbWriteTable(v_db, "tweets_new", results, row.names=F) # Tweets auf Datenbank speichern dbWriteTable(v_db, "user_twitter", userDF, row.names=F) # Twitter-User Data Frame auf Datenbank speichern # Werte von Datenbank laden, Verknüpfung der Tweets mit User-Liste, die nach Parteien codiert ist rs <- dbSendQuery(v_db, "SELECT akt.Partei, tweets.screenName, tweets.text, tweets.created, tweets.sentiment FROM `user` akt, `tweets_sentiment` tweets, `user_twitter` user WHERE akt.TwitterID = user.id AND user.screenName = tweets.screenName") werte <- fetch(rs, n=-1) werte$created <- as.POSIXct(werte$created, format='%Y-%m-%d %H:%M:%S') # Datum in R-Format für datetime umwandeln #### Analyse der Tweets mit Regular Expressions für Zuordnung zu Abstimmungsthemen library(stringr) # Für Textanalyse mit Regular Expressions # Liste mit Stichworten/Hashtags für Analyse bestimmen keywordsAbst <- c("#abst14", "#chvote", "abstimmung", "volksabstimmung", "volksinitiative" ) keywordsMEI <- c("#mei", "masseneinwanderung","personenfreizügigkeit", "einwanderung", "zuwanderung", "massenzuwanderung", "masseneinwanderungsinitiative", "abschottung", "bilaterale", "erasmus", "horizon2020", "svp-initiative", "dichtestress") keywordsGripen <- c("#gripen", "gripen", "kampfflugzeug", "militärflugzeug", "kampfjet", "kampfflieger", "luftwaffe") keywordsMilo <- c("#milo14", "#milo", "milo14", "mindestlohn", "mindestlohn-initiative","mindestlohninitiative","mindestlöhne", "lohngerechtigkeit", "lohnungerechtigkeit", "lohnkampf") keywordsPädo <- c("#pädo14", "#paedo14", "pädophileninitiative", "pädophilen-initiative", "pädo-initiative", "pädophile", "pädophilie", "pädosexuell") keywords <- c(keywordsAbst,keywordsMEI,keywordsGripen, keywordsMilo,keywordsPädo) # Funktion um Tweets in lowercase zu konvertieren + Auffangen von Errors tryTolower = function(x) { # create missing value # this is where the returned value will be y = NA # tryCatch error try_error = tryCatch(tolower(x), error = function(e) e) # if not an error if (!inherits(try_error, "error")) y = tolower(x) return(y) } # Funktion auf Tweets anwenden (Problem: NA bei Tweets mit Sonderzeichen) textlow <- sapply(werte$text, function(x) tryTolower(x)) # Loop pro Wort für Anaylse der Tweets for (word in keywords) { col <- str_detect(textlow, word) werte <- cbind(werte, col) werte <- rename(werte, c("col"= word)) # Pro Wort wird eine neue Spalte angehängt mit TRUE/FALSE } # Spalten umnennen wg. Problemen mit # werte <- rename(werte, c("#abst14"="Habst14","#chvote"="Hchvote", "#mei" = "Hmei", "#gripen"="Hgripen", "#milo14"="Hmilo14", "#milo"="Hmilo", "#pädo14"="Hpädo14", "#paedo14"="Hpaedo14")) werte <- rename(werte, c("svp-initiative"="svpinitiative")) werte <- rename(werte, c("pädophilen-initiative"="pädophilen_initiative")) werte <- rename(werte, c("mindestlohn-initiative"="mindestlohn_initiative")) werte <- rename(werte, c("pädo-initiative"="pädo_initiative")) # Tabelle umformen und aggregieren der Anzahl Nennungen pro Hashtag pro Tag werteHashtags <- werte[,c("created","Habst14", "Hchvote", "Hgripen", "Hmilo14", "Hpädo14")] Hashtags <- c("Habst14", "Hchvote", "Hgripen", "Hmilo14", "Hpädo14") for (word in Hashtags) {werteHashtags[[word]] <- werteHashtags[[word]]*1 } # TRUE/FALSE mit 1/O ersetzen werteHashtags$created <- as.Date(werteHashtags$created, format='%Y-%m-%d') # Datetime in Datum ohne Zeitangabe umwandeln # Summieren der Wortspalten, pro Tag dd.agg <- aggregate(cbind(Habst14,Hchvote,Hgripen,Hmilo14, Hpädo14) ~ created, werteHashtags, FUN = sum) dd.agg <- subset(dd.agg, created >= as.Date("2014-03-01")) write.csv(dd.agg, file = "/home/ddj/sina_b/Post3/Hashtags_agg.csv", row.names = FALSE) # als CSV abspeichern # Die Visualisierung der Tweets pro Tag pro Wort wurde dann mit Google Charts bzw. Datawrapper gemacht. #### Nur Nationalräte # Werte von Datenbank laden, Verknüpfung der Tweets mit User-Liste, die nach Parteien codiert ist sowie mit Abstimmungsergebnissen aus dem Parlament rs <- dbSendQuery(v_db, "SELECT user.Partei, user.Funktion,Parl.CouncillorName, tweets.screenName, tweets.text, tweets.created, tweets.sentiment, Parl.GripenNein, Parl.GripenJa, Parl.MEINein, Parl.MEIJa, Parl.MiLoNein, Parl.MiLoJa, Parl.PaedoNein, Parl.PaedoJa FROM `user` user, `tweets_sentiment` tweets, `user_twitter` akt, `Parl` Parl WHERE user.TwitterID = akt.id AND akt.screenName = tweets.screenName AND user.Name=Parl.CouncillorName") werteNRSR <- fetch(rs, n=-1) werteNRSR <- unique(werteNRSR) # Duplikate entfernen (für einzelne NR mussten nachträglich Tweets runtergeladen werden) # Plot aller gesendeten Tweets ab Okt. 13 werteNRSR$created <- as.POSIXct(werteNRSR$created, format='%Y-%m-%d %H:%M:%S') # Datum in R-Format für datetime umwandeln plotNRneu <- ggplot(data=subset(werteNRSR, created >= as.POSIXct('2013-10-01 00:00:00')), aes(x=created)) + # subset ab Datum geom_bar(aes(fill=Partei), binwidth=24*60*60) + # bindwidth auf einen Tag festlegen scale_x_datetime(limits=c(as.POSIXct('2013-10-01 00:00:00'), as.POSIXct('2014-05-11 23:00:00')),breaks = date_breaks("2 weeks"), labels = date_format("%d.%m")) + # x-Achse mit Datetime-Angaben anpassen scale_fill_manual(values = c("BDP"="gold", "CVP" =" orange", "SPS"= "red2", "GPS"="limegreen", "GLP"="olivedrab1","SVP" = "darkgreen", "Lega"= "darkmagenta", "FDP"="blue3")) + # Farbenschema anpassen theme(panel.grid.major = element_line("gray86"), panel.grid.minor = element_blank(), panel.background = element_blank(), plot.title = element_text(hjust = 0), axis.text=element_text(size=13), title=element_text(size=13,face="bold"), legend.text=element_text(size=13), legend.title=element_text(size=13)) + # Thema anpassen labs(list(title = "Anzahl Tweets pro Tag der Nationalräte von Oktober 2013 bis Mai 2014", x=NULL, y= NULL)) # Labels anpassen plotNRneu ## Analyse der Tweets mit Regular Expressions für Zuordnung zu Abstimmungsthemen textlow <- sapply(werteNRSR$text, function(x) tryTolower(x)) # lowercase-Funktion auf Tweets anwenden (Problem: NA bei Tweets mit Sonderzeichen) # Loop pro Wort für Anaylse der Tweets for (word in keywords) { col <- str_detect(textlow, word) werteNRSR <- cbind(werteNRSR, col) werteNRSR <- rename(werteNRSR, c("col"= word)) # Pro Wort wird eine neue Spalte angehängt mit TRUE/FALSE } # Spalten umnennen wg. Problemen mit # werteNRSR <- rename(werteNRSR, c("#abst14"="Habst14","#chvote"="Hchvote", "#mei" = "Hmei", "#gripen"="Hgripen", "#milo14"="Hmilo14", "#milo"="Hmilo", "#pädo14"="Hpädo14", "#paedo14"="Hpaedo14")) werteNRSR <- rename(werteNRSR, c("svp-initiative"="svpinitiative")) werteNRSR <- rename(werteNRSR, c("pädophilen-initiative"="pädophilen_initiative")) werteNRSR <- rename(werteNRSR, c("mindestlohn-initiative"="mindestlohn_initiative")) werteNRSR <- rename(werteNRSR, c("pädo-initiative"="pädo_initiative")) # Plot aller gesendeten Tweets pro Parlamentarier zw. März und Mai plotAct <- ggplot(data=subset(werteNRSR, created >= as.POSIXct('2014-03-01 00:00:00')), # subset ab Datum aes(x=reorder(CouncillorName,CouncillorName,function(x)-length(x)))) + # neu anordnen nach Anzahl Tweets geom_bar(aes(fill=Partei)) + # bindwidth auf einen Tag festlegen scale_fill_manual(values = c("BDP"="gold", "CVP" =" orange", "SPS"= "red2", "GPS"="limegreen", "GLP"="olivedrab1","SVP" = "darkgreen", "Lega"= "darkmagenta", "FDP"="blue3")) + # Farbenschema anpassen theme(panel.grid.major = element_line("gray86"), panel.grid.minor = element_blank(), panel.background = element_blank(), plot.title = element_text(hjust = 0), axis.text.x = element_text(angle=90, vjust=0.5, size=13), title=element_text(size=13,face="bold"), legend.text=element_text(size=13), legend.title=element_text(size=13)) + # Thema anpassen labs(list(title = "Anzahl Tweets pro Nationalrat zw. 1. März und 11. Mai 2014", x=NULL, y= NULL)) # Labels anpassen plotAct # Plot aller gesendeten Tweets pro Parlamentarier zum Thema Gripen plotActGripen <- ggplot(data=subset(NR.Gripen, created >= as.POSIXct('2014-03-01 00:00:00')), # subset ab Datum aes(x=reorder(CouncillorName,CouncillorName,function(x)-length(x)))) + # neu anordnen nach Anzahl Tweets geom_bar(aes(y = (..count..)/sum(..count..), fill=Partei)) + # bindwidth auf einen Tag festlegen scale_y_continuous(labels = percent_format())+ scale_fill_manual(values = c("BDP"="gold", "CVP" =" orange", "SPS"= "red2", "GPS"="limegreen", "GLP"="olivedrab1","SVP" = "darkgreen", "Lega"= "darkmagenta", "FDP"="blue3")) + # Farbenschema anpassen theme(plot.margin= unit(c(1, 1, 1, 5), "lines"), panel.grid.major = element_line("gray86"), panel.grid.minor = element_blank(), panel.background = element_blank(), plot.title = element_text(hjust = 0), axis.text.x = element_text(angle=45,hjust = 1, size=13), title=element_text(size=13,face="bold"), legend.text=element_text(size=13), legend.title=element_text(size=13)) + # Thema anpassen labs(list(title = "Welcher Nationalrat tweetet am meisten zum Gripen?", x=NULL, y= NULL)) # Labels anpassen plotActGripen ggsave(plotActGripen, file="Post3/PlotActGripen.jpeg") # Plot speichern # Plot aller gesendeten Tweets pro Parlamentarier zum Thema Mindestlohn plotActMilo <- ggplot(data=subset(NR.Milo, created >= as.POSIXct('2014-03-01 00:00:00')), # subset ab Datum aes(x=reorder(CouncillorName,CouncillorName,function(x)-length(x)))) + # neu anordnen nach Anzahl Tweets geom_bar(aes(y = (..count..)/sum(..count..), fill=Partei)) + # bindwidth auf einen Tag festlegen scale_y_continuous(labels = percent_format()) + scale_fill_manual(values = c("BDP"="gold", "CVP" =" orange", "SPS"= "red2", "GPS"="limegreen", "GLP"="olivedrab1","SVP" = "darkgreen", "Lega"= "darkmagenta", "FDP"="blue3")) + # Farbenschema anpassen theme(plot.margin= unit(c(1, 1, 1, 6), "lines"), panel.grid.major = element_line("gray86"), panel.grid.minor = element_blank(), panel.background = element_blank(), plot.title = element_text(hjust = 0), axis.text.x = element_text(angle=45,hjust = 1, size=13), title=element_text(size=13,face="bold"), legend.text=element_text(size=13), legend.title=element_text(size=13)) + # Thema anpassen labs(list(title = "Welcher Nationalrat tweetet am meisten zum Mindestlohn?", x=NULL, y= NULL)) # Labels anpassen plotActMilo ggsave(plotActMilo, file="Post3/PlotActMilo.jpeg") # Plot speichern # Plot aller gesendeten Tweets pro Parlamentarier zur Pädophileninitiative plotActPädo <- ggplot(data=subset(NR.Pädo, created >= as.POSIXct('2014-03-01 00:00:00')), # subset ab Datum aes(x=reorder(CouncillorName,CouncillorName,function(x)-length(x)))) + # neu anordnen nach Anzahl Tweets geom_bar(aes(y = (..count..)/sum(..count..), fill=Partei)) + # bindwidth auf einen Tag festlegen scale_y_continuous(labels = percent_format()) + scale_fill_manual(values = c("BDP"="gold", "CVP" =" orange", "SPS"= "red2", "GPS"="limegreen", "GLP"="olivedrab1","SVP" = "darkgreen", "Lega"= "darkmagenta", "FDP"="blue3")) + # Farbenschema anpassen theme(plot.margin= unit(c(1, 1, 1, 5), "lines"), panel.grid.major = element_line("gray86"), panel.grid.minor = element_blank(), panel.background = element_blank(), plot.title = element_text(hjust = 0), axis.text.x = element_text(angle=45,hjust = 1, size=13), title=element_text(size=13,face="bold"), legend.text=element_text(size=13), legend.title=element_text(size=13)) + # Thema anpassen labs(list(title = "Welcher Nationalrat tweetet am meisten zur Pädophileninitiative?", x=NULL, y= NULL)) # Labels anpassen plotActPädo ggsave(plotActPädo, file="Post3/PlotActPädo.jpeg") # Plot speichern ### Visualisierung der Gegner und Befürworter im Zeitverlauf # Subsets zu einzelnen Vorlagen NR.MEI <- subset(werteNRSR, Habst14==T|Hchvote==T|Hmei==T|masseneinwanderung==T| personenfreizügigkeit==T|einwanderung==T|zuwanderung==T|massenzuwanderung==T|masseneinwanderungsinitiative ==T|abschottung==T|bilaterale==T|erasmus==T|horizon2020==T|svpinitiative==T|dichtestress==T) NR.Gripen <- subset(werteNRSR, Hgripen==T|gripen==T|kampfflugzeug==T|militärflugzeug==T|kampfjet==T|kampfflieger==T|luftwaffe==T) NR.Milo <- subset(werteNRSR, Hmilo14==T|Hmilo==T|milo14==T|mindestlohn==T|mindestlohn_initiative==T|mindestlohninitiative==T|mindestlöhne==T|lohngerechtigkeit==T|lohnungerechtigkeit==T| lohnkampf==T) NR.Pädo <- subset(werteNRSR, Hpädo14==T| Hpaedo14==T|pädophileninitiative==T|pädophilen_initiative==T|pädo_initiative==T|pädophile==T|pädophilie==T|pädosexuell==T) NR.Abst <- subset(werteNRSR, Habst14==T|Hchvote==T|abstimmung==T|volksabstimmung==T|volksinitiative==T) NR.Mai18 <- subset(werteNRSR,Habst14==T|Hchvote==T|Hgripen==T|gripen==T|kampfflugzeug==T|militärflugzeug==T|kampfjet==T|kampfflieger==T|luftwaffe==T| Hmilo14==T|Hmilo==T|milo14==T|mindestlohn==T|mindestlohn_initiative==T|mindestlohninitiative==T|mindestlöhne==T|lohngerechtigkeit==T|lohnungerechtigkeit==T| lohnkampf==T|Hpädo14==T| Hpaedo14==T|pädophileninitiative==T|pädophilen_initiative==T|pädo_initiative==T|pädophile==T|pädophilie==T|pädosexuell==T ) # Umformen der Subsets NR.Gripen <- NR.Gripen[,c(1:9)] NR.Gripen$Gripen = 1 NR.Gripen$Milo = 0 NR.Gripen$Paedo = 0 NR.Gripen <- rename(NR.Gripen, c("GripenJa"="Ja", "GripenNein"="Nein")) NR.Milo <- NR.Milo[,c(1:7, 12, 13)] NR.Milo$Gripen = 0 NR.Milo$Milo = 1 NR.Milo$Paedo = 0 NR.Milo <- rename(NR.Milo, c("MiLoJa"="Ja", "MiLoNein"="Nein")) NR.Pädo <- NR.Pädo[,c(1:7, 14, 15)] NR.Pädo$Gripen = 0 NR.Pädo$Milo = 0 NR.Pädo$Paedo = 1 NR.Pädo <- rename(NR.Pädo, c("PaedoJa"="Ja", "PaedoNein"="Nein")) NR <- rbind(NR.Gripen, NR.Milo, NR.Pädo) # wieder zusammenfügen # Subsets nach Befürworter und Gegner von Vorlagen Gegner <- subset(NR, Nein==1) Gegner$created <- as.Date(Gegner$created, format='%Y-%m-%d') Gegner <- aggregate(cbind(Gripen,Milo,Paedo) ~ created, Gegner, FUN = sum) Gegner <- subset(Gegner, created >= as.Date("2014-03-01")) write.csv(Gegner, file = "/home/ddj/sina_b/Post3/Gegner.csv", row.names = FALSE) # als CSV abspeichern Befürworter <- subset(NR, Ja==1) Befürworter$created <- as.Date(Befürworter$created, format='%Y-%m-%d') Befürworter <- aggregate(cbind(Gripen,Milo,Paedo) ~ created, Befürworter, FUN = sum) Befürworter <- subset(Befürworter, created >= as.Date("2014-03-01")) write.csv(Befürworter, file = "/home/ddj/sina_b/Post3/Befürworter.csv", row.names = FALSE) # als CSV abspeichern # Die Visualisierung wurde dann mit Google Charts bzw. Datawrapper gemacht. ## Tabelle umformen und aggregieren der Anzahl Nennungen pro Wort pro Tag werteNRHashtags <- werteNRSR[,c("created","Habst14", "Hchvote", "Hgripen", "Hmilo14", "Hpädo14")] Hashtags <- c("Habst14", "Hchvote", "Hgripen", "Hmilo14", "Hpädo14") for (word in Hashtags) {werteNRHashtags[[word]] <- werteNRHashtags[[word]]*1 } # TRUE/FALSE mit 1/O ersetzen werteNRHashtags$created <- as.Date(werteNRHashtags$created, format='%Y-%m-%d') # Datetime in Datum ohne Zeitangabe umwandeln # Summieren der Wortspalten, pro Tag dd.agg.NR <- aggregate(cbind(Habst14,Hchvote,Hgripen,Hmilo14, Hpädo14) ~ created, werteNRHashtags, FUN = sum) dd.agg.NR <- subset(dd.agg.NR, created >= as.Date("2014-03-01")) write.csv(dd.agg.NR, file = "/home/ddj/sina_b/Post3/Hashtags_agg_NR.csv", row.names = FALSE) # als CSV abspeichern # Die Visualisierung der Tweets pro Tag pro Hashtag wurde dann mit Google Charts bzw. Datawrapper gemacht.