Reputation: 1702
I work with lists from APIs reasonably often, but I can't convert this simple list to a df.
Reprex data:
my_ls <- list(list(ip = "104.28.131.102", type = "ipv4", continent_code = "NA",
continent_name = "North America", country_code = "US", country_name = "United States",
region_code = "CA", region_name = "California", city = "San Jose",
zip = "95122", latitude = 37.3305282592773, longitude = -121.838226318359,
location = list(geoname_id = 5392171L, capital = "Washington D.C.",
languages = list(list(code = "en", name = "English",
native = "English")), country_flag = "https://assets.ipstack.com/flags/us.svg",
country_flag_emoji = "🇺🇸", country_flag_emoji_unicode = "U+1F1FA U+1F1F8",
calling_code = "1", is_eu = FALSE)), list(ip = "109.206.213.143",
type = "ipv4", continent_code = "EU", continent_name = "Europe",
country_code = "PL", country_name = "Poland", region_code = "MZ",
region_name = "Mazovia", city = "Piastów", zip = "05-850",
latitude = 52.2565498352051, longitude = 20.7680397033691,
location = list(geoname_id = 762381L, capital = "Warsaw",
languages = list(list(code = "pl", name = "Polish", native = "Polski")),
country_flag = "https://assets.ipstack.com/flags/pl.svg",
country_flag_emoji = "🇵🇱", country_flag_emoji_unicode = "U+1F1F5 U+1F1F1",
calling_code = "48", is_eu = TRUE)))
I want to get this into a simple data frame with 22 cols and 2 rows.
I've tried several things, but this illustrates the problem:
library(tidyverse)
my_df <- my_ls %>%
map(unlist) %>%
map(tibble)
The resulting data frame still has a list level of a character within it where I would have expected unlist
to remove that into a named character vector ready for tibble
to do its job, but there's still a list element in there for every tibble.
head(my_df)
[[1]]
# A tibble: 22 × 1
`<chr>`
<chr>
1 104.28.131.102
2 ipv4
3 NA
4 North America
5 US
6 United States
7 CA
8 California
9 San Jose
10 95122
# … with 12 more rows
I'm stumped - I'm sure it's something simple. Thanks.
Upvotes: 0
Views: 38
Reputation: 887741
The reason is that unlist
returns a vector
and tibble/data.frame
directly applied on that vector returns a single column. Instead, the vector needs to be either transposed (t
) or convert to list
(as.data.frame.list
in the current solution) to return as many columns as the length
of the vector
library(purrr)
library(dplyr)
out <- map_dfr(my_ls, ~ unlist(.x) %>%
as.data.frame.list()) %>%
type.convert(as.is = TRUE)
-output
> str(out)
'data.frame': 2 obs. of 22 variables:
$ ip : chr "104.28.131.102" "109.206.213.143"
$ type : chr "ipv4" "ipv4"
$ continent_code : chr NA "EU"
$ continent_name : chr "North America" "Europe"
$ country_code : chr "US" "PL"
$ country_name : chr "United States" "Poland"
$ region_code : chr "CA" "MZ"
$ region_name : chr "California" "Mazovia"
$ city : chr "San Jose" "Piastów"
$ zip : chr "95122" "05-850"
$ latitude : num 37.3 52.3
$ longitude : num -121.8 20.8
$ location.geoname_id : int 5392171 762381
$ location.capital : chr "Washington D.C." "Warsaw"
$ location.languages.code : chr "en" "pl"
$ location.languages.name : chr "English" "Polish"
$ location.languages.native : chr "English" "Polski"
$ location.country_flag : chr "https://assets.ipstack.com/flags/us.svg" "https://assets.ipstack.com/flags/pl.svg"
$ location.country_flag_emoji : chr "🇺🇸" "🇵🇱"
$ location.country_flag_emoji_unicode: chr "U+1F1FA U+1F1F8" "U+1F1F5 U+1F1F1"
$ location.calling_code : int 1 48
$ location.is_eu : logi FALSE TRUE
Upvotes: 1