Verschachtelte Spalten – Listen mit der Programmiersprache R erstellen (Paket tidyr und die Funktionen der unnest-Familie)

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.

Verschachtelte Spalten – Listen mit der Programmiersprache R erstellen (Paket tidyr und die Funktionen der unnest-Familie)

Dieser Artikel ist eine logische Fortsetzung der Veröffentlichung "Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider". 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 telegram und youtube Kanäle für Sie von Interesse sein. Der Großteil der Inhalte widmet sich der R-Sprache.

  1. Einführung
  2. GitHub-Repositories
  3. Charaktere aus Game of Thrones
  4. Geocodierung mit Google
  5. Diskografie von Sharly Gelfand
  6. Rectangling
  7. Fazit

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() oder unnest_wider().
  • hoist() ähnelt unnest_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 , hireable

In 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 Zeilen

Jetzt 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 Zeilen

Beachten 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 , repo

Anstatt ü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 ,
#> #   homepage

Geocodierung 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 ,
#> #   playedBy

Struktur 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 Zeilen

Ihre 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 Zeilen

Oder 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 Zeilen

Diskografie 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 Springfield

Die 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  OK

nicht 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  OK

Jetzt 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…  OK

Wir 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 ,
#> #   status

Und 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 ,
#> #   status

Ebenfalls, 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 ,
#> #   Status

Wir 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 Zeilen

Auf 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 , rating

Das 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 , rating

Alternativ 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 , rating

Hier 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 Zeilen

Sie 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

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster