Geneste kolommen - lijsten uitsplitsen met de programmeertaal R (het pakket tidyr en functies van de unnest-familie)

In de meeste gevallen, wanneer u werkt met de respons die u van een API hebt ontvangen, of met andere gegevens die een complexe boomstructuur hebben, komt u de JSON- en XML-indelingen tegen.

Deze indelingen hebben verschillende voordelen: ze slaan gegevens compact op en helpen bij het vermijden van overmatige duplicatie van informatie.

Het nadeel van deze indelingen is de complexiteit van hun verwerking en analyse. Ongestructureerde gegevens kunnen niet worden gebruikt in berekeningen en kunnen geen visualisaties op basis van hen opbouwen.

Geneste kolommen - lijsten uitsplitsen met de programmeertaal R (het pakket tidyr en functies van de unnest-familie)

Dit artikel is een logisch vervolg op de publicatie "De R-package tidyr en de nieuwe functies pivot_longer en pivot_wider". Het zal u helpen om ongestructureerde gegevensconstructies om te zetten naar een vertrouwd, analyse-vriendelijk tabelvorm met behulp van het package tidyr, dat deel uitmaakt van de kern van de bibliotheek tidyverse, en de functies in de familie unnest_*().

Inhoud

Als u geïnteresseerd bent in data-analyse, is het mogelijk dat mijn telegram en youtube kanalen interessant voor u zijn. Het grootste deel van de inhoud is gewijd aan de taal R.

  1. Inleiding
  2. GitHub-gebruikers
  3. GitHub-repositories
  4. Personages uit Game of Thrones
  5. Geocoderen met Google
  6. Discografie van Charlaine Harris
  7. Conclusie

Inleiding

Rectangling (opmerking van de vertaler, ik heb geen geschikte vertalingen voor deze term gevonden, daarom laat ik het zoals het is.) — is het proces van het omzetten van ongestructureerde gegevens met geneste arrays naar een tweedimensionale tabel, bestaande uit de vertrouwde rijen en kolommen. In tidyr zijn er verschillende functies die u helpen geneste lijst-kolommen uit te vouwen en de gegevens in een rechthoekige tabelvorm te brengen:

  • unnest_longer() neemt elk element van de lijst-kolom en creëert een nieuwe rij.
  • unnest_wider() neemt elk element van de lijst-kolom en creëert een nieuwe kolom.
  • unnest_auto() bepaalt automatisch welke functie het beste kan worden gebruikt
    unnest_longer() of unnest_wider().
  • hoist() lijkt op unnest_wider() maar selecteert alleen de gespecificeerde componenten en stelt u in staat om met meerdere niveaus van geneste gegevens te werken.

De meeste problemen met het omzetten van ongestructureerde gegevens met meerdere niveaus van genesting naar een tweedimensionale tabel kunnen worden opgelost door de genoemde functies met dplyr te combineren.

Voor de demonstratie van deze technieken zullen we het package repurrrsive, dat verschillende complexe, meerlagige lijsten biedt verkregen uit web-API's.

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

GitHub-gebruikers

Laten we beginnen met gh_users, een lijst die informatie bevat over zes GitHub-gebruikers. Laten we de lijst omzetten naar gh_users in tibble frame.:

users <- tibble( user = gh_users ) 

Het lijkt een beetje onlogisch: waarom een lijst gh_users, naar een complexere datstructuur? Maar het datastructuur heeft een groot voordeel: het combineert verschillende vectoren, zodat alles in één object wordt gevolgd.

Elk element van het object gebruikers is een genummerde lijst waarin elk element een kolom vertegenwoordigt.

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"

Er zijn twee manieren om de lijstcomponenten om te zetten in kolommen. unnest_wider() neemt elke component en maakt er een nieuwe kolom van:

users %>% unnest_wider(user)
#> # A 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.…
#> # … met 23 meer variabelen: 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 dit geval hebben we een tabel gekregen van 30 kolommen, en de meeste daarvan hebben we niet nodig, dus we kunnen in plaats daarvan unnest_wider() gebruikt hoist(). hoist() het stelt ons in staat om geselecteerde componenten te extraheren met dezelfde syntaxis als purrr::pluck():

