Analyse-Paket: "podlover" für R

Wie anderweitig beschrieben habe ich vor einiger Zeit damit angefangen, Analysefunktionen für Podlove-Daten in der statistischen Sprache R zu schreiben.

Über die Feiertage habe ich nun den ganzen Code überarbeitet und ein echtes (d.h. via devtools installierbares) Package gebaut. Es heisst “podlover” und hat hier sein Zuhause:

Das Paket kann alles, was die alten Funktionen auch schon konnten, und noch etwas mehr:

  • Daten aus der Wordpress MySQL-Datenbank des Podcasts holen
  • Daten zusammenführen und säubern (entweder via Download oder mit lokalen CSV-Tabellen)
  • Ausgabe von Downloadkurven mit verschiedenen Parametern:
    • Gesamtdownloads oder Gruppierung nach Episodentitel, -nummer, Quelle (z.B. Feed vs. Webplayer) und Endgerät
    • kumulierte Kurven (Gesamtzahl über Zeit) oder nichtkumulierte Kurven
    • absolute Zeitpunkte (Datum der Veröffentlichung) vs. relative Zeitpunkte (Zeit seit Veröffentlichung)
    • grafische Darstellungsoptionen, z.B. Kurvenart, Farben, Beschriftung etc.
  • Leistungslisten pro Episode (totale Downloads, durchschnittliche Downloads, durchschnittliche Downloads für bestimmte Zeiträume)
  • Leistungsvergleich im 2x2-Diagramm (Veröffentlichung vs. Langzeit)
  • Regressionsanalyse von Downloads X Tage nach Veröffentlichung vs. Episodennummer oder Zeit: Damit lässt sich berechnen und darstellen, ob die Downloads tendenziell steigen oder sinken (inspiriert von @rahra s Prototyp)
  • Eine Funktion, um zufällige Downloadzahlen zu generieren, womit sich die Funktionen testen lassen

Alle Funktionen sind in den Helpfiles dokumentiert. Eine umfangreiche Erklärung mit Beispielen findet ihr hier oder in der Vignette des Pakets. Tests und Feedback sind willkommen.

Einen herzlichen Dank an @schaarsen für seine freundliche Datenspende!

Hier noch eine kleine Gallerie von Dingen, die podlover kann:

Totale Downloads über Zeit (kumuliert)

Totale Downloads über Zeit (nicht kumuliert)

Podast-Zusammenfassung

#> 'downloads': 
#> 
#> A podcast with 10 episodes, released between 2019-01-01 and 2019-12-05.
#> 
#> Total runtime:  11m 4d 22H 0M 0S.
#> Average time between episodes: 2928240s (~4.84 weeks).
#> 
#> Episodes were downloaded 6739 times between 2019-01-01 and 2020-01-04.
#> 
#> Downloads per episode: 673.9
#> min: 132 | 25p: 375 | med: 703 | 75p: 791 | max: 1327
#> 
#> Downloads per day: 18.3
#> min: 1 | 25p: 3 | med: 7 | 75p: 16 | max: 572

Downloads pro Episode mit absoluter Zeitskala

Downloads pro Episode mit relativer Zeitskala

Downloads pro Quelle

Durchschnittliche Downloads während der Launchperiode

#>    title                  dls_per_day_at_launch
#>    <fct>                                  <dbl>
#>  1 Mary Toft                              226. 
#>  2 Ashton-under-Lyne                      198. 
#>  3 Shapinsay                              140  
#>  4 Samantha Smith                         133. 
#>  5 Gwoyeu Romatzyh                        132. 
#>  6 Acute myeloid leukemia                 131  
#>  7 Ficus aurea                             86.2
#>  8 Cortinarius violaceus                   75.4
#>  9 White-winged fairywren                  61.5
#> 10 Debora Green                            39

Leistungsanalyse Launch vs. Langzeit

Regressionsanalyse Downloads vs. Episodennummer

Regressionsanalyse: Statisches Modell mit Signifikanzen

#> Call:
#> stats::lm(formula = formula_string, data = df_regression_data)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -222.21  -23.69  -11.74   57.09  155.70 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)   789.47      71.28  11.075 3.94e-06 ***
#> ep_rank       -64.08      11.49  -5.578 0.000523 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 104.3 on 8 degrees of freedom
#> Multiple R-squared:  0.7955, Adjusted R-squared:  0.7699 
#> F-statistic: 31.12 on 1 and 8 DF,  p-value: 0.0005233
16 Like

Sehr cool … ein grund mehr von podbean weg zu gehen … ein cooles Tool

3 Like

Wow!!! :infinity: viel Respekt!!! :+1::+1::+1:

4 Like

Hi! Großes Kompliment - das ist echt sehr cool! Und dank Deiner Anleitung habe ich es auch hinbekommen, obwohl ich Null Ahnung von R hab. Wirklich klasse Arbeit!

