I de flesta fall, när du arbetar med svar som erhållits från ett API, eller med andra data som har en komplex trädstruktur, stöter du på formaten JSON och XML.
Dessa format har många fördelar: de lagrar data kompakt och undviker onödig duplicering av information.
Nackdelen med dessa format är att de är svåra att bearbeta och analysera. Ostrukturerade data kan inte användas i beräkningar och kan inte ligga till grund för visualisering.

Denna artikel är en logisk fortsättning på publiceringen . Den kommer att hjälpa dig att omvandla ostrukturerade datakonstruktioner till ett bekant och analysvänligt tabellformat med hjälp av paketet tidyr, som ingår i kärnan av biblioteket tidyverse, och dess funktioner av typen unnest_*().
Innehåll
Om du är intresserad av dataanalys kan du vara intresserad av mina och kanaler. Största delen av innehållet handlar om R-språket.
Introduktion
Rectangling (översättarens anmärkning, jag kunde inte hitta några adekvata översättningar av denna term, så vi behåller den som den är.) — är processen att omvandla ostrukturerade data med nästlade arrayer till en tvådimensionell tabell, bestående av bekanta rader och kolumner. Det finns flera funktioner i tidyr som hjälper dig att utveckla nästlade lista-kolumner och omvandla data till rektangulärt, tabellformat:
unnest_longer()tar varje element av lista-kolumnen och skapar en ny rad.unnest_wider()tar varje element av lista-kolumnen och skapar en ny kolumn.unnest_auto()identifierar automatiskt vilken av funktionerna som är bäst att använda.
unnest_longer()ellerunnest_wider().hoist()liknar påunnest_wider()men plockar endast ut de angivna komponenterna och tillåter arbete med flera nivåer av nästling.
De flesta problemen relaterade till att omvandla ostrukturerade data med flera nivåer av nästling till en tvådimensionell tabell kan lösas genom att kombinera de nämnda funktionerna med dplyr.
För att visa dessa tekniker kommer vi att använda paketet repurrrsive, som tillhandahåller flera komplexa, fler-radiga listor hämtade från webb-API.
library(tidyr)
library(dplyr)
library(repurrrsive)GitHub-användare
Vi börjar med gh_users, en lista som innehåller information om sex GitHub-användare. Först omvandlar vi listan gh_users på tibble ram.:
users <- tibble( user = gh_users ) Det verkar lite ologiskt: varför lista gh_users, till en mer komplex databas? Men en data frame har en stor fördel: den samlar flera vektorer, så att allt spåras i ett objekt.
Varje element i objektet users är en namngiven lista där varje element representerar en kolumn.
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"Det finns två sätt att omvandla listkomponenter till kolumner. unnest_wider() tar varje komponent och skapar en ny kolumn:
users %>% unnest_wider(user)
#> # En 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.…
#> # … med 23 fler variabler: 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 , hireableI det här fallet fick vi en tabell bestående av 30 kolumner, och de flesta av dem behövs inte, därför kan vi istället unnest_wider() använda hoist(). hoist() låta oss extrahera valda komponenter med samma syntax som purrr::pluck():
användare %> hoist(user,
följare = "followers",
inloggning = "login",
url = "html_url"
)
#> # En tibble: 6 x 4
#> följare inloggning url användare
#>
#> 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() tar bort angivna namngivna komponenter från kolumnlistan användare, så du kan betrakta hoist() som att flytta komponenter från den interna listan av dataframe till dess översta nivå.
GitHub-repositorier
Justerar listan gh_repos börjar vi på samma sätt, omvandlar den till tibble:
repos # En tibble: 6 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6Denna gång representerar elementen användare en lista av repositorier som tillhör denna användare. Varje repository är en separat observation, så enligt konceptet för städade data (tidy data) de bör bli nya rader, vilket innebär att vi använder unnest_longer() snarare än unnest_wider():
repos % unnest_longer(repo)
repos
#> # En tibble: 176 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … med 166 fler raderNu kan vi använda unnest_wider() eller hoist() :
repos %>% hoist(repo,
inloggning = c("owner", "login"),
namn = "name",
startsida = "homepage",
tittare = "watchers_count"
)
#> # En tibble: 176 x 5
#> inloggning namn startsida tittare repo
#>
#> 1 gaborcsardi efter 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
#> # … med 166 fler raderObservera användningen av c("owner", "login"): detta tillåter oss att få värdet på den andra nivån från den inre listan ownerEtt alternativt angreppssätt är att hämta hela listan owner och sedan använda en funktion unnest_wider() för att placera varje element i en kolumn:
repos %>%
hoist(repo, owner = "owner") %>%
unnest_wider(owner)
#> # En 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.…
#> # … med 166 fler rader, och 11 fler variabler: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoIstället för att fundera på valet av den rätta funktionen unnest_longer() eller unnest_wider() kan du använda unnest_auto(). Denna funktion använder flera heuristiska metoder för att välja den mest lämpliga funktionen för att transformera data och ger en meddelande om den valda metoden.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Använder `unnest_longer(repo)`; inget element har namn
#> Använder `unnest_wider(repo)`; elementen har 68 gemensamma namn
#> # En tibble: 176 x 67
#> id name full_name ägare privat html_url beskrivning fork url
#>
#> 1 6.12e7 efter gaborcsa… 2 4.05e7 argu… gaborcsa… 3 3.64e7 fråga gaborcsa… 4 3.49e7 bas… gaborcsa… 5 6.16e7 citat… 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… # … med 166 fler rader, och 58 fler variabler: 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 , språk , har_problem ,
#> # har_nedladdningar , har_wiki , har_sidor ,
#> # forks_count , open_issues_count , forks ,
#> # open_issues , watchers , default_branch ,
#> # homepageKaraktärer från Game of Thrones
got_chars har en identisk struktur som gh_users: detta är en uppsättning namngivna listor där varje element i den inre listan beskriver en viss egenskap hos en karaktär i Game of Thrones. Omvandlingen got_chars till tabellformat börjar vi med att skapa en data ram, precis som i de tidigare exemplen, och sedan kommer vi att översätta varje element till en separat kolumn:
chars # En tibble: 30 x 1
#> char
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … med 20 fler rader
chars2 % unnest_wider(char)
chars2
#> # En 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 … 2 http… 1052 Tyri… Male "" In 2… "" TRUE <chr … 3 http… 1074 Vict… Male Ironbo… In 2… "" TRUE <chr … 4 http… 1109 Will Male "" "" In 2… FALSE <chr … 5 http… 1166 Areo… Male Norvos… In 2… "" TRUE <chr … 6 http… 1267 Chett Male "" At H… In 2… FALSE <chr … 7 http… 1295 Cres… Male "" In 2… In 2… FALSE <chr … 8 http… 130 Aria… Female Dornish In 2… "" TRUE <chr … 9 http… 1303 Daen… Female Valyri… In 2… "" TRUE <chr … 10 http… 1319 Davo… Male Wester… In 2… "" TRUE <chr … # … med 20 fler rader, och 7 fler variabler: mother , spouse ,
#> # allegiances , books , povBooks , tvSeries ,
#> # playedByStruktur got_chars mer komplicerat än gh_users, eftersom vissa komponenter i listan char själva är listor, vilket resulterar i kolumner — listor:
chars2 %>% select_if(is.list)
#> # En tibble: 30 x 7
#> titles aliases allegiances books povBooks tvSeries playedBy
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … med 20 fler raderDina nästa steg beror på analysens mål. Du kanske behöver placera informationen om varje bok och TV-serie där karaktären förekommer i rader:
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
#> # … med 170 fler raderEller kanske vill du skapa en tabell som gör det möjligt att koppla karaktären till verket:
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 ""
#> # … med 50 fler rader(Observera, att det finns tomma värden "" i fältet title, detta beror på fel vid inmatning av data i got_chars: egentligen bör karaktärer utan motsvarande titlar för böcker och TV-serier i fältet title ha en vektor av längd 0, inte en vektor av längd 1 som innehåller en tom sträng.)
Vi kan omskriva det ovanstående exemplet med hjälp av funktionen unnest_auto(). Denna metod är praktisk för engångsanalys, men det är inte klokt att lita på unnest_auto() för att använda den regelbundet. Frågan är att om din datastuktur förändras unnest_auto() kan det ändra den valda mekanismen för datatransformation, om den ursprungligen utgjorde kolumner-listor till rader med hjälp av unnest_longer(), så kan logiken förändras till förmån för unnest_wider(), och att använda en sådan metod på konstant basis kan leda till oförutsedda fel.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> Använder `unnest_wider(char)`; elementen har 18 gemensamma namn
#> Använder `unnest_longer(title)`; ingen element har namn
#> # En 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 (enligt lagen i de gröna länderna)
#> 4 Tyrion Lannister Acting Hand of the King (fd)
#> 5 Tyrion Lannister Master of Coin (fd)
#> 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 ""
#> # … med 50 fler raderGeokodning med Google
Vi kommer nu att titta på en mer komplex datakonstruktion som erhålls från Google Geocoding-tjänsten. Caching av autentiseringsuppgifter strider mot villkoren för att använda Google Maps API, så jag kommer först att skriva en enkel wrapper till API:et. Den baseras på att lagra Google Maps API-nyckeln i en miljövariabel; om du inte har sparat en nyckel för att använda Google Maps API i miljövariabler, kommer kodfragmenten som presenteras i det här avsnittet inte att köras.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Ingen Google Maps API-nyckel hittad; kodblock kommer inte att köras")
}
# 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)
}Listan som den här funktionen returnerar är ganska komplex:
houston List of 2
#> $ results:List of 1
#> ..$ :List of 5
#> .. ..$ address_components:List of 4
#> .. .. ..$ :List of 3
#> .. .. .. ..$ long_name : chr "Houston"
#> .. .. .. ..$ short_name: chr "Houston"
#> .. .. .. ..$ types :List of 2
#> .. .. .. .. ..$ : chr "locality"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :List of 3
#> .. .. .. ..$ long_name : chr "Harris County"
#> .. .. .. ..$ short_name: chr "Harris County"
#> .. .. .. ..$ types :List of 2
#> .. .. .. .. ..$ : chr "administrative_area_level_2"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :List of 3
#> .. .. .. ..$ long_name : chr "Texas"
#> .. .. .. ..$ short_name: chr "TX"
#> .. .. .. ..$ types :List of 2
#> .. .. .. .. ..$ : chr "administrative_area_level_1"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :List of 3
#> .. .. .. ..$ long_name : chr "United States"
#> .. .. .. ..$ short_name: chr "US"
#> .. .. .. ..$ types :List of 2
#> .. .. .. .. ..$ : chr "country"
#> .. .. .. .. ..$ : chr "political"
#> .. ..$ formatted_address : chr "Houston, TX, USA"
#> .. ..$ geometry :List of 4
#> .. .. ..$ bounds :List of 2
#> .. .. .. ..$ northeast:List of 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:List of 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. .. ..$ location :List of 2
#> .. .. .. ..$ lat: num 29.8
#> .. .. .. ..$ lng: num -95.4
#> .. .. ..$ location_type: chr "APPROXIMATE"
#> .. .. ..$ viewport :List of 2
#> .. .. .. ..$ northeast:List of 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:List of 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. ..$ place_id : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ types :List of 2
#> .. .. ..$ : chr "locality"
#> .. .. ..$ : chr "political"
#> $ status : chr "OK"Lyckligtvis kan vi steg för steg lösa problemet med att konvertera dessa data till tabellformat med hjälp av funktioner tidyr. För att göra uppgiften lite mer utmanande och realistisk kommer jag att börja med att geokoda några städer:
city <- c ( "Houston" , "LA" , "New York" , "Chicago" , "Springfield" ) city_geo <- purrr::map (city, geocode) Det resulterande resultatet kommer jag att konvertera till tibble, för att underlätta kommer jag att lägga till en kolumn med städernas motsvarande namn.
loc # A tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldDen första nivån innehåller komponenterna status och result, som vi kan expandera med hjälp av unnest_wider() :
loc %>%
unnest_wider(json)
#> # A tibble: 5 x 3
#> city results status
#>
#> 1 Houston OK
#> 2 LA OK
#> 3 New York OK
#> 4 Chicago OK
#> 5 Springfield OKObservera att results är en flerdimensionell lista. De flesta städer har 1 element (som representerar ett unikt värde som motsvarar API:et för geokodning), men Springfield har två. Vi kan extrahera dem till separata rader med hjälp av unnest_longer() :
loc %>%
unnest_wider(json) %>%
unnest_longer(results)
#> # En tibble: 5 x 3
#> city results status
#>
#> 1 Houston OK
#> 2 LA OK
#> 3 New York OK
#> 4 Chicago OK
#> 5 Springfield OKNu har alla samma komponenter, vilket kan verifieras med hjälp av unnest_wider():
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results)
#> # En 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… OKVi kan hitta latitud- och longitudkoordinater för varje stad genom att expandera listan geometry:
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry)
#> # En 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… # … med 4 fler variabler: viewport , place_id , types ,
#> # statusOch sedan platsen, vilket kräver att man expanderar plats:
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
#> # En tibble: 5 x 11
#> stad adresskomponenten… formaterad_adress… gränser lat lng plats_typ
#>
#> 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… # … med 4 fler variabler: vyfönster , plats_id , typer ,
#> # statusÅterigen, unnest_auto() förenklar den beskrevna operationen med vissa risker som kan orsakas av förändringar i strukturen av de inkommande data:
loc %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location)
#> Använder `unnest_wider(json)`; element har 2 namn gemensamt
#> Använder `unnest_longer(results)`; inget element har namn
#> Använder `unnest_wider(results)`; element har 5 namn gemensamt
#> Använder `unnest_wider(geometry)`; element har 4 namn gemensamt
#> Använder `unnest_wider(location)`; element har 2 namn gemensamt
#> # En tibble: 5 x 11
#> stad adresskomponenten… formaterad_adress… gränser lat lng plats_typ
#>
#> 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… # … med 4 fler variabler: vyfönster , plats_id , typer ,
#> # statusVi kan också bara titta på den första adressen för varje stad:
loc %>%
unnest_wider(json) %>%
hoist(results, first_result = 1) %>%
unnest_wider(first_result) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
#> # En tibble: 5 x 11
#> stad adresskomponenten… formaterad_adress… gränser lat lng plats_typ
#>
#> 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… # … med 4 fler variabler: vyfönster , plats_id , typer ,
#> # statusEller använda hoist() för flerdimensionell neddykning för att gå direkt till lat och lng.
lok %>%
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
#> # En 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.3Diskografi av Sharla Gelfand
I slutet kommer vi att titta på den mest komplexa konstruktionen — diskografin för Sharly Gelfand. Precis som i de tidigare exemplen börjar vi med att konvertera listan till en data ram med en kolumn, och sedan utvidgar vi den så att varje komponent blir en separat kolumn. Jag kommer också att omvandla kolumnen date_added till rätt datum- och tidsformat i R.
discs %
unnest_wider(disc) %>%
mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S")))
discs
#> # En 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
#> # … med 145 fler raderPå den här nivån har vi fått information om när varje skiva lades till i Sharlys diskografi, men vi har ingen information om dessa skivor. För detta behöver vi utvidga kolumnen basic_information:
discs %>% unnest_wider(basic_information)
#> Kolumnnamnet `id` får inte dupliceras.
#> Använd .name_repair för att specificera reparation.Tyvärr kommer vi att få ett fel, eftersom det finns en kolumn med samma namn basic_information . Vid sådana fel kan man snabbt identifiera orsaken genom att använda basic_informationnames_repair = "unique" om det upprepas:
skivor %>% unnest_wider(basic_information, names_repair = "unique")
#> Nya namn:
#> * id -> id...6
#> * id -> id...14
#> # En 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 # … med 145 fler rader, samt 7 fler variabler: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , id...14 , ratingProblemet är att basic_information det upprepar kolumnen id som också lagras på översta nivån, så vi kan helt enkelt ta bort den:
skivor %>%
select(-id) %>%
unnest_wider(basic_information)
#> # En 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 # … med 145 fler rader, samt 6 fler variabler: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingSom ett alternativ skulle vi kunna använda hoist():
skivor %>%
hoist(basic_information,
title = "title",
year = "year",
label = list("labels", 1, "name"),
artist = list("artists", 1, "name")
)
#> # A 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…
#> # … med 145 fler rader, och 3 fler variabler: basic_information ,
#> # id , ratingHär extraherar jag snabbt namnet på den första etiketten och artisten enligt indexet, genom att dyka ner i den inre listan.
En mer systematisk metod är att skapa separata tabeller för artist och etikett:
skivor %>%
hoist(basic_information, artist = "artists") %>%
select(disc_id = id, artist) %>%
unnest_longer(artist) %>%
unnest_wider(artist)
#> # A 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
#> # … med 157 fler rader
skivor %>%
hoist(basic_information, format = "formats") %>%
select(disc_id = id, format) %>%
unnest_longer(format) %>%
unnest_wider(format) %>%
unnest_longer(descriptions)
#> # A tibble: 280 x 5
#> disc_id descriptions text name qty
#>
#> 1 7496378 Numbered Black 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
#> # … med 270 fler raderSedan kan du återansluta dem till den ursprungliga datamängden vid behov.
Slutsats
I kärnan av biblioteket tidyverse ingår många användbara paket som förenas av en gemensam filosofi för databehandling.
I denna artikel har vi gått igenom familjen av funktioner unnest_*(), som syftar till att arbeta med extrahering av element från inbäddade listor. Detta paket innehåller många andra användbara funktioner som förenklar datakonvertering enligt konceptet Tidy Data.
Källa: habr.com