gebruikers %> % hoist(user, 
  volgers = "volgers", 
  login = "login", 
  url = "html_url"
)
#> # Een tibble: 6 x 4
#>   volgers login       url                            gebruiker             
#>                                                    
#> 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() verwijdert de opgegeven benoemde componenten uit de lijst-kolom gebruiker, zodat je het kunt beschouwen als hoist() een verplaatsing van componenten uit de interne lijst van de data frame naar het hoogste niveau.

GitHub-repositories

Lijstaand gh_repos beginnen we op dezelfde manier door het om te zetten naar tibble:

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

Deze keer zijn de elementen gebruiker een lijst van repositories die aan deze gebruiker toebehoren. Elke repository is een afzonderlijke waarneming, dus volgens het concept van nette gegevens (opmerking: nette data) moeten ze nieuwe rijen worden, daarom gebruiken we unnest_longer() in plaats van unnest_wider():

repos % unnest_longer(repo)
repos
#> # Een tibble: 176 x 1
#>    repo             
#>               
#>  1 
#>  2 
#>  3 
#>  4 
#>  5 
#>  6 
#>  7 
#>  8 
#>  9 
#> 10 
#> # … met 166 meer rijen

Nu kunnen we gebruiken unnest_wider() of hoist() :

repos %>% hoist(repo, 
  login = c("eigenaar", "login"), 
  naam = "naam",
  homepage = "homepage",
  kijkers = "watchers_count"
)
#> # Een tibble: 176 x 5
#>    login       naam        homepage kijkers 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 
#> # … met 166 meer rijen

Let op het gebruik van c("eigenaar", "login"): dit stelt ons in staat om de waarde van het tweede niveau uit de geneste lijst te verkrijgen ownerEen alternatief is om de volledige lijst te verkrijgen owner en vervolgens met behulp van de functie unnest_wider() elk element in een kolom te plaatsen:

repos %>% 
  hoist(repo, owner = "owner") %>% 
  unnest_wider(owner)
#> # Een 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.…
#> # … met 166 meer rijen en 11 meer variabelen: following_url ,
#> #   gists_url , starred_url , subscriptions_url ,
#> #   organizations_url , repos_url , events_url ,
#> #   received_events_url , type , site_admin , repo

In plaats van na te denken over de keuze van de juiste functie unnest_longer() of unnest_wider() kun je gebruiken unnest_auto(). Deze functie maakt gebruik van verschillende heuristische methoden om de meest geschikte functie voor datatransformatie te vinden en geeft een bericht over de gekozen aanpak.

tibble(repo = gh_repos) %>% 
  unnest_auto(repo) %>% 
  unnest_auto(repo)
#> Gebruik `unnest_longer(repo)`; geen enkel element heeft namen
#> Gebruik `unnest_wider(repo)`; elementen hebben 68 namen gemeen
#> # Een tibble: 176 x 67
#>        id naam  volledige_naam eigenaar privé html_url beschrijving fork  url  
#>                                  
#>  1 6.12e7 na  gaborcsa…   2 4.05e7 argu… gaborcsa…   3 3.64e7 vraag gaborcsa…   4 3.49e7 basis 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…  # … met 166 meer rijen, en 58 meer variabelen: 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 , taal , heeft_issues ,
#> #   heeft_downloads , heeft_wiki , heeft_pages ,
#> #   forks_count , open_issues_count , forks ,
#> #   open_issues , watchers , default_branch ,
#> #   homepage

Personages uit Game of Thrones

got_chars heeft een identieke structuur met gh_users: dit is een verzameling van genummerde lijsten, waarbij elk element van de innerlijke lijst een bepaald kenmerk van een personage uit Game of Thrones beschrijft. We beginnen met het omzetten got_chars in tabelvorm door eerst een dataframe te creëren, net zoals in de eerder gegeven voorbeelden, en vervolgens elk element in een aparte kolom te plaatsen:

chars  # A tibble: 30 x 1
#>    char             
#>               
#>  1 
#>  2 
#>  3 
#>  4 
#>  5 
#>  6 
#>  7 
#>  8 
#>  9 
#> 10 
#> # … met 20 meer rijen

