Zhvillojmë kolona të ngulitura – lista nëpërmjet gjuhës R (paketi tidyr dhe funksionet e familjes unnest)

Në shumicën e rasteve, kur punoni me përgjigjjen e marrë nga API, ose me çdo të dhënë tjetër që ka një strukturë të ndërlikuar, përballeni me formatet JSON dhe XML.

Këto formate kanë shumë përfitime: ato ruajnë të dhënat në mënyrë të kompaktë dhe lejojnë evitimin e dyfishimeve të panevojshme të informacionit.

Këto formate kanë një mangësi: ndërlikueshmëria e përpunimit dhe analizës. Të dhënat e pa strukturuara nuk mund të përdoren në llogaritje dhe nuk mund të krijohen vizualizime mbi to.

Zhvillojmë kolona të ngulitura – lista nëpërmjet gjuhës R (paketi tidyr dhe funksionet e familjes unnest)

Ky artikull është një vazhdim logjik i publikimit "Paketa R tidyr dhe funksionet e saj të reja pivot_longer dhe pivot_wider". Ai do t'ju ndihmojë të sillni struktura të pa strukturuara të të dhënave në një format tabelar të njohur dhe të përshtatshëm për analizë me ndihmën e paketës tidyr, e cila është pjesë e bërthamës së bibliotekës tidyverse, dhe funksioneve të familjes së saj unnest_*().

Përmbajtja

Nëse jeni të interesuar për analizën e të dhënave, ndoshta do t'ju interesojnë kanalet e mia telegram dhe youtube në të cilat shumica e përmbajtjes është e kushtuar gjuhës R.

  1. Hyrje
  2. Përdoruesit e GitHub
  3. Repozita Github
  4. Personazhet elojës së fronave
  5. Gjeokodimi me ndihmën e Google
  6. Diskografia e Sharlës Gelfand
  7. Përfundimi

Hyrje

Rectangling (shënim i përkthyesit, nuk kam gjetur variante të përshtatshme për përkthimin e këtij termi, prandaj do ta lëmë ashtu siç është.) — është procesi i shndërrimit të të dhënave të pa strukturuara me struktura të thella në një tabelë dy-dimensionale, e cila përbëhet nga rreshta dhe kolona të njohura për ne. tidyr Ekzistojnë disa funksione që do t'ju ndihmojnë të zgjidhni kolona-lista të thella dhe të sillni të dhënat në një format tabelar:

  • unnest_longer() merr çdo element të listës-kolonë dhe krijon një rresht të ri.
  • unnest_wider() merr çdo element të listës-kolonë dhe krijon një kolonë të re.
  • unnest_auto() automatikisht përcakton se cila nga funksionet është më e përshtatshme për të përdorur
    unnest_longer() ose unnest_wider().
  • hoist() është i ngjashëm me unnest_wider() por seleksionon vetëm komponentët e specifikuar dhe lejon punimin me disa nivele të thellësisë.

Shumica e problemeve të lidhura me shndërrimin e të dhënave të pa strukturuara me disa nivele thellësie në një tabelë dy-dimensionale mund të zgjidhen duke kombinuar funksionet e përmendura me dplyr.

Për të demonstruar këto teknika, ne do të përdorim paketën repurrrsive, e cila ofron disa lista komplekse shumë-nivel, të marra nga API-të e webit.

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

Përdoruesit e GitHub

Le të fillojmë me gh_users, një listë që përmban informacion rreth gjashtë përdoruesve të GitHub. Fillimisht, le të zbulojmë listën gh_userstibble DataFrame:

users <- tibble( user = gh_users ) 

Kjo duket paksa e çuditshme: pse t'i shndërrojmë listën gh_users, në një strukturë më të ndërlikuar të të dhënave? Por DataFrame ka një avantazh të madh: ai bashkon disa vektora, kështu që gjithçka ndahet në një objekt.

Çdo element i objektit përdoruesit është një listë e emëruar, ku çdo element përfaqëson një kolonë.

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"

Ka dy mënyra për të shndërruar komponentët e listës në kolona. unnest_wider() merr çdo komponent dhe krijon një kolonë të re:

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.…
#> # … me 23 variabla të tjera: 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ë këtë rast, ne morëm një tabelë me 30 kolona, dhe shumica e tyre nuk do të na duhen, prandaj mund të përdorim në vend të unnest_wider() përdorimi hoist(). hoist() na lejon të nxjerrim komponentët e zgjedhur, duke përdorur të njëjtin sintaksë si purrr::pluck():

përdoruesit %>% hoist(user, 
  followers = "followers", 
  login = "login", 
  url = "html_url"
)
#> # Një 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() heq komponentet e emëruara të caktuara nga lista-kolonë përdorues, kështu që mund ta mendoni hoist() si një lëvizje e komponentëve nga lista e brendshme e kuadrit të të dhënave në nivelin e tij më të lartë.

Repozita Github

Rreshtimi i listës gh_repos fillojmë ngjashëm, duke e transformuar atë në tibble:

repos <- tibble(repo = gh_repos)
repos
#> # Një tibble: 6 x 1
#>   repo       
#>   <list>     
#> 1 <list [30]>
#> 2 <list [30]>
#> 3 <list [30]>
#> 4 <list [26]>
#> 5 <list [30]>
#> 6 <list [30]>

Këtë herë elementet përdorues përbëjnë një listë reposiresh, që i takojnë këtij përdoruesi. Çdo repo është një vëzhgim i veçantë, kështu që sipas konceptit të të dhënave të pastra (shën. tidy data) ata duhet të bëhen rreshta të rinj, ndaj ne e përdorim unnest_longer() në vend të unnest_wider():

repos <- repos %>% unnest_longer(repo)
repos
#> # Një 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]>
#> # … me 166 rreshta të tjerë

Tani mund të përdorim unnest_wider() ose hoist() :

repos %>% hoist(repo, 
  login = c("owner", "login"), 
  name = "name",
  homepage = "homepage",
  watchers = "watchers_count"
)
#> # Një 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]>
#> # … me 166 rreshta të tjerë

Kujdesi me përdorimin e c("owner", "login"): kjo na lejon të marrim vlerën e nivelit të dytë nga lista e zhvendosur owner. Një qasje alternative është të marrim të gjithë listën owner dhe pastaj me anë të funksionit unnest_wider() të vendosim çdo element të saj në një kolonë:

repos %>% 
  hoist(repo, owner = "owner") %>% 
  unnest_wider(owner)
#> # Një tibble: 176 x 18
#>    login     id avatar_url gravatar_id url   html_url followers_url
#>    <chr>  <int> <chr>      <chr>       <chr> <chr>    <chr>        
#>  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.…
#> # … me 166 rreshta të tjerë, dhe 11 variabla të tjerë: following_url <chr>,
#> #   gists_url <chr>, starred_url <chr>, subscriptions_url <chr>,
#> #   organizations_url <chr>, repos_url <chr>, events_url <chr>,
#> #   received_events_url <chr>, type <chr>, site_admin <lgl>, repo <list>

Në vend që të mendojmë për zgjedhjen e funksionit të duhur unnest_longer() ose unnest_wider() mund të përdorim unnest_auto(). Ky funksion përdor disa metoda heuristike për të vendosur funksionin më të përshtatshëm për transformimin e të dhënave dhe tregon një mesazh mbi mënyrën e zgjedhjes.

tibble(repo = gh_repos) %>% 
  unnest_auto(repo) %>% 
  unnest_auto(repo)
#> Përdorimi i `unnest_longer(repo)`; asnjë element nuk ka emra
#> Përdorimi i `unnest_wider(repo)`; elementët kanë 68 emra të zakonshëm
#> # Një 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…  # … me 166 rreshta të tjerë, dhe 58 variabla të tjerë: 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

Personazhet elojës së fronave

got_chars ka një strukturë identike me gh_users: kjo është një shumësi listash të emëruara, ku çdo element i listës së brendshme përshkruan ndonjë atribut të karaktereve të Game of Thrones. Shndërrimi got_chars në format tabelar fillon me krijimin e një dataframe, ashtu si në shembujt e mëparshëm, e pastaj do ta konvertojmë çdo element në një kolonë të veçantë:

chars  # Një tibble: 30 x 1
#>    char             
#>               
#>  1 
#>  2 
#>  3 
#>  4 
#>  5 
#>  6 
#>  7 
#>  8 
#>  9 
#> 10 
#> # … me 20 rreshta të tjerë

