Na maioria dos casos, ao lidar com a resposta recebida da API ou com quaisquer outros dados que tenham uma estrutura hierárquica complexa, você se depara com os formatos JSON e XML.
Esses formatos têm muitas vantagens: eles armazenam os dados de forma bastante compacta e evitam a duplicação excessiva de informações.
Um ponto negativo desses formatos é a complexidade do seu processamento e análise. Dados não estruturados não podem ser usados em cálculos e não podem ser visualizados.

Este artigo é uma continuação lógica da publicação . Ele ajudará você a transformar estruturas de dados não estruturadas em uma forma tabular familiar e utilizável para análise, usando o pacote tidyr, que faz parte do núcleo da biblioteca tidyverse, e suas funções da família unnest_*().
Conteúdo
youtube e O processo de construção do bot
Introdução
Retangulamento (nota do tradutor: não encontrei uma tradução adequada para esse termo, então vamos deixá-lo como está.) — é o processo de transformar dados não estruturados com arrays aninhados em uma tabela bidimensional, composta por linhas e colunas familiares. No tidyr existem várias funções que ajudarão você a expandir colunas-listas aninhadas e trazer os dados para uma forma retangular e tabular:
unnest_longer()pega cada elemento da lista-coluna e cria uma nova linha.unnest_wider()pega cada elemento da lista-coluna e cria uma nova coluna.unnest_auto()determina automaticamente qual das funções usar
unnest_longer()ouunnest_wider().hoist()é semelhante aunnest_wider()mas seleciona apenas os componentes especificados e permite trabalhar com vários níveis de aninhamento.
A maioria dos problemas relacionados à transformação de dados não estruturados com múltiplos níveis de aninhamento em uma tabela bidimensional pode ser resolvida combinando as funções listadas com dplyr.
Para demonstrar essas técnicas, utilizaremos o pacote repurrrsive, que fornece várias listas complexas e multilayer, obtidas de uma API da web.
library(tidyr)
library(dplyr)
library(repurrrsive)Usuários do GitHub
Vamos começar com gh_users, uma lista que contém informações sobre seis usuários do GitHub. Primeiro, vamos transformar a lista gh_users em tibble em um data frame:
users <- tibble(user = gh_users) Isso parece um pouco ilógico: por que trazer uma lista gh_users, para uma estrutura de dados mais complexa? Mas o data frame tem uma grande vantagem: ele combina vários vetores, de modo que tudo é rastreado em um único objeto.
Cada elemento do objeto tinham endereços de e-mail em domínios representa uma lista nomeada, onde cada elemento representa uma coluna.
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"Existem duas maneiras de transformar componentes da lista em colunas. unnest_wider() pega cada componente e cria uma nova coluna:
users %>% unnest_wider(user)
#> # A tibble: 6 x 30
#> login id avatar_url gravatar_id url html_url followers_url
#> <chr> <int> <chr> <chr> <chr> <chr> <chr>
#> 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.…
#> # … com mais 23 variáveis: 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>, name <chr>, company <chr>, blog <chr>,
#> # location <chr>, email <chr>, public_repos <int>, public_gists <int>,
#> # followers <int>, following <int>, created_at <chr>, updated_at <chr>,
#> # bio <chr>, hireable <lgl>Neste caso, obtemos uma tabela composta por 30 colunas, e a maioria delas não será necessária, portanto podemos usar em vez de unnest_wider() utilizados hoist(). hoist() nos permite extrair componentes selecionados, usando a mesma sintaxe que purrr::pluck():
usuários %> hoist(user,
seguidores = "followers",
login = "login",
url = "html_url"
)
#> # Um tibble: 6 x 4
#> seguidores login url usuário
#>
#> 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() remove os componentes nomeados especificados da lista-coluna user, portanto você pode considerar hoist() como a movimentação de componentes da lista interna do data frame para seu nível superior.
Repositórios do Github
Alinhamento da lista gh_repos começamos de maneira semelhante, convertendo-o em tibble:
repos # Um tibble: 6 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6Desta vez, os elementos user representam uma lista de repositórios pertencentes a este usuário. Cada repositório é uma observação separada, portanto, de acordo com o conceito de dados organizados (nota: dados organizados) eles devem se tornar novas linhas, e por isso usamos unnest_longer() em vez de unnest_wider():
repos % unnest_longer(repo)
repos
#> # Um tibble: 176 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … com 166 linhas adicionaisAgora podemos usar unnest_wider() ou hoist() :
repos %> hoist(repo,
login = c("owner", "login"),
nome = "name",
homepage = "homepage",
watchers = "watchers_count"
)
#> # Um tibble: 176 x 5
#> login nome homepage watchers 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
#> # … com 166 linhas adicionaisPreste atenção no uso de c("owner", "login"): isso nos permite obter o valor de segundo nível da lista aninhada ownerUma abordagem alternativa consiste em obter toda a lista owner e, em seguida, usar a função unnest_wider() para colocar cada um de seus elementos em uma coluna:
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.…
#> # … com 166 linhas a mais e 11 variáveis adicionais: following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoEm vez de pensar sobre qual função escolher unnest_longer() ou unnest_wider() você pode usar unnest_auto(). Esta função utiliza vários métodos heurísticos para encontrar a função mais adequada para a transformação dos dados e fornece uma mensagem sobre a abordagem escolhida.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Usando `unnest_longer(repo)`; nenhum elemento tem nomes
#> Usando `unnest_wider(repo)`; os elementos têm 68 nomes em comum
#> # Um tibble: 176 x 67
#> id nome nome_completo proprietário privado url_html descrição fork url
#>
#> 1 6.12e7 depois gaborcsa… 2 4.05e7 argu… gaborcsa… 3 3.64e7 pergunte 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… FALSO https:/… VERDADEIRO http…
#> 10 2.43e7 cray… gaborcsa… # … com 166 linhas a mais, e 58 variáveis a mais: 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 ,
#> # homepagePersonagens de Game of Thrones
got_chars tem uma estrutura idêntica a gh_users: é um conjunto de listas nomeadas, onde cada elemento da lista interna descreve um determinado atributo de um personagem de Game of Thrones. A conversão got_chars para uma forma tabular começa com a criação de um dataframe, assim como nos exemplos anteriores, e depois traduzimos cada elemento em uma coluna separada:
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… Masculino Ironbo… Em 2… "" TRUE <chr … <chr [… ""
#> 2 http… 1052 Tyri… Masculino "" Em 2… "" TRUE <chr … <chr [… ""
#> 3 http… 1074 Vict… Masculino Ironbo… Em 2… "" TRUE <chr … <chr [… ""
#> 4 http… 1109 Will Masculino "" "" Em 2… FALSE <chr … <chr [… ""
#> 5 http… 1166 Areo… Masculino Norvos… Em 2… "" TRUE <chr … <chr [… ""
#> 6 http… 1267 Chett Masculino "" At H… Em 2… FALSE <chr … <chr [… ""
#> 7 http… 1295 Cres… Masculino "" Em 2… Em 2… FALSE <chr … <chr [… ""
#> 8 http… 130 Aria… Feminino Dornish Em 2… "" TRUE <chr … <chr [… ""
#> 9 http… 1303 Daen… Feminino Valyri… Em 2… "" TRUE <chr … <chr [… ""
#> 10 http… 1319 Davo… Masculino Wester… Em 2… "" TRUE <chr … <chr [… ""
#> # … with 20 more rows, and 7 more variables: mother <chr>, spouse <chr>,
#> # allegiances <list>, books <list>, povBooks <list>, tvSeries <list>,
#> # playedBy <list>Estrutura got_chars um pouco mais complicado do que gh_users, pois alguns componentes da lista char são eles mesmos listas, resultando em colunas — listas:
chars2 %>% select_if(is.list)
#> # A tibble: 30 x 7
#> titles aliases allegiances books povBooks tvSeries playedBy
#>
#> 1 <chr [3]> <chr [4]> <chr [1]> <chr [3]> <chr [2]> <chr [6]> <chr [1]>
#> 2 <chr [2]> <chr [11]> <chr [1]> <chr [2]> <chr [4]> <chr [6]> <chr [1]>
#> 3 <chr [2]> <chr [1]> <chr [1]> <chr [3]> <chr [2]> <chr [1]> <chr [1]>
#> 4 <chr [1]> <chr [1]> <???> <chr [1]> <chr [1]> <chr [1]> <chr [1]>
#> 5 <chr [1]> <chr [1]> <chr [1]> <chr [3]> <chr [2]> <chr [2]> <chr [1]>
#> 6 <chr [1]> <chr [1]> <???> <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#> 7 <chr [1]> <chr [1]> <???> <chr [2]> <chr [1]> <chr [1]> <chr [1]>
#> 8 <chr [1]> <chr [1]> <chr [1]> <chr [4]> <chr [1]> <chr [1]> <chr [1]>
#> 9 <chr [5]> <chr [11]> <chr [1]> <chr [1]> <chr [4]> <chr [6]> <chr [1]>
#> 10 <chr [4]> <chr [5]> <chr [2]> <chr [1]> <chr [3]> <chr [5]> <chr [1]>
#> # … with 20 more rowsSuas próximas ações dependem dos objetivos da análise. Pode ser necessário inserir informações em linhas sobre cada livro e série em que o personagem aparece:
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
#> # … com mais 170 linhasOu talvez você queira criar uma tabela que permita corresponder o personagem à obra:
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 ""
#> # … com mais 50 linhas(Observe os valores em branco, "" no campo o título, isso se deve a erros cometidos na entrada de dados em got_chars: na verdade, os personagens para os quais não há títulos correspondentes de livros e séries no campo o título devem ter um vetor de comprimento 0, e não um vetor de comprimento 1 contendo uma string vazia.)
Podemos reescrever o exemplo acima usando a função unnest_auto(). Essa abordagem é conveniente para análises pontuais, mas não deve ser usada de forma regular. unnest_auto() O problema é que se a sua estrutura de dados mudar, unnest_auto() pode alterar o mecanismo de transformação de dados escolhido. Se inicialmente ele expandiu as colunas-listas em linhas usando unnest_longer(), então, ao mudar a estrutura dos dados de entrada, a lógica pode ser alterada em favor de unnest_wider(), e o uso de tal abordagem de forma contínua pode levar a erros inesperados.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> Usando `unnest_wider(char)`; elementos têm 18 nomes em comum
#> Usando `unnest_longer(title)`; nenhum elemento possui nomes
#> # Um tibble: 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Príncipe de Winterfell
#> 2 Theon Greyjoy Capitão da Sea Bitch
#> 3 Theon Greyjoy Senhor das Ilhas de Ferro (por lei das terras verdes)
#> 4 Tyrion Lannister Mão do Rei (anterior)
#> 5 Tyrion Lannister Mestre do Dinheiro (anterior)
#> 6 Victarion Greyjoy Senhor Capitão da Frota de Ferro
#> 7 Victarion Greyjoy Mestre da Vitória de Ferro
#> 8 Will ""
#> 9 Areo Hotah Capitão da Guarda em Sunspear
#> 10 Chett ""
#> # … com 50 linhas a maisGeocodificação com o Google
A seguir, examinaremos uma estrutura de dados mais complexa obtida do serviço de geocodificação do Google. O armazenamento em cache das credenciais contradiz os termos de uso da API do Google Maps, portanto, primeiro escrevei uma simples interface para a API. Que é baseada no armazenamento da chave da API do Google Maps em uma variável de ambiente; se você não tiver a chave salva nas variáveis de ambiente para trabalhar com a API do Google Maps, os trechos de código apresentados nesta seção não serão executados.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Nenhuma chave da API do Google Maps encontrada; os trechos de código não serão executados")
}
# 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)
}A lista retornada por esta função é bastante complexa:
houston Lista de 2
#> $ results: Lista de 1
#> ..$ : Lista de 5
#> .. ..$ address_components: Lista de 4
#> .. .. ..$ : Lista de 3
#> .. .. .. ..$ long_name : chr "Houston"
#> .. .. .. ..$ short_name: chr "Houston"
#> .. .. .. ..$ types : Lista de 2
#> .. .. .. .. ..$ : chr "locality"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ : Lista de 3
#> .. .. .. ..$ long_name : chr "Harris County"
#> .. .. .. ..$ short_name: chr "Harris County"
#> .. .. .. ..$ types : Lista de 2
#> .. .. .. .. ..$ : chr "administrative_area_level_2"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ : Lista de 3
#> .. .. .. ..$ long_name : chr "Texas"
#> .. .. .. ..$ short_name: chr "TX"
#> .. .. .. ..$ types : Lista de 2
#> .. .. .. .. ..$ : chr "administrative_area_level_1"
#> .. .. .. .. ..$ : chr "political"
#> .. .. ..$ : Lista de 3
#> .. .. .. ..$ long_name : chr "United States"
#> .. .. .. ..$ short_name: chr "US"
#> .. .. .. ..$ types : Lista de 2
#> .. .. .. .. ..$ : chr "country"
#> .. .. .. .. ..$ : chr "political"
#> .. ..$ formatted_address : chr "Houston, TX, EUA"
#> .. ..$ geometry : Lista de 4
#> .. .. ..$ bounds : Lista de 2
#> .. .. .. ..$ northeast: Lista de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest: Lista de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. .. ..$ location : Lista de 2
#> .. .. .. ..$ lat: num 29.8
#> .. .. .. ..$ lng: num -95.4
#> .. .. ..$ location_type: chr "APPROXIMATE"
#> .. .. ..$ viewport : Lista de 2
#> .. .. .. ..$ northeast: Lista de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ southwest: Lista de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. ..$ place_id : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ types : Lista de 2
#> .. .. ..$ : chr "locality"
#> .. .. ..$ : chr "political"
#> $ status : chr "OK"Felizmente, podemos resolver passo a passo o problema de converter esses dados em formato tabular usando funções tidyr. Para tornar a tarefa um pouco mais complexa e realista, começarei geocodificando algumas cidades:
city <- c ("Houston", "LA", "New York", "Chicago", "Springfield") city_geo <- purrr::map (city, geocode) O resultado obtido será transformado em tibble, para facilitar, adicionarei uma coluna com o nome correspondente da cidade.
loc # A tibble: 5 x 2
#> city json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldO primeiro nível contém os componentes status e result, que podemos expandir usando 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 OKObserve que results é uma lista multilayer. A maioria das cidades tem 1 elemento (representando um valor único correspondente à API de geocodificação), mas Springfield tem dois. Podemos extraí-los em linhas separadas usando 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 OKAgora todos eles têm os mesmos componentes, o que pode ser verificado com 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… OKPodemos encontrar as coordenadas de latitude e longitude de cada cidade expandindo a lista 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… # … com 4 variáveis a mais: viewport , place_id , types ,
#> # statusE então a localização, para isso é necessário expandir localização:
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 ,
#> # statusNovamente, unnest_auto() simplifica a operação descrita com alguns riscos que podem ser causados pela mudança na estrutura dos dados de entrada:
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 ,
#> # statusPodemos também simplesmente olhar para o primeiro endereço de cada cidade:
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 ,
#> # statusOu usar hoist() para imersão em camadas, a fim de ir diretamente para lat e lng.
loc %>%
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
# > # A tibble: 5 x 4
# > city lat lng json
# >
# > 1 Houston 29.8 -95.4
# > 2 LA 34.1 -118.
# > 3 New York 40.7 -74.0
# > 4 Chicago 41.9 -87.6
# > 5 Springfield 37.2 -93.3Discografia de Sharly Gelfand
No final, vamos analisar a estrutura mais complexa — a discografia de Sharla Gelfand. Assim como nos exemplos anteriores, começamos convertendo a lista em um dataframe com uma coluna, e então ampliaremos, para que cada componente fique em uma coluna separada. Também vou converter a coluna date_added para o formato correspondente de data e hora no 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
# > # … with 145 more rowsNeste nível, conseguimos informações sobre quando cada disco foi adicionado à discografia da Sharla, mas ainda não vemos dados sobre esses discos. Para isso, precisamos expandir a coluna basic_information:
discs %>% unnest_wider(basic_information)
# > O nome da coluna `id` não pode ser duplicado.
# > Use .name_repair para especificar a correção.Infelizmente, receberemos um erro, pois dentro da lista basic_information existe uma coluna homônima basic_information. Ao ocorrer esse tipo de erro, para identificar rapidamente a causa, podemos usar names_repair = "unique":
discs %>% unnest_wider(basic_information, names_repair = "unique")
#> Novos nomes:
#> * id -> id...6
#> * id -> id...14
#> # Um 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 # … com 145 mais linhas e 7 mais variáveis: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , id...14 , ratingO problema é que basic_information repete a coluna id que também está armazenada no nível superior, então podemos simplesmente removê-la:
discs %>%
select(-id) %>%
unnest_wider(basic_information)
#> # Um 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 # … com 145 mais linhas e 6 mais variáveis: formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingComo alternativa, poderíamos usar 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…
#> # … com 145 linhas a mais e 3 variáveis a mais: basic_information ,
#> # id , ratingAqui, extraio rapidamente o nome do primeiro rótulo e do artista pelo índice, mergulhando em uma lista aninhada.
Uma abordagem mais sistemática envolve a criação de tabelas separadas para o artista e o rótulo:
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
#> # … com 157 linhas a mais
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
#> # … com 270 linhas a maisDepois, você pode juntá-los novamente ao conjunto de dados original conforme necessário.
Conclusão
A biblioteca central tidyverse inclui uma variedade de pacotes úteis que compartilham uma filosofia comum de processamento de dados.
Neste artigo, examinamos uma família de funções unnest_*(), que são voltadas para a extração de elementos de listas aninhadas. Este pacote contém diversas outras funções úteis que simplificam a transformação de dados de acordo com o conceito de Dados Organizados.
Fonte: habr.com