Hast Du Pläne, das Initialisieren und Auswerten in ein “Script” zusammenzufassen? Ich weiß nicht, ob das in R geht - meine Vorstellung wäre, ich wähle eine Methode am Anfang aus (z.B. "Direkt aus der WP-DB) und dann generiert mir R durch das “Script” die entsprechenden Grafiken automatisch. Könnte man sicherlich überlegen, welche die Default Grafiken sind…

Wie auch immer, ganz tolle Arbeit und noch einmal vielen Dank!!!

2 Like

Herzlichen Dank für dein Feedback, das freut mich sehr! Dass du als R-Anfänger das auf Anhieb hinbekommen hast, macht mir viel Hoffnung!

Zusammenfassen lassen sich Funktionen immer - man muss sich bloss entscheiden, wie viel Flexibilität für wie viel Komfort man einbüssen möchte. Ich plane, gewisse Funktionen zusammen zu führen, die aktuell getrennt sind (speziell die Datenvorbereitung und -darstellung für die Downloadkurven). Mittelfristig könne auch ein Dashboard drin liegen. Dazu ist es jetzt noch zu früh, weil ich gerne noch mehr darüber erfahren möchte, was Podcastende überhaupt an Analytics am Herzen liegt.

1 Like

Danke, Sven!

1 Like

Merci, Oli!

1 Like

Verstehe ich! Ich baue gerade ein R Script, dass ich dann mit /usr/local/bin/Rscript “/Users//Documents/auswertung.R” ausführen kann - mal sehen, ob ich das hinbekomme :slight_smile:

Es scheinen “{rstudioapi} package” benutzt zu werden, die man wohl per Rscript nicht aufrufen kann, wenn ich das hier richtig verstehe: https://stackoverflow.com/questions/48632701/rscript-in-terminal-yields-in-error-rstudio-not-running - ich bekomme nämlich im Terminal: “Fehler: RStudio not running”. Daher werde ich das so bauen, dass ich es nur im RStudio copy-pasten kann

Lass uns das per DM diskutieren, zu spezifisch für diesen Post.

Gerne!

Das geht jetzt per C&P im RStudio… (falls mal jemand drüber stolpert):

# Workdirectory
setwd("~/Downloads")

# Library laden
library(podlover)

# Daten holen
downloads <- podlove_get_and_clean()

# Basis Tabelle
total_dls_acc <- podlove_prepare_stats_for_graph(df_stats = downloads,
                                                 hourly = FALSE,
                                                 relative = FALSE)

# Gesamtdownloads
g_tdlacc <- podlove_graph_download_curves(df_tidy_data = total_dls_acc,
                                          cumulative = TRUE,
                                          plot_type = "line",
                                          printout = FALSE)
png("Gesamtdownloads.png")
print(g_tdlacc)
dev.off()

# Spikes
g_tdl <- podlove_graph_download_curves(df_tidy_data = total_dls_acc,
                                          cumulative = FALSE,
                                          plot_type = "line",
                                          printout = FALSE)
png("Spikes.png")
print(g_tdl)
dev.off()

# Downloads by Episodes
ep_dls_acc <- podlove_prepare_stats_for_graph(df_stats = downloads,
                                              gvar = title, # group by episode title
                                              hourly = FALSE,
                                              relative = FALSE)

g_ep_dlsacc <- podlove_graph_download_curves(df_tidy_data = ep_dls_acc,
                                             gvar = title, # use the same gvar!
                                             cumulative = TRUE,
                                             plot_type = "line",
                                             labelmethod = "first.points",
                                             printout = FALSE)
png("Episodes.png")
print(g_ep_dlsacc)
dev.off()

# Episodes relative
ep_dls_acc_rel <- podlove_prepare_stats_for_graph(df_stats = downloads,
                                              gvar = title,
                                              hourly = FALSE,
                                              relative = TRUE) # relative plotting

g_ep_dlsaccrel <- podlove_graph_download_curves(df_tidy_data = ep_dls_acc_rel,
                                             gvar = title,
                                             cumulative = TRUE,
                                             plot_type = "line",
                                             labelmethod = "last.points",
                                             printout = FALSE)
png("Episodes-relative.png")
print(g_ep_dlsaccrel)
dev.off()

# Ridge
ep_dls <- podlove_prepare_stats_for_graph(df_stats = downloads,
                                              gvar = ep_num_title, # better for sorting
                                              hourly = FALSE,
                                              relative = FALSE)

g_ep_dls <- podlove_graph_download_curves(df_tidy_data = ep_dls,
                                             gvar = ep_num_title,
                                             cumulative = FALSE, # no cumulation
                                             plot_type = "ridge", # use a ridgeline plot
                                             printout = FALSE)
png("Ridge.png")
print(g_ep_dls)
dev.off()

# Feed vs Watch
source_acc <- podlove_prepare_stats_for_graph(df_stats = downloads,
                                              gvar = source, # new gvar
                                              hourly = FALSE,
                                              relative = FALSE)

g_source_acc <- podlove_graph_download_curves(df_tidy_data = source_acc,
                                             gvar = source,  # same as above!
                                             cumulative = TRUE,
                                             plot_type = "line",
                                             labelmethod = "angled.boxes",
                                             printout = FALSE)
png("Feed-Watch.png")
print(g_source_acc)
dev.off()

# Performance
perf <- podlove_performance_stats(downloads, launch = 3, post_launch = 30)
g_perf <- podlove_graph_performance(perf, printout = FALSE)
png("Performance.png")
print(g_perf)
dev.off()

# Regression
du <- podlove_downloads_until(downloads, 30)
png("Regression.png")
g_reg <- podlove_graph_regression(du, predictor = ep_rank)
dev.off()
2 Like

Super Projekt! Bin gerade darüber gestolpert und find’s echt klasse.

Langfristig kann man vielleicht die Podlove-Devs fragen ob sie einen Export to Podlover -Button einbauen wollen, um den UserInnen den Datenbankexport zu erleichtern.

Wäre es möglich auch (nur!) eine Datenspende “public” zu haben (ggf. auch schon vorarbeitet und anonymisiert), damit man auch ohne eigene Podlove Installation mitentwickeln kann?

1 Like

Danke, @msp!

Langfristig kann man vielleicht die Podlove-Devs fragen ob sie einen Export to Podlover -Button einbauen wollen, um den UserInnen den Datenbankexport zu erleichtern.

Mal schauen, wie sich das Projekt entwickelt, steckt ja noch in den Kinderschuhen. Wenn es den Bedarf dafür gibt, kann ich mal anfragen. Vielleicht gibt es auch die Möglichkeit in die andere Richtung, d.h. die Auswertungen serverseitig auszuführen und als Report auszugeben. Da möchte ich die Erwartungen aber bewusst tief halten.

Wäre es möglich auch (nur!) eine Datenspende “public” zu haben (ggf. auch schon vorarbeitet und anonymisiert), damit man auch ohne eigene Podlove Installation mitentwickeln kann?

Dafür habe ich die Funktion podlove_create_example() geschrieben - die kreiert dir Beispieltabellen oder gesäuberte Datensätze mit einigermassen realistischen Zufallsdaten (mehr dazu gerne per PM). Wenn es dir bewusst um echte Daten von echten Podcasts geht, müsste ich einen Spender finden und klären, ob damit allfällige Datenschutzfragen auftreten könnten.

2 Like

Kurzes Update: Ich habe jetzt ein kleines Shell Script für mich gebastelt, dass einen SSH Tunnel zu meinem Server aufmacht, die benötigten Daten holt und Podlover R schickt und von dem ich dann Grafiken in meinem “Downloads” Ordner bekomme.

Wenn jemand Interesse daran hat und ein wenig Ahnung von Shell und Terminal - schickt mir gerne ne PN.

Dabei kommen dann folgende Grafiken raus (sind meine echten Daten, denke aber, das ist kein Geheimnis…)

5 Like

Danke fürs Teilen! Toller Showcase!

2 Like

Gern! Wenn du weitere Auswertungen oder Interpretationen meiner ner Zahlen hast, gerne her damit:)

Gruß,

Thomas

2 Like

V 1.0.6 ist raus. Keine neuen Analysefeatures, dafür sind nun Unit Tests via {testthat} für alle Funktionen verfügbar (auch die grafischen!). Das bedeutet, dass die Entwicklung von neuen Funktionen durch automatische Tests unterstützt wird, damit weniger Überraschungen auftauchen.

Installation/Update wie schon bisher via

# install devtools if you don't have it already
install.packages("devtools")

# install podlover from GitHub
devtools::install_github("lordyo/podlover")
4 Like

V 1.0.7 ist ebenfalls raus. Sie fixt einen ziemlich gewichtigen Bug, der dazu geführt hat, dass zu viele Daten beim Cleaning rausfallen. Das ist nun gelöst.

4 Like

V 1.1.1 enthält zwei neue Funktionen, die es erlauben, Kurven- und Performance-Plots direkt aus den gesäuberten Daten zu erstellen. Es ist also nicht mehr nötig, die Daten über eine Zwischenfunktion aufzubereiten (wenn man will, kann man das aber noch). Ein paar Beispiele findet ihr in der Vignette / im Readme im Repo.

5 Like

Schönes Projekt! Leider scheitere ich daran, die lokalen Tabellen zu säubern. Nachdem ich die CSV-Dateien erfolgreich eingelesen habe gebe ich den clean-Befehl ein, erhalte aber eine Fehlermeldung. Was mache ich falsch?

33