chars2 % unnest_wider(char)
chars2
#> # A 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… Man    Ironbo… In 2… ""    TRUE  <chr … <chr [… ""    
#>  2 http…  1052 Tyri… Man    ""      In 2… ""    TRUE  <chr … <chr [… ""    
#>  3 http…  1074 Vict… Man    Ironbo… In 2… ""    TRUE  <chr … <chr [… ""    
#>  4 http…  1109 Will  Man    ""      ""    In 2… FALSE <chr … <chr [… ""    
#>  5 http…  1166 Areo… Man    Norvos… In 2… ""    TRUE  <chr … <chr [… ""    
#>  6 http…  1267 Chett Man    ""      At H… In 2… FALSE <chr … <chr [… ""    
#>  7 http…  1295 Cres… Man    ""      In 2… In 2… FALSE <chr … <chr [… ""    
#>  8 http…   130 Aria… Vrouw  Dornish In 2… ""    TRUE  <chr … <chr [… ""    
#>  9 http…  1303 Daen… Vrouw  Valyri… In 2… ""    TRUE  <chr … <chr [… ""    
#> 10 http…  1319 Davo… Man    Wester… In 2… ""    TRUE  <chr … <chr [… ""    
#> # … met 20 meer rijen, en 7 meer variabelen: moeder <chr>, partner <chr>,
#> #   loyaliteiten <list>, boeken <list>, povBoeken <list>, tvSeries <list>,
#> #   gespeeldDoor <list>

Structuur got_chars iets moeilijker dan gh_users, omdat sommige onderdelen van de lijst char op zichzelf lijsten zijn, wat resulteert in kolommen — lijsten:

chars2 %>% select_if(is.list)
#> # A tibble: 30 x 7
#>    titles    aliases    loyaliteiten boeken     povBoeken  tvSeries  gespeeldDoor 
#>    <list>    <list>     <list>      <list>    <list>    <list>    <list>   
#>  1 <chr [3]> <chr [4]>  <chr [1]>   <chr [3]> <chr [2]> <chr [6]> <chr [1]>
#>  2 <chr [2]> <chr [11]> <chr [1]>   <chr [2]> <chr [4]> <chr [6]> <chr [1]>
#>  3 <chr [2]> <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr [1]> <chr [1]>
#>  4 <chr [1]> <chr [1]>  <???>       <chr [1]> <chr [1]> <chr [1]> <chr [1]>
#>  5 <chr [1]> <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr [2]> <chr [1]>
#>  6 <chr [1]> <chr [1]>  <???>       <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#>  7 <chr [1]> <chr [1]>  <???>       <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#>  8 <chr [1]> <chr [1]>  <chr [1]>   <chr [4]> <chr [1]> <chr [1]> <chr [1]>
#>  9 <chr [5]> <chr [11]> <chr [1]>   <chr [1]> <chr [4]> <chr [6]> <chr [1]>
#> 10 <chr [4]> <chr [5]>  <chr [2]>   <chr [1]> <chr [3]> <chr [5]> <chr [1]>
#> # … met 20 meer rijen

Uw volgende stappen hangen af van uw analysem doelen. Mogelijk moet u informatie over elk boek en elke serie waarin het personage voorkomt in de rijen plaatsen:

chars2 %>% 
  select(name, books, tvSeries) %>% 
  pivot_longer(c(books, tvSeries), names_to = "media", values_to = "value") %>% 
  unnest_longer(value)
#> # A 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
#> # … met 170 meer rijen

Of misschien wilt u een tabel maken die het personage en het werk aan elkaar koppelt:

chars2 %>% 
  select(name, title = titles) %>% 
  unnest_longer(title)
#> # A 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             ""                                                  
#> # … met 50 meer rijen

(Let op de lege waarden "" in het veld title, dit komt door fouten die zijn gemaakt bij het invoeren van gegevens in got_chars: eigenlijk zouden personages waarvoor geen bijbehorende titels van boeken en series in het veld zijn, title een vector van lengte 0 moeten hebben, en niet een vector van lengte 1 met een lege string.)

