Beágyazzuk a beágyazott oszlopokat — listák R nyelven (tidyr csomag és az unnest funkció család) segítségével

A legtöbb esetben, amikor az API-tól kapott válasszal, vagy bármilyen más, bonyolult fa struktúrával rendelkező adattal dolgozik, a JSON és XML formátumokkal találkozik.

Ezek a formátumok számos előnnyel rendelkeznek: a data kompakt módon tárolja az információt, és elkerüli a felesleges adatduplikációt.

E formátumok hátránya a feldolgozásuk és elemzésük nehézsége. A strukturálatlan adatok nem használhatóak számításokban, és nem lehet rájuk alapozva vizualizációt készíteni.

Beágyazzuk a beágyazott oszlopokat — listák R nyelven (tidyr csomag és az unnest funkció család) segítségével

Ez a cikk logikai folytatása a közzétett "A tidyr csomag és új pivot_longer és pivot_wider funkciói". Segíteni fog abban, hogy a strukturálatlan adatstruktúrákat egy ismerős, analízisre alkalmas táblázatos formába hozza a tidyr, a könyvtár magjának részeként tidyverse, és annak unnest_*().

Tartalom

Ha érdeklik az adatelemzés, talán érdekesek lesznek a telegram és youtube csatornák. A tartalom nagyobb része R nyelvvel foglalkozik.

  1. Bevezetés
  2. GitHub felhasználók
  3. GitHub repozitóriumok
  4. Trónok harca karakterek
  5. Geokódolás a Google segítségével
  6. Scharla Gelfand diszkográfiája
  7. Zárás

Bevezetés

Rectangling (a fordító megjegyzése, nem találtam megfelelő fordítást erre a kifejezésre, ezért úgy hagyjuk, ahogy van.) — a folyamat, amely a strukturálatlan, beágyazott tömbökkel rendelkező adatokat két dimenziós táblázatokká alakítja, amely ismert sorokból és oszlopokból áll. A tidyr használ több olyan funkciót, amelyek segítenek kibővíteni a beágyazott oszlop-listákat, és az adatokat téglalap alakú, táblázatos formára hozni:

  • unnest_longer() minden lista-oszlop elemét véve új sort hoz létre.
  • unnest_wider() minden lista-oszlop elemét véve új oszlopot hoz létre.
  • unnest_auto() automatikusan meghatározza, hogy melyik funkciót érdemes használni
    unnest_longer() vagy unnest_wider().
  • hoist() hasonlít a unnest_wider() de csak a megadott komponenseket választja ki, és lehetővé teszi a több szintű beágyazással való munkát.

A strukturálatlan adatok két dimenziós táblázattá alakításával kapcsolatos problémák többsége megoldható az említett funkciók és a dplyr kombinálásával.

Ezeknek a technikáknak a bemutatására a repurrrsive, amely több bonyolult, többszintű listát biztosít, amelyet web-API-ból kaptunk.

library(tidyr)
library(dplyr)
library(repurrrsive)

GitHub felhasználók

Kezdjük a gh_users, egy listával, amely hat GitHub felhasználó információit tartalmazza. Először is alakítsuk át a listát gh_users be. tibble keretbe:

users <- tibble( user = gh_users ) 

Ez egy kicsit illogikusnak tűnik: miért készítenénk listát gh_users, egy bonyolultabb adatszerkezetről? De a data frame-nek van egy nagy előnye: több vektort egyesít, így minden egy objektumban nyomon követhető.

Minden objektumelemet Miután megállapítottuk, hogy ki küldte a kérést, meg kell határoznunk, hogy az adott alanynak mit engedélyeztek. Egyes szolgáltatásrácsok lehetővé teszik alapvető irányelvek (arról, hogy ki mit tehet) megadását YAML-fájlok formájában vagy parancssoron, míg mások integrációt kínálnak olyan keretrendszerekkel, mint az egy névvel ellátott lista képvisel, amelyben minden elem egy oszlopot képvisel.

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"

Két mód van arra, hogy a listaelemeket oszlopokká alakítsuk. unnest_wider() azt veszi, hogy minden komponenst külön oszlopba helyez:

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.…
#> # … 23 további változóval: 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

Ebben az esetben egy 30 oszlopból álló táblázatot kaptunk, és a többségére nincs szükségünk, ezért helyette unnest_wider() használni hoist(). hoist() lehetővé teszi számunkra, hogy a kiválasztott komponenseket azonos szintaxissal nyerjük ki, mint purrr::pluck():

