Ve většině případů, když pracujete s odpovědí získanou z API nebo s jakýmikoli jinými daty, která mají složitou stromovou strukturu, se setkáváte s formáty JSON a XML.
Tyto formáty mají mnoho výhod: údaje ukládají poměrně kompaktně a umožňují se vyhnout nadměrnému duplicitnímu informování.
Nevýhodou těchto formátů je složitost jejich zpracování a analýzy. Nestrukturovaná data nelze použít ve výpočtech a nelze na jejich základě vytvářet vizualizace.

Tento článek je logickým pokračováním publikace . Pomůže vám přetvořit nestrukturované datové struktury do obvyklého a pro analýzu vhodného tabulkového formátu pomocí balíčku tidyr, který je součástí jádra knihovny tidyverse, a jeho funkcí rodiny unnest_*().
Obsah
Pokud se zajímáte o analýzu dat, možná vás zaujmou mé a kanály. Většina obsahu je věnována jazyku R.
Úvod
Rectangling (pozn. překladatele, nenašel jsem adekvátní varianty překladu tohoto výrazu, proto ho necháme tak, jak je.) — je proces přetvoření nestrukturovaných dat s vnořenými poli do dvourozměrné tabulky, tvořené obvyklými řádky a sloupci. V tidyr existuje několik funkcí, které vám pomohou rozbalit vnořené sloupce-seznamy a přetvořit data do obdélníkového, tabulkového formátu:
unnest_longer()vezme každý prvek seznamu-sloupce a vytvoří nový řádek.unnest_wider()vezme každý prvek seznamu-sloupce a vytvoří nový sloupec.unnest_auto()automaticky určuje, kterou z funkcí je lepší použít
unnest_longer()nebounnest_wider().hoist()je podobnýunnest_wider()ale vybírá pouze určené komponenty a umožňuje pracovat s několika úrovněmi vnoření.
Většinu problémů spojených s převedením nestrukturovaných dat s více úrovněmi vnoření do dvourozměrné tabulky lze vyřešit kombinováním uvedených funkcí s dplyr.
Pro demonstraci těchto technik budeme používat balíček repurrrsive, který poskytuje několik složitých, vícerozměrných seznamů získaných z webového API.
library(tidyr)
library(dplyr)
library(repurrrsive)Uživatelé GitHub
Začneme s gh_users, seznam, který obsahuje informace o šesti uživatelích GitHubu. Nejprve převedeme seznam gh_users v tibble data frame.:
users <- tibble( user = gh_users ) To se může zdát trochu nelogické: proč převádět seznam gh_users, na složitější datovou strukturu? Ale datový rámec má velkou výhodu: sloučí několik vektorů, takže je vše sledováno v jednom objektu.
Každý prvek objektu users je pojmenovaný seznam, kde každý prvek představuje sloupec.
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"Existují dva způsoby, jak převést komponenty seznamu na sloupce. unnest_wider() vezme každou komponentu a vytvoří nový sloupec:
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.…
#> # … s 23 dalšími proměnnými: 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 , hireableV tomto případě jsme získali tabulku o 30 sloupcích a většina z nich nebude potřebná, takže můžeme místo unnest_wider() použít hoist(). hoist() nám umožňuje extrahovat vybrané komponenty, pomocí stejné syntaxe jako purrr::pluck():
uživatelé %> % hoist(user,
založené_komponenty = "followers",
přihlášení = "login",
url = "html_url"
)
#> # A tibble: 6 x 4
#> založené_komponenty přihlášení url uživatel
#>
#> 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() odstraňuje uvedené pojmenované komponenty ze seznamu-sloupce uživatel, proto můžete považovat hoist() za přesun komponent z vnitřního seznamu datového rámce na jeho vrchní úroveň.
Republiky Github
Zarovnání seznamu gh_repos začínáme analogicky, převáděním ho na tibble:
repos # A tibble: 6 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6Tentokrát prvky uživatel představují seznam repozitářů, které patří tomuto uživateli. Každý repozitář je samostatným pozorováním, takže podle konceptu tidy data (pozn. tidy data) by měly být nový řádky, proto používáme unnest_longer() a ne unnest_wider():
repos % unnest_longer(repo)
repos
#> # A tibble: 176 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … s 166 dalšími řádkyNyní můžeme použít unnest_wider() nebo hoist() :
repos %> % hoist(repo,
přihlášení = c("owner", "login"),
název = "name",
domovská stránka = "homepage",
pozorovatelé = "watchers_count"
)
#> # A tibble: 176 x 5
#> přihlášení název domovská stránka pozorovatelé 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
#> # … s 166 dalšími řádkyVšimněte si použití c("owner", "login"): to nám umožňuje získat hodnotu druhé úrovně z vnořeného seznamu vlastníkAlternativní přístup spočívá v získání celého seznamu vlastník a poté pomocí funkce unnest_wider() vložit každý jeho prvek do sloupce:
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.…
#> # … s 166 dalšími řádky a 11 dalšími proměnnými: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoMísto abyste přemýšleli o výběru správné funkce unnest_longer() nebo unnest_wider() můžete použít unnest_auto(). Tato funkce používá několik heuristických metod k nalezení nejvhodnější funkce pro transformaci dat a vypisuje zprávu o zvoleném způsobu.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Používá `unnest_longer(repo)`; žádný prvek nemá názvy
#> Používá `unnest_wider(repo)`; prvky mají 68 společných názvů
#> # A 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… # … s 166 dalšími řádky, a 58 dalšími proměnnými: 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 ,
#> # homepagePostavy ze Hry o trůny
got_chars má identickou strukturu s gh_users: je to soubor pojmenovaných seznamů, kde každý prvek vnitřního seznamu popisuje určitou vlastnost postavy Hry o trůny. Převod got_chars do tabulkové podoby začínáme vytvořením datového rámce, stejně jako v předchozích příkladech, a poté převedeme každý prvek do samostatného sloupce:
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 ,
#> # playedByStruktura got_chars je o něco složitější než gh_users, protože některé komponenty seznamu char samy o sobě jsou seznamy, a tím se vytvářejí sloupce – seznamy:
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 rowsVaše další kroky závisí na cílech analýzy. Možná budete muset do řádků umístit informace o každé knize a seriálu, ve kterém se postava objevuje:
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 Hra o trůny
#> 2 Theon Greyjoy books Bouře mečů
#> 3 Theon Greyjoy books Hostina pro vrabce
#> 4 Theon Greyjoy tvSeries Řada 1
#> 5 Theon Greyjoy tvSeries Řada 2
#> 6 Theon Greyjoy tvSeries Řada 3
#> 7 Theon Greyjoy tvSeries Řada 4
#> 8 Theon Greyjoy tvSeries Řada 5
#> 9 Theon Greyjoy tvSeries Řada 6
#> 10 Tyrion Lannister books Hostina pro vrabce
#> # … s 170 dalšími řádkyNebo možná chcete vytvořit tabulku, která vám umožní přiřadit postavu a dílo:
chars2 %>%
select(name, title = titles) %>%
unnest_longer(title)
#> # A tibble: 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Princ z Winterfellu
#> 2 Theon Greyjoy Kapitán mořské mrchy
#> 3 Theon Greyjoy Lord Železných ostrovů (podle zákona zelených zemí)
#> 4 Tyrion Lannister Činný Ruka krále (bývalý)
#> 5 Tyrion Lannister Mistr mincí (bývalý)
#> 6 Victarion Greyjoy Lord kapitán Železné flotily
#> 7 Victarion Greyjoy Mistr Železného vítězství
#> 8 Will ""
#> 9 Areo Hotah Kapitán stráže na Sunspear
#> 10 Chett ""
#> # … s 50 dalšími řádky(Všimněte si prázdných hodnot "" v poli title, to je způsobeno chybami, které byly udělány při zadávání dat v got_chars: ve skutečnosti postavy, pro které neexistují odpovídající názvy knih a seriálů, by měly mít vektor délky 0, nikoli vektor délky 1 obsahující prázdný řetězec.) title Můžeme přepracovat výše uvedený příklad pomocí funkce
. Tento přístup je vhodný pro jednorázovou analýzu, ale není dobré se na něj spoléhat unnest_auto()pro pravidelné použití. Věc se má tak, že pokud se vaše struktura dat změní unnest_auto() může se vybraná metoda transformace dat změnit; pokud původně převáděla sloupce-seznamy na řádky pomocí unnest_auto() , pak změna struktury vstupních dat může změnit logiku ve prospěch unnest_longer(), a používání takového přístupu na stálém základě může vést k neočekávaným chybám. unnest_wider(), a používání takového přístupu na trvalé základě může vést k nepředvídaným chybám.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> Použití `unnest_wider(char)`; prvky mají 18 společných názvů
#> Použití `unnest_longer(title)`; žádný prvek nemá názvy
#> # A tibble: 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Prince z Winterfellu
#> 2 Theon Greyjoy Kapitán mořské mrchy
#> 3 Theon Greyjoy Pán železných ostrovů (podle zákona zelených zemí)
#> 4 Tyrion Lannister Jednající ruka krále (bývalý)
#> 5 Tyrion Lannister Mistr mincí (bývalý)
#> 6 Victarion Greyjoy Lord kapitán železné flotily
#> 7 Victarion Greyjoy Mistr železného vítěze
#> 8 Will ""
#> 9 Areo Hotah Kapitán stráže v Sunspear
#> 10 Chett ""
#> # … s 50 dalšími řádkyGeokódování s pomocí Google
Dále se podíváme na složitější strukturu dat získávaných ze služby geokódování Google. Ukládání přihlašovacích údajů do cache je v rozporu s podmínkami použití API Google Maps, proto nejprve napíšu jednoduchý wrapper k API. Ten bude založen na ukládání klíče API Google Maps do proměnné prostředí; pokud v prostředních proměnných nemáte uložený klíč pro práci s API Google Maps, kódové fragmenty uvedené v této části se neprovedou.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Klíč API Google Maps nenalezen; kódové bloky nebudou spuštěny")
}
# 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)
}Seznam, který tato funkce vrací, je poměrně složitý:
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"Naštěstí můžeme krok za krokem vyřešit problém převodu těchto dat do tabulkového formátu pomocí funkcí tidyr. Aby byla úloha mírně složitější a realistická, začnu geokódováním několika měst:
city <- c ("Houston", "LA", "New York", "Chicago", "Springfield") city_geo <- purrr::map (city, geocode) Výsledný údaj převedu na tibble, abych přidal sloupec s odpovídajícím názvem města.
loc # A tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldPrvní úroveň obsahuje komponenty status a result, které můžeme rozbalit pomocí 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 OKVšimněte si, že results je vícestupňový seznam. Většina měst má 1 prvek (reprezentující unikátní hodnotu odpovídající geokódovacímu API), ale Springfield má dva. Můžeme je vytáhnout do samostatných řádků pomocí 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 OKNyní mají všechny stejné komponenty, což lze ověřit pomocí 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… OKMůžeme najít souřadnice zeměpisné šířky a délky pro každé město rozbalením seznamu 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… # … with 4 more variables: viewport , place_id , types ,
#> # statusA poté polohu, k čemuž je třeba rozbalit 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 ,
#> # statusZnovu, unnest_auto() zjednodušuje popsanou operaci s některými riziky, které mohou vzniknout změnou struktury vstupních dat:
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 ,
#> # statusMůžeme se také jednoduše podívat na první adresu pro každé město:
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 ,
#> # statusNebo použít hoist() pro vícestupňové ponoření, abyste přešli přímo k lat a lng.
lok %>%
zvedni(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.3Diskografie Šarly Gelfand
Na závěr se podíváme na nejkomplexnější strukturu — diskografii Šarly Gelfandové. Stejně jako v předchozích příkladech začneme konverzí seznamu na datový rámec s jedním sloupcem a poté jej rozšíříme, aby každý komponent byl v samostatném sloupci. Také převedu sloupec date_added do odpovídajícího formátu data a času v 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
#> # … s 145 dalšími řádkyNa této úrovni jsme získali informace o tom, kdy byl každý disk přidán do diskografie Šarly, ale zároveň nevidíme žádná data o těchto discích. K tomu potřebujeme rozšířit sloupec basic_information:
discs %>% unnest_wider(basic_information)
#> Název sloupce `id` nesmí být duplikován.
#> Použijte .name_repair k určení opravy.Bohužel dostaneme chybu, protože uvnitř seznamu basic_information existuje sloupec se stejným názvem basic_information. Když dojde k podobné chybě, lze rychle zjistit její příčinu použitím names_repair = "unique":
disky %>% unnest_wider(basic_information, names_repair = "unique")
#> Nová jména:
#> * id -> id...6
#> * id -> id...14
#> # Tibbles: 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 # … s 145 dalšími řádky a 7 dalšími proměnnými: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , id...14 , ratingProblém je v tom, že basic_information opakuje sloupec id, který je také uložen na vrchní úrovni, proto ho můžeme jednoduše odstranit:
disky %>%
select(-id) %>%
unnest_wider(basic_information)
#> # Tibbles: 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 # … s 145 dalšími řádky a 6 dalšími proměnnými: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingJako alternativu bychom mohli použít hoist():
discs %>%
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…
#> # … with 145 more rows, and 3 more variables: basic_information ,
#> # id , ratingZde rychle vyextractuji jméno prvního labelu a umělce podle indexu, ponořením se do vnořeného seznamu.
Systematičtější přístup spočívá ve vytvoření samostatných tabulek pro umělce a label:
discs %>%
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
#> # … with 157 more rows
discs %>%
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
#> # … with 270 more rowsPoté je můžete podle potřeby znovu připojit k původnímu datovému souboru.
Závěr
Jádro knihovny tidyverse obsahuje řadu užitečných balíčků sjednocených společnou filosofií zpracování dat.
V tomto článku jsme probrali rodinu funkcí unnest_*(), které se zaměřují na práci s extrakcí prvků z vnořených seznamů. Tento balíček obsahuje řadu dalších užitečných funkcí, které usnadňují transformaci dat podle konceptu Úhledná data.
Zdroj: habr.com
