8 Daten laden, modifizieren und speichern
In diesem Kapitel sehen wir uns grundlegende Arbeitsschritte und Funktionen des Datenhandlings: das Einlesen von Daten, einfaches Modifizieren von Datensätzen und das Abspeichern der Ergebnisse. Für all diese Schritte arbeiten wir mit Funktionen aus dem Tidyverse – falls noch nicht geschehen, sollten Sie das Package jetzt also installieren.
Und dann laden wir das Package zu Beginn unseres Auswertungsskripts:
8.1 Daten laden
Wir sprechen von lokalen Daten, wenn wir diese in Form einer Datei auf unserer Festplatte gespeichert haben. Externe Daten liegen beispielsweise auf Webservern oder sind in Packages enthalten. Zunächst laden wir nur lokale Daten.
8.1.1 CSV-Dateien einlesen
Wenn Sie den Schritten in Kapitel 6.2 gefolgt sind, haben Sie ein R-Projektverzeichnis auf des Festplatte. Auf Moodle finden Sie den Datensatz facebook_europawahl.csv
, der Informatationen zu Facebook-Posts der deutschen Parteien im Vorfeld der Europawahl 2019 enthält.12 Speichern Sie diesen Datensatz in Ihrem Projektverzeichnis ab – im Beispiel liegt der Datensatz im Unterordner data
.
Funktionen zum Einlesen von Daten folgen im Tidyverse dem Namensschema read_
, wobei nach dem Unterstrich der Dateityp folgt. Für CSV-Dateien sind zwei Funktionen relevant:
read_csv()
liest CSV-Dateien, die ein Komma,
als Spalten- und einen Punkt.
als Dezimaltrennzeichen verwendenread_csv2()
liest CSV-Dateien, die ein Semikolon;
als Spalten- und das Komma,
als Dezimaltrennzeichen verwenden
Bei beiden Funktionen handelt es sich um spezifische Varianten der Funktion read_delim()
, bei der Trennzeichen etc. einzeln definiert werden können. In der Regel sollten aber die beiden oben genannten Funktionen ausreichen. Im Zweifelsfall können CSV-Dateien durch anklicken im Files-Bereich in RStudio geöffnet werden, sodass ersichtlich wird, wie diese aufgebaut sind und welches Trennzeichen verwendet wird.
Alle Funktionen aus der read_
-Familie benötigen als erstes (und oft auch einziges) Argument den Dateipfad (relativ zum Arbeitsverzeichnis) als Textobjekt. Da unser Datensatz im Unterordner data
liegt, lautet der gesamte Dateipfad also "data/facebook_europawahl.csv"
. Natürlich sollten wir das Resultat der Funktion einem treffend benannten Objekt zuweisen.13
##
## -- Column specification ----------------------------------------------------------------------------------------------------------------------------------------
## cols(
## id = col_double(),
## URL = col_character(),
## party = col_character(),
## timestamp = col_datetime(format = ""),
## type = col_character(),
## message = col_character(),
## link = col_character(),
## comments_count = col_double(),
## shares_count = col_double(),
## reactions_count = col_double(),
## like_count = col_double(),
## love_count = col_double(),
## wow_count = col_double(),
## haha_count = col_double(),
## sad_count = col_double(),
## angry_count = col_double()
## )
Die Funktion teilt uns direkt mit, welche Objekttypen für welche Variable verwendet wurden, sodass wir hier auch direkt sehen können, ob z. B. eine numerische Variable als Text eingelesen wurde. Zudem ist der eingelesene Datensatz direkt ein Tibble, wir müssen also nicht mehr durch as_tibble()
konvertieren.14
Schauen wir uns unseren gerade geladenen Datensatz einmal an – da unser Datensatz als Tibble vorliegt, erhalten wir die wichtigsten Informationen zu Struktur und einen Einblick in die Daten direkt in Konsole, wenn wir das Datensatz-Objekt aufrufen:
## # A tibble: 902 x 16
## id URL party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <dbl> <chr> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 http~ oedp~ 2019-04-28 09:00:00 video "Guido~ http~ 0 4 9 9 0 0 0 0
## 2 2 http~ tier~ 2019-04-28 13:57:00 photo "Aus u~ http~ 17 130 395 354 23 3 11 2
## 3 3 http~ B90D~ 2019-04-28 06:00:01 video "Beim ~ http~ 70 28 215 174 14 3 16 0
## 4 4 http~ FDP 2019-04-28 11:49:59 photo "Unser~ http~ 16 9 262 254 7 0 1 0
## 5 5 http~ tier~ 2019-04-28 08:24:15 link "Eine ~ http~ 6 46 145 129 14 2 0 0
## 6 6 http~ CDU 2019-04-28 09:12:19 video "Freih~ http~ 239 136 398 292 8 0 58 2
## 7 7 http~ SPD 2019-04-28 13:06:09 photo "Katar~ http~ 180 54 699 576 34 4 79 1
## 8 8 http~ Pira~ 2019-04-28 17:36:30 video "Unser~ http~ 0 NA 7 6 0 1 0 0
## 9 9 http~ DieP~ 2019-04-28 07:44:28 link "Der a~ http~ 35 76 612 509 49 0 54 0
## 10 10 http~ CSU 2019-04-28 08:21:00 photo "#Klar~ http~ 174 61 458 334 3 5 90 2
## # ... with 892 more rows, and 1 more variable: angry_count <dbl>
Wir haben also einen Datensatz mit 902 Zeilen bzw. Fällen – im diesen Falle also Facebook-Posts – und 16 Spalten bzw. Variablen. Darunter sind:
- eine numerische
id
, dieURL
und ein Zeitstempel (timestamp
) des Posts - Die Parteiseite
party
von der der Post abgesetzt wurde - Der Typ (
type
) des Posts (Video, Photo, Link oder Status) - Der Text (
message
) des Posts und ein etwaiger enthaltenerlink
- Die Anzahl verschiedener Facebook-Metriken, darunter Kommentare, Shares sowie Reactions gesamt und getrennt in einzelne Typen, allesamt auf
_count
endend
8.1.2 Andere Dateiformate
Andere Dateiformate funktionieren analog – in der Regel reicht es, die korrekte Funktion zu verwenden und den Dateipfad anzugeben. Allerdings müssen für proprietäre Dateiformate erst die – mit dem Tidyverse bereits installierten – Packages geladen werden:
- das Paket
readxl
bietet Funktionen zum Import von Excel-Dateien, z. B.readxl::read_xlsx()
- das Paket
haven
deckt den Import von Datensätzen aus anderer Statistiksoftware (SAS, Stata, SPSS) ab, z. B.haven::read_sav()
für SPSS-Datensätze
8.2 Daten modifizieren
Zur Datenmodifikation betrachten wir sechs zentrale Funktionen (+ einige zugehörige Hilfsfunktionen bzw. Variationen davon), die das Tidyverse – genauer gesagt das Teilpaket dplyr
15 – zur Verfügung stellt:
select()
zum Auswählen von Variablen (spaltenweise)filter()
zum Filtern von Variablen (zeilenweise)arrange()
zum Sortieren des Datensatzesmutate()
zum Erzeugen neuer Variablensummarize()
zum Zusammenfassen von Variablengroup_by
zum Gruppieren von Variablen
Alle Funktionen haben dabei gemeinsam (und das trifft auf nahezu alle Funktionen des Tidyverse zu), dass das erste Argument der Datensatz selbst (als Tibble) ist und auch das Resultat der Funktion wiederum ein Tibble ist.
8.2.1 Variablen spaltenweise auswählen mit select()
Mit select()
können wir bestimmte Spalten eines Datensatzes auswählen. Hierzu übergeben wir nach dem Datensatz einfach alle Variablen, die wir benötigen, direkt als Objektnamen – in unserem Beispiel id
, URL
, party
usw. – ohne Anführungszeichen durch Kommas getrennt:16
## # A tibble: 902 x 3
## id party timestamp
## <dbl> <chr> <dttm>
## 1 1 oedp.de 2019-04-28 09:00:00
## 2 2 tierschutzpartei 2019-04-28 13:57:00
## 3 3 B90DieGruenen 2019-04-28 06:00:01
## 4 4 FDP 2019-04-28 11:49:59
## 5 5 tierschutzpartei 2019-04-28 08:24:15
## 6 6 CDU 2019-04-28 09:12:19
## 7 7 SPD 2019-04-28 13:06:09
## 8 8 Piratenpartei 2019-04-28 17:36:30
## 9 9 DiePARTEI 2019-04-28 07:44:28
## 10 10 CSU 2019-04-28 08:21:00
## # ... with 892 more rows
Durch ein vorangstelltes -
werden Variablen ausgeschlossen:
## # A tibble: 902 x 14
## party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count angry_count
## <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 oedp.~ 2019-04-28 09:00:00 video "Guido ~ https~ 0 4 9 9 0 0 0 0 0
## 2 tiers~ 2019-04-28 13:57:00 photo "Aus un~ https~ 17 130 395 354 23 3 11 2 2
## 3 B90Di~ 2019-04-28 06:00:01 video "Beim W~ https~ 70 28 215 174 14 3 16 0 8
## 4 FDP 2019-04-28 11:49:59 photo "Unser ~ https~ 16 9 262 254 7 0 1 0 0
## 5 tiers~ 2019-04-28 08:24:15 link "Eine n~ https~ 6 46 145 129 14 2 0 0 0
## 6 CDU 2019-04-28 09:12:19 video "Freihe~ https~ 239 136 398 292 8 0 58 2 38
## 7 SPD 2019-04-28 13:06:09 photo "Katari~ https~ 180 54 699 576 34 4 79 1 5
## 8 Pirat~ 2019-04-28 17:36:30 video "Unser ~ https~ 0 NA 7 6 0 1 0 0 0
## 9 DiePA~ 2019-04-28 07:44:28 link "Der ab~ https~ 35 76 612 509 49 0 54 0 0
## 10 CSU 2019-04-28 08:21:00 photo "#Klart~ https~ 174 61 458 334 3 5 90 2 24
## # ... with 892 more rows
Durch ein vorangestelltes neuer_objektname =
können wir Variablen beim Auswählen auch direkt umbenennen:
# Benenne party und message beim Auswählen um in partei respektive inhalt
select(df_fb_eu, partei = party, inhalt = message)
## # A tibble: 902 x 2
## partei inhalt
## <chr> <chr>
## 1 oedp.de "Guido #Klamt aus #Ludwigsburg, Listenplatz 5 auf der Kandidatenliste der #ÖDP zur #Europawahl, stellt sich und seine Ziele in diesem Video ~
## 2 tierschutzpart~ "Aus unserem Europawahlprogramm, Kapitel 7: Gesundheits- und Sozialpolitik: Bezahlbarer Wohnraum ist wesentliches Element sozialer Politik.~
## 3 B90DieGruenen "Beim Wahlkampf-Camp in Berlin waren gestern hunderte Freiwillige, die sich im Tür-zu-Tür-Wahlkampf, beim Mobilisieren von Freiwilligen oder~
## 4 FDP "Unser neuer Bundesvorstand \U0001f389\U0001f38a\U0001f388 #ChancenNutzen \U0001f680#BPT19"
## 5 tierschutzpart~ "Eine neue Studie der Universität Oxford zeigt, dass eine vegane Ernährung der wahrscheinlich größte Hebel ist, um den eigenen ökologischen ~
## 6 CDU "Freiheit ist nicht selbstverständlich. #UnserEuropa steht für freiheitliche Werte, Vertrauen und gute Partnerschaften. Mehr dazu in unserem~
## 7 SPD "Katarina Barley sagt: Eine weitere Koalition mit der #EVP will ich nicht - wir sagen: gut so! Wir wollen ein soziales #Europa. Ein Europa, ~
## 8 Piratenpartei "Unser Spitzenkandidat Patrick Breyer mit einem Update zum EU19 Workshop in Koblenz:"
## 9 DiePARTEI "Der absolut härteste „Martin-Sonneborn-Moment“ kommt für Watson.de nach der Machtübernahme... Smiley!"
## 10 CSU "#Klartext von Bayerns Ministerpräsident und CSU-Chef Markus Söder in der \"Welt am Sonntag\": Ohne schwarz zu malen: Die Wirtschaft wird l~
## # ... with 892 more rows
Um Variablen basierend auf Namensbestandteilen auszuwählen, sind einige Hilfsfunktionen - z. B. starts_with()
, ends_with()
und contains()
- verfügbar. Da in unserem Beispiel alle Facebook-Metriken auf _count
enden, können wir diese gesammelt mit ends_with("count")
auswählen:
## # A tibble: 902 x 10
## party comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count angry_count
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 oedp.de 0 4 9 9 0 0 0 0 0
## 2 tierschutzpartei 17 130 395 354 23 3 11 2 2
## 3 B90DieGruenen 70 28 215 174 14 3 16 0 8
## 4 FDP 16 9 262 254 7 0 1 0 0
## 5 tierschutzpartei 6 46 145 129 14 2 0 0 0
## 6 CDU 239 136 398 292 8 0 58 2 38
## 7 SPD 180 54 699 576 34 4 79 1 5
## 8 Piratenpartei 0 NA 7 6 0 1 0 0 0
## 9 DiePARTEI 35 76 612 509 49 0 54 0 0
## 10 CSU 174 61 458 334 3 5 90 2 24
## # ... with 892 more rows
Schließlich kann die Hilfsfunktion everything()
(ohne Argumente) genutzt werden, um sämtliche nicht zuvor angegebenen Variablen auswählen – das ist hilfreich, wenn nur bestimmte Variablen z. B. umbenannt oder an den Anfang des Datensatzes gestellt werden sollen, aber man nicht alle anderen Variablen von Hand tippen möchte:
# Stelle party umbenannt in Partei an den Anfang und hänge alle verbleibenden Variablen an
select(df_fb_eu, Partei = party, everything())
## # A tibble: 902 x 16
## Partei id URL timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <chr> <dbl> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 oedp.~ 1 http~ 2019-04-28 09:00:00 video "Guido~ http~ 0 4 9 9 0 0 0 0
## 2 tiers~ 2 http~ 2019-04-28 13:57:00 photo "Aus u~ http~ 17 130 395 354 23 3 11 2
## 3 B90Di~ 3 http~ 2019-04-28 06:00:01 video "Beim ~ http~ 70 28 215 174 14 3 16 0
## 4 FDP 4 http~ 2019-04-28 11:49:59 photo "Unser~ http~ 16 9 262 254 7 0 1 0
## 5 tiers~ 5 http~ 2019-04-28 08:24:15 link "Eine ~ http~ 6 46 145 129 14 2 0 0
## 6 CDU 6 http~ 2019-04-28 09:12:19 video "Freih~ http~ 239 136 398 292 8 0 58 2
## 7 SPD 7 http~ 2019-04-28 13:06:09 photo "Katar~ http~ 180 54 699 576 34 4 79 1
## 8 Pirat~ 8 http~ 2019-04-28 17:36:30 video "Unser~ http~ 0 NA 7 6 0 1 0 0
## 9 DiePA~ 9 http~ 2019-04-28 07:44:28 link "Der a~ http~ 35 76 612 509 49 0 54 0
## 10 CSU 10 http~ 2019-04-28 08:21:00 photo "#Klar~ http~ 174 61 458 334 3 5 90 2
## # ... with 892 more rows, and 1 more variable: angry_count <dbl>
8.2.2 Variablen zeilenweise filtern mit filter()
Um nur bestimmte Zeilen auswählen, können wir mittels filter()
eine oder mehrere Bedingungen übergeben, die analog zu den if
-Bedingungen in Kapitel 4.1 angegeben werden. Zuerst wird erneut der Datensatz übergeben:
# Wähle alle Facebookposts mit mindestens einem Kommentar
filter(df_fb_eu, comments_count > 0)
# Achten Sie in der Ausgabe auf die veränderte Zeilenanzahl
## # A tibble: 832 x 16
## id URL party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <dbl> <chr> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2 http~ tier~ 2019-04-28 13:57:00 photo "Aus u~ http~ 17 130 395 354 23 3 11 2
## 2 3 http~ B90D~ 2019-04-28 06:00:01 video "Beim ~ http~ 70 28 215 174 14 3 16 0
## 3 4 http~ FDP 2019-04-28 11:49:59 photo "Unser~ http~ 16 9 262 254 7 0 1 0
## 4 5 http~ tier~ 2019-04-28 08:24:15 link "Eine ~ http~ 6 46 145 129 14 2 0 0
## 5 6 http~ CDU 2019-04-28 09:12:19 video "Freih~ http~ 239 136 398 292 8 0 58 2
## 6 7 http~ SPD 2019-04-28 13:06:09 photo "Katar~ http~ 180 54 699 576 34 4 79 1
## 7 9 http~ DieP~ 2019-04-28 07:44:28 link "Der a~ http~ 35 76 612 509 49 0 54 0
## 8 10 http~ CSU 2019-04-28 08:21:00 photo "#Klar~ http~ 174 61 458 334 3 5 90 2
## 9 11 http~ DieP~ 2019-04-28 08:11:28 video "Anste~ http~ 61 312 1601 1344 77 1 179 0
## 10 12 http~ alte~ 2019-04-28 14:55:00 link "++ Ha~ http~ 1163 1499 3944 540 4 56 239 96
## # ... with 822 more rows, and 1 more variable: angry_count <dbl>
Natürlich können auch Boolesche Operatoren (!
für NICHT
, &
für UND
, |
für ODER
) verwendet werden. Mehrere Bedingungen können auch per ,
getrennt werden (UND
-Verknüpfung):
# Wähle nur Video-Posts der großen Koalition, die keine fehlenden Werte bei den Shares haben
filter(df_fb_eu, party %in% c("CDU", "CSU", "SPD"), type == "video", !is.na(shares_count))
## # A tibble: 62 x 16
## id URL party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <dbl> <chr> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6 http~ CDU 2019-04-28 09:12:19 video "Freih~ http~ 239 136 398 292 8 0 58 2
## 2 18 http~ CDU 2019-04-29 11:38:01 video "Live:~ http~ 140 17 190 131 4 2 27 1
## 3 26 http~ SPD 2019-04-29 09:02:51 video "Press~ http~ 174 67 312 239 30 1 23 2
## 4 31 http~ CDU 2019-04-29 09:58:45 video "Da st~ http~ 274 76 448 302 14 0 112 1
## 5 93 http~ CDU 2019-05-01 09:00:47 video "Heute~ http~ 305 113 513 308 17 3 131 1
## 6 122 http~ CDU 2019-05-02 18:00:01 video "Heute~ http~ 122 97 296 243 12 1 34 1
## 7 141 http~ CDU 2019-05-03 14:06:29 video "Anneg~ http~ 158 65 247 157 0 1 69 1
## 8 152 http~ SPD 2019-05-03 14:00:10 video "Jetzt~ http~ 515 114 747 572 48 2 92 2
## 9 178 http~ CSU 2019-05-05 08:30:00 video "Die E~ http~ 153 55 245 124 0 3 80 2
## 10 197 http~ CSU 2019-05-06 11:04:28 video "Press~ http~ 98 23 198 136 11 6 27 1
## # ... with 52 more rows, and 1 more variable: angry_count <dbl>
8.2.3 Daten sortieren mit arrange()
Um den Datensatz für die Ansicht umzusortieren, wird die Funktion arrange()
genutzt, die aufsteigend nach den angegebenen Variablen sortiert:
## # A tibble: 902 x 16
## id URL party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <dbl> <chr> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 3 http~ B90D~ 2019-04-28 06:00:01 video "Beim ~ http~ 70 28 215 174 14 3 16 0
## 2 13 http~ FDP 2019-04-28 06:18:18 photo "Wir w~ http~ 47 110 622 589 24 0 7 0
## 3 9 http~ DieP~ 2019-04-28 07:44:28 link "Der a~ http~ 35 76 612 509 49 0 54 0
## 4 11 http~ DieP~ 2019-04-28 08:11:28 video "Anste~ http~ 61 312 1601 1344 77 1 179 0
## 5 10 http~ CSU 2019-04-28 08:21:00 photo "#Klar~ http~ 174 61 458 334 3 5 90 2
## 6 5 http~ tier~ 2019-04-28 08:24:15 link "Eine ~ http~ 6 46 145 129 14 2 0 0
## 7 1 http~ oedp~ 2019-04-28 09:00:00 video "Guido~ http~ 0 4 9 9 0 0 0 0
## 8 6 http~ CDU 2019-04-28 09:12:19 video "Freih~ http~ 239 136 398 292 8 0 58 2
## 9 15 http~ FDP 2019-04-28 10:40:57 photo "Weil ~ http~ 14 19 226 210 12 0 4 0
## 10 4 http~ FDP 2019-04-28 11:49:59 photo "Unser~ http~ 16 9 262 254 7 0 1 0
## # ... with 892 more rows, and 1 more variable: angry_count <dbl>
Werden mehrere Variablen angegeben, wird zunächst nach der ersten Variablen, dann innerhalb der ersten Variablen nach der zweiten Variablen usw. sortiert. Soll eine Variable stattdessen absteigend sortiert werden, wird der Variablenname in die Hilfsfunktion desc()
gepackt:
# Sortiere alphabetisch aufsteigend nach Partei
# und innerhalb von Parteien absteigend nach Kommentaranzahl
arrange(df_fb_eu, party, desc(comments_count))
## # A tibble: 902 x 16
## id URL party timestamp type message link comments_count shares_count reactions_count like_count love_count wow_count haha_count sad_count
## <dbl> <chr> <chr> <dttm> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 870 http~ alte~ 2019-05-26 15:50:19 video "+++ H~ http~ 4829 804 6472 5036 1319 13 45 19
## 2 752 http~ alte~ 2019-05-23 17:24:43 video "+++ E~ http~ 3294 992 4469 3730 688 10 16 6
## 3 616 http~ alte~ 2019-05-20 09:49:00 photo "++ Me~ http~ 3100 11719 10163 3857 19 250 146 123
## 4 802 http~ alte~ 2019-05-24 17:01:12 video "+++ S~ http~ 2874 589 3164 2566 547 8 25 6
## 5 44 http~ alte~ 2019-04-30 19:02:07 video "2. Te~ http~ 2862 846 2781 2279 450 8 28 2
## 6 335 http~ alte~ 2019-05-10 17:27:58 video "+++ H~ http~ 2425 717 2695 2195 456 6 11 7
## 7 343 http~ alte~ 2019-05-11 13:12:00 photo "++ Di~ http~ 2363 5842 7225 1742 14 87 64 205
## 8 50 http~ alte~ 2019-04-30 17:18:13 video "+++ H~ http~ 2246 635 2692 2218 433 7 16 3
## 9 198 http~ alte~ 2019-05-06 09:15:00 photo "++ Tä~ http~ 1947 4037 8411 1319 11 76 33 1024
## 10 868 http~ alte~ 2019-05-26 11:19:19 photo "++ Wi~ http~ 1744 1281 11277 10662 498 14 73 8
## # ... with 892 more rows, and 1 more variable: angry_count <dbl>
8.2.4 Neue Variablen hinzufügen mit mutate()
Mit mutate()
, dem vielleicht einzigen nicht selbsterklärenden Funktionsnamen der sechs diskutierten Funktionen, können wir Datensätzen neue Variablen hinzufügen (oder alte überschreiben). Hierzu geben wir den neuen Variablennamen an, gefolgt von einem =
und der Berechnung bzw. Konstruktion der neuen Variablen. Wird als Variablenname ein schon im Datensatz bestehender Variablenname verwendet, so wird diese Variable überschrieben. Mit Kommas getrennt können auch mehrere neue Variablen erstellt werden.
# Wir erstellen eine neue Variable comments_centered,
# die die Kommentarzahl am allgemeinen Mittelwert zentriert
# indem wir von jedem Wert den Mittelwert der Kommentarzahl abziehen
# und wandeln die bestehende Variable message in Kleinschreibung
# mittels der Funktion tolower() um.
#
# Zur Darstellung werden die beiden 'mutierten' Variablen
# anschließend mit select() ausgewählt
df_mutated <- mutate(df_fb_eu,
comments_centered = comments_count - mean(comments_count, na.rm = TRUE),
message = tolower(message))
select(df_mutated, comments_centered, message)
## # A tibble: 902 x 2
## comments_centered message
## <dbl> <chr>
## 1 -159. "guido #klamt aus #ludwigsburg, listenplatz 5 auf der kandidatenliste der #ödp zur #europawahl, stellt sich und seine ziele in diesem vide~
## 2 -142. "aus unserem europawahlprogramm, kapitel 7: gesundheits- und sozialpolitik: bezahlbarer wohnraum ist wesentliches element sozialer politi~
## 3 -89.2 "beim wahlkampf-camp in berlin waren gestern hunderte freiwillige, die sich im tür-zu-tür-wahlkampf, beim mobilisieren von freiwilligen od~
## 4 -143. "unser neuer bundesvorstand \U0001f389\U0001f38a\U0001f388 #chancennutzen \U0001f680#bpt19"
## 5 -153. "eine neue studie der universität oxford zeigt, dass eine vegane ernährung der wahrscheinlich größte hebel ist, um den eigenen ökologische~
## 6 79.8 "freiheit ist nicht selbstverständlich. #unsereuropa steht für freiheitliche werte, vertrauen und gute partnerschaften. mehr dazu in unser~
## 7 20.8 "katarina barley sagt: eine weitere koalition mit der #evp will ich nicht - wir sagen: gut so! wir wollen ein soziales #europa. ein europa~
## 8 -159. "unser spitzenkandidat patrick breyer mit einem update zum eu19 workshop in koblenz:"
## 9 -124. "der absolut härteste „martin-sonneborn-moment“ kommt für watson.de nach der machtübernahme... smiley!"
## 10 14.8 "#klartext von bayerns ministerpräsident und csu-chef markus söder in der \"welt am sonntag\": ohne schwarz zu malen: die wirtschaft wird~
## # ... with 892 more rows
8.2.5 Variablen zusammenfassen mit summarize()
Mit summarize()
17 fassen wir Variablen zusammen, indem wir Funktionen auf eine Variable anwenden. Das Resultat ist ein neues Tibble, das die zusammengefassten Variablen als Spalten enthält. Die Funktionsweise ist ähnlich wie bei mutate()
:
# Mittelwert der drei zentralen Facebook-Metriken berechen
summarize(df_fb_eu,
mean_comments = mean(comments_count, na.rm = TRUE),
mean_shares = mean(shares_count, na.rm = TRUE),
mean_reactions = mean(reactions_count, na.rm = TRUE))
## # A tibble: 1 x 3
## mean_comments mean_shares mean_reactions
## <dbl> <dbl> <dbl>
## 1 159. 249. 846.
8.2.6 Variablen gruppieren mit group_by()
Mittels group_by()
können wir unseren Datensatz nach einer oder mehrerer Variablen gruppieren. Das Resultat ist erstmal ein Tibble, das nicht weiter von unserem Ausgangs-Tibble unterscheidet. Die Gruppierung wird dann jedoch bei folgenden Funktionen wie mutate()
oder summarize()
berücksichtig.
# Wir berechnen erneut die zentralen Facebook-Metriken
# mit summarize(), gruppieren aber zuvor nach Partei
grouped_df <- group_by(df_fb_eu, party)
summarize(grouped_df, mean_comments = mean(comments_count, na.rm = TRUE),
mean_shares = mean(shares_count, na.rm = TRUE),
mean_reactions = mean(reactions_count, na.rm = TRUE))
## # A tibble: 14 x 4
## party mean_comments mean_shares mean_reactions
## <chr> <dbl> <dbl> <dbl>
## 1 alternativefuerde 863. 1796. 4032.
## 2 B90DieGruenen 106. 184. 660.
## 3 CDU 349. 87.1 628.
## 4 CSU 136. 57.1 499.
## 5 DiePARTEI 60.4 160. 1343.
## 6 FamilienParteiDeutschlands 0.633 16.0 10.5
## 7 FDP 70 67.3 447.
## 8 freie.waehler.bundesvereinigung 25.3 43.1 141.
## 9 linkspartei 116. 218. 935.
## 10 oedp.de 6.58 29.5 110.
## 11 Piratenpartei 11.4 42.5 124.
## 12 SPD 220. 149. 719.
## 13 tierschutzpartei 67.4 391. 979.
## 14 VoltDeutschland 15.6 39.8 230.
Analog wird auch bei mutate()
die Gruppierung in den Berechnungen berücksichtigt. Wenden wir die oben durchgeführte Mittelwert-Zentrierung der Kommentaranzahl auf unseren gruppierten Datensatz an, wird durch die mean()
-Funktion der Mittelwert innerhalb der Gruppen (hier also der Parteien) berechnet. Im Ergebnis bekommen wir also für jeden Facebook-Post einen Wert, wie dieser von der durchschnittlichen Kommentaranzahl auf dieser Parteienseite abweicht:
mutated_df <- mutate(grouped_df,
comments_group_centered = comments_count - mean(comments_count, na.rm = TRUE))
select(mutated_df, party, comments_group_centered, comments_count)
## # A tibble: 902 x 3
## # Groups: party [14]
## party comments_group_centered comments_count
## <chr> <dbl> <dbl>
## 1 oedp.de -6.58 0
## 2 tierschutzpartei -50.4 17
## 3 B90DieGruenen -35.5 70
## 4 FDP -54 16
## 5 tierschutzpartei -61.4 6
## 6 CDU -110. 239
## 7 SPD -40.4 180
## 8 Piratenpartei -11.4 0
## 9 DiePARTEI -25.4 35
## 10 CSU 37.7 174
## # ... with 892 more rows
Wir können auch nach mehreren Variablen gruppieren:
# Wir berechnen erneut die zentralen Facebook-Metriken
# mit summarize(), gruppieren aber zuvor nach Partei UND Post-Typ
grouped_df <- group_by(df_fb_eu, party, type)
summarize(grouped_df, mean_comments = mean(comments_count, na.rm = TRUE),
mean_shares = mean(shares_count, na.rm = TRUE),
mean_reactions = mean(reactions_count, na.rm = TRUE))
## # A tibble: 46 x 5
## # Groups: party [14]
## party type mean_comments mean_shares mean_reactions
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 alternativefuerde link 826. 1341. 2953
## 2 alternativefuerde photo 860. 2466. 5269.
## 3 alternativefuerde video 875. 847. 2340.
## 4 B90DieGruenen photo 134. 183. 902.
## 5 B90DieGruenen video 77.9 184. 425.
## 6 CDU photo 394. 114. 810.
## 7 CDU video 293. 53.0 400.
## 8 CSU link 25 11 132.
## 9 CSU photo 143. 63.8 566.
## 10 CSU status 416. 112. 1106
## # ... with 36 more rows
Wir sehen hier also, dass die AfD im Mittel 826.14 Kommentare auf Links bekommt, 860.49 auf Photos usw.
Gruppierungen können (und sollten) im Anschluss mittels ungroup()
wieder entfernt werden (auch hier wird der Datensatz als Argument übergeben), um Probleme bei der weiteren Datentransformation zu vermeiden.
Eine besondere Variante von group_by()
ist rowwise()
, die den Datensatz zeilenweise gruppiert; dies ermöglicht zeilenweise Berechnungen mit Funktionen über mehrere Variablen hinweg, z. B. die Erstellung von Mittelwerts-Indizes:
# Gruppiere den Datensatz zeilenweise, um für jeden Post
# den Mittelwert der einzelnen Reactions (Like, Love etc.)
# zu berechnen
rowwise_df <- rowwise(df_fb_eu)
mutated_df <- mutate(rowwise_df,
mean_reactions = mean(c(like_count, love_count, wow_count, haha_count, sad_count, angry_count),
na.rm = TRUE))
select(mutated_df, mean_reactions)
## # A tibble: 902 x 1
## # Rowwise:
## mean_reactions
## <dbl>
## 1 1.5
## 2 65.8
## 3 35.8
## 4 43.7
## 5 24.2
## 6 66.3
## 7 116.
## 8 1.17
## 9 102
## 10 76.3
## # ... with 892 more rows
Eine Funktion, die einen häufigen Anwendungsfall von group_by()
, summarize()
und ungroup()
kombiniert, ist count()
, die die Fallzahl einer oder mehrerer Gruppierungsvariablen ausgibt. Mit dem Argument sort = TRUE
kann die Ausgabe zudem direkt absteigend nach Anzahl sortiert werden:
## # A tibble: 14 x 2
## party n
## <chr> <int>
## 1 alternativefuerde 79
## 2 B90DieGruenen 67
## 3 CDU 64
## 4 CSU 103
## 5 DiePARTEI 96
## 6 FamilienParteiDeutschlands 30
## 7 FDP 94
## 8 freie.waehler.bundesvereinigung 30
## 9 linkspartei 38
## 10 oedp.de 71
## 11 Piratenpartei 73
## 12 SPD 49
## 13 tierschutzpartei 33
## 14 VoltDeutschland 75
# Zähle Posts pro Partei und Post-Typ und sortiere absteigend nach Anzahl
count(df_fb_eu, party, type, sort = TRUE)
## # A tibble: 46 x 3
## party type n
## <chr> <chr> <int>
## 1 CSU photo 76
## 2 FDP photo 67
## 3 DiePARTEI photo 53
## 4 alternativefuerde photo 45
## 5 oedp.de photo 40
## 6 Piratenpartei photo 39
## 7 B90DieGruenen video 35
## 8 CDU photo 35
## 9 VoltDeutschland photo 35
## 10 SPD photo 33
## # ... with 36 more rows
8.3 Daten speichern
Wenn wir unsere Dateien modifiziert haben, möchten wir diese wohl auch speichern bzw. exportieren.
8.3.1 Tabellarische Daten exportieren
Analog zu den read_
-Funktionen stehen daher Exportfunktionen nach dem Schema write_
zur Verfügung. Als Argumente werden dabei der Datensatz, der gespeichert werden soll, sowie der Dateipfad der zu speichernden Datei übergeben. Haben wir durch Modifikation beispielsweise das Tibble df_modified
erstellt und möchten es in der Datei datensatz_modifiziert.csv
im Unterordner data
abspeichern, führen wir die write_csv()
-Funktion aus:
write_csv()
nutzt dabei das Komma ,
als Spalten- und einen Punkt .
als Dezimaltrennzeichen. Möchten wir stattdessen das in Deutschland gebräuchliche Format mit Semikolon ;
als Spalten- und Komma ,
als Dezimaltrennzeichen haben, verwenden wir analog zu read_csv2()
write_csv2()
.
Da Excel öfters Probleme mit dem Einlesen von CSV-Dateien hat, können, soll die Datei danach in Excel betrachtet werden, auch die Funktionen write_excel_csv()
bzw. write_excel_csv2()
verwendet werden. Dies fügt ein spezielles Zeichen hinzu, das Excel den Datenimport erleichert.
8.3.2 R-Objekte exportieren
Beim Export als CSV gehen unweigerlich auch Informationen verloren – bei unserem Datensatz beispielsweise die Objekttypen, die R den jeweiligen Variablen zugeordnet hat. Wollen wir R-Objekte daher für die zukünftige Verwendung in R abspeichern, lohnt es sich, direkt das jeweilige R-Objekt zu exportieren. Hierfür steht das Dateiformat .rds
zur Verfügung, mit dem beliebige R-Objekte – neben Datensätzen also auch Vektoren, Listen, statistische Modelle etc. - gespeichert werden können.
Die zugehörige Funktion lautet saveRDS()
und wird analog zu den write_
-Funktionen verwendet:
RDS-Dateien können dann jederzeit mit der Funktion readRDS()
wieder geladen werden.
Sollen mehrere R-Objekte exportiert werden – also beispielsweise ein Ausgangsdatensatz, ein modifizierter Arbeitsdatensatz und zugehörige statistische Modelle – kann das Dateiformat .RData
und die Funktion save()
verwendet werden. Dabei werden alle zu speichernden Objekte gefolgt von dem benannten Argument file =
, das den Dateipfad angibt, in dem Funktionsaufruf genannt:
So exportiere Objekte können dann gesammelt über die load()
-Funktion, die den Dateipfad als Argument benötigt, wieder geladen werden, was sehr praktisch ist, um direkt den gesamten Arbeitsstand wiederherzustellen.
8.4 Übungsaufgaben
Erstellen Sie für die folgenden Übungsaufgaben eine eigene Skriptdatei oder eine R-Markdown-Datei und speichern diese als ue8_nachname.R
bzw. ue8_nachname.Rmd
ab.
Laden Sie die Datei facebook_europawahl.csv
aus Moodle in Ihr Projektverzeichnis herunter und laden Sie den Datensatz in R.
Erstellen Sie einen Teildatensatz, der:
- nur Posts der aktuell im Bundestag vertretenen Parteien enthält (CDU, CSU, SPD, FDP, Linke, Grüne, AfD); Tipp: Betrachten Sie vorab die Schreibweise der Parteien (bzw. deren Facebook-Accounts)
- nur die Variablen
party
,timestamp
,type
sowie alle Facebook-Metriken enthält - eine neue Variable
total_count
enthält, in der für jeden Post die Gesamtzahl der Kommentare, Shares und Reactions angegeben ist
Speichern Sie diesen Teildatensatz sowohl als CSV- als auch als RDS-Datei.
Nutzen Sie die oben vorgestellten Funktionen, um pro Partei Mittelwert und Standardabweichung der drei Facebook-Metriken (Kommentare, Shares, Reactions) aller Posts zu berechnen, die in der Woche vor der Wahl (also nach dem 19.05.2019) erschienen sind.
Tipp: Logische Operatoren funktionieren auch mit Datums- und Zeitvariablen; Text, der wie ein Datum aussieht, wird dabei automatisch in ein Datum bzw. eine Zeitangabe konvertiert.
Vielen Dank an den Kollegen Jörg Haßler!↩︎
Häufig verwendete Objektnamen für Datensätze sind
df
unddata
, aber es schadet auch nicht, etwas spezifischere Namen zu vergeben, besonders wenn mit mehreren Datensätzen gearbeitet wird.↩︎Auch die Basisversion von R bietet Funktionen zum Einlesen von CSV-Dateien, die
read.csv()
,read.csv2()
etc. heißen. Diese erzeugen einen Dataframe und sind weniger gut für große Dateien optimiert, sodass ich empfehle, immer direkt die Tidyverse-Funktionen zu nutzen. Generell erkennen Sie Tidyverse-Varianten von Funktionen der R-Basisversion daran, dass diese einen Unterstrich anstatt eines Punkts zur Worttrennung nutzen.↩︎Wobei umstritten ist, ob man das Paket dee_plier oder deeply_ar ausspricht.↩︎
Und natürlich müssen wir das Resultat der Funktionen immer einem Objekt zuweisen, wenn wir damit weiterarbeiten wollen – zu Demonstrationszwecken reicht aber der reine Aufruf der Funktion.↩︎
Wer the King’s English bevorzugt:
summarise()
funktioniert auch.↩︎