chars2 % unnest_wider(char)
chars2
#> # Një tibble: 30 x 18
#>    url      id name  gender culture born  died  alive titles aliases father
#>                   
#>  1 http…  1022 Theo… Male   Ironbo… Në 2… ""    TRUE  <chr …   2 http…  1052 Tyri… Male   ""      Në 2… ""    TRUE  <chr …   3 http…  1074 Vict… Male   Ironbo… Në 2… ""    TRUE  <chr …   4 http…  1109 Will  Male   ""      ""    Në 2… FALSE <chr …   5 http…  1166 Areo… Male   Norvos… Në 2… ""    TRUE  <chr …   6 http…  1267 Chett Male   ""      Në H… Në 2… FALSE <chr …   7 http…  1295 Cres… Male   ""      Në 2… Në 2… FALSE <chr …   8 http…   130 Aria… Female Dornish Në 2… ""    TRUE  <chr …   9 http…  1303 Daen… Female Valyri… Në 2… ""    TRUE  <chr …  10 http…  1319 Davo… Male   Wester… Në 2… ""    TRUE  <chr …  # … me 20 rreshta të tjerë, dhe 7 variabla të tjerë: mother , spouse ,
#> #   allegiances , books , povBooks , tvSeries ,
#> #   playedBy

Struktura got_chars pak më të ndërlikuar se gh_users, sepse disa komponente të listës char në vetvete janë lista, si rezultat ne marrim kolona – lista:

chars2 %>% select_if(is.list)
#> # Një tibble: 30 x 7
#>    titles    aliases    allegiances books     povBooks  tvSeries  playedBy 
#>                                  
#>  1          
#>  2         
#>  3          
#>  4              
#>  5          
#>  6              
#>  7              
#>  8          
#>  9         
#> 10          
#> # … me 20 rreshta të tjerë

Veprimet tuaja të ardhshme varen nga objektivat e analizës. Mund të jetë e nevojshme të vendosni informacion për çdo libër dhe serial ku shfaqet personazhi:

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 rows

Ose, ndoshta dëshironi të krijoni një tabelë që do t'ju lejojë të lidheni personazhin me veprën:

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

(Vini re se vlerat bosh, "" në fushën title, kjo lidhet me gabimet e bëra gjatë futur të dhënave në got_chars: në të vërtetë personazhet për të cilët nuk ka tituj përkatës në fushën title duhet të kenë një vektor me gjatësi 0, jo një vektor me gjatësi 1 që përmban një varg të bardhë.)

Mund të rishtyjmë shembullin e mëposhtëm duke përdorur funksionin unnest_auto(). Ky qasje është e përshtatshme për një analizë sporadike, por nuk duhen mbështetur në unnest_auto() për përdorim të rregullt. Problemi është se nëse struktura juaj e të dhënave ndryshon unnest_auto() mund të ndrojë mekanizmin e zgjedhur të transformimit të të dhënave, nëse fillimisht ai shndërronte kolonat-liste në rreshta duke përdorur unnest_longer(), atëherë me ndryshimin e strukturës së të dhënave hyrëse logjika mund të ndryshojë në dobi të unnest_wider(), dhe përdorimi i një qasje të tillë në baza të vazhdueshme mund të çojë në gabime të papritura.

tibble(char = got_chars) %>% 
  unnest_auto(char) %>% 
  select(name, title = titles) %>% 
  unnest_auto(title)
#> Using `unnest_wider(char)`; elements have 18 names in common
#> Using `unnest_longer(title)`; no element has names
#> # 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

Gjeokodimi me ndihmën e Google

Më pas, ne do të shqyrtojmë një strukturë më të komplikuar të të dhënave që vijnë nga shërbimi i gjeokodimit të Google. Ruajtja e akreditimeve është në kundërshtim me kushtet e përdorimit të API të Google Maps, prandaj fillimisht do të shkruaj një mbështetje të thjeshtë për API-në. E cila bazohet në ruajtjen e çelësit API të Google Maps në një variabël mjedisor; nëse në variablat mjedisorë nuk keni ruajtur një çelës për përdorim me API-në e Google Maps, fragmentet e kodit të paraqitura në këtë seksion nuk do të ekzekutohen.

has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
  message("No Google Maps API key found; code chunks will not be run")
}

# 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 që kthehet nga kjo funksion është mjaft komplekse:

