I de fleste tilfælde, når du arbejder med svar fra API'et eller med andre data, der har en kompleks træstruktur, vil du støde på formaterne JSON og XML.
Disse formater har mange fordele: de opbevarer data ret kompakt og undgår unødig duplication af information.
Ulempen ved disse formater er kompleksiteten i deres behandling og analyse. Ustrukturerede data kan ikke bruges i beregninger og kan ikke danne grundlag for visualisering.

Denne artikel er en logisk fortsættelse af publikationen . Den vil hjælpe dig med at omdanne ustrukturerede datakonfigurationer til et velkendt, og analytisk egnet tabel format med hjælp fra pakken tidyr, som er en del af kernen i biblioteket tidyverse, og dets funktioner i familien unnest_*().
Indhold
Hvis du er interesseret i dataanalyse, kan mine og kanaler være interessante. Størstedelen af indholdet er dedikeret til R-sprog.
Introduktion
Rectangling (oversætterens note, jeg fandt ikke passende oversættelsesmuligheder for dette udtryk, så vi lader det være som det er.) — er processen med at bringe ustrukturerede data med indlejrede arrays ind i en todimensional tabel, der består af de velkendte rækker og kolonner. I tidyr er der flere funktioner, der kan hjælpe dig med at udvide indlejrede liste-kolonner og omdanne data til en rektangulær, tabel-lignende form:
unnest_longer()tager hvert element fra liste-kolonnen og skaber en ny række.unnest_wider()tager hvert element fra liste-kolonnen og skaber en ny kolonne.unnest_auto()bestemmer automatisk, hvilken af funktionerne der er bedst at bruge
unnest_longer()ellerunnest_wider().hoist()lignerunnest_wider()men vælger kun de specificerede komponenter og tillader arbejde med flere niveauer af indlejring.
De fleste problemer relateret til at bringe ustrukturerede data med flere niveauer af indlejring ind i en todimensional tabel kan løses ved at kombinere de nævnte funktioner med dplyr.
For at demonstrere disse teknikker vil vi bruge pakken repurrrsive, som giver flere komplekse, multilevel lister, der er opnået fra web-API'er.
library(tidyr)
library(dplyr)
library(repurrrsive)GitHub-brugere
Lad os begynde med gh_users, en liste, der indeholder information om seks GitHub-brugere. Først vil vi omdanne listen gh_users i tibble frame.:
users <- tibble( user = gh_users ) Det virker lidt ulogisk: hvorfor lave en liste gh_users, til en mere kompleks datakonstruktion? Men dataframen har en stor fordel: den samler flere vektorer, så alt holdes i ét objekt.
Hvert element i objektet brugere er en navngivet liste, hvor hvert element repræsenterer en kolonne.
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"Der er to måder at omdanne listekomponenter til kolonner på. unnest_wider() tager hver komponent og opretter en ny kolonne:
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.…
#
# … med 23 flere variable: 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 dette tilfælde fik vi en tabel bestående af 30 kolonner, og de fleste af dem har vi ikke brug for, så vi kan i stedet unnest_wider() bruges hoist(). hoist() giver os mulighed for at udtrække valgte komponenter ved hjælp af den samme syntax som purrr::pluck():
brugere %>% hoist(user,
followers = "followers",
login = "login",
url = "html_url"
)
#> # En tibble: 6 x 4
#> followers login url user
#> <int> <chr> <chr> <list>
#> 1 303 gaborcsardi https://github.com/gaborcsardi <named list [27]>
#> 2 780 jennybc https://github.com/jennybc <named list [27]>
#> 3 3958 jtleek https://github.com/jtleek <named list [27]>
#> 4 115 juliasilge https://github.com/juliasilge <named list [27]>
#> 5 213 leeper https://github.com/leeper <named list [27]>
#> 6 34 masalmon https://github.com/masalmon <named list [27]>hoist() fjerner de angivne navngivne komponenter fra listekolonnen user, så du kan betragte hoist() som en flytning af komponenter fra den indre liste i dataframen til dets øverste niveau.
GitHub-repositories
Udfladning af listen gh_repos starter vi på lignende måde ved at omdanne den til tibble:
repos <- tibble(repo = gh_repos)
repos
#> # En tibble: 6 x 1
#> repo
#> <list>
#> 1 <list [30]>
#> 2 <list [30]>
#> 3 <list [30]>
#> 4 <list [26]>
#> 5 <list [30]>
#> 6 <list [30]>Denne gang repræsenterer elementerne user en liste med repositoryer, der tilhører denne bruger. Hver repository er en separat observation, så i henhold til konceptet med pæne data (bemærk. pæne data) bør de blive nye rækker, hvilket betyder, at vi bruger unnest_longer() i stedet for unnest_wider():
repos <- repos %>% unnest_longer(repo)
repos
#> # En tibble: 176 x 1
#> repo
#> <list>
#> 1 <named list [68]>
#> 2 <named list [68]>
#> 3 <named list [68]>
#> 4 <named list [68]>
#> 5 <named list [68]>
#> 6 <named list [68]>
#> 7 <named list [68]>
#> 8 <named list [68]>
#> 9 <named list [68]>
#> 10 <named list [68]>
#> # … med 166 flere rækkerNu kan vi bruge unnest_wider() eller hoist() :
repos %>% hoist(repo,
login = c("owner", "login"),
name = "name",
homepage = "homepage",
watchers = "watchers_count"
)
#> # En tibble: 176 x 5
#> login name homepage watchers repo
#> <chr> <chr> <chr> <int> <list>
#> 1 gaborcsardi after <NA> 5 <named list [65]>
#> 2 gaborcsardi argufy <NA> 19 <named list [65]>
#> 3 gaborcsardi ask <NA> 5 <named list [65]>
#> 4 gaborcsardi baseimports <NA> 0 <named list [65]>
#> 5 gaborcsardi citest <NA> 0 <named list [65]>
#> 6 gaborcsardi clisymbols "" 18 <named list [65]>
#> 7 gaborcsardi cmaker <NA> 0 <named list [65]>
#> 8 gaborcsardi cmark <NA> 0 <named list [65]>
#> 9 gaborcsardi conditions <NA> 0 <named list [65]>
#> 10 gaborcsardi crayon <NA> 52 <named list [65]>
#> # … med 166 flere rækkerVær opmærksom på brugen af c("owner", "login"): det tillader os at få værdien i det andet niveau fra den indre liste ownerEn alternativ tilgang er at få hele listen owner og derefter med hjælp af funktionen unnest_wider() placere hvert element i en kolonne:
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 flere rækker og 11 flere variabler: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoI stedet for at overveje valget af den rigtige funktion unnest_longer() eller unnest_wider() kan du bruge unnest_auto(). Denne funktion anvender flere heuristiske metoder til at finde den mest passende funktion til datatransformationen og giver en meddelelse om den valgte tilgang.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Bruger `unnest_longer(repo)`; ingen element har navne
#> Bruger `unnest_wider(repo)`; elementer har 68 navne i fællesskab
#> # En tibble: 176 x 67
#> id navn fulde_navn ejer privat html_url beskrivelse fork url
#>
#> 1 6.12e7 efter 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… # … med 166 flere rækker, og 58 flere 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 , størrelse , stargazers_count ,
#> # watchers_count , sprog , har_issues ,
#> # har_downloads , har_wiki , har_sider ,
#> # forks_count , open_issues_count , forks ,
#> # open_issues , watchers , default_branch ,
#> # hjemmesideKarakterer fra Game of Thrones
got_chars har en identisk struktur med gh_users: det er en samling af navngivne lister, hvor hvert element i den indre liste beskriver en bestemt egenskab ved en karakter i Game of Thrones. At bringe got_chars til tabelformat starter vi med at oprette et datasæt, ligesom i de tidligere nævnte eksempler, og derefter vil vi konvertere hvert element til en separat kolonne:
chars # A tibble: 30 x 1
#> char
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … with 20 more rows
chars2 % unnest_wider(char)
chars2
#> # A tibble: 30 x 18
#> url id name gender culture born died alive titles aliases father
#> &
#> 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 … # … with 20 more rows, and 7 more variables: mother , spouse ,
#> # allegiances , books , povBooks , tvSeries ,
#> # playedByStruktur got_chars mere komplekse end gh_users, da nogle af komponenterne i listen char i sig selv er lister, hvilket resulterer i, at vi får kolonner — lister:
chars2 %>% select_if(is.list)
#> # A tibble: 30 x 7
#> titles aliases allegiances books povBooks tvSeries playedBy
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … with 20 more rowsDine videre handlinger afhænger af analyseformålene. Måske er du nødt til at placere information i rækkerne for hver bog og tv-serie, hvori karakteren optræder:
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
#> # … with 170 more rowsEller måske vil du oprette en tabel, der muliggør, at du kan matche karakteren med værket:
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 ""
#> # … with 50 more rows(Bemærk de tomme værdier "" i feltet title, det skyldes fejl begået ved dataindtastningen i got_chars: i virkeligheden bør karakterer, for hvilke der ikke er tilsvarende titler på bøger og serier i feltet title have en vektor på længde 0, ikke en vektor på længde 1, der indeholder en tom streng.)
Vi kan omskrive det ovenstående eksempel ved hjælp af funktionen unnest_auto(). Denne tilgang er praktisk til engangsanalysen, men man skal ikke stole på unnest_auto() til brug på regelmæssig basis. Problemet er, at hvis din datastruktur ændrer sig unnest_auto() kan det ændre den valgte dataomstillingsmekanisme, hvis den oprindeligt udvidede listekolonner til rækker ved hjælp af unnest_longer(), så kan logikken ændres til fordel for unnest_wider(), og brugen af en sådan tilgang på permanent basis kan føre til uventede fejl.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> Bruger `unnest_wider(char)`; elementer har 18 navne til fælles
#> Bruger `unnest_longer(title)`; intet element har navne
#> # En tibble: 60 x 2
#> navn titel
#>
#> 1 Theon Greyjoy Prins af Winterfell
#> 2 Theon Greyjoy Kaptajn af Sea Bitch
#> 3 Theon Greyjoy Herre af Jernøerne (ifølge loven fra de grønne lande)
#> 4 Tyrion Lannister Handlende for Kongen (tidligere)
#> 5 Tyrion Lannister Kasserer (tidligere)
#> 6 Victarion Greyjoy Herre Kaptajn af Jernflåden
#> 7 Victarion Greyjoy Kasserer af Jernsejren
#> 8 Will ""
#> 9 Areo Hotah Kaptajn af Vagten i Sunspear
#> 10 Chett ""
#> # … med 50 flere rækkerGeokodning med Google
Næste vil vi se på en mere kompleks datamodel, der modtages fra Google Geocoding-tjenesten. Caching af legitimationsoplysninger er i strid med betingelserne for brug af Google Maps API, så jeg vil først skrive en simpel wrapper til API'et. Den er baseret på at gemme Google Maps API-nøglen i en miljøvariabel; hvis du ikke har gemt nøglen til brug med Google Maps API i miljøvariablerne, vil kodefragmenterne præsenteret i dette afsnit ikke blive kørt.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Ingen Google Maps API-nøgle fundet; kodefragmenter vil ikke blive kørt")
}
# 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)
}Listen, som denne funktion returnerer, er ret kompleks:
houston Liste over 2
#> $ results:Liste over 1
#> ..$ :Liste over 5
#> .. ..$ address_components:Liste over 4
#> .. .. ..$ :Liste over 3
#> .. .. .. ..$ long_name : chr "Houston"
#> .. .. .. ..$ short_name: chr "Houston"
#> .. .. .. ..$ types :Liste over 2
#> .. .. .. .. ..$ : chr "locality"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste over 3
#> .. .. .. ..$ long_name : chr "Harris County"
#> .. .. .. ..$ short_name: chr "Harris County"
#> .. .. .. ..$ types :Liste over 2
#> .. .. .. .. ..$ : chr "administrative_area_level_2"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste over 3
#> .. .. .. ..$ long_name : chr "Texas"
#> .. .. .. ..$ short_name: chr "TX"
#> .. .. .. ..$ types :Liste over 2
#> .. .. .. .. ..$ : chr "administrative_area_level_1"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ :Liste over 3
#> .. .. .. ..$ long_name : chr "United States"
#> .. .. .. ..$ short_name: chr "US"
#> .. .. .. ..$ types :Liste over 2
#> .. .. .. .. ..$ : chr "country"
#> .. .. .. .. ..$ : chr "political"
#> .. ..$ formatted_address : chr "Houston, TX, USA"
#> .. ..$ geometry :Liste over 4
#> .. .. ..$ bounds :Liste over 2
#> .. .. .. ..$ northeast:Liste over 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:Liste over 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. .. ..$ location :Liste over 2
#> .. .. .. ..$ lat: num 29.8
#> .. .. .. ..$ lng: num -95.4
#> .. .. ..$ location_type: chr "APPROXIMATE"
#> .. .. ..$ viewport :Liste over 2
#> .. .. .. ..$ northeast:Liste over 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:Liste over 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. ..$ place_id : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ types :Liste over 2
#> .. .. ..$ : chr "locality"
#> .. .. ..$ : chr "political"
#> $ status : chr "OK"Heldigvis kan vi trin for trin løse problemet med at konvertere disse data til tabelformat ved hjælp af funktioner tidyr. For at gøre opgaven lidt mere kompleks og realistisk, vil jeg starte med geokodning af flere byer:
city <- c ("Houston", "LA", "New York", "Chicago", "Springfield") city_geo <- purrr::map (city, geocode) Det opnåede resultat vil jeg konvertere til tibble, og for nemheds skyld vil jeg tilføje en kolonne med det tilsvarende bynavn.
loc # En tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldDet første niveau indeholder komponenterne status og resultat, som vi kan udvide ved hjælp af unnest_wider() :
loc %>%
unnest_wider(json)
#> # En tibble: 5 x 3
#> city results status
#>
#> 1 Houston OK
#> 2 LA OK
#> 3 New York OK
#> 4 Chicago OK
#> 5 Springfield OKBemærk venligst, at results er en hierarkisk liste. De fleste byer har 1 element (som repræsenterer en unik værdi, der svarer til API-geokodning), men Springfield har to. Vi kan udtrække dem i separate rækker ved hjælp af 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 OKNu har de alle de samme komponenter, hvilket kan verificeres ved hjælp af 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… OK
#> 2 LA Los Angeles, CA… <named … ChIJE9on… OK
#> 3 New Y… New York, NY, U… <named … ChIJOwg_… OK
#> 4 Chica… Chicago, IL, USA <named … ChIJ7cv0… OK
#> 5 Sprin… Springfield, MO… <named … ChIJP5jI… OKVi kan finde bredde- og længdegrader for hver by ved at udvide listen geometry:
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… # … med 4 yderligere variabler: viewport , place_id , types ,
#> # statusOg derefter beliggenheden, hvilket kræver at udvide location:
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
#> # En 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… # … med 4 flere variabler: viewport , place_id , types ,
#> # statusIgen, unnest_auto() forenkler den beskrevne operation med visse risici, der kan opstå ved ændringer i strukturen af de indkommende data:
loc %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location)
#> Bruger `unnest_wider(json)`; elementer har 2 navne, der er fælles
#> Bruger `unnest_longer(results)`; ingen elementer har navne
#> Bruger `unnest_wider(results)`; elementer har 5 navne, der er fælles
#> Bruger `unnest_wider(geometry)`; elementer har 4 navne, der er fælles
#> Bruger `unnest_wider(location)`; elementer har 2 navne, der er fælles
#> # En 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… # … med 4 flere variabler: viewport , place_id , types ,
#> # statusVi kan også blot se på den første adresse for hver by:
loc %>%
unnest_wider(json) %>%
hoist(results, first_result = 1) %>%
unnest_wider(first_result) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
#> # En 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… # … med 4 flere variabler: viewport , place_id , types ,
#> # statusEller bruge hoist() til multi-niveau dykning for at gå direkte til lat og lng.
loc %>%
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 for Sharly Gelfand
Til slut vil vi se på den mest komplekse struktur — diskografien for Sharly Gelfand. Som i de tidligere nævnte eksempler starter vi med at konvertere en liste til en data frame med én kolonne, og derefter vil vi udvide den, så hver komponent er i sin egen kolonne. Jeg vil også omdanne kolonnen date_added til det relevante dato- og 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 flere rækkerPå dette niveau har vi fået information om, hvornår hver disk blev tilføjet til Sharlys diskografi, men vi ser stadig ingen data om disse diske. For at få disse oplysninger skal vi udvide kolonnen basic_information:
discs %>% unnest_wider(basic_information)
#> Kolonnenavn `id` må ikke dupliceres.
#> Brug .name_repair til at angive reparation.Desværre vil vi få en fejl, da der inde i listen basic_information er en kolonne med samme navn basic_information. Når en sådan fejl opstår, kan man hurtigt finde årsagen ved at bruge names_repair = "unique":
plader %>% unnest_wider(basic_information, names_repair = "unique")
#> Nye navne:
#> * 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 flere rækker, og 7 flere variabler: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , id...14 , ratingProblemet er, at basic_information gentager kolonnen id, som også findes på det øverste niveau, så vi kan blot fjerne den:
plader %>%
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 flere rækker, og 6 flere variabler: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingAlternativt kunne vi have brugt hoist():
discs %>%
hoist(basic_information,
title = "title",
year = "year",
label = list("labels", 1, "name"),
artist = list("artists", 1, "name")
)
#> # En 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 flere rækker, og 3 flere variabler: basic_information ,
#> # id , ratingHer henter jeg hurtigt navnet på den første label og kunstner ved at dykke ned i den indlejrede liste.
En mere systematisk tilgang indebærer at oprette separate tabeller for kunstneren og labelen:
discs %>%
hoist(basic_information, artist = "artists") %>%
select(disc_id = id, artist) %>%
unnest_longer(artist) %>%
unnest_wider(artist)
#> # En 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 flere rækker
discs %>%
hoist(basic_information, format = "formats") %>%
select(disc_id = id, format) %>%
unnest_longer(format) %>%
unnest_wider(format) %>%
unnest_longer(descriptions)
#> # En 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 flere rækkerDerefter kan du knytte dem tilbage til det oprindelige datasæt efter behov.
Konklusion
I kernen af biblioteket tidyverse er der mange nyttige pakker samlet under en fælles filosofi for databehandling.
I denne artikel har vi gennemgået familien af funktioner unnest_*(), der er designet til at arbejde med udtrækning af elementer fra indlejrede lister. Denne pakke indeholder mange andre nyttige funktioner, der forenkler datatransformationer i overensstemmelse med konceptet Tidy Data.
Kilde: habr.com