felhasználók %>% hoist(user, 
  követők = "followers", 
  bejelentkezés = "login", 
  url = "html_url"
)
#> # A tibble: 6 x 4
#>   követők bejelentkezés url                            felhasználó             
#>                                                    
#> 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/masalmon

hoist() eltávolítja a megadott névvel ellátott összetevőket a lista-oszlopból user, ezért úgy tekintheti hoist() a belső lista komponenseinek áthelyezéseként a dátakeret felső szintjére.

GitHub repozitóriumok

Lista kiegyenlítése gh_repos hasonlóan kezdünk, átalakítva tibble:

repos  # A tibble: 6 x 1
#>   repo       
#>        
#> 1 
#> 2 
#> 3 
#> 4 
#> 5 
#> 6

Ezúttal az elemek user azokat a repozitóriumokat képviselik, amelyek ennek a felhasználónak a tulajdonában vannak. Minden repozitórium külön megfigyelés, így a tiszta adatok koncepciója szerint (tiszta adatok az megjegyzés) új sorokká kell válniuk, ezért használjuk unnest_longer() nem pedig unnest_wider():

repos % unnest_longer(repo)
repos
#> # A tibble: 176 x 1
#>    repo             
#>               
#>  1 
#>  2 
#>  3 
#>  4 
#>  5 
#>  6 
#>  7 
#>  8 
#>  9 
#> 10 
#> # … 166 további sorral

Most már használhatjuk unnest_wider() vagy hoist() :

repos %>% hoist(repo, 
  bejelentkezés = c("owner", "login"), 
  név = "name",
  honlap = "homepage",
  nézők = "watchers_count"
)
#> # A tibble: 176 x 5
#>    bejelentkezés név        honlap nézők repo             
#>                                     
#>  1 gaborcsardi után                   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 
#> # … 166 további sorral

Figyeljünk a használatra c("owner", "login"): ez lehetővé teszi számunkra, hogy elérjük a második szintű értéket a beágyazott listából tulajdonosAz alternatív megközelítés az, hogy megszerezzük az összes listát tulajdonos majd egy funkció segítségével unnest_wider() minden elemét egy oszlopba helyezzük:

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.…
#> # … 166 további sorral, és 11 további változóval: following_url ,
#> #   gists_url , starred_url , subscriptions_url ,
#> #   organizations_url , repos_url , events_url ,
#> #   received_events_url , type , site_admin , repo

Ahelyett, hogy a megfelelő funkció kiválasztásán tűnődne unnest_longer() vagy unnest_wider() használhatja unnest_auto(). Ez a funkció több heurisztikus módszert alkalmaz az adatok átalakításához legjobban megfelelő funkció kiválasztására, és üzenetet ad az alkalmazott módszerről.

tibble(repo = gh_repos) %>% 
  unnest_auto(repo) %>% 
  unnest_auto(repo)
#> Using `unnest_longer(repo)`; no element has names
#> Using `unnest_wider(repo)`; elements have 68 names in common
#> # 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…  # … with 166 more rows, and 58 more variables: 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 ,
#> #   homepage

Trónok harca karakterek

got_chars azonos struktúrával rendelkezik gh_users: egy nevekkel ellátott listák halmaza, ahol minden belső lista egy karakter attribútumát írja le a Trónok harcában. A táblázatos formázáshoz got_chars adatkeretet hozunk létre, akárcsak az előző példákban, majd minden elemet külön oszlopba alakítunk át:

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 , 
#> #   playedBy

Struktúra got_chars kicsit bonyolultabb, mint gh_users, mivel egyes elemek a listában char önállóan listákat alkotnak, így oszlopokat kapunk — tartalmaznak listákat:

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 rows

A további lépései a feltárás céljaitól függenek. Lehet, hogy szüksége van arra, hogy soronként feltüntesse az információkat minden könyvről és sorozatról, amelyben a karakter szerepel:

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 Trónok harca
#>  2 Theon Greyjoy    books    A Kardok vihara
#>  3 Theon Greyjoy    books    A Hollók lakomája
#>  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 Hollók lakomája
#> # … 170 további sorral

Vagy lehet, hogy szeretne létrehozni egy táblázatot, amely lehetővé teszi a karakter és a mű párosítását:

chars2 %>% 
  select(name, title = titles) %>% 
  unnest_longer(title)