We kunnen het bovenstaande voorbeeld herschrijven met behulp van de functie unnest_auto(). Deze aanpak is handig voor eenmalige analyses, maar het is niet verstandig om te vertrouwen op unnest_auto() voor regelmatig gebruik. Het probleem is dat als uw datastructuur verandert, unnest_auto() kan de gekozen methode voor gegevenstransformatie veranderen; als het aanvankelijk kolommenlijsten naar rijen omzet met behulp van unnest_longer(), dan kan de logica bij wijzigingen in de inkomende gegevens verschuiven naar unnest_wider(), en het gebruik van zo'n aanpak voor de lange termijn kan leiden tot onvoorziene fouten.

tibble(char = got_chars) %>% 
  unnest_auto(char) %>% 
  select(name, title = titles) %>% 
  unnest_auto(title)
#> Gebruik `unnest_wider(char)`; elementen hebben 18 namen gemeen
#> Gebruik `unnest_longer(title)`; geen enkel element heeft namen
#> # Een tibble: 60 x 2
#>    naam               titel                                               
#>                                                                
#>  1 Theon Greyjoy     Prins van Winterfell                                
#>  2 Theon Greyjoy     Kapitein van Sea Bitch                             
#>  3 Theon Greyjoy     Heer van de IJzeren Eilanden (volgens de wet van de groene landen)
#>  4 Tyrion Lannister  Hand van de Koning (voormalig)                     
#>  5 Tyrion Lannister  Meester van de Munt (voormalig)                    
#>  6 Victarion Greyjoy Heer Kapitein van de IJzervloot                     
#>  7 Victarion Greyjoy Meester van de IJzeren Overwinning                   
#>  8 Will              ""                                                  
#>  9 Areo Hotah        Kapitein van de Garde bij Sunspear                 
#> 10 Chett             ""                                                  
#> # … met 50 meer rijen

Geocoderen met Google

Laten we een complexere gegevensstructuur bekijken die afkomstig is van de Google-geocoderingdienst. Het cachen van inloggegevens is in strijd met de gebruiksvoorwaarden voor de Google Maps API, dus ik zal eerst een eenvoudige wrapper voor de API schrijven. Deze is gebaseerd op het opslaan van de API-sleutel van Google Maps in een omgevingsvariabele; als je geen sleutel hebt opgeslagen in de omgevingsvariabelen voor gebruik met de Google Maps API, zullen de codesegmenten die in dit gedeelte worden gepresenteerd niet worden uitgevoerd.

has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
  message("Geen Google Maps API-sleutel gevonden; codesegmenten zullen niet worden uitgevoerd")
}

# 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)
}

De lijst die deze functie retourneert, is nogal complex:

houston  Lijst van 2
#>  $ resultaten: Lijst van 1
#>   ..$ : Lijst van 5
#>   .. ..$ adres_componenten: Lijst van 4
#>   .. .. ..$ : Lijst van 3
#>   .. .. .. ..$ lange_naam : chr "Houston"
#>   .. .. .. ..$ korte_naam: chr "Houston"
#>   .. .. .. ..$ types     : Lijst van 2
#>   .. .. .. .. ..$ : chr "locality"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ : Lijst van 3
#>   .. .. .. ..$ lange_naam : chr "Harris County"
#>   .. .. .. ..$ korte_naam: chr "Harris County"
#>   .. .. .. ..$ types     : Lijst van 2
#>   .. .. .. .. ..$ : chr "administrative_area_level_2"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ : Lijst van 3
#>   .. .. .. ..$ lange_naam : chr "Texas"
#>   .. .. .. ..$ korte_naam: chr "TX"
#>   .. .. .. ..$ types     : Lijst van 2
#>   .. .. .. .. ..$ : chr "administrative_area_level_1"
#>   .. .. .. .. ..$ : chr "political"
#>   .. .. ..$ : Lijst van 3
#>   .. .. .. ..$ lange_naam : chr "Verenigde Staten"
#>   .. .. .. ..$ korte_naam: chr "US"
#>   .. .. .. ..$ types     : Lijst van 2
#>   .. .. .. .. ..$ : chr "country"
#>   .. .. .. .. ..$ : chr "political"
#>   .. ..$ geformatteerd_adres : chr "Houston, TX, VS"
#>   .. ..$ geometrie          : Lijst van 4
#>   .. .. ..$ grenzen       : Lijst van 2
#>   .. .. .. ..$ noordoost: Lijst van 2
#>   .. .. .. .. ..$ lat: num 30.1
#>   .. .. .. .. ..$ lng: num -95
#>   .. .. .. ..$ zuidwest: Lijst van 2
#>   .. .. .. .. ..$ lat: num 29.5
#>   .. .. .. .. ..$ lng: num -95.8
#>   .. .. ..$ locatie     : Lijst van 2
#>   .. .. .. ..$ lat: num 29.8
#>   .. .. .. ..$ lng: num -95.4
#>   .. .. ..$ locatie_type: chr "APPROXIMATE"
#>   .. .. ..$ viewport     : Lijst van 2
#>   .. .. .. ..$ noordoost: Lijst van 2
#>   .. .. .. .. ..$ lat: num 30.1
#>   .. .. .. .. ..$ lng: num -95
#>   .. .. .. ..$ zuidwest: Lijst van 2
#>   .. .. .. .. ..$ lat: num 29.5
#>   .. .. .. .. ..$ lng: num -95.8
#>   .. ..$ plaats_id          : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#>   .. ..$ types             : Lijst van 2
#>   .. .. ..$ : chr "locality"
#>   .. .. ..$ : chr "political"
#>  $ status : chr "OK"

Gelukkig kunnen we stap voor stap het probleem oplossen om deze gegevens in tabelvorm te verwerken met behulp van functies tidyr. Om de taak iets ingewikkelder en realistischer te maken, begin ik met het geocoderen van verschillende steden:

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

Het verkregen resultaat zet ik om in tibble, en ik voeg voor de duidelijkheid een kolom toe met de overeenkomstige stadsnaam.

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

De eerste laag bevat de componenten status en resultaat, die we kunnen uitbreiden met behulp van unnest_wider() :

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

Let op dat resultaten is een gelaagde lijst. De meeste steden hebben 1 element (dat een unieke waarde vertegenwoordigt die overeenkomt met de geocodering API), maar Springfield heeft er twee. We kunnen ze in aparte rijen halen met behulp van unnest_longer() :

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

Nu hebben ze allemaal dezelfde componenten, wat kan worden bevestigd met behulp van unnest_wider():

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results) %>% 
  unnest_wider(results)
#> # Een 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

We kunnen de coördinaten van de breedte- en lengtegraad van elke stad vinden door de lijst uit te klappen geometry:

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results) %>% 
  unnest_wider(results) %>% 
  unnest_wider(geometry)
#> # Een 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…  # … met 4 meer variabelen: viewport , place_id , types ,
#> #   status

En dan de locatie, waarvoor het nodig is om uit te klappen locatie:

loc %>%
  unnest_wider(json) %>%
  unnest_longer(results) %>%
  unnest_wider(results) %>%
  unnest_wider(geometry) %>%
  unnest_wider(location)
#> # A tibble: 5 x 11
#>   city  address_compone… formatted_addre… bounds   lat    lng location_type
#>                                       
#> 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…  # … with 4 more variables: viewport , place_id , types ,
#> #   status

Opnieuw, unnest_auto() vereenvoudigt de beschreven operatie met bepaalde risico's die kunnen ontstaan door wijzigingen in de structuur van de binnenkomende gegevens:

loc %>%
  unnest_auto(json) %>%
  unnest_auto(results) %>%
  unnest_auto(results) %>%
  unnest_auto(geometry) %>%
  unnest_auto(location)
#> Using `unnest_wider(json)`; elements have 2 names in common
#> Using `unnest_longer(results)`; no element has names
#> Using `unnest_wider(results)`; elements have 5 names in common
#> Using `unnest_wider(geometry)`; elements have 4 names in common
#> Using `unnest_wider(location)`; elements have 2 names in common
#> # A tibble: 5 x 11
#>   city  address_compone… formatted_addre… bounds   lat    lng location_type
#>                                       
#> 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…  # … with 4 more variables: viewport , place_id , types ,
#> #   status

We kunnen ook gewoon naar het eerste adres voor elke stad kijken:

loc %>%
  unnest_wider(json) %>%
  hoist(results, first_result = 1) %>%
  unnest_wider(first_result) %>%
  unnest_wider(geometry) %>%
  unnest_wider(location)
#> # A tibble: 5 x 11
#>   city  address_compone… formatted_addre… bounds   lat    lng location_type
#>                                       
#> 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…  # … with 4 more variables: viewport , place_id , types ,
#> #   status

Of gebruik hoist() voor multidimensioneel duiken, om direct toegang te krijgen tot lat en lng.

loc %>%
  hoist(json,
    lat = list("results", 1, "geometry", "location", "lat"),
    lng = list("results", 1, "geometry", "location", "lng")
  )
#> # Een tibble: 5 x 4
#>   city          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

Discografie van Charlaine Harris

In de conclusie bekijken we de meest complexe structuur — de discografie van Sharly Gelfand. Zoals in de bovenstaande voorbeelden beginnen we met het omzetten van de lijst naar een data frame met één kolom, en breiden deze vervolgens uit zodat elk onderdeel een aparte kolom is. Ook zal ik de kolom date_added omzetten naar het juiste datum- en tijdformaat in R.

discs % 
  unnest_wider(disc) %>% 
  mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S"))) 
discs
#> # Een 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
#> # … met 145 meer rijen

Op dit niveau hebben we informatie gekregen over wanneer elk schijf in de discografie van Sharly werd toegevoegd, maar we zien nog geen gegevens over deze schijven. Hiervoor moeten we de kolom basic_information:

discs %>% unnest_wider(basic_information)
#> Kolomnaam `id` mag niet gedupliceerd worden.
#> Gebruik .name_repair om reparatie aan te geven.

Helaas krijgen we een foutmelding, omdat er binnen de lijst basic_information een kolom met dezelfde naam is. basic_informationWanneer zo'n foutmelding optreedt, kan je om snel de oorzaak ervan te achterhalen, names_repair = "unique":

discs %>% unnest_wider(basic_information, names_repair = "unique")
#>% Nieuwe namen:
#>% * id -> id...6
#>% * id -> id...14
#>% # Een 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 % # … met 145 meer rijen, en 7 meer variabelen: formats ,
#>% #   cover_image , resource_url , master_id ,
#>% #   master_url , id...14 , rating

Het probleem is dat basic_information de kolom id herhaalt die ook op het hoogste niveau is opgeslagen, dus kunnen we deze gewoon verwijderen:

discs %>% 
  select(-id) %>% 
  unnest_wider(basic_information)
#>% # Een 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 % # … met 145 meer rijen, en 6 meer variabelen: formats ,
#>% #   cover_image , resource_url , master_id ,
#>% #   master_url , rating

Als alternatief zouden we kunne gebruiken hoist():

schijven %>% 
  hoist(basic_information,
    title = "title",
    year = "year",
    label = list("labels", 1, "name"),
    artist = list("artists", 1, "name")
  )
#> # Een 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…
#> # … met 145 meer rijen, en 3 meer variabelen: basic_information ,
#> #   id , rating

Hier haal ik snel de naam van het eerste label en de uitvoerende artiest op basis van de index uit de geneste lijst.

Een meer systematische aanpak is om aparte tabellen voor de uitvoerende artiest en het label te maken:

schijven %>% 
  hoist(basic_information, artist = "artists") %>% 
  select(disc_id = id, artist) %>% 
  unnest_longer(artist) %>% 
  unnest_wider(artist)
#> # Een tibble: 167 x 8
#>     disc_id join  naam        anv   tracks rol  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
#> # … met 157 meer rijen

schijven %>% 
  hoist(basic_information, format = "formats") %>% 
  select(disc_id = id, format) %>% 
  unnest_longer(format) %>% 
  unnest_wider(format) %>% 
  unnest_longer(descriptions)
#> # Een tibble: 280 x 5
#>     disc_id beschrijvingen tekst  naam     qty  
#>                     
#>  1  7496378 Genummerd     Zwarte Cassette 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    
#> # … met 270 meer rijen

Vervolgens kunt u ze indien nodig terugkoppelen aan de oorspronkelijke dataset.

Conclusie

In de kern van de bibliotheek tidyverse zitten veel nuttige pakketten die zijn samengevoegd met een gezamenlijke filosofie voor gegevensverwerking.

In dit artikel hebben we de functie-familie bekeken unnest_*(), die is gericht op het werken met het extraheren van elementen uit geneste lijsten. Dit pakket bevat tal van andere nuttige functies die de gegevensconversie vergemakkelijken in overeenstemming met het concept van Tidy Data.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster