În cele mai multe cazuri, lucrând cu răspunsul obținut de la API sau cu alte date care au o structură complexă de tip arbore, întâlniți formatele JSON și XML.
Aceste formate au numeroase avantaje: ele stochează datele într-un mod relativ compact și permit evitarea duplicării excesive a informațiilor.
Un dezavantaj al acestor formate este complexitatea de procesare și analiză. Datele nestructurate nu pot fi utilizate în calcule și nu pot fi folosite pentru a crea vizualizări.

Acest articol este o continuare logică a publicației . El vă va ajuta să transformați construcțiile de date nestructurate într-o formă tabelară familiare, potrivită pentru analiză, cu ajutorul pachetului tidyr, care face parte din nucleul bibliotecii tidyverse, și a funcțiilor sale din familia unnest_*().
Cuprins
Dacă sunteți interesat de analiza datelor, s-ar putea să vă intereseze canalele mele. și Marea parte a conținutului lor este dedicată limbajului R.
Introducere
Rectangulare (nota traducătorului, nu am găsit variante adecvate pentru a traduce acest termen, așa că îl vom lăsa așa cum este.) — este procesul de transformare a datelor nestructurate cu array-uri imbricate într-un tabel bidimensional, format din linii și coloane familiare. În tidyr există câteva funcții care vă vor ajuta să desfășurați coloanele-listă și să aduceți datele într-o formă rectangulară, tabelară:
unnest_longer()preia fiecare element al coloanei-listă și creează un nou rând.unnest_wider()preia fiecare element al coloanei-listă și creează o nouă coloană.unnest_auto()determină automat care dintre funcții este mai bun de utilizat
unnest_longer()sauunnest_wider().hoist()se aseamănă cuunnest_wider()dar selectează doar componentele specificate și permite lucrul cu mai multe niveluri de imbricare.
Cele mai multe probleme legate de transformarea datelor nestructurate cu mai multe niveluri de imbricare într-un tabel bidimensional pot fi soluționate prin combinarea funcțiilor enumerate cu dplyr.
Pentru a demonstra aceste tehnici, vom folosi pachetul repurrrsive, care oferă mai multe liste complexe și multi-nivel, obținute de la un API web.
library(tidyr)
library(dplyr)
library(repurrrsive)Utilizatorii GitHub
Să începem cu gh_users, o listă care conține informații despre șase utilizatori GitHub. Mai întâi, să transformăm lista gh_users în tibble cadru.:
users <- tibble(user = gh_users) Acest lucru pare puțin nelogic: de ce să folosești o listă gh_users, într-o structură de date mai complexă? Dar cadrul de date are un mare avantaj: reunește mai mulți vectori, astfel încât totul este urmărit într-un singur obiect.
Fiecare element al obiectului aveau adrese de email pe domeniile reprezintă o listă denumită, în care fiecare element reprezintă o coloană.
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"Există două moduri de a transforma componentele listelor în coloane. unnest_wider() preia fiecare componentă și creează o coloană nouă:
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.…
#> # … cu 23 de variabile suplimentare: 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În acest caz, am obținut un tabel format din 30 de coloane, iar majoritatea dintre ele nu ne vor fi necesare, așa că putem în loc de unnest_wider() folosi hoist(). hoist() ne permite să extragem componentele selectate folosind aceeași sintaxă ca și purrr::pluck():
utilizatori %>% hoist(user,
followers = "followers",
login = "login",
url = "html_url"
)
#> # Un 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() șterge componentele numite specificate din lista-coloană utilizator, așa că poți considera hoist() ca mutarea componentelor din lista internă a cadrului de date la nivelul său superior.
Repositoarele Github
Alinierea listei gh_repos începem similar, transformându-l în tibble:
repos <- tibble(repo = gh_repos)
repos
#> # Un tibble: 6 x 1
#> repo
#> <list>
#> 1 <list [30]>
#> 2 <list [30]>
#> 3 <list [30]>
#> 4 <list [26]>
#> 5 <list [30]>
#> 6 <list [30]>De data aceasta, elementele utilizator reprezintă o listă de repozitorii care aparțin acestui utilizator. Fiecare repozitoriu este o observație separată, așa că, conform conceptului de date curate (notă: tidy data) ele trebuie să devină noi rânduri, motiv pentru care folosim unnest_longer() și nu unnest_wider():
repos <- repos %>% unnest_longer(repo)
repos
#> # Un 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]>
#> # … cu 166 de rânduri suplimentareAcum putem folosi unnest_wider() sau hoist() :
repos %>% hoist(repo,
login = c("owner", "login"),
name = "name",
homepage = "homepage",
watchers = "watchers_count"
)
#> # Un 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]>
#> # … cu 166 de rânduri suplimentareAtenție la utilizarea c("owner", "login"): acest lucru ne permite să obținem valoarea de nivelul doi din lista încrustată ownerO abordare alternativă constă în a obține întreaga listă owner și apoi, folosind o funcție unnest_wider() să plasăm fiecare element într-o coloană:
repos %>%
hoist(repo, owner = "owner") %>%
unnest_wider(owner)
#> # A tibble: 176 x 18
#> login id avatar_url gravatar_id url html_url followers_url
#>
#> 1 gabo… 660288 https://a… "" http… https:… https://api.…
#> 2 gabo… 660288 https://a… "" http… https:… https://api.…
#> 3 gabo… 660288 https://a… "" http… https:… https://api.…
#> 4 gabo… 660288 https://a… "" http… https:… https://api.…
#> 5 gabo… 660288 https://a… "" http… https:… https://api.…
#> 6 gabo… 660288 https://a… "" http… https:… https://api.…
#> 7 gabo… 660288 https://a… "" http… https:… https://api.…
#> 8 gabo… 660288 https://a… "" http… https:… https://api.…
#> 9 gabo… 660288 https://a… "" http… https:… https://api.…
#> 10 gabo… 660288 https://a… "" http… https:… https://api.…
#> # … cu 166 de rânduri în plus și 11 variabile mai multe: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoÎn loc să reflectați asupra alegerii funcției potrivite unnest_longer() sau unnest_wider() puteți utiliza unnest_auto(). Această funcție folosește mai multe metode heuristice pentru a determina cea mai potrivită funcție de transformare a datelor și afișează un mesaj despre metoda aleasă.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Utilizând `unnest_longer(repo)`; nici un element nu are denumiri
#> Utilizând `unnest_wider(repo)`; elementele au 68 de denumiri comune
#> # Un tibble: 176 x 67
#> id name full_name owner private html_url description fork url
#>
#> 1 6.12e7 after gaborcsa… 2 4.05e7 argu… gaborcsa… 3 3.64e7 ask gaborcsa… 4 3.49e7 base… gaborcsa… 5 6.16e7 cite… gaborcsa… 6 3.39e7 clis… gaborcsa… 7 3.72e7 cmak… gaborcsa… 8 6.80e7 cmark gaborcsa… 9 6.32e7 cond… gaborcsa… <nam… FALSE https:… TRUE http…
#> 10 2.43e7 cray… gaborcsa… # … cu 166 de rânduri suplimentare și 58 de variabile suplimentare: forks_url ,
#> # keys_url , collaborators_url , teams_url ,
#> # hooks_url , issue_events_url , events_url ,
#> # assignees_url , branches_url , tags_url ,
#> # blobs_url , git_tags_url , git_refs_url ,
#> # trees_url , statuses_url , languages_url ,
#> # stargazers_url , contributors_url , subscribers_url ,
#> # subscription_url , commits_url , git_commits_url ,
#> # comments_url , issue_comment_url , contents_url ,
#> # compare_url , merges_url , archive_url ,
#> # downloads_url , issues_url , pulls_url ,
#> # milestones_url , notifications_url , labels_url ,
#> # releases_url , deployments_url , created_at ,
#> # updated_at , pushed_at , git_url , ssh_url ,
#> # clone_url , svn_url , size , stargazers_count ,
#> # watchers_count , language , has_issues ,
#> # has_downloads , has_wiki , has_pages ,
#> # forks_count , open_issues_count , forks ,
#> # open_issues , watchers , default_branch ,
#> # homepagePersonajele din Jocul Tronurilor
got_chars are identice ca structură cu gh_users: este un set de liste numite, unde fiecare element din lista interioară descrie un anumit atribut al unui personaj din Game of Thrones. Convertirea got_chars în format tabular începe cu crearea unui cadru de date, exact ca în exemplele date anterior, după care vom transforma fiecare element într-o coloană separată:
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
#> <int> <chr> <chr> <chr> <chr> <chr> <lgl> <list> <list> <chr>
#> 1 http… 1022 Theo… Male Ironbo… In 2… "" TRUE <chr … <chr [… ""
#> 2 http… 1052 Tyri… Male "" In 2… "" TRUE <chr … <chr [… ""
#> 3 http… 1074 Vict… Male Ironbo… In 2… "" TRUE <chr … <chr [… ""
#> 4 http… 1109 Will Male "" "" In 2… FALSE <chr … <chr [… ""
#> 5 http… 1166 Areo… Male Norvos… In 2… "" TRUE <chr … <chr [… ""
#> 6 http… 1267 Chett Male "" At H… In 2… FALSE <chr … <chr [… ""
#> 7 http… 1295 Cres… Male "" In 2… In 2… FALSE <chr … <chr [… ""
#> 8 http… 130 Aria… Female Dornish In 2… "" TRUE <chr … <chr [… ""
#> 9 http… 1303 Daen… Female Valyri… In 2… "" TRUE <chr … <chr [… ""
#> 10 http… 1319 Davo… Male Wester… In 2… "" TRUE <chr … <chr [… ""
#> # … with 20 more rows, and 7 more variables: mother , spouse ,
#> # allegiances , books , povBooks , tvSeries ,
#> # playedByStructura got_chars mai complicat decât gh_users, deoarece unele componente ale listei char sunt ele însele liste, rezultatul este că obținem coloane — liste:
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 rowsAcțiunile tale ulterioare depind de obiectivele analizei. Este posibil să fie necesar să introduci informații în rânduri despre fiecare carte și serial în care apare personajul:
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 rowsSau, poate vrei să creezi un tabel care să îți permită să corelezi personajul cu opera:
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(Reține că există valori goale "" în câmpul titlul, aceasta este cauzată de erori comise în momentul introducerii datelor în got_chars: de fapt, personajele pentru care nu există titluri corespunzătoare de cărți și seriale în câmpul titlul ar trebui să aibă un vector de lungime 0, nu un vector de lungime 1 care conține un șir gol.)
Putem rescrie exemplul de mai sus folosind funcția unnest_auto(). Această abordare este convenabilă pentru o analiză unică, dar nu trebuie să ne bazăm pe unnest_auto() pentru utilizare regulată. Problema este că, dacă structura datelor tale se schimbă unnest_auto() s-ar putea schimba mecanismul selectat de transformare a datelor; dacă inițial acesta desfăcea coloanele-liste în rânduri folosind unnest_longer(), atunci când structura datelor de intrare se schimbă, logica ar putea fi modificată în favoarea unnest_wider(), iar utilizarea unei astfel de abordări pe termen lung poate duce la erori neașteptate.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
# > Folosind `unnest_wider(char)`; elementele au 18 nume comune
# > Folosind `unnest_longer(title)`; niciun element nu are nume
# > # Un tibble: 60 x 2
# > name title
# >
# > 1 Theon Greyjoy Prințul de Winterfell
# > 2 Theon Greyjoy Căpitanul Bărcilor de Mare
# > 3 Theon Greyjoy Lord al Insulelor de Fier (prin legea pământurilor verzi)
# > 4 Tyrion Lannister Mâna Regelui în exercițiu (fost)
# > 5 Tyrion Lannister Stăpânul Monedei (fost)
# > 6 Victarion Greyjoy Lord Căpitan al Flotei de Fier
# > 7 Victarion Greyjoy Stăpânul Victoriei de Fier
# > 8 Will ""
# > 9 Areo Hotah Căpitanul Pazei la Sunspear
# > 10 Chett ""
# > # … cu 50 de rânduri mai multeGeocodare folosind Google
În continuare, vom explora o structură de date mai complexă obținută de la serviciul de geocodare Google. Stocarea cheilor de autentificare contravin termenilor de utilizare a API-ului Google Maps, așa că mai întâi voi scrie un simplu wrapper pentru API. Care se bazează pe stocarea cheii API Google Maps într-o variabilă de mediu; dacă nu aveți salvat în variabilele de mediu cheia pentru utilizarea API-ului Google Maps, fragmentele de cod prezentate în această secțiune nu vor fi executate.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Nu s-a găsit cheia API Google Maps; bucățile de cod nu vor fi rulate")
}
# 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)
}Lista pe care o returnează această funcție este destul de complexă:
houston Lista de 2
#> $ results:Lista de 1
#> ..$ :Lista de 5
#> .. ..$ address_components:Lista de 4
#> .. .. ..$ :Lista de 3
#> .. .. .. ..$ long_name : chr "Houston"
#> .. .. .. ..$ short_name: chr "Houston"
#> .. .. .. ..$ types :Lista de 2
#> .. .. .. .. ..$ : chr "localitate"
#> .. .. .. .. ..$ : chr "politic"
#> .. .. ..$ :Lista de 3
#> .. .. .. ..$ long_name : chr "Harris County"
#> .. .. .. ..$ short_name: chr "Harris County"
#> .. .. .. ..$ types :Lista de 2
#> .. .. .. .. ..$ : chr "nivel_administrativ_2"
#> .. .. .. .. ..$ : chr "politic"
#> .. .. ..$ :Lista de 3
#> .. .. .. ..$ long_name : chr "Texas"
#> .. .. .. ..$ short_name: chr "TX"
#> .. .. .. ..$ types :Lista de 2
#> .. .. .. .. ..$ : chr "nivel_administrativ_1"
#> .. .. .. .. ..$ : chr "politic"
#> .. .. ..$ :Lista de 3
#> .. .. .. ..$ long_name : chr "Statele Unite"
#> .. .. .. ..$ short_name: chr "US"
#> .. .. .. ..$ types :Lista de 2
#> .. .. .. .. ..$ : chr "țară"
#> .. .. .. .. ..$ : chr "politic"
#> .. ..$ formatted_address : chr "Houston, TX, SUA"
#> .. ..$ geometry :Lista de 4
#> .. .. ..$ bounds :Lista de 2
#> .. .. .. ..$ northeast:Lista de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:Lista de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. .. ..$ location :Lista de 2
#> .. .. .. ..$ lat: num 29.8
#> .. .. .. ..$ lng: num -95.4
#> .. .. ..$ location_type: chr "APROXIMAT"
#> .. .. ..$ viewport :Lista de 2
#> .. .. .. ..$ northeast:Lista de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest:Lista de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. ..$ place_id : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ types :Lista de 2
#> .. .. ..$ : chr "localitate"
#> .. .. ..$ : chr "politic"
#> $ status : chr "OK"Din fericire, putem rezolva pas cu pas problema transformării acestor date într-un format tabular folosind funcții tidyr. Pentru a face sarcina puțin mai complexă și mai realistă, voi începe cu geocodarea câtorva orașe:
city <- c ( "Houston" , "LA" , "New York" , "Chicago" , "Springfield" ) city_geo <- purrr::map (city, geocode) Rezultatul obținut îl voi transforma în tibble, pentru comoditate, voi adăuga o coloană cu numele corespunzător al orașului.
loc # A tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldPrimul nivel conține componente status și rezultat, pe care le putem desfășura folosind 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 OKRețineți că results este un listă ierarhică. Cele mai multe orașe au 1 element (reprezentând o valoare unică corespunzătoare API-ului de geocodare), dar Springfield are două. Le putem extrage în rânduri separate folosind 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 OKAcum toate au aceleași componente, ceea ce poate fi verificat folosind unnest_wider():
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results)
#> # A tibble: 5 x 7
#> city address_componen… formatted_addre… geometry place_id types status
#>
#> 1 Houst… Houston, TX, USA <named … ChIJAYWN… 2 LA Los Angeles, CA… <named … ChIJE9on… 3 New Y… New York, NY, U… <named … ChIJOwg_… 4 Chica… Chicago, IL, USA <named … ChIJ7cv0… 5 Sprin… Springfield, MO… <named … ChIJP5jI… <lis… OKPutem găsi coordonatele de latitudine și longitudine pentru fiecare oraș prin desfășurarea listei 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… # … cu 4 variabile mai: viewport , place_id , types ,
#> # statusȘi apoi locația, pentru care trebuie desfășurată location:
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 ,
#> # statusDin nou, unnest_auto() simplifică operațiunea descrisă cu unele riscuri care pot fi cauzate de schimbarea structurii datelor de intrare:
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 ,
#> # statusPutem, de asemenea, să ne uităm la prima adresă pentru fiecare oraș:
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 ,
#> # statusSau folosi hoist() pentru o aprofundare ierarhică, pentru a ajunge direct la lat și lng.
loc %>%
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
#> # A 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.3Discografia Sharlei Gelfand
În final, vom analiza cea mai complexă construcție - discografia Sharly Gelfand. La fel ca în exemplele anterioare, începem prin a converti lista într-un cadru de date cu o singură coloană, iar apoi îl vom extinde, astfel încât fiecare component să fie într-o coloană separată. De asemenea, voi transforma coloana date_added în formatul corespunzător de dată și oră în R.
discs %
unnest_wider(disc) %>%
mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S")))
discs
#> # A 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
#> # … cu 145 de rânduri suplimentareLa acest nivel, am obținut informații despre când fiecare disc a fost adăugat în discografia Sharly, dar nu vedem date despre aceste discuri. Pentru aceasta, trebuie să extindem coloana basic_information:
discs %>% unnest_wider(basic_information)
#> Numele coloanei `id` nu trebuie să fie duplicat.
#> Folosiți .name_repair pentru a specifica reparația.Din păcate, vom primi o eroare, deoarece în interiorul listei basic_information există o coloană cu același nume. basic_informationCând apare o astfel de eroare, pentru a determina rapid cauza acesteia, se poate folosi names_repair = "unique":
discuri %>% unnest_wider(basic_information, names_repair = "unique")
#> Nume noi:
#> * id -> id...6
#> * id -> id...14
#> # Un tibble: 155 x 15
#> instance_id data_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 # … cu 145 de rânduri suplimentare și 7 variabile mai: formate ,
#> # imagine_copertă , url_resursă , id_master ,
#> # url_master , id...14 , ratingProblema este că basic_information repetă coloana id care este, de asemenea, stocată la nivel înalt, deci putem să o ștergem pur și simplu:
discuri %>%
select(-id) %>%
unnest_wider(basic_information)
#> # Un 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 # … cu 145 de rânduri suplimentare și 6 variabile mai: formate ,
#> # imagine_copertă , url_resursă , id_master ,
#> # url_master , ratingCa alternativă, am putea folosi hoist():
discuri %>%
hoist(basic_information,
title = "titlu",
year = "an",
label = list("etichete", 1, "nume"),
artist = list("artiști", 1, "nume")
)
#> # 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…
#> # … cu 145 mai multe rânduri, și 3 mai multe variabile: basic_information ,
#> # id , ratingAici extrag rapid numele primei etichete și al artistului după index, adâncindu-mă în lista învelită.
O abordare mai sistematică constă în crearea unor tabele separate pentru artist și etichetă:
discuri %>%
hoist(basic_information, artist = "artiști") %>%
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
#> # … cu 157 mai multe rânduri
discuri %>%
hoist(basic_information, format = "formate") %>%
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 Numărul 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
#> # … cu 270 mai multe rânduriApoi, le poți alătura înapoi la setul de date original după cum este necesar.
Concluzie
În nucleul bibliotecii tidyverse se află numeroase pachete utile unite de o filosofie comună de procesare a datelor.
În acest articol am analizat familia de funcții unnest_*(), care se concentrează pe extragerea elementelor din listele încadrați. Acest pachet conține numeroase alte funcții utile, care simplifică transformarea datelor conform conceptului de Date Ordinate.
Sursa: habr.com
