Analysieren von Smartphone-Nutzungsdaten mit R - Teil 2: Langformat
By Clara Vetter on Aug 10, 2022
Im ersten Teil unserer Tutorial-Reihe haben wir euch gezeigt, wie ihr ein besseres Verständnis für die am häufigsten genutzten Apps nach Nutzungsdauer erhaltet und wie ihr Änderungen der App-Nutzungsdauer im Laufe der Zeit darstellen könnt. Hier werden wir uns eure Daten im Langformat ansehen, einer anderen Art der Datenaggregation, die ihr vom Murmuras Researcher Portal herunterladen könnt. Wie ihr feststellen werdet, enthält dieses Format viele verschiedene Informationstypen. Keine Sorge, wir werden sie Schritt für Schritt durchgehen.
Einrichten von RStudio und Vorbereiten der Daten
Wir verwenden RStudio als Arbeitsumgebung. Wenn ihr in RStudio mitarbeiten möchtet, ladet einfach diese Zip-Datei mit der Rmd-Datei und den Übungsdatensätzen herunter, kopiert sie in Ihr Projektverzeichnis und öffnet sie in RStudio.
library(tidyverse)
library(lubridate)
Download der Daten
Beim Herunterladen von Daten aus dem Murmuras Researcher Portal habt ihr die Wahl zwischen verschiedenen Formaten und unterschiedlichen Granularitäten. Für die Zwecke dieses Tutorials, wählt long format und daily granularity. Dies bedeutet, dass die Nutzungszeiten pro Tag zusammengefasst werden. Es ist auch möglich, pro Stunde zusammenzufassen. Einzelne App-Sitzungen werden jeweils für jede App summiert.
Arbeitsverzeichnis festlegen
Wir müssen R mitteilen, woher die Daten geladen werden. Setzt das Arbeitsverzeichnis auf euren Datenordner: Setzt im grauen Codeblock unter diesem Abschnitt den Pfad hinter dem ‘<-’ in Anführungszeichen auf das Verzeichnis, in dem ihr die Daten auf eurem Rechner gespeichert habt. Klickt Sie anschließend auf den kleinen grünen Pfeil in der oberen rechten Ecke des grauen Codeblocks.
path <- "/Users/Shared/r-intro-part2-data/"
knitr::opts_knit$set(root.dir = path)
Daten laden
Nun laden wir die Daten in unsere R-Umgebung. Die Umgebung enthält alle Variablen, die wir definieren. Ihr seht diese in der rechten Seite des RStudio-Fensters. Kopiert den Namen eures Datensatzes zwischen die Klammern in der Funktion read.csv() im untenstehenden Codeabschnitt. Drückt anschließend erneut auf den grünen Pfeil.
# load data
long_format_daily <- read.csv("Testdata.csv")
# remove column with row numbers
#long_format_daily <- long_format_daily[2:ncol(long_format_daily)]
Prüft eure Daten
Es ist immer eine gute Idee, die Daten zu prüfen und zu verstehen, welche Variablen enthalten sind. Lasst uns die Daten bewerten.
{r, message = F}
# show the first few rows of the dataset
head(long_format_daily)
# show the last few rows of the dataset
tail(long_format_daily)
# show column names of the dataset
colnames(long_format_daily)
# number of participants
length(unique(long_format_daily$participant))
Wie ihr seht, enthält der Datensatz 8 Spalten: 1. id: jeder Eintrag erhält eine eindeutige ID. 2. participant: jeder Teilnehmer erhält eine eindeutige Kennung. 3. granularity: die Granularität der Datenaggregation, die ihr beim Herunterladen der Daten gewählt habt. Diese Spalte hat in jeder Zeile den Wert “daily”. Sie dient hauptsächlich dazu, euch daran zu erinnern, welche Granularität ihr gewählt habt. Dies kann nützlich sein, wenn ihr die Daten sowohl in täglicher als auch in stündlicher Granularität heruntergeladen habt. 4. aggregation_type: Dies ist eine wichtige Variable, auf die wir im nächsten Abschnitt näher eingehen werden. Sie kategorisiert die vielen verschiedenen Arten von Informationen, die in diesem Datensatz enthalten sind. 5. time: das Datum 6. key: Falls zutreffend, ist dies der Name der App, wie er im App Store verwendet wird. Im Gegensatz zu app_name ist dieser in jeder Sprache gleich und sollte daher zur Gruppierung von Apps verwendet werden. Bei einigen Aggregationsarten ist das nicht anwendbar, z. B. bei phone_usage. In diesen Fällen hat key den gleichen Wert wie aggregation_type. 7. app_name: der Name der Anwendung. Bei einigen Aggregationsarten ist er nicht anwendbar und daher leer (z.B. phone_usage, da es sich um eine Zusammenfassung der Gesamtnutzung aller Apps handelt). 8. value: Der Wert kann je nach Aggregationsart eine andere Bedeutung haben. Für phone usage oder app usage ist dies beispielsweise die Gesamtnutzungsdauer des Telefons oder der Apps in Sekunden pro Tag. Im Falle von phone sessions beschreibt der Wert die Anzahl an Einzelsitzungen die das Telefon an einem Tag genutzt wurde (beides bei täglicher Granularität).
Teilnehmer umbenennen
Um die nächsten Schritte leichter nachvollziehen zu können, standardisieren wir den Teilnehmer-Code auf das Format “Participant_laufende Nummer). Hinweis: Insbesondere für unseren Testdatensatz könnt ihr diesen Schritt auch überspringen, da wir den Datensatz bereits vorformatiert haben.
for (i in 1:length(unique(long_format_daily$participant))){
current_pp <- unique(long_format_daily$participant)[i]
long_format_daily<- long_format_daily %>%
mutate(participant = ifelse(participant == current_pp, paste0("Participant_",i), participant))
}
head(long_format_daily)
Die verschiedenen Aggregationsarten
Inzwischen solltet ihr bemerkt haben, dass das Langformat verschiedene Datentypen enthält, die durch die Variable aggregation_type organisiert werden. Aber was bedeutet das überhaupt und was für Datentypen sind das überhaupt? Das lässt sich leicht erklären, wenn man sich ansieht, welche Aggregationsarten es gibt. Es gibt 10 verschiedene Aggregationsarten:
unique(long_format_daily$aggregation_type)
PHONE_USAGE
Dies ist die tägliche Telefonnutzungsdauer. Berechnen wir nun die durchschnittliche tägliche Telefonnutzungsdauer für jede*n Teilnehmer*in. Zunächst müssen die Daten gefiltert werden, damit nur die Zeilen übrig bleiben, die Informationen über die Telefonnutzung enthalten. Dann gruppieren wir nach Teilnehmer*innen und fassen die Nutzungsdauer zusammen (die in der “value” Variable gespeichert ist). Wenn ihr mit der Pipelinestruktur von dplyr nicht vertraut seid, empfehle ich die leicht verständliche Dokumentation. Natürlich gibt es auch andere Möglichkeiten, diese Metriken zu berechnen, ich zeige euch hier nur eine Variante. Die Zeitdauer wird in Sekunden angegeben, aber wir können sie in Minuten umrechnen, indem wir durch 60 teilen.
mean_phone_usage <- long_format_daily %>%
# filter only the rows containing information about phone usage
filter(aggregation_type == "PHONE_USAGE") %>%
group_by(participant) %>%
summarize(mean_daily_duration = mean(value)/60) %>% # in minutes
# order from largest to lowest mean duration
arrange(desc(mean_daily_duration))
head(mean_phone_usage)
PHONE_SESSIONS_COUNT
Wir können die durchschnittliche Anzahl der Telefonsitzungen auf ähnliche Weise berechnen. Um die Sache etwas interessanter zu machen, vergleichen wir die verschiedenen Wochentage und stellen die Ergebnisse grafisch dar. Dazu müssen wir eine weitere Spalte hinzufügen, die Informationen darüber enthält, welcher Tag dem Datum entspricht. Glücklicherweise gibt es dafür Funktionen, so dass wir das nicht selbst nachschlagen müssen.
mean_phone_sessions_weekday <- long_format_daily %>%
# filter only the rows containing information about phone usage
filter(aggregation_type == "PHONE_SESSIONS_COUNT") %>%
# add column with corresponding weekday
mutate(weekday = weekdays(date(time))) %>%
group_by(weekday) %>%
summarize(mean_daily_sessions = mean(value))
# order from Monday to Sunday
mean_phone_sessions_weekday$weekday <- factor(mean_phone_sessions_weekday$weekday,levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
mean_phone_sessions_weekday <- mean_phone_sessions_weekday %>%
arrange(weekday)
# plot the mean number of phone sessions per weekday
ggplot(data=mean_phone_sessions_weekday, aes(x=weekday, y=mean_daily_sessions)) +
geom_bar(stat="identity", fill = "steelblue")
APP_USAGE and APP_STARTS
Diese beiden Aggregationsarten sind mit der Telefonnutzung und den Telefonsitzungen vergleichbar. Der Unterschied besteht darin, dass die tägliche Dauer und die tägliche Anzahl der Sitzungen (Starts) für jede App separat gezählt werden.
Könnt ihr die durchschnittliche tägliche Gesamtnutzungsdauer für jede App über alle Teilnehmer hinweg zu berechnen? Und sind die Top-10-Apps nach Nutzungsdauer dieselben wie bei der Betrachtung der Anzahl der Starts der Apps?
Rausfinden können wir das z.B. so:
# overall daily mean app usage duration
mean_app_usage <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "APP_USAGE") %>%
# remember: if you want to group by app, use key instead of app_name
group_by(key) %>%
summarize(mean_app_usage_duration = mean(value)/60) %>% # in minutes
arrange(desc(mean_app_usage_duration))
# only the top 10 apps
top_apps_duration <- head(mean_app_usage, 10)
# plot it!
ggplot(data=top_apps_duration, aes(x=key, y=mean_app_usage_duration)) +
geom_bar(stat="identity", fill = "steelblue") +
coord_flip()
# overall daily mean number of app starts
mean_app_starts <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "APP_STARTS") %>%
# remember: if you want to group by app, use key instead of app_name
group_by(key) %>%
summarize(mean_app_starts_count = mean(value)) %>%
arrange(desc(mean_app_starts_count))
# only the top 10 apps
top_apps_starts <- head(mean_app_starts, 10)
# plot it!
ggplot(data=top_apps_starts, aes(x=key, y=mean_app_starts_count)) +
geom_bar(stat="identity", fill = "steelblue") +
coord_flip()
Vielleicht ist euch aufgefallen, dass viele Apps mit “launcher” oder “systemui” in ihrem key zu den Top-Apps gehören. Das sind in der Regel die Homescreens oder App-Launcher der Telefone und keine “echten” Apps von Interesse. Ihr solltet in Erwägung ziehen, alle diese Apps vor der Analyse auszuschließen. Dazu könnt ihr die Funktion filter() verwenden, ähnlich wie wir nach dem Aggregationstyp gefiltert haben. Das ‘!’ teilt der Funktion mit, dass wir die genannten Apps ausschließen wollen. Je nachdem, welche Apps ihr ausschließen möchtet, sollte dies in etwa so aussehen:
# exclude not relevant apps
long_format_daily_filtered <- long_format_daily %>%
filter(!key %in% c("net.oneplus.launcher",
"com.sec.android.app.launcher",
"com.android.systemui"))
Für die Zwecke dieses Tutorials haben wir diese Apps bereits vor der Analyse aus unseren Daten ausgeschlossen.
CATEGORY_USAGE and CATEGORY_STARTS
Diese beiden Aggregationsarten können in ähnlicher Weise wie app usage und app starts verwendet werden. Der Unterschied besteht darin, dass hier die tägliche Nutzungsdauer und die Starts für jede App-Kategorie, wie vom Google PlayStore angegeben, zusammengefasst werden. So wie die Variable key die App identifiziert, enthält sie in diesem Aggregationstyp nun die jeweilige Kategorie. Ihr könnt die Codeschnipsel aus dem vorherigen Abschnitt verwenden, um ähnliche Berechnungen für Kategorien zu erstellen.
Welche Kategorien haben Ihre Nutzer am häufigsten verwendet? Hinweis: Es kann vorkommen, dass die Nutzungszeiten und Starts der Kategorien in unserem Testdatensatz nicht mit den summierten Nutzungszeiten und Starts der Apps übereinstimmen, da wir nur die am häufigsten genutzten Apps in diesen Datensatz aufgenommen haben.
# overall daily mean category usage duration
mean_cat_usage <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "CATEGORY_USAGE") %>%
# remember: if you want to group by category, use key
group_by(key) %>%
summarize(mean_cat_usage_duration = mean(value)/60) %>% # in minutes
arrange(desc(mean_cat_usage_duration))
# only the top 10 apps
top_cats_duration <- head(mean_cat_usage, 10)
# plot it!
ggplot(data=top_cats_duration, aes(x=key, y=mean_cat_usage_duration)) +
geom_bar(stat="identity", fill = "steelblue") +
coord_flip()
# overall daily mean number of category starts
mean_cat_starts <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "CATEGORY_STARTS") %>%
# remember: if you want to group by category, use key
group_by(key) %>%
summarize(mean_cat_starts_count = mean(value)) %>%
arrange(desc(mean_cat_starts_count))
# only the top 10 apps
top_cats_starts <- head(mean_cat_starts, 10)
# plot it!
ggplot(data=top_cats_starts, aes(x=key, y=mean_cat_starts_count)) +
geom_bar(stat="identity", fill = "steelblue") +
coord_flip()
MSCORE
Dies ist eine Aggregationsart aus dem Menthal-Projekt. Sie wird in der Menthal-App verwendet, um euren “Stimmungswert” (“mood score”) zu ermitteln. Wenn ihr daran interessiert seid, schaut euch die Menthal-App an. Murmuras App verwendet MSCORE nicht, ihr könnt es also getrost ignorieren.
LOCATION_POINTS
Smartphone-Daten ermöglichen euch, Aufenthaltsorte von Personen zu analysieren, da Smartphones Standortinformationen als GPS-Koordinaten erheben. Der Aggregationstyp LOCATION_POINTS gibt euch die Anzahl der vom Telefon des Nutzers pro Tag gesendeten Standortpunkte an. Dies kann je nach Telefonmodell variieren. Daher sollten alle standortbezogenen Daten nur als Näherungswert betrachtet werden. Sie können euch jedoch einen Eindruck davon vermitteln, wo sich die Nutzer*innen bei der Verwendung einer bestimmten App aufhalten und wie ihr Mobilitätsverhalten aussieht.
DISTANCE_TRAVELED
Diese Aggregationsart fasst die täglich zurückgelegte Entfernung (in Metern) zusammen. Sie wird durch Summierung der Entfernung zwischen allen aufgezeichneten Standortpunkten geschätzt. Sie ist daher davon abhängig, wie viele Punkte ein bestimmtes Telefonmodell sendet. Stellen wir dar, an welchen Wochentagen unsere Teilnehmer die größte Entfernung zurücklegen:
distance <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "DISTANCE_TRAVELED") %>%
# add column with corresponding weekday
mutate(weekday = weekdays(date(time))) %>%
group_by(weekday) %>%
summarize(mean_daily_distance = mean(value))
# order from Monday to Sunday
distance$weekday <- factor(distance$weekday,levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
distance <- distance %>%
arrange(weekday)
# plot the mean number of phone sessions per weekday
ggplot(data=distance, aes(x=weekday, y=mean_daily_distance)) +
geom_bar(stat="identity", fill = "steelblue")
Es wäre interessant zu sehen, wie die Mobilitätsdaten sich zwischen Home-Office und regulärem Büro-Betrieb unterscheiden. Vielleicht eine Idee für künftige Studien? Interessant ist auch der Mobilitätseinbruch an Donnerstagen.
DIAMETER
Der Aggregationstyp diameter (Durchmesser) zeigt die größte Entfernung zwischen zwei Standortpunkten an einem Tag an. Er gibt die Größe des Gebiets an, in dem sich eine Person bewegt hat. Ich würde gerne wissen, ob wir für das Reisegebiet denselben Trend beobachten können wie für die zurückgelegte Entfernung:
diameter <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "DIAMETER") %>%
# add column with corresponding weekday
mutate(weekday = weekdays(date(time))) %>%
group_by(weekday) %>%
summarize(mean_daily_diameter = mean(value))
# order from Monday to Sunday
diameter$weekday <- factor(diameter$weekday,levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
diameter <- diameter %>%
arrange(weekday)
# plot the mean number of phone sessions per weekday
ggplot(data=diameter, aes(x=weekday, y=mean_daily_diameter)) +
geom_bar(stat="identity", fill = "steelblue")
Hinzufügen des Beschäftigungsstatus zum Entfernungsdatensatz
Zum krönenden Abschluss zeigen wir euch, wie ihr demografische Daten in eure Analysen integrieren könnt. Wir wollen vergleichen, ob es Unterschiede bei den zurückgelegten Entfernungen und den Größen der Mobilitätsgebiete bei Studierenden im Vergleich zu arbeitenden Personen gibt. Zunächst laden wir die demografischen Daten vom Murmuras Researcher Portal herunter. Als Nächstes extrahieren wir den Beschäftigungsstatus und fügen ihn als neue Variable zu unseren Entfernungs- und Durchmesser-Datensätzen hinzu. Bitte beachtet, dass euer Demographiedatensatz anders aussehen könnte als unsere Testdatei. Um den Datensatz klein zu halten, haben wir nicht alle Onboarding-Fragen aufgenommen. Außerdem müsst ihr die Fragebogendaten anhand der user_id auf die Smartphone-Daten matchen. Für unseren vereinfachten Testdatensatz reicht die oben standardisierte participant Variable.
In unserer Teststichprobe haben wir 3 Studierende (employment_status = “in education “) und zwei Berufstätige. Um repräsentativ zu sein, bräuchten wir natürlich eine viel größere Stichprobe, aber die Analyseschritte sind im Wesentlichen dieselben. Abschließend stellen wir unsere Ergebnisse visuell dar.
# import demographics data
demo <- read.csv("Testdemo.csv")
# extract employment_status
employment <- demo %>%
select(participant, employment_status)
# add employment_status to distance dataset
distance_es <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "DISTANCE_TRAVELED") %>%
left_join(employment) %>%
# add column with corresponding weekday
mutate(weekday = weekdays(date(time))) %>%
group_by(weekday,employment_status) %>%
summarize(mean_daily_distance = mean(value))
# order from Monday to Sunday
distance_es$weekday <- factor(distance_es$weekday,levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
distance_es <- distance_es %>%
arrange(weekday)
# plot the mean number of phone sessions per weekday
ggplot(data=distance_es, aes(x=weekday, y=mean_daily_distance, fill = employment_status)) +
geom_bar(stat="identity",position=position_dodge())
# diameter dataset
diameter_es <- long_format_daily %>%
# filter the right data
filter(aggregation_type == "DIAMETER") %>%
left_join(employment) %>%
# add column with corresponding weekday
mutate(weekday = weekdays(date(time))) %>%
group_by(weekday,employment_status) %>%
summarize(mean_daily_diameter = mean(value))
# order from Monday to Sunday
diameter_es$weekday <- factor(diameter_es$weekday,levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
diameter_es <- diameter_es %>%
arrange(weekday)
# plot the mean number of phone sessions per weekday
ggplot(data=diameter_es, aes(x=weekday, y=mean_daily_diameter, fill = employment_status)) +
geom_bar(stat="identity",position=position_dodge())
Das war der zweite Teil unserer R-Tutorial-Serie. Viel Erfolg bei euren eigenen Datenanalysen! Meldet euch gern, wenn ihr Fragen zu den Daten und deren Handhabung habt. Schaut euch auch das vorherige Tutorial an, in dem ich das Datenformat der App-Sessions erkläre.