#> # A tibble: 60 x 2
#>    name              title                                               
#>                                                                
#>  1 Theon Greyjoy     Winterfell hercege                                
#>  2 Theon Greyjoy     A Tenger Banya kapitánya                                
#>  3 Theon Greyjoy     A Vasmadarak ura (a zöld földek törvénye szerint)
#>  4 Tyrion Lannister  A király megbízott keze (volt)                    
#>  5 Tyrion Lannister  A Pénzura (volt)                             
#>  6 Victarion Greyjoy A Vasmadarak flotta főkapitánya                      
#>  7 Victarion Greyjoy A Vasmadarak győzelme mestere                          
#>  8 Will              ""                                                  
#>  9 Areo Hotah        A Vigyázó kapitánya a Napgyümölcsben                    
#> 10 Chett             ""                                                  
#> # … 50 további sorral

(Vegye figyelembe, hogy a null értékek "" a mezőben title, ez a bemeneti adatok bevitele során elkövetett hibák miatt van got_chars: valójában azoknak a karaktereknek, akiknek nincs megfelelő könyv- és sorozatcímük a mezőben title nulla hosszúságú vektornak kellene lenniük, és nem egy hosszúságú 1-es vektornak, amely üres stringet tartalmaz.)

A fenti példát átkonvertálhatjuk a(z) használatával unnest_auto(). Ez a módszer kényelmes egyszeri elemzéshez, de nem érdemes rendszeresen támaszkodni rá. unnest_auto() Az a helyzet, hogy ha az adatszerkezete megváltozik, unnest_auto() meg akarja változtatni a kiválasztott adatformátum-átalakító mechanizmust; ha az eredetileg oszlop-listákat alakított át sorokká a(z) révén, unnest_longer()akkor az adatbeviteli struktúra módosítása esetén a logikát módosítani lehet a a(z)-ért, és az ilyen megközelítés tartós használata váratlan hibákhoz vezethet. unnest_wider(), és ennek a megközelítésnek a folyamatos alkalmazása váratlan hibákhoz vezethet.

tibble(char = got_chars) %>% 
  unnest_auto(char) %>% 
  select(name, title = titles) %>% 
  unnest_auto(title)
#> Az `unnest_wider(char)` használatával; az elemeknek 18 közös neve van
#> Az `unnest_longer(title)` használatával; egyetlen elemnek sincs neve
#> # A tibble: 60 x 2
#>    name              title                                               
#>                                                                
#>  1 Theon Greyjoy     Winterfell hercege                                  
#>  2 Theon Greyjoy     Sea Bitch kapitánya                               
#>  3 Theon Greyjoy     A Vas-szigetek ura (a zöld földek törvénye szerint)
#>  4 Tyrion Lannister  A király megbízott keze (korábban)                
#>  5 Tyrion Lannister  Pénzügyminiszter (korábban)                       
#>  6 Victarion Greyjoy A Vaskoszorú lordkapitánya                       
#>  7 Victarion Greyjoy A Vas-győzelem mestere                            
#>  8 Will              ""                                                  
#>  9 Areo Hotah        A napsugár őrség kapitánya                          
#> 10 Chett             ""                                                  
#> # … 50 további sorral

Geokódolás a Google segítségével

Most egy bonyolultabb adatstruktúrát fogunk megvizsgálni, amelyet a Google geokódoló szolgáltatásától kapunk. Az API Google térképekkel való használatának feltételeivel ellentétes az adatok gyorsítótárazása, ezért először írok egy egyszerű API-környezetet. Amely az API Google térképekkel való használatához szükséges kulcs környezeti változóban való tárolásán alapul; ha a környezeti változók között nincs mentett kulcs az API Google térképekhez, akkor az ebben a szakaszban bemutatott kódok nem fognak működni.

has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
  message("Nincs Google Maps API kulcs; a kódrészletek nem fognak lefutni")
}

# 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)
}

Ez a funkció egy meglehetősen bonyolult listát ad vissza:

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 "Egyesült Államok"
#>   .. .. .. ..$ 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"

Szerencsére lépésről lépésre meg tudjuk oldani az adatok táblázatos formátumba történő átalakításának problémáját a funkciók segítségével. tidyr. Hogy a feladat egy kicsit nehezebb és valósághűbb legyen, elkezdem néhány város geokódolásával:

  city <- c ( "Houston" , "LA" , "New York" , "Chicago" , "Springfield" )  city_geo <- purrr::map (city, geocode) 

