Verschachtelte Spalten öffnen - Listen mit der Sprache R (Paket tidyr und Funktionen der Familie unnest)

In den meisten Fällen, wenn Sie mit der von der API erhaltenen Antwort oder mit anderen Daten arbeiten, die eine komplexe baumartige Struktur aufweisen, stoßen Sie auf die Formate JSON und XML.

Diese Formate bieten viele Vorteile: Sie speichern Daten ausreichend kompakt und vermeiden übermäßige Datenwiederholungen.

Ein Nachteil dieser Formate ist die Schwierigkeit ihrer Verarbeitung und Analyse. Unstrukturierte Daten können nicht in Berechnungen verwendet werden, und auf ihrer Grundlage kann keine Visualisierung erstellt werden.

Verschachtelte Spalten öffnen - Listen mit der Sprache R (Paket tidyr und Funktionen der Familie unnest)

Dieser Artikel ist eine logische Fortsetzung der Veröffentlichung "R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider". Er wird Ihnen helfen, unstrukturierte Datenkonstrukte in ein gewohntes, analysierbares tabellarisches Format mit Hilfe des Pakets tidyr, das zum Kern der Bibliothek gehört tidyverse, und seiner Funktionen aus der Familie unnest_*().

Inhalt

Wenn Sie sich für Datenanalyse interessieren, könnten Ihnen meine Telegram und youtube Kanäle interessieren. Der Großteil des Inhalts widmet sich der Programmiersprache R.

  1. Einführung
  2. Benutzer von GitHub
  3. GitHub-Repositories
  4. Charaktere von Game of Thrones
  5. Geokodierung mit Google
  6. Diskografie von Sharlot Gelfand
  7. Fazit

Einführung

Rectangling (Anmerkung des Übersetzers: Ich habe keine passenden Übersetzungen für diesen Begriff gefunden, daher lassen wir ihn so.) — ist der Prozess, unstrukturierte Daten mit verschachtelten Arrays in eine zweidimensionale Tabelle zu bringen, die aus gewohnten Zeilen und Spalten besteht. In tidyr gibt es mehrere Funktionen, die Ihnen helfen, verschachtelte Listen-Spalten zu entfalten und die Daten in ein rechteckiges, tabellarisches Format 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 Funktion am besten zu verwenden ist.
    unnest_longer() oder unnest_wider().
  • hoist() ähnelt der unnest_wider() aber filtert nur die angegebenen Komponenten und ermöglicht die Arbeit mit mehreren Ebenen der Verschachtelung.

Die meisten Probleme, die mit der Umwandlung unstrukturierter Daten mit mehreren Ebenen der Verschachtelung in eine zweidimensionale Tabelle verbunden sind, können durch die Kombination der genannten Funktionen mit dplyr gelöst werden.

Um diese Techniken zu demonstrieren, werden wir das Paket repurrrsive, das mehrere komplexe, mehrstufige Listen enthält, die aus einer Web-API erhalten wurden, verwenden.

library(tidyr)
library(dplyr)
library(repurrrsive)

Benutzer von GitHub

Lassen Sie uns mit gh_users, Liste, die Informationen über sechs GitHub-Benutzer enthält. Lassen Sie uns zunächst die Liste umwandeln gh_users in Tabelle rahmen.:

users <- tibble( user = gh_users ) 

Das scheint etwas unlogisch zu sein: Warum sollte man eine Liste gh_users, in eine komplexere Datenstruktur umwandeln? Aber ein Datenrahmen hat einen großen Vorteil: Er vereint mehrere Vektoren, sodass alles in einem Objekt nachverfolgt wird.

Jedes Element des Objekts users stellt eine benannte Liste dar, in der jedes Element eine Spalte repräsentiert.

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, die Komponenten einer Liste in Spalten umzuwandeln. unnest_wider() Nimmt jede Komponente und erstellt eine neue Spalte:

users %>% unnest_wider(user)
# > # Eine Tabelle: 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 nicht benötigt werden, daher können wir anstelle von unnest_wider() man bereits etwas braucht (ich arbeite mit dem clusterfähigen Proxmox VE 5.x und ZFS über iSCSI). hoist(). hoist() erlaubt uns, ausgewählte Komponenten zu extrahieren, indem wir dieselbe Syntax verwenden wie purrr::pluck():

Benutzer %>% hoist(user, 
  followers = "followers", 
  login = "login", 
  url = "html_url"
)
#> # Ein tibble: 6 x 4
#>   followers login       url                            user             
#>                                                    
#> 1       303 gaborcsardi https://github.com/gaborcsardi 
#> 2       780 jennybc     https://github.com/jennybc     
#> 3      3958 jtleek      https://github.com/jtleek      
#> 4       115 juliasilge  https://github.com/juliasilge  
#> 5       213 leeper      https://github.com/leeper      
#> 6        34 masalmon    https://github.com/masalmon

hoist() entfernt die angegebenen benannten Komponenten aus der Spaltenliste Benutzer, daher können Sie betrachten hoist() als Verschieben von Komponenten aus der inneren Liste des Data Frames auf die oberste Ebene.

GitHub-Repositories

Ausrichten der Liste gh_repos beginnen wir ähnlich, indem wir es umwandeln in Tabelle:

repos  # Ein tibble: 6 x 1
#>   repo       
#>        
#> 1 
#> 2 
#> 3 
#> 4 
#> 5 
#> 6

Dieses Mal stellen die Elemente Benutzer eine Liste von Repositories dar, die diesem Benutzer gehören. Jedes Repository ist eine separate Beobachtung, daher sollten sie gemäß dem Konzept sauberer Daten (Anm. tidy data) neue Zeilen werden, weshalb wir verwenden unnest_longer() anstatt 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"): es ermöglicht uns, den Wert der zweiten Ebene aus der verschachtelten Liste abzurufen ownerEin 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)
#> # A 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 darüber nachzudenken, welche Funktion zu wählen unnest_longer() oder unnest_wider() können Sie verwenden unnest_auto(). Diese Funktion verwendet mehrere heuristische Methoden, um die passendste Funktion zur Transformation der Daten auszuwählen, und gibt eine Nachricht über die gewählte Methode aus.

tibble(repo = gh_repos) %>% 
  unnest_auto(repo) %>% 
  unnest_auto(repo)
#>% Mit `unnest_longer(repo)`; kein Element hat Namen
#>% Mit `unnest_wider(repo)`; Elemente haben 68 Namen gemeinsam
#>% # Ein Tibble: 176 x 67
#>%        id name  full_name owner private html_url description fork  url  
#>%                            
#>%  1 6.12e7 after 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

Charaktere von Game of Thrones

got_chars hat eine identische Struktur wie gh_users: es handelt sich um eine Sammlung von benannten Listen, bei denen jedes Element der inneren Liste ein bestimmtes Attribut eines Charakters aus Game of Thrones beschreibt. Die Umwandlung got_chars in tabellarischer Form beginnen wir mit der Erstellung eines Data Frames, ebenso wie in den zuvor genannten Beispielen, und dann konvertieren wir jedes Element in eine separate Spalte:

chars  # Ein tibble: 30 x 1
#>    char             
#>               
#>  1 
#>  2 
#>  3 
#>  4 
#>  5 
#>  6 
#>  7 
#>  8 
#>  9 
#> 10 
#> # … mit 20 weiteren Zeilen

chars2 % unnest_wider(char)
chars2
#> # Ein tibble: 30 x 18
#>    url      id name  gender culture born  died  alive titles aliases father
#>     <int> <chr> <chr>  <chr>   <chr> <chr> <lgl> <list> <list>  <chr> 
#>  1 http…  1022 Theo… Male   Ironbo… In 2… ""    TRUE  <chr … <chr [… ""    
#>  2 http…  1052 Tyri… Male   ""      In 2… ""    TRUE  <chr … <chr [… ""    
#>  3 http…  1074 Vict… Male   Ironbo… In 2… ""    TRUE  <chr … <chr [… ""    
#>  4 http…  1109 Will  Male   ""      ""    In 2… FALSE <chr … <chr [… ""    
#>  5 http…  1166 Areo… Male   Norvos… In 2… ""    TRUE  <chr … <chr [… ""    
#>  6 http…  1267 Chett Male   ""      At H… In 2… FALSE <chr … <chr [… ""    
#>  7 http…  1295 Cres… Male   ""      In 2… In 2… FALSE <chr … <chr [… ""    
#>  8 http…   130 Aria… Female Dornish In 2… ""    TRUE  <chr … <chr [… ""    
#>  9 http…  1303 Daen… Female Valyri… In 2… ""    TRUE  <chr … <chr [… ""    
#> 10 http…  1319 Davo… Male   Wester… In 2… ""    TRUE  <chr … <chr [… ""    
#> # … mit 20 weiteren Zeilen und 7 weiteren Variablen: mother <chr>, spouse <chr>,
#> #   allegiances <list>, books <list>, povBooks <list>, tvSeries <list>,
#> #   playedBy <list>

Struktur got_chars ein wenig komplizierter als gh_users, da einige Komponenten der Liste char selbst wiederum Listen sind, insgesamt erhalten wir Spalten – Listen:

chars2 %>% select_if(is.list)
#> # Ein tibble: 30 x 7
#>    titles    aliases    allegiances books     povBooks  tvSeries  playedBy 
#>                                  
#>  1  <chr [4]>  <chr [1]>    <chr [2]> <chr [6]> <chr [1]>
#>  2  <chr [11]> <chr [1]>   <chr [2]> <chr [4]> <chr [6]> <chr [1]>
#>  3  <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr [1]> <chr [1]>
#>  4  <chr [1]>  <???>       <chr [1]> <chr [1]> <chr [1]> <chr [1]>
#>  5  <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr [2]> <chr [1]>
#>  6  <chr [1]>  <???>       <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#>  7  <chr [1]>  <???>       <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#>  8  <chr [1]>  <chr [1]>   <chr [4]> <chr [1]> <chr [1]> <chr [1]>
#>  9  <chr [11]> <chr [1]>   <chr [1]> <chr [4]> <chr [6]> <chr [1]>
#> 10  <chr [5]>  <chr [2]>   <chr [1]> <chr [3]> <chr [5]> <chr [1]>
#> # … mit 20 weiteren Zeilen

Ihre weiteren Schritte hängen von den Zielen der Analyse ab. Möglicherweise müssen Sie für jede Figur Informationen zu den Büchern und Serien einfügen, in denen sie erscheint:

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, die Figur 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 of Winterfell                                
#>  2 Theon Greyjoy     Captain of Sea Bitch                                
#>  3 Theon Greyjoy     Lord of the Iron Islands (by law of the green lands)
#>  4 Tyrion Lannister  Acting Hand of the King (former)                    
#>  5 Tyrion Lannister  Master of Coin (former)                             
#>  6 Victarion Greyjoy Lord Captain of the Iron Fleet                      
#>  7 Victarion Greyjoy Master of the Iron Victory                          
#>  8 Will              ""                                                  
#>  9 Areo Hotah        Captain of the Guard at Sunspear                    
#> 10 Chett             ""                                                  
#> # … mit 50 weiteren Zeilen

(Bitte beachten Sie die leeren Werte "" im Feld title, dies liegt an Fehlern, die bei der Dateneingabe gemacht wurden got_chars: Tatsächlich sollten die Figuren, für die es keine entsprechenden Titel von Büchern und Serien im Feld gibt, einen Vektor der Länge 0 und nicht einen Vektor der Länge 1, der einen leeren String enthält, haben.) title Wir können das obige Beispiel unter Verwendung der Funktion umschreiben

. Dieser Ansatz ist für eine einmalige Analyse praktisch, aber man sollte sich nicht darauf verlassen unnest_auto(), um ihn regelmäßig zu verwenden. Das Problem ist, dass wenn sich Ihre Datenstruktur ändert unnest_auto() kann der ausgewählte Mechanismus zur Datenumwandlung ändern, wenn er ursprünglich Spalten-Listen in Zeilen umgewandelt hat, indem er unnest_auto() verwendet hat, kann sich die Logik bei Änderungen der eingehenden Daten zugunsten von unnest_longer(), ändern, und die ständige Verwendung eines solchen Ansatzes kann zu unvorhergesehenen Fehlern führen. unnest_wider()ändern, und die Verwendung eines solchen Ansatzes auf Dauer kann zu unerwarteten 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 gemeinsam genutzte 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 See-Hexe                               
#>  3 Theon Greyjoy     Lord der Eiseninseln (durch das Gesetz der grünen Ländereien)
#>  4 Tyrion Lannister  Amtierender Hand des Königs (ehemalig)              
#>  5 Tyrion Lannister  Meister des Geldes (ehemalig)                       
#>  6 Victarion Greyjoy Lord Kapitän der Eisenflotte                       
#>  7 Victarion Greyjoy Meister der Eisen-Sieg                                                  
#>  8 Will              ""                                                  
#>  9 Areo Hotah        Kapitän der Garde in Sunspear                    
#> 10 Chett             ""                                                  
#> # … mit 50 weiteren Zeilen

Geokodierung mit Google

Als nächstes werden wir eine komplexere Datenstruktur betrachten, die von der Google Geocodierungsdienst bereitgestellt wird. Das Caching von Anmeldeinformationen widerspricht den AGB für die Nutzung der Google Maps API, daher werde ich zunächst ein einfaches Wrapper für die API schreiben. Dieses basiert auf der Speicherung des Google Maps API-Schlüssels in einer Umgebungsvariable; wenn der Schlüssel zur Nutzung der Google Maps API nicht in den Umgebungsvariablen gespeichert ist, werden die in diesem Abschnitt angegebenen 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  Liste von 2
#>  $ results:Liste von 1
#>   ..$ :Liste von 5
#>   .. ..$ address_components:Liste von 4
#>   .. .. ..$ :Liste von 3
#>   .. .. .. ..$ long_name : chr "Houston"
#>   .. .. .. ..$ short_name: chr "Houston"
#>   .. .. .. ..$ types     :Liste von 2
#>   .. .. .. .. ..$ : chr "locality"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ :Liste von 3
#>   .. .. .. ..$ long_name : chr "Harris County"
#>   .. .. .. ..$ short_name: chr "Harris County"
#>   .. .. .. ..$ types     :Liste von 2
#>   .. .. .. .. ..$ : chr "administrative_area_level_2"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ :Liste von 3
#>   .. .. .. ..$ long_name : chr "Texas"
#>   .. .. .. ..$ short_name: chr "TX"
#>   .. .. .. ..$ types     :Liste von 2
#>   .. .. .. .. ..$ : chr "administrative_area_level_1"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ :Liste von 3
#>   .. .. .. ..$ long_name : chr "United States"
#>   .. .. .. ..$ short_name: chr "US"
#>   .. .. .. ..$ types     :Liste von 2
#>   .. .. .. .. ..$ : chr "country"
#>   .. .. .. .. ..$ : chr "political"
#>   .. ..$ formatted_address : chr "Houston, TX, USA"
#>   .. ..$ geometry          :Liste von 4
#>   .. .. ..$ bounds       :Liste von 2
#>   .. .. .. ..$ northeast:Liste von 2
#>   .. .. .. .. ..$ lat: num 30.1
#>   .. .. .. .. ..$ lng: num -95
#>   .. .. .. ..$ southwest:Liste von 2
#>   .. .. .. .. ..$ lat: num 29.5
#>   .. .. .. .. ..$ lng: num -95.8
#>   .. .. ..$ location     :Liste von 2
#>   .. .. .. ..$ lat: num 29.8
#>   .. .. .. ..$ lng: num -95.4
#>   .. .. ..$ location_type: chr "APPROXIMATE"
#>   .. .. ..$ viewport     :Liste von 2
#>   .. .. .. ..$ northeast:Liste von 2
#>   .. .. .. .. ..$ lat: num 30.1
#>   .. .. .. .. ..$ lng: num -95
#>   .. .. .. ..$ southwest:Liste von 2
#>   .. .. .. .. ..$ lat: num 29.5
#>   .. .. .. .. ..$ lng: num -95.8
#>   .. ..$ place_id          : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#>   .. ..$ types             :Liste von 2
#>   .. .. ..$ : chr "locality"
#>   .. .. ..$ : chr "political"
#>  $ status : chr "OK"

Glücklicherweise können wir das Problem, diese Daten in eine tabellarische Form zu bringen, schrittweise mit Funktionen lösen. tidyr. Um die Aufgabe etwas schwieriger und realitätsnäher zu gestalten, werde ich mit der Geokodierung mehrerer Städte beginnen:

  city <-   c ( "Houston" , "LA" , "New York" , "Chicago" , "Springfield" )  city_geo <-   purrr::map (city, geocode) 

Das erhaltene Ergebnis werde ich umwandeln in Tabelle, und zur Vereinfachung eine Spalte mit dem entsprechenden Stadtnamen hinzufügen.

loc  # Ein tibble: 5 x 2
#>   city        json            
#>                    
#> 1 Houston     
#> 2 LA          
#> 3 New York    
#> 4 Chicago     
#> 5 Springfield

Die erste Ebene enthält die Komponenten status und result, die wir mit Hilfe von unnest_wider() :

loc %>%
  unnest_wider(json)
#> # Ein tibble: 5 x 3
#>   city        results    status
#>                
#> 1 Houston      OK    
#> 2 LA           OK    
#> 3 New York     OK    
#> 4 Chicago      OK    
#> 5 Springfield  OK

Beachten Sie, dass results ist eine mehrstufige Liste. Die meisten Städte haben ein Element (das einen уникальное значение, das dem Geokodierungs-API entspricht, darstellt), aber Springfield hat zwei. Wir können sie mit Hilfe von unnest_longer() :

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results)
#> # A tibble: 5 x 3
#>   city        results          status
#>                      
#> 1 Houston      OK    
#> 2 LA           OK    
#> 3 New York     OK    
#> 4 Chicago      OK    
#> 5 Springfield  OK

Jetzt haben sie alle die gleichen Komponenten, wie Sie mit Hilfe von unnest_wider():

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results) %>% 
  unnest_wider(results)
#> # A tibble: 5 x 7
#>   city   address_componen… formatted_addre… geometry place_id  types status
#>                                       
#> 1 Houst…         Houston, TX, USA <named … ChIJAYWN…  2 LA             Los Angeles, CA… <named … ChIJE9on…  3 New Y…         New York, NY, U… <named … ChIJOwg_…  4 Chica…         Chicago, IL, USA <named … ChIJ7cv0…  5 Sprin…         Springfield, MO… <named … ChIJP5jI… <lis… OK

Wir können die Breiten- und Längengrade jeder Stadt finden, indem wir die Liste entfalten geometrie:

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results) %>% 
  unnest_wider(results) %>% 
  unnest_wider(geometry)
#> # A tibble: 5 x 10
#>   city  address_compone… formatted_addre… bounds location location_type
#>                                       
#> 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 die Position, wofür das Entfalten erforderlich ist Standort:

loc %>%
  unnest_wider(json) %>%
  unnest_longer(results) %>%
  unnest_wider(results) %>%
  unnest_wider(geometry) %>%
  unnest_wider(location)
#> # Ein Tibble: 5 x 11
#>   Stadt   Adressenteile… formatierte_adre… 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

Wieder, unnest_auto() vereinfacht die beschriebene Operation mit einigen Risiken, die durch Änderungen der Eingabestruktur hervorgerufen 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   Adressenteile… formatierte_adre… 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

Wir können 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   Adressenteile… formatierte_adre… 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

Oder verwenden hoist() eine mehrstufige Entfaltung, um direkt zu lat und lng.

loc %>%
  hoist(json,
    lat = list("results", 1, "geometry", "location", "lat"),
    lng = list("results", 1, "geometry", "location", "lng")
  )
#> # Ein tibble: 5 x 4
#>   Stadt         lat    lng json            
#>                        
#> 1 Houston      29.8  -95.4 
#> 2 LA           34.1 -118.  
#> 3 New York     40.7  -74.0 
#> 4 Chicago      41.9  -87.6 
#> 5 Springfield  37.2  -93.3

Diskografie von Sharlot Gelfand

Zum Abschluss betrachten wir die komplexeste Struktur — die Diskografie von Sharly Gelfand. Wie in den obigen Beispielen beginnen wir mit der Konvertierung der Liste in einen Data Frame mit einer Spalte und erweitern ihn dann, sodass jede Komponente eine separate Spalte ist. Ich werde auch die Spalte date_added in das entsprechende Datum- 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          grundlegende_information       id bewertung
#>                                                 
#>  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 diesem Niveau haben wir Informationen darüber erhalten, wann jede Disk hinzugefügt wurde, aber wir sehen keine Daten zu diesen Disks. Dafür müssen wir die Spalte basics_information:

discs %>% unnest_wider(basics_information)
#> Der Spaltenname `id` darf nicht dupliziert werden.
#> Verwenden Sie .name_repair, um die Reparatur anzugeben.

Leider erhalten wir einen Fehler, da innerhalb der Liste basics_information es eine gleichnamige Spalte basics_information. Bei einem solchen Fehler kann man schnell die Ursache bestimmen, indem man names_repair = "unique":

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 basics_information die Spalte id wiederholt wird, die auch auf der obersten Ebene gespeichert ist, daher können wir sie einfach entfernen:

Discs %>% 
  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 hätten wir verwenden können hoist():

discs %>% 
  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 aus der verschachtelten Liste.

Ein systematischerer Ansatz besteht darin, separate Tabellen für den Künstler und das 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

Dann können Sie sie bei Bedarf wieder an das ursprüngliche Datenset anschließen.

Fazit

Der Kern der Bibliothek tidyverse umfasst zahlreiche nützliche Pakete, die durch eine gemeinsame Philosophie der Datenverarbeitung verbunden sind.

In diesem Artikel haben wir die Familie von Funktionen behandelt unnest_*(), die sich mit der Arbeit an der Extraktion von Elementen aus verschachtelten Listen beschäftigen. Dieses Paket enthält viele weitere nützliche Funktionen, die die Datenumwandlung gemäß dem Konzept Tidy Data.

Quelle: habr.com

60GB SSD 8Gb DDR4