Bei der Arbeit mit Antworten, die von APIs erhalten werden, oder mit anderen Daten, die eine komplexe hierarchische Struktur aufweisen, stoßen Sie häufig auf JSON- und XML-Formate.
Diese Formate bieten zahlreiche Vorteile: Sie speichern Daten kompakt und vermeiden übermäßige Informationsduplizierung.
Ein Nachteil dieser Formate liegt in der Schwierigkeit ihrer Verarbeitung und Analyse. Unstrukturierte Daten können nicht für Berechnungen verwendet werden, und es ist unmöglich, darauf basierende Visualisierungen zu erstellen.

Dieser Artikel ist eine logische Fortsetzung der Veröffentlichung . Er wird Ihnen helfen, unstrukturierte Datenkonstrukte in ein vertrautes und für die Analyse geeignetes tabellarisches Format zu bringen, mithilfe des Pakets tidyr, das zum Kern der Bibliothek gehört, und seiner Funktionen aus der Familie tidyverseunnest_*() GitHub-Nutzer.
Inhalt
Falls Sie sich für Datenanalyse interessieren, könnten meine und Kanäle für Sie von Interesse sein. Der Großteil der Inhalte widmet sich der R-Sprache.
Einführung
(Anmerkung des Übersetzers: Ich habe keine adäquaten Übersetzungsvarianten für diesen Begriff gefunden, daher lassen wir ihn so.) (Anmerkung des Übersetzers, ich habe keine angemessenen Übersetzungsvarianten für diesen Begriff gefunden, daher lassen wir ihn so.) — ist der Prozess, bei dem unstrukturierte Daten mit verschachtelten Arrays in eine zweidimensionale Tabelle überführt werden, die aus den uns vertrauten Zeilen und Spalten besteht. In tidyr gibt es mehrere Funktionen, die Ihnen helfen, verschachtelte Listen-Spalten zu entrollen und die Daten in eine rechteckige, tabellarische Form zu bringen:
unnest_longer()nimmt jedes Element der Listen-Spalte und erstellt eine neue Zeile.unnest_wider()nimmt jedes Element der Listen-Spalte und erstellt eine neue Spalte.unnest_auto()bestimmt automatisch, welche der Funktionen besser zu verwenden ist
unnest_longer()oderunnest_wider().hoist()ähneltunnest_wider()filtert jedoch nur die angegebenen Komponenten heraus und ermöglicht die Arbeit mit mehreren Ebenen der Verschachtelung.
Die meisten Probleme, die mit der Umwandlung unstrukturierter Daten mit mehreren Verschachtelungsebenen in eine zweidimensionale Tabelle verbunden sind, können durch die Kombination der genannten Funktionen mit dplyr gelöst werden.
Zur Demonstration dieser Techniken werden wir das Paket repurrrsive, das mehrere komplexe, mehrstufige Listen aus einer Web-API bereitstellt, verwenden.
library(tidyr)
library(dplyr)
library(repurrrsive)GitHub-Repositories
Lass uns anfangen mit gh_users, eine Liste, die Informationen über sechs GitHub-Nutzer enthält. Zunächst transformieren wir die Liste gh_users in tibble in ein DataFrame:
users <- tibble( user = gh_users ) Das erscheint etwas unlogisch: Warum eine Liste in eine komplexere Datenstruktur umwandeln? gh_usersAber ein Dataframe hat einen großen Vorteil: Er vereint mehrere Vektoren, sodass alles in einem einzigen Objekt verfolgt werden kann.
Jedes Element des Objekts users ist eine benannte Liste, in der jedes Element eine Spalte darstellt.
names(users$user[[1]])
#> [1] "login" "id" "avatar_url"
#> [4] "gravatar_id" "url" "html_url"
#> [7] "followers_url" "following_url" "gists_url"
#> [10] "starred_url" "subscriptions_url" "organizations_url"
#> [13] "repos_url" "events_url" "received_events_url"
#> [16] "type" "site_admin" "name"
#> [19] "company" "blog" "location"
#> [22] "email" "hireable" "bio"
#> [25] "public_repos" "public_gists" "followers"
#> [28] "following" "created_at" "updated_at"Es gibt zwei Möglichkeiten, Listenelemente in Spalten umzuwandeln. unnest_wider() nimmt jedes Element und erstellt eine neue Spalte:
Benutzer %>% unnest_wider(Benutzer)
#> # Ein Tibble: 6 x 30
#> login id avatar_url gravatar_id url html_url followers_url
#>
#> 1 gabo… 6.60e5 https://a… "" http… https:/… https://api.…
#> 2 jenn… 5.99e5 https://a… "" http… https:/… https://api.…
#> 3 jtle… 1.57e6 https://a… "" http… https:/… https://api.…
#> 4 juli… 1.25e7 https://a… "" http… https:/… https://api.…
#> 5 leep… 3.51e6 https://a… "" http… https:/… https://api.…
#> 6 masa… 8.36e6 https://a… "" http… https:/… https://api.…
#> # … mit 23 weiteren Variablen: following_url , gists_url ,
#> # starred_url , subscriptions_url , organizations_url ,
#> # repos_url , events_url , received_events_url ,
#> # type , site_admin , name , company , blog ,
#> # location , email , public_repos , public_gists ,
#> # followers , following , created_at , updated_at ,
#> # bio , hireableIn diesem Fall haben wir eine Tabelle mit 30 Spalten erhalten, von denen die meisten für uns nicht erforderlich sind, daher können wir anstelle von unnest_wider() eingesetzt zu werden. hoist(). hoist() ermöglicht es uns, ausgewählte Komponenten zu extrahieren, indem wir die gleiche Syntax verwenden wie purrr::pluck():
Benutzer %>% hoist(user,
followers = "followers",
login = "login",
url = "html_url"
)
#> # Ein Tibble: 6 x 4
#> followers login url user
#> <int> <chr> <chr> <list>
#> 1 303 gaborcsardi https://github.com/gaborcsardi <benannte Liste [27]>
#> 2 780 jennybc https://github.com/jennybc <benannte Liste [27]>
#> 3 3958 jtleek https://github.com/jtleek <benannte Liste [27]>
#> 4 115 juliasilge https://github.com/juliasilge <benannte Liste [27]>
#> 5 213 leeper https://github.com/leeper <benannte Liste [27]>
#> 6 34 masalmon https://github.com/masalmon <benannte Liste [27]>hoist() entfernt die angegebenen benannten Komponenten aus der Listen-Spalte Benutzer, daher können Sie betrachten hoist() als Verschieben von Komponenten aus der inneren Liste des Datenrahmens auf die oberste Ebene.
Charaktere aus Game of Thrones
Ausrichten der Liste gh_repos beginnen wir ähnlich, indem wir es in umwandeln tibble:
repos <- tibble(repo = gh_repos)
repos
#> # Ein Tibble: 6 x 1
#> repo
#> <list>
#> 1 <Liste [30]>
#> 2 <Liste [30]>
#> 3 <Liste [30]>
#> 4 <Liste [26]>
#> 5 <Liste [30]>
#> 6 <Liste [30]>Diesmal stellen die Elemente Benutzer eine Liste von Repositories dar, die diesem Benutzer gehören. Jedes Repository ist eine separate Beobachtung, daher gemäß dem Konzept der sauberen Daten (Hinweis: saubere Daten) Sie sollten neue Zeilen werden, weshalb wir verwenden unnest_longer() statt unnest_wider():
repos % unnest_longer(repo)
repos
#> # Ein tibble: 176 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … mit 166 weiteren ZeilenJetzt können wir verwenden unnest_wider() oder hoist() :
repos %>% hoist(repo,
login = c("owner", "login"),
name = "name",
homepage = "homepage",
watchers = "watchers_count"
)
#> # Ein tibble: 176 x 5
#> login name homepage watchers repo
#>
#> 1 gaborcsardi after 5
#> 2 gaborcsardi argufy 19
#> 3 gaborcsardi ask 5
#> 4 gaborcsardi baseimports 0
#> 5 gaborcsardi citest 0
#> 6 gaborcsardi clisymbols "" 18
#> 7 gaborcsardi cmaker 0
#> 8 gaborcsardi cmark 0
#> 9 gaborcsardi conditions 0
#> 10 gaborcsardi crayon 52
#> # … mit 166 weiteren ZeilenBeachten Sie die Verwendung von c("owner", "login"): dies ermöglicht uns, den Wert der zweiten Ebene aus der verschachtelten Liste zu erhalten owner. Ein alternativer Ansatz besteht darin, die gesamte Liste zu erhalten owner und dann mit der Funktion unnest_wider() jedes Element in eine Spalte zu setzen:
repos %>%
hoist(repo, owner = "owner") %>%
unnest_wider(owner)
#> # Ein Tibble: 176 x 18
#> login id avatar_url gravatar_id url html_url followers_url
#>
#> 1 gabo… 660288 https://a… "" http… https:… https://api.…
#> 2 gabo… 660288 https://a… "" http… https:… https://api.…
#> 3 gabo… 660288 https://a… "" http… https:… https://api.…
#> 4 gabo… 660288 https://a… "" http… https:… https://api.…
#> 5 gabo… 660288 https://a… "" http… https:… https://api.…
#> 6 gabo… 660288 https://a… "" http… https:… https://api.…
#> 7 gabo… 660288 https://a… "" http… https:… https://api.…
#> 8 gabo… 660288 https://a… "" http… https:… https://api.…
#> 9 gabo… 660288 https://a… "" http… https:… https://api.…
#> 10 gabo… 660288 https://a… "" http… https:… https://api.…
#> # … mit 166 weiteren Zeilen und 11 weiteren Variablen: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoAnstatt über die Auswahl der richtigen Funktion nachzudenken, unnest_longer() oder unnest_wider() können Sie verwenden unnest_auto(). Diese Funktion verwendet mehrere heuristische Methoden, um die am besten geeignete Funktion zur Datenumwandlung auszuwählen, und gibt eine Nachricht über die gewählte Methode aus.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Verwendung von `unnest_longer(repo)`; kein Element hat Namen
#> Verwendung von `unnest_wider(repo)`; Elemente haben 68 gemeinsame Namen
#> # Ein tibble: 176 x 67
#> id name full_name owner private html_url description fork url
#>
#> 1 6.12e7 nach gaborcsa… 2 4.05e7 argu… gaborcsa… 3 3.64e7 ask gaborcsa… 4 3.49e7 base… gaborcsa… 5 6.16e7 cite… gaborcsa… 6 3.39e7 clis… gaborcsa… 7 3.72e7 cmak… gaborcsa… 8 6.80e7 cmark gaborcsa… 9 6.32e7 cond… gaborcsa… <nam… FALSE https:/… TRUE http…
#> 10 2.43e7 cray… gaborcsa… # … mit 166 weiteren Zeilen und 58 weiteren Variablen: forks_url ,
#> # keys_url , collaborators_url , teams_url ,
#> # hooks_url , issue_events_url , events_url ,
#> # assignees_url , branches_url , tags_url ,
#> # blobs_url , git_tags_url , git_refs_url ,
#> # trees_url , statuses_url , languages_url ,
#> # stargazers_url , contributors_url , subscribers_url ,
#> # subscription_url , commits_url , git_commits_url ,
#> # comments_url , issue_comment_url , contents_url ,
#> # compare_url , merges_url , archive_url ,
#> # downloads_url , issues_url , pulls_url ,
#> # milestones_url , notifications_url , labels_url ,
#> # releases_url , deployments_url , created_at ,
#> # updated_at , pushed_at , git_url , ssh_url ,
#> # clone_url , svn_url , size , stargazers_count ,
#> # watchers_count , language , has_issues ,
#> # has_downloads , has_wiki , has_pages ,
#> # forks_count , open_issues_count , forks ,
#> # open_issues , watchers , default_branch ,
#> # homepageGeocodierung mit Google
hat die gleichen Strukturen wie : es ist eine Sammlung von benannten Listen, wobei jedes Element der inneren Liste ein bestimmtes Attribut eines Charakters aus Game of Thrones beschreibt. Um gh_usersin Tabellenform zu bringen, beginnen wir mit der Erstellung eines DataFrames, wie in den vorherigen Beispielen, und wandeln dann jedes Element in eine separate Spalte um: hat die gleichen Strukturen wie Wir beginnen mit dem Erstellen eines Dataframes in tabellarischer Form, wie auch in den vorherigen Beispielen, und übersetzen dann jedes Element in eine separate Spalte:
chars # A tibble: 30 x 1
#> char
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … mit 20 weiteren Zeilen
chars2 % unnest_wider(char)
chars2
#> # A tibble: 30 x 18
#> url id name gender culture born died alive titles aliases father
#> <int>
#> 1 http… 1022 Theo… Male Ironbo… In 2… "" TRUE <chr … 2 http… 1052 Tyri… Male "" In 2… "" TRUE <chr … 3 http… 1074 Vict… Male Ironbo… In 2… "" TRUE <chr … 4 http… 1109 Will Male "" "" In 2… FALSE <chr … 5 http… 1166 Areo… Male Norvos… In 2… "" TRUE <chr … 6 http… 1267 Chett Male "" At H… In 2… FALSE <chr … 7 http… 1295 Cres… Male "" In 2… In 2… FALSE <chr … 8 http… 130 Aria… Female Dornish In 2… "" TRUE <chr … 9 http… 1303 Daen… Female Valyri… In 2… "" TRUE <chr … 10 http… 1319 Davo… Male Wester… In 2… "" TRUE <chr … # … mit 20 weiteren Zeilen und 7 weiteren Variablen: mother , spouse ,
#> # allegiances , books , povBooks , tvSeries ,
#> # playedByStruktur hat die gleichen Strukturen wie ein bisschen komplizierter als gh_users, da einige Komponenten der Liste char selbst Listen sind, wodurch wir Spalten - Listen erhalten:
chars2 %>% select_if(is.list)
#>% # Ein tibble: 30 x 7
#>% titles aliases allegiances books povBooks tvSeries playedBy
#>%
#>% 1
#>% 2
#>% 3
#>% 4
#>% 5
#>% 6
#>% 7
#>% 8
#>% 9
#>% 10
#>% # … mit 20 weiteren ZeilenIhre weiteren Schritte hängen von den Zielen der Analyse ab. Möglicherweise müssen Sie Informationen zu jedem Buch und jeder Serie, in der der Charakter vorkommt, in die Zeilen einfügen:
chars2 %>%
select(name, books, tvSeries) %>%
pivot_longer(c(books, tvSeries), names_to = "media", values_to = "value") %>%
unnest_longer(value)
#> # Ein Tibble: 180 x 3
#> name media value
#>
#> 1 Theon Greyjoy books A Game of Thrones
#> 2 Theon Greyjoy books A Storm of Swords
#> 3 Theon Greyjoy books A Feast for Crows
#> 4 Theon Greyjoy tvSeries Season 1
#> 5 Theon Greyjoy tvSeries Season 2
#> 6 Theon Greyjoy tvSeries Season 3
#> 7 Theon Greyjoy tvSeries Season 4
#> 8 Theon Greyjoy tvSeries Season 5
#> 9 Theon Greyjoy tvSeries Season 6
#> 10 Tyrion Lannister books A Feast for Crows
#> # … mit 170 weiteren ZeilenOder vielleicht möchten Sie eine Tabelle erstellen, die es Ihnen ermöglicht, den Charakter mit dem Werk zu verknüpfen:
chars2 %>%
select(name, title = titles) %>%
unnest_longer(title)
#> # Ein tibble: 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Prince von Winterfell
#> 2 Theon Greyjoy Kapitän der See Bitch
#> 3 Theon Greyjoy Lord der Eiseninseln (nach dem Gesetz der grünen Länder)
#> 4 Tyrion Lannister Amtierender Hand des Königs (ehemalig)
#> 5 Tyrion Lannister Meister der Münze (ehemalig)
#> 6 Victarion Greyjoy Lord Kapitän der Eisenflotte
#> 7 Victarion Greyjoy Meister des Eisen-Siegs
#> 8 Will ""
#> 9 Areo Hotah Kapitän der Garde in Sunspear
#> 10 Chett ""
#> # … mit 50 weiteren Zeilen(Bitte beachten Sie die leeren Werte "" im Feld Anrede, dies liegt an Fehlern, die bei der Dateneingabe gemacht wurden in hat die gleichen Strukturen wie: tatsächlich sollten die Charaktere, für die es keine entsprechenden Titel aus Büchern und Serien im Feld gibt Anrede einen Vektor der Länge 0 haben, anstatt einen Vektor der Länge 1, der eine leere Zeichenfolge enthält.)
Wir können das obige Beispiel umschreiben, indem wir die Funktion unnest_auto(). Dieser Ansatz ist für eine einmalige Analyse praktisch, aber man sollte sich nicht auf unnest_auto() für die regelmäßige Nutzung verlassen. Das Problem ist, dass sich Ihre Datenstruktur ändern könnte unnest_auto() kann den ausgewählten Datentransformationsmechanismus ändern, wenn er ursprünglich Spalten-Listen in Zeilen umgewandelt hat, indem er unnest_longer(), dann kann die Logik bei Änderungen der Eingabestruktur zugunsten von unnest_wider(), und die dauerhafte Anwendung eines solchen Ansatzes kann zu unvorhergesehenen Fehlern führen.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> Verwendung von `unnest_wider(char)`; Elemente haben 18 gemeinsame Namen
#> Verwendung von `unnest_longer(title)`; kein Element hat Namen
#> # Ein tibble: 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Prinz von Winterfell
#> 2 Theon Greyjoy Kapitän der Sea Bitch
#> 3 Theon Greyjoy Lord der Eiseninseln (nach Gesetz der grünen Ländereien)
#> 4 Tyrion Lannister Aktueller Hand des Königs (ehemalig)
#> 5 Tyrion Lannister Meister der Münze (ehemalig)
#> 6 Victarion Greyjoy Lordkapitän der Eisenflotte
#> 7 Victarion Greyjoy Meister des Eisernen Sieges
#> 8 Will ""
#> 9 Areo Hotah Kapitän der Garde in Sunspear
#> 10 Chett ""
#> # … mit 50 weiteren ZeilenDiskografie von Sharly Gelfand
In diesem Abschnitt werden wir eine komplexere Datenstruktur betrachten, die von dem Geokodierungsdienst von Google bereitgestellt wird. Das Caching der Anmeldedaten widerspricht den Nutzungsbedingungen für die Google Maps API, weshalb ich zunächst eine einfache Wrapper-Funktion für die API schreiben werde. Diese basiert darauf, den Google Maps API-Schlüssel in einer Umgebungsvariable zu speichern; wenn der Schlüssel für die Interaktion mit der Google Maps API nicht in den Umgebungsvariablen gespeichert ist, werden die in diesem Abschnitt dargestellten Codefragmente nicht ausgeführt.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Kein Google Maps API-Schlüssel gefunden; Codeabschnitte werden nicht ausgeführt")
}
# https://developers.google.com/maps/documentation/geocoding
geocode <- function(address, api_key = Sys.getenv("GOOGLE_MAPS_API_KEY")) {
url <- "https://maps.googleapis.com/maps/api/geocode/json"
url <- paste0(url, "?address=", URLencode(address), "&key=", api_key)
jsonlite::read_json(url)
}Die Liste, die diese Funktion zurückgibt, ist ziemlich komplex:
houston
str(houston)
#> Liste von 2
#> $ Ergebnisse:Liste von 1
#> ..$ :Liste von 5
#> .. ..$ adressenbestandteile:Liste von 4
#> .. .. ..$ :Liste von 3
#> .. .. .. ..$ lang_name : chr "Houston"
#> .. .. .. ..$ kurz_name: chr "Houston"
#> .. .. .. ..$ typen :Liste von 2
#> .. .. .. .. ..$ : chr "locality"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste von 3
#> .. .. .. ..$ lang_name : chr "Harris County"
#> .. .. .. ..$ kurz_name: chr "Harris County"
#> .. .. .. ..$ typen :Liste von 2
#> .. .. .. .. ..$ : chr "administrative_area_level_2"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste von 3
#> .. .. .. ..$ lang_name : chr "Texas"
#> .. .. .. ..$ kurz_name: chr "TX"
#> .. .. .. ..$ typen :Liste von 2
#> .. .. .. .. ..$ : chr "administrative_area_level_1"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste von 3
#> .. .. .. ..$ lang_name : chr "Vereinigte Staaten"
#> .. .. .. ..$ kurz_name: chr "US"
#> .. .. .. ..$ typen :Liste von 2
#> .. .. .. .. ..$ : chr "country"
#> .. .. .. .. ..$ : chr "political"
#> .. ..$ formatierte_adresse : chr "Houston, TX, USA"
#> .. ..$ geodaten :Liste von 4
#> .. .. ..$ grenzen :Liste von 2
#> .. .. .. ..$ nordost:Liste von 2
#> .. .. .. .. ..$ lat: num 30,1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ südwest:Liste von 2
#> .. .. .. .. ..$ lat: num 29,5
#> .. .. .. .. ..$ lng: num -95,8
#> .. .. ..$ standort :Liste von 2
#> .. .. .. ..$ lat: num 29,8
#> .. .. .. ..$ lng: num -95,4
#> .. .. ..$ standort_typ: chr "UNGEFÄHR"
#> .. .. ..$ ansicht :Liste von 2
#> .. .. .. ..$ nordost:Liste von 2
#> .. .. .. .. ..$ lat: num 30,1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ südwest:Liste von 2
#> .. .. .. .. ..$ lat: num 29,5
#> .. .. .. .. ..$ lng: num -95,8
#> .. ..$ ort_id : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ typen :Liste von 2
#> .. .. ..$ : chr "locality"
#> .. .. ..$ : chr "political"
#> $ status : chr "OK"Glücklicherweise können wir Schritt für Schritt das Problem der Transformation dieser Daten in tabellarische Form mit Funktionen lösen. tidyr. Um die Aufgabe etwas anspruchsvoller und realistischer zu gestalten, beginne ich mit der Geokodierung mehrerer Städte:
city <- c("Houston", "LA", "New York", "Chicago", "Springfield") city_geo <- purrr::map(city, geocode) Das erhaltene Ergebnis verwandele ich in tibble, um die Handhabung zu erleichtern, füge ich eine Spalte mit dem entsprechenden Stadtnamen hinzu.
loc # A tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldDie erste Ebene enthält Komponenten status und result, die wir mit Hilfe von unnest_wider() :
loc %>%
unnest_wider(json)
#> # A tibble: 5 x 3
#> city results status
#>
#> 1 Houston OK
#> 2 LA OK
#> 3 New York OK
#> 4 Chicago OK
#> 5 Springfield OKnicht mit einem Bindestrich beginnt. In YAML bedeutet dies, dass results ist eine mehrstufige Liste. Die meisten Städte haben 1 Element (das einen einzigartigen Wert darstellt, der der Geokodierungs-API entspricht), aber Springfield hat zwei. Wir können diese in separate Zeilen mit Hilfe von unnest_longer() :
loc %>%
unnest_wider(json) %>%
unnest_longer(results)
#>% # Ein tibble: 5 x 3
#>% Stadt Ergebnisse Status
#>%
#>% 1 Houston OK
#>% 2 LA OK
#>% 3 New York OK
#>% 4 Chicago OK
#>% 5 Springfield OKJetzt haben sie alle die gleichen Komponenten, was man überprüfen kann mit unnest_wider():
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results)
#>% # Ein tibble: 5 x 7
#>% Stadt Adresskomponenten… formatierte_Adress… Geometrie place_id Typen Status
#>%
#>% 1 Houst… Houston, TX, USA <benannte … ChIJAYWN… OK
#>% 2 LA Los Angeles, CA… <benannte … ChIJE9on… OK
#>% 3 New Y… New York, NY, U… <benannte … ChIJOwg_… OK
#>% 4 Chica… Chicago, IL, USA <benannte … ChIJ7cv0… OK
#>% 5 Sprin… Springfield, MO… <benannte … ChIJP5jI… OKWir können die Breiten- und Längengrade jeder Stadt finden, indem wir die Liste aufklappen Geometrie:
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry)
#> # Ein tibble: 5 x 10
#> Stadt Adresskompo… formatierte_Adr… Grenzen Standort Standortstyp
#>
#> 1 Hous… Houston, TX, USA <name… 2 LA Los Angeles, CA… <name… 3 New … New York, NY, U… <name… 4 Chic… Chicago, IL, USA <name… 5 Spri… Springfield, MO… <name… # … mit 4 weiteren Variablen: viewport , place_id , types ,
#> # statusUnd dann wird der Standort benötigt, um weiter auszubauen location:
lok %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
#> # Ein Tibble: 5 x 11
#> Stadt Adressbestand… formatierte_Adress… Grenzen lat lng Standorttyp
#>
#> 1 Hous… Houston, TX, USA 2 LA Los Angeles, CA… 3 New … New York, NY, U… 4 Chic… Chicago, IL, USA 5 Spri… Springfield, MO… # … mit 4 weiteren Variablen: viewport , place_id , types ,
#> # statusEbenfalls, unnest_auto() vereinfachte die beschriebene Operation mit einigen Risiken, die durch Änderungen in der Struktur der eingehenden Daten verursacht werden können:
loc %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location)
#> Verwendung von `unnest_wider(json)`; Elemente haben 2 gemeinsame Namen
#> Verwendung von `unnest_longer(results)`; kein Element hat Namen
#> Verwendung von `unnest_wider(results)`; Elemente haben 5 gemeinsame Namen
#> Verwendung von `unnest_wider(geometry)`; Elemente haben 4 gemeinsame Namen
#> Verwendung von `unnest_wider(location)`; Elemente haben 2 gemeinsame Namen
#> # Ein Tibble: 5 x 11
#> Stadt Adressbestandteile… formatierte_Adresse… Grenzen Breite Länge Standorttyp
#>
#> 1 Hous… Houston, TX, USA 2 LA Los Angeles, CA… 3 New … New York, NY, U… 4 Chic… Chicago, IL, USA 5 Spri… Springfield, MO… # … mit 4 weiteren Variablen: Sichtfenster , place_id , Typen ,
#> # StatusWir können uns auch einfach die erste Adresse für jede Stadt ansehen:
loc
unnest_wider(json) %>
hoist(results, first_result = 1) %>
unnest_wider(first_result) %>
unnest_wider(geometry) %>
unnest_wider(location)
#> # Ein Tibble: 5 x 11
#> Stadt Adressbestandteile… formatierte_Adresse… Grenzen Breite Länge Art_der_Standort
#> <list> <list> <dbl> <dbl> <chr>
#> 1 Hous… <list [4]> Houston, TX, USA <name… 29.8 -95.4 APPROXIMATIV
#> 2 LA <list [4]> Los Angeles, CA… <name… 34.1 -118. APPROXIMATIV
#> 3 New … <list [3]> New York, NY, U… <name… 40.7 -74.0 APPROXIMATIV
#> 4 Chic… <list [4]> Chicago, IL, USA <name… 41.9 -87.6 APPROXIMATIV
#> 5 Spri… <list [5]> Springfield, MO… <name… 37.2 -93.3 APPROXIMATIV
#> # … mit 4 weiteren Variablen: viewport <list>, place_id <chr>, Typen <list>,
#> # Status <chr>Oder verwenden hoist() für eine mehrstufige Erkundung, um direkt zu Breite und Länge.
loc
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
#> # Ein Tibble: 5 x 4
#> Stadt Breite Länge json
#> <chr> <dbl> <dbl> <list>
#> 1 Houston 29.8 -95.4 <benannte Liste [2]>
#> 2 LA 34.1 -118. <benannte Liste [2]>
#> 3 New York 40.7 -74.0 <benannte Liste [2]>
#> 4 Chicago 41.9 -87.6 <benannte Liste [2]>
#> 5 Springfield 37.2 -93.3 <benannte Liste [2]>Rectangling
Abschließend betrachten wir die komplexeste Struktur – die Diskografie von Sharla Gelfand. Wie in den vorherigen Beispielen beginnen wir mit der Konvertierung der Liste in ein Dataframe mit einer Spalte und erweitern es dann, sodass jede Komponente eine separate Spalte darstellt. Außerdem werde ich die Spalte date_added in das entsprechende Datums- und Zeitformat in R umwandeln.
discs %
unnest_wider(disc) %>%
mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S")))
discs
#> # Ein tibble: 155 x 5
#> instance_id date_added basic_information id rating
#>
#> 1 354823933 2019-02-16 17:48:59 7496378 0
#> 2 354092601 2019-02-13 14:13:11 4490852 0
#> 3 354091476 2019-02-13 14:07:23 9827276 0
#> 4 351244906 2019-02-02 11:39:58 9769203 0
#> 5 351244801 2019-02-02 11:39:37 7237138 0
#> 6 351052065 2019-02-01 20:40:53 13117042 0
#> 7 350315345 2019-01-29 15:48:37 7113575 0
#> 8 350315103 2019-01-29 15:47:22 10540713 0
#> 9 350314507 2019-01-29 15:44:08 11260950 0
#> 10 350314047 2019-01-29 15:41:35 11726853 0
#> # … mit 145 weiteren ZeilenAuf dieser Ebene haben wir Informationen darüber erhalten, wann jede Festplatte zur Diskografie von Sharly hinzugefügt wurde, aber wir sehen keine Daten zu diesen Festplatten. Dafür müssen wir die Spalte erweitern. grundlegende_informationen:
discs %>% unnest_wider(basics_information)
#> Der Spaltenname `id` darf nicht dupliziert werden.
#> Verwenden Sie .name_repair, um eine Reparatur anzugeben.Leider erhalten wir einen Fehler, da es innerhalb der Liste grundlegende_informationen eine gleichnamige Spalte gibt. grundlegende_informationenBei einem solchen Fehler können Sie zur schnellen Identifizierung der Ursache names_repair = "einzigartig":
discs %>% unnest_wider(basic_information, names_repair = "unique")
# > Neue Namen:
# > * id -> id...6
# > * id -> id...14
# > # Ein Tibble: 155 x 15
# > instance_id date_added labels year artists id...6 thumb title
# >
# > 1 354823933 2019-02-16 17:48:59 <list… 2015 2 354092601 2019-02-13 14:13:11 <list… 2013 3 354091476 2019-02-13 14:07:23 <list… 2017 4 351244906 2019-02-02 11:39:58 <list… 2017 5 351244801 2019-02-02 11:39:37 <list… 2015 6 351052065 2019-02-01 20:40:53 <list… 2019 7 350315345 2019-01-29 15:48:37 <list… 2014 8 350315103 2019-01-29 15:47:22 <list… 2015 9 350314507 2019-01-29 15:44:08 <list… 2017 10 350314047 2019-01-29 15:41:35 <list… 2017 # … mit 145 weiteren Zeilen und 7 weiteren Variablen: formats ,
# > # cover_image , resource_url , master_id ,
# > # master_url , id...14 , ratingDas Problem ist, dass grundlegende_informationen der Spalte id, die auch auf oberster Ebene gespeichert ist, daher können wir sie einfach entfernen:
Scheiben %>%
select(-id) %>%
unnest_wider(basic_information)
#> # Ein Tibble: 155 x 14
#> instance_id date_added labels year artists id thumb title
#>
#> 1 354823933 2019-02-16 17:48:59 <list… 2015 2 354092601 2019-02-13 14:13:11 <list… 2013 3 354091476 2019-02-13 14:07:23 <list… 2017 4 351244906 2019-02-02 11:39:58 <list… 2017 5 351244801 2019-02-02 11:39:37 <list… 2015 6 351052065 2019-02-01 20:40:53 <list… 2019 7 350315345 2019-01-29 15:48:37 <list… 2014 8 350315103 2019-01-29 15:47:22 <list… 2015 9 350314507 2019-01-29 15:44:08 <list… 2017 10 350314047 2019-01-29 15:41:35 <list… 2017 # … mit 145 weiteren Zeilen und 6 weiteren Variablen: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingAlternativ könnten wir auch hoist():
Scheiben %>%
hoist(basic_information,
title = "title",
year = "year",
label = list("labels", 1, "name"),
artist = list("artists", 1, "name")
)
#>% # Ein Tibble: 155 x 9
#>% instance_id date_added title year label artist
#>%
#>% 1 354823933 2019-02-16 17:48:59 Demo 2015 Tobi… Mollot
#>% 2 354092601 2019-02-13 14:13:11 Obse… 2013 La V… Una B…
#>% 3 354091476 2019-02-13 14:07:23 I 2017 La V… S.H.I…
#>% 4 351244906 2019-02-02 11:39:58 Oído… 2017 La V… Rata …
#>% 5 351244801 2019-02-02 11:39:37 A Ca… 2015 Kato… Ivy (…
#>% 6 351052065 2019-02-01 20:40:53 Tash… 2019 High… Tashme
#>% 7 350315345 2019-01-29 15:48:37 Demo 2014 Mind… Desgr…
#>% 8 350315103 2019-01-29 15:47:22 Let … 2015 Not … Phant…
#>% 9 350314507 2019-01-29 15:44:08 Sub … 2017 Not … Sub S…
#>% 10 350314047 2019-01-29 15:41:35 Demo 2017 Pres… Small…
#>% # … mit 145 weiteren Zeilen und 3 weiteren Variablen: basic_information ,
#>% # id , ratingHier extrahiere ich schnell den Namen des ersten Labels und des Künstlers anhand des Index, indem ich in die verschachtelte Liste eintauche.
Ein systematischerer Ansatz besteht darin, separate Tabellen für Künstler und Label zu erstellen:
discs %>%
hoist(basic_information, artist = "artists") %>%
select(disc_id = id, artist) %>%
unnest_longer(artist) %>%
unnest_wider(artist)
#> # Ein tibble: 167 x 8
#> disc_id join name anv tracks role resource_url id
#>
#> 1 7496378 "" Mollot "" "" "" https://api.discog… 4.62e6
#> 2 4490852 "" Una Bèstia… "" "" "" https://api.discog… 3.19e6
#> 3 9827276 "" S.H.I.T. (… "" "" "" https://api.discog… 2.77e6
#> 4 9769203 "" Rata Negra "" "" "" https://api.discog… 4.28e6
#> 5 7237138 "" Ivy (18) "" "" "" https://api.discog… 3.60e6
#> 6 13117042 "" Tashme "" "" "" https://api.discog… 5.21e6
#> 7 7113575 "" Desgraciad… "" "" "" https://api.discog… 4.45e6
#> 8 10540713 "" Phantom He… "" "" "" https://api.discog… 4.27e6
#> 9 11260950 "" Sub Space … "" "" "" https://api.discog… 5.69e6
#> 10 11726853 "" Small Man … "" "" "" https://api.discog… 6.37e6
#> # … mit 157 weiteren Zeilen
discs %>%
hoist(basic_information, format = "formats") %>%
select(disc_id = id, format) %>%
unnest_longer(format) %>%
unnest_wider(format) %>%
unnest_longer(descriptions)
#> # Ein tibble: 280 x 5
#> disc_id descriptions text name qty
#>
#> 1 7496378 Nummeriert Schwarze Kassette 1
#> 2 4490852 LP Vinyl 1
#> 3 9827276 "7"" Vinyl 1
#> 4 9827276 45 RPM Vinyl 1
#> 5 9827276 EP Vinyl 1
#> 6 9769203 LP Vinyl 1
#> 7 9769203 Album Vinyl 1
#> 8 7237138 "7"" Vinyl 1
#> 9 7237138 45 RPM Vinyl 1
#> 10 13117042 "7"" Vinyl 1
#> # … mit 270 weiteren ZeilenSie können diese dann bei Bedarf wieder mit dem ursprünglichen Datensatz verbinden.
Fazit
Im Kern der Bibliothek tidyverse befinden sich zahlreiche nützliche Pakete, die eine gemeinsame Philosophie der Datenverarbeitung vereinen.
In diesem Artikel haben wir die Funktionsfamilie behandelt, GitHub-Nutzerdie sich mit dem Extrahieren von Elementen aus verschachtelten Listen befasst. Dieses Paket enthält viele weitere nützliche Funktionen, die die Datenkonvertierung gemäß dem Konzept Tidy Data.
Quelle: habr.com