Az eredményeket átalakítom tibble, a kényelem kedvéért hozzáadok egy városnevének megfelelő oszlopot.

loc  # A tibble: 5 x 2
#>   city        json            
#>                    
#> 1 Houston     
#> 2 LA          
#> 3 New York    
#> 4 Chicago     
#> 5 Springfield

Az első szint tartalmazza az összetevőket status és eredmény, amelyeket a következő módon bontogathatunk ki 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  OK

p(x) – results többszintű lista. A legtöbb városnak 1 eleme van (ami egyedi értéket képvisel, amely a geokódoló API-hoz tartozik), de Springfieldnek kettő van. Ezeket külön sorokba tudjuk húzni a 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  OK

Most már mindegyiknek azonos összetevői vannak, amit akár a következővel is ellenőrizhetünk 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…  OK

Meg tudjuk találni minden város szélességi és hosszúsági koordinátáit a lista kibővítésével 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…  # … 4 további változóval: viewport , place_id , types ,
#> #   status

Aztán a helymeghatározás, amihez szét kell bontani 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 ,
#> #   status

Ismét, unnest_auto() egyszerűsíti a leírt műveletet bizonyos kockázatokkal, amelyeket a bejövő adatok struktúrájának megváltozása okozhat:

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 ,
#> #   status

Esetleg egyszerűen nézhetjük meg az első címet minden városra:

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 ,
#> #   status

Vagy használható hoist() többszintű megközelítésként, hogy közvetlenül a következőhöz jussunk lat és 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.3

Scharla Gelfand diszkográfiája

A végén a legbonyolultabb struktúrát fogjuk megvizsgálni — Sharla Gelfand diszkográfiáját. Ahogy a korábban említett példákban, a lista egy oszlopos adatkeretbe való konvertálásával kezdjük, majd bővítjük azt, hogy minden komponens külön oszlop legyen. Azt is átalakítom, hogy az oszlop date_added a megfelelő dátum- és időformátumra kerüljön R-ben.

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
#> # … with 145 more rows

Ezen a szinten megtudtuk, mikor adták hozzá az egyes lemezeket Sharla diszkográfiájához, de az egyes lemezek adatai nem láthatók. Ehhez ki kell bővítenünk az oszlopot basic_information:

discs %>% unnest_wider(basic_information)
#> A `id` oszlop nevét nem lehet megismételni.
#> Használja a .name_repair-t a javításhoz.

Sajnos hibát fogunk kapni, mivel a listában basic_information van egy azonos nevű oszlop basic_information. Ilyen hiba esetén a probléma gyors meghatározásához használhatja names_repair = "unique":

lemezek %>% unnest_wider(basic_information, names_repair = "unique")
#> Új nevek:
#> * id -> id...6
#> * id -> id...14
#> # A 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  # … összesen 145 egyéb sor, és 7 további változó: formats ,
#> #   cover_image , resource_url , master_id ,
#> #   master_url , id...14 , rating

A probléma az, hogy basic_information ismétli az id oszlopot, amely szintén a legfelső szinten tárolódik, ezért egyszerűen eltávolíthatjuk:

lemezek %>% 
  select(-id) %>% 
  unnest_wider(basic_information)
#> # A 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  # … összesen 145 egyéb sor, és 6 további változó: formats ,
#> #   cover_image , resource_url , master_id ,
#> #   master_url , rating

Alternatív megoldásként használhatnánk hoist():

deszkák %>%
  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…
#> # … 145 további sorral és 3 további változóval: basic_information ,
#> #   id , értékelés

Itt gyorsan kiemelem az első címke és művész nevét az index alapján, belemerülve a beágyazott listába.

A célravezetőbb megközelítés az, ha külön táblázatokat hozunk létre a művészek és a címkék számára:

deszkák %>%
  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
#> # … 157 további sorral

deszkák %>%
  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 Számozott     Fekete kazetta 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    
#> # … 270 további sorral

Ezután szükség szerint visszaillesztheti őket az eredeti adatgyűjteménybe.

Zárás

A könyvtár magjában tidyverse számos hasznos csomag található, amelyek közös adatfeldolgozási filozófiával rendelkeznek.

Ebben a cikkben a funkciók családját vizsgáltuk meg unnest_*(), amelyek a beágyazott listákból történő elemek kinyerésére összpontosítanak. Ez a csomag számos más hasznos funkciót tartalmaz, amelyek megkönnyítik az adatok átalakítását a Tiszta Adatok.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster