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.

Dit artikel is een logisch vervolg op de publicatie . 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 en kanalen interessant voor u zijn. Het grootste deel van de inhoud is gewijd aan de taal R.
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()ofunnest_wider().hoist()lijkt opunnest_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 , hireableIn 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/masalmonhoist() 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
#> 6Deze 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 rijenNu 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 rijenLet 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 , repoIn 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 ,
#> # homepagePersonages 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 rijenUw 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 rijenOf 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 rijenGeocoderen 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 SpringfieldDe 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 OKLet 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 OKNu 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… OKWe 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 ,
#> # statusEn 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 ,
#> # statusOpnieuw, 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 ,
#> # statusWe 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 ,
#> # statusOf 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.3Discografie 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 rijenOp 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 , ratingHet 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 , ratingAls 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 , ratingHier 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 rijenVervolgens 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
