Sitzung 12 APIs
12.1 Vorbereitung
Für diese Lektion werden die Pakete benötigt:
library(tidyverse)
library(jsonlite)
12.2 SWAPI
Die Star Wars API ist eine eigens für Übungszwecke eingerichtete API, und steht uns deshalb (anders als andere APIs) ohne Login zur Verfügung. Wir sollten bei der Benutzung darauf achten, sie nicht zu überladen.
Jede gute API kommt mit einer ausführlichen Dokumentation in der die Endpunkte und Abfrageoptionen erklärt sind.
Bei den hier besprochenen REST-APIs geht es eigentlich nur darum, die richtige Abfrage als URL zu formulieren. Die Antwort des Servers gibt uns dann die Daten, die wir brauchen, und zwar üblicherweise im JSON-Format.
Zum Beispiel fragen wir so Informationen über Han Solo ab:
<- read_json("https://www.swapi.tech/api/people/14/")$result han_solo
Die Antwort ist eine Liste, deren Elemente sich wie gewohnt mit $
ansprechen lassen und wiederum Hinweise auf API-Abfragen enthalten können:
$properties$eye_color
han_solo## [1] "brown"
$properties$homeworld
han_solo## [1] "https://www.swapi.tech/api/planets/22"
Diese Information ließe sich wiederum abfragen durch:
$properties$homeworld %>%
han_soloread_json() %>%
$result %>%
.$properties %>%
.$name
.## [1] "Corellia"
Eine (recht willkürlich gewählte) Herausforderung wäre es nun, die Namen aller Charaktere herauszufinden, die in Return of the Jedi vorkommen.
Zunächst können wir den richtigen Film suchen mit:
read_json("https://www.swapi.tech/api/films/")$result %>%
map("properties") %>%
map("title")
## [[1]]
## [1] "A New Hope"
##
## [[2]]
## [1] "The Empire Strikes Back"
##
## [[3]]
## [1] "Return of the Jedi"
##
## [[4]]
## [1] "The Phantom Menace"
##
## [[5]]
## [1] "Attack of the Clones"
##
## [[6]]
## [1] "Revenge of the Sith"
Dann lässt sich die gewünschte Liste ziehen mit:
read_json("https://www.swapi.tech/api/films/3")$result$properties$characters -> return_characters
Für jeden dieser Charaktere ließe sich der Name herausfinden mit
read_json("https://www.swapi.tech/api/people/1/")$result$properties$name
## [1] "Luke Skywalker"
read_json("https://www.swapi.tech/api/people/4/")$result$properties$name
## [1] "Darth Vader"
# usw.
Können wir aber auch die Namen nicht einzeln, sondern automatisch Abfragen?
12.3 Exkurs: Funktionen schreiben
Funktionen sind überall in R. Funktionen haben eine Eingabe (parameters) und eine Ausgabe (return values). Z.B. hat die Funktion mean()
als Eingabe einen numerischen Vektor, und als Ausgabe das arithmetische Mittel dieses Vektors:
data(diamonds)
mean(diamonds$carat)
## [1] 0.7979397
Wir können auch eigene Funktionen schreiben. Die Definition einer eigenen Funktionen hat immer diese Form:
<- function(EINGABE) {
FUNKTIONSNAME
...
AUSGABE }
Wenn es die Funktion mean()
nicht gäbe, könnten wir sie (bzw. so etwas ähnliches) selbst schreiben, mit:
<- function(verteilung) {
my_mean sum(verteilung) / length(verteilung)
}
Wenn wir die Definition ausführen, erscheint die Funktion in unserem Environment, genauso wie andere Objekte. Wir können sie dann genauso anwenden wie andere Funktionen:
my_mean(diamonds$carat)
## [1] 0.7979397
my_mean(diamonds$depth)
## [1] 61.7494
12.4 Abfragefunktion
Eine Funktion zur automatischen Abfrage der Charakternamen bräuchte als Eingabe die URL der API, und als Ausgabe den Charakternamen. Eigentlich geht es nur um eine Abstraktion des konkreten Befehls:
read_json("https://www.swapi.tech/api/people/1/")$result$properties$name
## [1] "Luke Skywalker"
Die Definition der Funktion könnte so aussehen:
<- function(url) {
get_character_name read_json(url)$result$properties$name
}
Testweise lässt sie sich anwenden:
get_character_name("https://www.swapi.tech/api/people/1/")
## [1] "Luke Skywalker"
Leider lässt sie sich nicht so einfach (wie andere Funktionen) auf den Vektor von URLS anwenden, da die Funktion keinen Vektor als Eingabe erwartet:
Abhilfe schafft der Befehl map()
aus dem purrr
-Paket (Teil von tidyverse
). Hier lässt sich ein Vektor (oder eine Liste) angeben, sowie der Name einer Funktion, die dann auf jedes Element des Vektors angewendet wird:
map(return_characters, get_character_name)
Resultat ist eine Liste, die sich mit unlist()
auch zu einem Vektor wandeln ließe… oder man benutzt direkt die Abwandlung map_chr()
, die nach Möglichkeit immer einen character vector ausgibt:
map_chr(return_characters, get_character_name)
## [1] "Luke Skywalker" "C-3PO" "R2-D2"
## [4] "Darth Vader" "Leia Organa" "Obi-Wan Kenobi"
## [7] "Chewbacca" "Han Solo" "Jabba Desilijic Tiure"
## [10] "Wedge Antilles" "Yoda" "Palpatine"
## [13] "Boba Fett" "Lando Calrissian" "Ackbar"
## [16] "Mon Mothma" "Arvel Crynyd" "Wicket Systri Warrick"
## [19] "Nien Nunb" "Bib Fortuna"