Dans la plupart des cas, lorsque vous travaillez avec la réponse obtenue d'une API ou avec d'autres données ayant une structure arborescente complexe, vous êtes confronté aux formats JSON et XML.
Ces formats présentent de nombreux avantages : ils stockent les données de manière assez compacte et permettent d'éviter une duplication excessive de l'information.
Le principal inconvénient de ces formats est la complexité de leur traitement et de leur analyse. Les données non structurées ne peuvent pas être utilisées dans des calculs et ne peuvent pas servir à créer des visualisations.

Cet article est une suite logique de la publication . Il vous aidera à transformer des constructions de données non structurées en un format tabulaire familier et approprié pour l'analyse à l'aide du paquet tidyr, qui fait partie intégrante de la bibliothèque tidyverse, et des fonctions de la famille unnest_*().
Contenu
Si vous vous intéressez à l'analyse de données, mes et canaux pourraient vous intéresser. La majeure partie du contenu y est consacrée au langage R.
Introduction
Rectangulation (note du traducteur, je n'ai pas trouvé d'équivalent adéquat pour ce terme, donc nous le laisserons tel quel.) — c'est le processus de conversion de données non structurées avec des tableaux imbriqués en un tableau bidimensionnel composé de lignes et de colonnes familières. Dans tidyr il existe plusieurs fonctions qui vous aideront à développer des colonnes de listes imbriquées et à convertir les données en une forme rectangulaire, tabulaire :
unnest_longer()prend chaque élément de la colonne-liste et crée une nouvelle ligne.unnest_wider()prend chaque élément de la colonne-liste et crée une nouvelle colonne.unnest_auto()détermine automatiquement quelle fonction utiliser.
unnest_longer()ouunnest_wider().hoist()est similaire àunnest_wider()mais ne sélectionne que les composants spécifiés et permet de travailler avec plusieurs niveaux d'imbrication.
La plupart des problèmes liés à la conversion de données non structurées avec plusieurs niveaux d'imbrication en un tableau bidimensionnel peuvent être résolus en combinant les fonctions énumérées avec dplyr.
Pour démontrer ces techniques, nous allons utiliser le paquet repurrrsive, qui fournit plusieurs listes complexes et hiérarchiques issues d'une API web.
library(tidyr)
library(dplyr)
library(repurrrsive)Utilisateurs de GitHub
Commençons par gh_users, une liste contenant des informations sur six utilisateurs de GitHub. Pour commencer, transformons la liste gh_users dans tibble frame:
users <- tibble( user = gh_users ) Cela peut sembler un peu illogique : pourquoi faire référence à une liste, gh_userspour une structure de données plus complexe ? Mais le DataFrame a un grand avantage : il regroupe plusieurs vecteurs, de sorte que tout est suivi dans un seul objet.
Chaque élément de l'objet users est une liste nommée où chaque élément représente une colonne.
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"Il existe deux façons de transformer les composants de la liste en colonnes. unnest_wider() prend chaque composant et crée une nouvelle colonne :
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.…
#> # … avec 23 variables supplémentaires : 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 , hireableDans ce cas, nous obtenons un tableau composé de 30 colonnes, et la plupart d'entre elles ne nous seront pas nécessaires, donc nous pouvons à la place unnest_wider() d'utiliser hoist(). hoist() nous permet d'extraire les composants sélectionnés en utilisant la même syntaxe que purrr::pluck():
utilisateurs %> % hoist(user,
followers = "followers",
login = "login",
url = "html_url"
)
#> # A tibble: 6 x 4
#> followers login url user
#>
#> 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() supprime les composants nommés spécifiés de la liste-colonne user, donc vous pouvez considérer hoist() comme le déplacement de composants de la liste interne du cadre de données vers son niveau supérieur.
Dépôts GitHub
Alignement de la liste gh_repos nous commençons de manière similaire, en le transformant en tibble:
repos # A tibble: 6 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6Cette fois, les éléments user représentent une liste de dépôts appartenant à cet utilisateur. Chaque dépôt est une observation distincte, donc selon le concept de données organisées (note : tidy data) ils doivent devenir de nouvelles lignes, c'est pourquoi nous utilisons unnest_longer() et non pas unnest_wider():
repos % unnest_longer(repo)
repos
#> # A tibble: 176 x 1
#> repo
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … avec 166 autres lignesMaintenant, nous pouvons utiliser unnest_wider() ou hoist() :
repos %> % hoist(repo,
login = c("owner", "login"),
name = "name",
homepage = "homepage",
watchers = "watchers_count"
)
#> # A tibble: 176 x 5
#> login name 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
#> # … avec 166 autres lignesFaites attention à l'utilisation de c("owner", "login"): cela nous permet d'obtenir la valeur de deuxième niveau à partir de la liste imbriquée propriétaire. Une approche alternative consiste à obtenir toute la liste propriétaire et ensuite, à l'aide de la fonction unnest_wider() placer chacun de ses éléments dans une colonne :
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.…
#> # … avec 166 autres lignes, et 11 autres variables : following_url ,
#> # gists_url , starred_url , subscriptions_url ,
#> # organizations_url , repos_url , events_url ,
#> # received_events_url , type , site_admin , repoAu lieu de réfléchir au choix de la fonction appropriée unnest_longer() ou unnest_wider() vous pouvez utiliser unnest_auto(). Cette fonction utilise plusieurs méthodes heuristiques pour sélectionner la fonction la plus appropriée pour transformer les données et affiche un message sur la méthode choisie.
tibble(repo = gh_repos) %>%
unnest_auto(repo) %>%
unnest_auto(repo)
#> Utilisation de `unnest_longer(repo)` ; aucun élément n'a de noms
#> Utilisation de `unnest_wider(repo)` ; les éléments ont 68 noms en commun
#> # Un tibble : 176 x 67
#> id nom nom_complet propriétaire privé url_html description fork url
#>
#> 1 6.12e7 après gaborcsa… 2 4.05e7 argu… gaborcsa… 3 3.64e7 demander gaborcsa… 4 3.49e7 base… gaborcsa… 5 6.16e7 citer… gaborcsa… 6 3.39e7 clis… gaborcsa… 7 3.72e7 cmak… gaborcsa… 8 6.80e7 cmark gaborcsa… 9 6.32e7 cond… gaborcsa… <nam… FAUX https:… VRAI http…
#> 10 2.43e7 cray… gaborcsa… # … avec 166 autres lignes, et 58 autres 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 ,
#> # homepagePersonnages de Game of Thrones
caractères_got a une structure identique à gh_users: il s'agit d'un ensemble de listes nommées, où chaque élément de la liste interne décrit un certain attribut d'un personnage de Game of Thrones. La transformation caractères_got en format tabulaire commence par la création d'un cadre de données, tout comme dans les exemples précédents, puis nous diviserons chaque élément en colonnes séparées :
chars # Un tibble : 30 x 1
#> char
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … avec 20 autres lignes
chars2 % unnest_wider(char)
chars2
#> # Un 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 … # … avec 20 autres lignes, et 7 autres variables : mother , spouse ,
#> # allegiances , books , povBooks , tvSeries ,
#> # playedByStructure caractères_got un peu plus compliqué que gh_users, car certains composants de la liste char eux-mêmes étant des listes, ce qui nous donne des colonnes — listes :
chars2 %>% select_if(is.list)
#> # Un tibble : 30 x 7
#> titles aliases allegiances books povBooks tvSeries playedBy
#>
#> 1
#> 2
#> 3
#> 4
#> 5
#> 6
#> 7
#> 8
#> 9
#> 10
#> # … avec 20 autres lignesVos prochaines actions dépendent des objectifs de l'analyse. Il peut être nécessaire d'inscrire dans les lignes des informations sur chaque livre et chaque série dans laquelle le personnage apparaît :
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
#> # … avec 170 autres lignesOu peut-être voulez-vous créer un tableau qui vous permettra d'associer un personnage à une œuvre :
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 (par la loi des terres vertes)
#> 4 Tyrion Lannister Acting Hand of the King (ancien)
#> 5 Tyrion Lannister Master of Coin (ancien)
#> 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 ""
#> # … avec 50 autres lignes(Veuillez noter les valeurs vides, "" dans le champ title, cela est dû à des erreurs survenues lors de la saisie des données dans caractères_got: en réalité, les personnages pour lesquels il n'y a pas de titres de livres et de séries correspondants dans le champ title devraient avoir un vecteur de longueur 0, et non un vecteur de longueur 1 contenant une chaîne vide.)
Nous pouvons réécrire l'exemple ci-dessus en utilisant la fonction unnest_auto(). Cette approche est appropriée pour une analyse ponctuelle, mais il n'est pas conseillé de compter sur unnest_auto() pour une utilisation régulière. En effet, si votre structure de données change, unnest_auto() peut modifier le mécanisme de transformation des données choisi si à l'origine il dépliait les colonnes-liste en lignes en utilisant unnest_longer(), alors en cas de changement de la structure des données d'entrée, la logique peut être modifiée en faveur de unnest_wider(), et l'utilisation de cette approche de manière permanente peut entraîner des erreurs imprévues.
tibble(char = got_chars) %>%
unnest_auto(char) %>%
select(name, title = titles) %>%
unnest_auto(title)
#> En utilisant `unnest_wider(char)` ; les éléments ont 18 noms en commun
#> En utilisant `unnest_longer(title)` ; aucun élément n'a de noms
#> # Un tibble : 60 x 2
#> name title
#>
#> 1 Theon Greyjoy Prince de Winterfell
#> 2 Theon Greyjoy Capitaine de Sea Bitch
#> 3 Theon Greyjoy Seigneur des Îles de Fer (par la loi des terres vertes)
#> 4 Tyrion Lannister Main du Roi par intérim (ancien)
#> 5 Tyrion Lannister Maître des Finances (ancien)
#> 6 Victarion Greyjoy Commandant de la Flotte de Fer
#> 7 Victarion Greyjoy Maître de l'Iron Victory
#> 8 Will ""
#> 9 Areo Hotah Capitaine de la Garde à Sunspear
#> 10 Chett ""
#> # … avec 50 lignes supplémentairesGéocodage avec Google
Nous allons maintenant examiner une structure de données plus complexe provenant du service de géocodage de Google. Le cache des informations d'identification va à l'encontre des conditions d'utilisation de l'API Google Maps, donc je vais d'abord écrire une simple enveloppe pour l'API. Celle-ci est basée sur le stockage de la clé API Google Maps dans une variable d'environnement ; si vous n'avez pas enregistré de clé pour utiliser l'API Google Maps dans vos variables d'environnement, les morceaux de code présentés dans cette section ne s'exécuteront pas.
has_key <- !identical(Sys.getenv("GOOGLE_MAPS_API_KEY"), "")
if (!has_key) {
message("Aucune clé API Google Maps trouvée ; les morceaux de code ne seront pas exécutés")
}
# 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)
}La liste renvoyée par cette fonction est assez complexe :
houston Liste de 2
#> $ résultats: Liste de 1
#> ..$ : Liste de 5
#> .. ..$ composants_adresse: Liste de 4
#> .. .. ..$ : Liste de 3
#> .. .. .. ..$ nom_long : chr "Houston"
#> .. .. .. ..$ nom_court: chr "Houston"
#> .. .. .. ..$ types : Liste de 2
#> .. .. .. .. ..$ : chr "localité"
#> .. .. .. .. ..$ : chr "politique"
#> .. .. ..$ : Liste de 3
#> .. .. .. ..$ nom_long : chr "Comté de Harris"
#> .. .. .. ..$ nom_court: chr "Comté de Harris"
#> .. .. .. ..$ types : Liste de 2
#> .. .. .. .. ..$ : chr "niveau_de_zone_administratif_2"
#> .. .. .. .. ..$ : chr "politique"
#> .. .. ..$ : Liste de 3
#> .. .. .. ..$ nom_long : chr "Texas"
#> .. .. .. ..$ nom_court: chr "TX"
#> .. .. .. ..$ types : Liste de 2
#> .. .. .. .. ..$ : chr "niveau_de_zone_administratif_1"
#> .. .. .. .. ..$ : chr "politique"
#> .. .. ..$ : Liste de 3
#> .. .. .. ..$ nom_long : chr "États-Unis"
#> .. .. .. ..$ nom_court: chr "US"
#> .. .. .. ..$ types : Liste de 2
#> .. .. .. .. ..$ : chr "pays"
#> .. .. .. .. ..$ : chr "politique"
#> .. ..$ adresse_formatée : chr "Houston, TX, USA"
#> .. ..$ géométrie : Liste de 4
#> .. .. ..$ limites : Liste de 2
#> .. .. .. ..$ nord-est: Liste de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ sud-ouest: Liste de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. .. ..$ emplacement : Liste de 2
#> .. .. .. ..$ lat: num 29.8
#> .. .. .. ..$ lng: num -95.4
#> .. .. ..$ type_emplacement: chr "APPROXIMATIF"
#> .. .. ..$ fenêtre_affichage : Liste de 2
#> .. .. .. ..$ nord-est: Liste de 2
#> .. .. .. .. ..$ lat: num 30.1
#> .. .. .. .. ..$ lng: num -95
#> .. .. .. ..$ sud-ouest: Liste de 2
#> .. .. .. .. ..$ lat: num 29.5
#> .. .. .. .. ..$ lng: num -95.8
#> .. ..$ id_lieu : chr "ChIJAYWNSLS4QIYROwVl894CDco"
#> .. ..$ types : Liste de 2
#> .. .. ..$ : chr "localité"
#> .. .. ..$ : chr "politique"
#> $ statut : chr "OK"Heureusement, nous pouvons résoudre ce problème de conversion de ces données en format tabulaire étape par étape grâce à des fonctions tidyr. Pour rendre la tâche un peu plus complexe et réaliste, je vais commencer par géocoder plusieurs villes :
ville <- c ( "Houston" , "LA" , "New York" , "Chicago" , "Springfield" ) ville_geo <- purrr::map (ville, geocode) Le résultat obtenu sera transformé en tibble, pour plus de commodité, j'ajouterai une colonne portant le nom de la ville correspondante.
loc # Un tibble: 5 x 2
#> ville json
#>
#> 1 Houston
#> 2 LA
#> 3 New York
#> 4 Chicago
#> 5 SpringfieldLe premier niveau contient des composants statut et result, que nous pouvons déplier à l'aide de unnest_wider() :
loc %>%
unnest_wider(json)
#> # Un tibble: 5 x 3
#> ville résultats statut
#>
#> 1 Houston OK
#> 2 LA OK
#> 3 New York OK
#> 4 Chicago OK
#> 5 Springfield OKVeuillez noter que résultats est une liste à plusieurs niveaux. La plupart des villes ont 1 élément (représentant une valeur unique associée à l'API de géocodage), mais Springfield en a deux. Nous pouvons les extraire en lignes séparées à l'aide de 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 OKMaintenant, tous ont des composants identiques, ce que nous pouvons vérifier avec 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… OKNous pouvons trouver les coordonnées de latitude et de longitude de chaque ville en développant la liste geometry:
loc %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry)
#> # A tibble: 5 x 10
#> city address_compone… formatted_addre… bounds location location_type
#>
#> 1 Hous… Houston, TX, USA <name… 2 LA Los Angeles, CA… <name… 3 New … New York, NY, U… <name… 4 Chic… Chicago, IL, USA <name… 5 Spri… Springfield, MO… <name… # … with 4 more variables: viewport , place_id , types ,
#> # statusEt ensuite la localisation, pour cela il est nécessaire de développer 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 ,
#>% # statusEncore une fois, unnest_auto() simplifie l'opération décrite avec certains risques qui peuvent être causés par des changements dans la structure des données d'entrée :
loc %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location)
#>% Utilisation de `unnest_wider(json)` ; les éléments ont 2 noms en commun
#>% Utilisation de `unnest_longer(results)` ; aucun élément n'a de noms
#>% Utilisation de `unnest_wider(results)` ; les éléments ont 5 noms en commun
#>% Utilisation de `unnest_wider(geometry)` ; les éléments ont 4 noms en commun
#>% Utilisation de `unnest_wider(location)` ; les éléments ont 2 noms en commun
#>% # 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… % # … avec 4 autres variables : viewport , place_id , types ,
#>% # statutNous pouvons également simplement examiner la première adresse pour chaque ville :
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… % # … avec 4 autres variables : viewport , place_id , types ,
#>% # statutOu utiliser hoist() pour une immersion multi-niveaux, afin de passer directement à lat et lng.
loc %>%
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
#> # Un 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.3Discographie de Sharla Gelfand
Pour conclure, nous allons examiner la structure la plus complexe - la discographie de Sharly Gel'fand. Comme dans les exemples précédents, nous commencerons par convertir la liste en un cadre de données avec une seule colonne, puis nous l'étendrons pour que chaque composant soit dans une colonne séparée. Je vais également transformer la colonne date_added au format date et heure approprié dans R.
discs %
unnest_wider(disc) %>%
mutate(date_added = as.POSIXct(strptime(date_added, "%Y-%m-%dT%H:%M:%S")))
discs
#> # Un 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
#> # … avec 145 lignes de plusÀ ce niveau, nous avons obtenu des informations sur la date à laquelle chaque disque a été ajouté à la discographie de Sharly, mais nous ne voyons pas encore de données sur ces disques. Pour cela, nous devons élargir la colonne basic_information:
discs %>% unnest_wider(basic_information)
#> Le nom de la colonne `id` ne doit pas être dupliqué.
#> Utilisez .name_repair pour spécifier la réparation.Malheureusement, nous obtiendrons une erreur, car à l'intérieur de la liste basic_information il y a une colonne du même nom. basic_informationLorsqu'une telle erreur se produit, pour identifier rapidement sa cause, vous pouvez utiliser names_repair = "unique":
discs %>% unnest_wider(basic_information, names_repair = "unique")
#> Nouvelles noms :
#> * id -> id...6
#> * id -> id...14
#> # Un 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 # … avec 145 lignes supplémentaires, et 7 variables de plus : formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , id...14 , ratingLe problème est que basic_information répète la colonne id qui est également stockée au niveau supérieur, donc nous pouvons simplement l'éliminer :
discs %>%
select(-id) %>%
unnest_wider(basic_information)
#> # Un tibble : 155 x 14
#> instance_id date_added labels year artists id thumb title
#>
#> 1 354823933 2019-02-16 17:48:59 <list… 2015 2 354092601 2019-02-13 14:13:11 <list… 2013 3 354091476 2019-02-13 14:07:23 <list… 2017 4 351244906 2019-02-02 11:39:58 <list… 2017 5 351244801 2019-02-02 11:39:37 <list… 2015 6 351052065 2019-02-01 20:40:53 <list… 2019 7 350315345 2019-01-29 15:48:37 <list… 2014 8 350315103 2019-01-29 15:47:22 <list… 2015 9 350314507 2019-01-29 15:44:08 <list… 2017 10 350314047 2019-01-29 15:41:35 <list… 2017 # … avec 145 lignes supplémentaires, et 6 variables de plus : formats ,
#> # cover_image , resource_url , master_id ,
#> # master_url , ratingEn alternative, nous pourrions utiliser 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…
#> # … avec 145 autres lignes, et 3 autres variables : basic_information ,
#> # id , ratingIci, j'extrais rapidement le nom du premier label et de l'artiste par indice, en plongeant dans une liste imbriquée.
Une approche plus systématique consiste à créer des tables distinctes pour l'artiste et le label :
discs %>%
hoist(basic_information, artist = "artists") %>%
select(disc_id = id, artist) %>%
unnest_longer(artist) %>%
unnest_wider(artist)
#> # A tibble: 167 x 8
#> disc_id join name anv tracks role resource_url id
#>
#> 1 7496378 "" Mollot "" "" "" https://api.discog… 4.62e6
#> 2 4490852 "" Una Bèstia… "" "" "" https://api.discog… 3.19e6
#> 3 9827276 "" S.H.I.T. (… "" "" "" https://api.discog… 2.77e6
#> 4 9769203 "" Rata Negra "" "" "" https://api.discog… 4.28e6
#> 5 7237138 "" Ivy (18) "" "" "" https://api.discog… 3.60e6
#> 6 13117042 "" Tashme "" "" "" https://api.discog… 5.21e6
#> 7 7113575 "" Desgraciad… "" "" "" https://api.discog… 4.45e6
#> 8 10540713 "" Phantom He… "" "" "" https://api.discog… 4.27e6
#> 9 11260950 "" Sub Space … "" "" "" https://api.discog… 5.69e6
#> 10 11726853 "" Small Man … "" "" "" https://api.discog… 6.37e6
#> # … avec 157 autres lignes
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
#> # … avec 270 autres lignesEnsuite, vous pouvez les rejoindre à nouveau avec l'ensemble de données d'origine si nécessaire.
Conclusion
Au cœur de la bibliothèque tidyverse se trouvent de nombreux packages utiles unis par une philosophie commune de traitement des données.
Dans cet article, nous avons examiné la famille de fonctions unnest_*(), qui sont destinées à travailler avec l'extraction d'éléments à partir de listes imbriquées. Ce package contient de nombreuses autres fonctions utiles qui facilitent la transformation des données selon le concept de Données Propre.
Source : habr.com