houston <- geocode("Houston TX")
str(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"

К счастью, мы можем пошагово решить проблему преобразования этих данных в табличный вид с помощью функций tidyr. Чтобы сделать задачу немного сложнее и реалистичнее, я начну с геокодирования нескольких городов:

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

Полученный результат я преобразую в tibble, для удобства добавлю столбец с соответствующим названием города.

loc <- tibble(city = city, json = city_geo)
loc
#> # A tibble: 5 x 2
#>   city        json            
#>   <chr>       <list>          
#> 1 Houston     <named list [2]>
#> 2 LA          <named list [2]>
#> 3 New York    <named list [2]>
#> 4 Chicago     <named list [2]>
#> 5 Springfield <named list [2]>

Первый уровень содержит компоненты status dhe result, который мы можем развернуть с помощью unnest_wider() :

loc %>%
  unnest_wider(json)
#> # A tibble: 5 x 3
#>   city        results    status
#>   <chr>       <list>     <chr> 
#> 1 Houston     <list [1]> OK    
#> 2 LA          <list [1]> OK    
#> 3 New York    <list [1]> OK    
#> 4 Chicago     <list [1]> OK    
#> 5 Springfield <list [1]> OK

Merrni parasysh se results является многоуровневым списком. У большинства городов есть 1 элемент (представляющий уникальное значение, соответствующее API геокодирования), но у Спрингфилда их два. Мы можем вытащить их в отдельные строки с помощью unnest_longer() :

loc %>%
  unnest_wider(json) %>% 
  unnest_longer(results)
#> # A tibble: 5 x 3
#>   city        results          status
#>   <chr>       <list>           <chr> 
#> 1 Houston     <named list [5]> OK    
#> 2 LA          <named list [5]> OK    
#> 3 New York    <named list [5]> OK    
#> 4 Chicago     <named list [5]> OK    
#> 5 Springfield <named list [5]> OK

Теперь все они имеют одинаковые компоненты, в чём можно убедиться с помощью 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
#>   <chr>  <list>            <chr>            <list>   <chr>     <lis> <chr> 
#> 1 Houst… <list [4]>        Houston, TX, USA <named … ChIJAYWN… <lis… OK    
#> 2 LA     <list [4]>        Los Angeles, CA… <named … ChIJE9on… <lis… OK    
#> 3 New Y… <list [3]>        New York, NY, U… <named … ChIJOwg_… <lis… OK    
#> 4 Chica… <list [4]>        Chicago, IL, USA <named … ChIJ7cv0… <lis… OK    
#> 5 Sprin… <list [5]>        Springfield, MO… <named … ChIJP5jI… <lis… OK

Мы можем найти координаты широты и долготы каждого города развернув список 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
#>   <chr> <list>           <chr>            <list> <list>   <chr>        
#> 1 Hous… <list [4]>       Houston, TX, USA <name… <named … APPROXIMATE  
#> 2 LA    <list [4]>       Los Angeles, CA… <name… <named … APPROXIMATE  
#> 3 New … <list [3]>       New York, NY, U… <name… <named … APPROXIMATE  
#> 4 Chic… <list [4]>       Chicago, IL, USA <name… <named … APPROXIMATE  
#> 5 Spri… <list [5]>       Springfield, MO… <name… <named … APPROXIMATE  
#> # … with 4 more variables: viewport <list>, place_id <chr>, types <list>,
#> #   status <chr>

А затем местоположение, для чего требуется развернуть lokacionin:

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
#>   <chr> <list>           <chr>            <list> <dbl>  <dbl> <chr>        
#> 1 Hous… <list [4]>       Houston, TX, USA <name…  29.8  -95.4 APPROXIMATE  
#> 2 LA    <list [4]>       Los Angeles, CA… <name…  34.1 -118.  APPROXIMATE  
#> 3 New … <list [3]>       New York, NY, U… <name…  40.7  -74.0 APPROXIMATE  
#> 4 Chic… <list [4]>       Chicago, IL, USA <name…  41.9  -87.6 APPROXIMATE  
#> 5 Spri… <list [5]>       Springfield, MO… <name…  37.2  -93.3 APPROXIMATE  
#> # … with 4 more variables: viewport <list>, place_id <chr>, types <list>,
#> #   status <chr>

Опять же, unnest_auto() shinjon i përshkruar me disa rreziqe që mund të shkaktohen nga ndryshimi i strukturës së të dhënave të hyra;

loc %>%
  unnest_auto(json) %>%
  unnest_auto(results) %>%
  unnest_auto(results) %>%
  unnest_auto(geometry) %>%
  unnest_auto(location)
#> Duke përdorur `unnest_wider(json)`; elementët kanë 2 emra të zakonshëm
#> Duke përdorur `unnest_longer(results)`; asnjë element nuk ka emra
#> Duke përdorur `unnest_wider(results)`; elementët kanë 5 emra të zakonshëm
#> Duke përdorur `unnest_wider(geometry)`; elementët kanë 4 emra të zakonshëm
#> Duke përdorur `unnest_wider(location)`; elementët kanë 2 emra të zakonshëm
#> # Një tibble: 5 x 11
#>   qytet  komponente_adrese… adresa_e_formatuar… kufijtë   lat    lng lloji_i_lokacionit
#>                                       
#> 1 Hous…        Houston, TX, SHBA  2 LA           Los Angeles, CA…  3 New …        New York, NY, U…  4 Chic…        Chicago, IL, SHBA  5 Spri…        Springfield, MO…  # … me 4 variabla të tjerë: pamja e hartës , id_e_vendit , llojet ,
#> #   statusi

Ne gjithashtu mund të shohim thjesht adresën e parë për secilin qytet:

loc %>%
  unnest_wider(json) %>%
  hoist(results, first_result = 1) %>%
  unnest_wider(first_result) %>%
  unnest_wider(geometry) %>%
  unnest_wider(location)
#> # Një tibble: 5 x 11
#>   qytet  komponente_adrese… adresa_e_formatuar… kufijtë   lat    lng lloji_i_lokacionit
#>                                       
#> 1 Hous…        Houston, TX, SHBA  2 LA           Los Angeles, CA…  3 New …        New York, NY, U…  4 Chic…        Chicago, IL, SHBA  5 Spri…        Springfield, MO…  # … me 4 variabla të tjerë: pamja e hartës , id_e_vendit , llojet ,
#> #   statusi

Ose të përdorim hoist() për një zhytje me shumë nivele, për të kaluar direkt në lat dhe lng.

loc %>%
  hoist(json,
    lat = list("results", 1, "geometry", "location", "lat"),
    lng = list("results", 1, "geometry", "location", "lng")
  )
#> # Një tibble: 5 x 4
#>   qytet          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

Diskografia e Sharlës Gelfand

Në përfundim, ne do të shqyrtojmë konstrukcionin më të komplikuar - diskografia e Sharlës Gelfand. Siç kemi bërë në shembujt e mësipërm, fillojmë me konvertimin e listës në një data frame me një kolonë, dhe pastaj do ta zgjerim atë, në mënyrë që secili komponent të jetë një kolonë e veçantë. Gjithashtu, unë do ta transformoj kolonën date_added në formatin përkatës të datës dhe orës në R.

discs % 
  unnest_wider(disc) %>%
  mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S"))) 
discs
#> # Një tibble: 155 x 5
#>    instance_id date_added          informacioni_bazik       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
#> # … me 145 rreshta të tjerë

Në këtë nivel, ne kemi marrë informacionin se kur çdo disk u shtua në diskografinë e Sharlës, por nuk shohim asnjë të dhënën mbi këta disqe. Për këtë, na nevojitet të zgjeromë kolonën informacioni_bazik:

discs %>% unnest_wider(informacioni_bazik)
#> Emri i kolonës `id` nuk duhet të jetë i dyfishuar.
#> Përdoreni .name_repair për të specifikuar riparimin.

Fatkeqësisht do të marrim një gabim, sepse brenda listës informacioni_bazik ka një kolonë me të njëjtin emër informacioni_bazik. Kur ndodh një gabim i tillë, për të përcaktuar shpejt shkakun e tij mund të përdorim names_repair = "unique":

discs %>% unnest_wider(informacioni_bazik, names_repair = "unique")
#> Emrat e rinj:
#> * id -> id...6
#> * id -> id...14
#> # Një tibble: 155 x 15
#>    instance_id date_added          etiketat  viti artistët id...6 thumb titulli
#>                                
#>  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  # … me 145 rreshta të tjerë, dhe 7 variabla të tjerë: formatet ,
#> #   imazhi_i_kopertinës , url_i_burimit , id_master ,
#> #   url_master , id...14 , vlerësimi

Problemi është se informacioni_bazik ripërton kolonën id e cila gjithashtu ruhet në nivelin e sipërm, prandaj ne mund ta fshijmë atë:

discs %>% 
  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  # … with 145 more rows, and 6 more variables: formats ,
#> #   cover_image , resource_url , master_id ,
#> #   master_url , rating

Si alternativë, mund të përdornim 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 , rating

Këtu shpejt nxjerr emrin e label-it dhe artistit të parë sipas indeksit, duke u zhytur në listën e thellë.

Një qasje më sistematike është të krijoni tabela të veçanta për artistët dhe label-at:

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 rows

Më pas mund t'i bashkoni përsëri ato me setin origjinal të të dhënave sipas nevojës.

Përfundimi

Në bërthamën e bibliotekës tidyverse janë shumë paketa të dobishme të bashkuara nga një filozofi e përbashkët për trajtimin e të dhënave.

Në këtë artikull ne shqyrtuam familjen e funksioneve unnest_*(), të cilët janë të orientuar për të punuar me nxjerrjen e elementeve nga listat e thelluara. Ky paketë përmban shumë funksione të tjera të dobishme që thjeshtojnë transformimin e të dhënave sipas konceptit të Tidy Data.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster