使用来自Wickhams的examples来介绍数据科学中的purrr,我试图创建一个双嵌套列表。
library(gapminder)
library(purrr)
library(tidyr)
gapminder
nest_data <- gapminder %>% group_by(continent) %>% nest(.key = by_continent)
如何进一步嵌套国家/地区,以便nest_data包含by_continent和最高级别的嵌套by_contry,最终包含tibble by_year?
此外,在为gapminder数据创建此数据结构后,您将如何为每个国家的bookchapter运行回归模型示例?
答案 0 :(得分:5)
我的解决方案,下面有一些解释。
library(gapminder)
library(purrr)
library(tidyr)
library(broom)
nest_data <- gapminder %>% group_by(continent) %>% nest(.key = by_continent)
nested_again<-
nest_data %>% mutate(by_continent = map(by_continent, ~.x %>%
group_by(country) %>%
nest(.key = by_country)))
# Level 1
nested_again
# # A tibble: 5 × 2
# continent by_continent
# <fctr> <list>
# 1 Asia <tibble [33 × 2]>
# 2 Europe <tibble [30 × 2]>
# 3 Africa <tibble [52 × 2]>
# 4 Americas <tibble [25 × 2]>
# 5 Oceania <tibble [2 × 2]>
# Level 2
nested_again %>% unnest %>% slice(1:2)
# # A tibble: 2 × 3
# continent country by_country
# <fctr> <fctr> <list>
# 1 Asia Afghanistan <tibble [12 × 4]>
# 2 Asia Bahrain <tibble [12 × 4]>
sol1<-mutate(nested_again, models = map(by_continent, "by_country") %>%
at_depth(2, ~lm(lifeExp ~ year, data = .x)))
sol1
# # A tibble: 5 × 3
# continent by_continent models
# <fctr> <list> <list>
# 1 Asia <tibble [33 × 2]> <list [33]>
# 2 Europe <tibble [30 × 2]> <list [30]>
# 3 Africa <tibble [52 × 2]> <list [52]>
# 4 Americas <tibble [25 × 2]> <list [25]>
# 5 Oceania <tibble [2 × 2]> <list [2]>
sol1 %>% unnest(models)
# Error: Each column must either be a list of vectors or a list of data frames [models]
sol1 %>% unnest(by_continent) %>% slice(1:2)
# # A tibble: 2 × 3
# continent country by_country
# <fctr> <fctr> <list>
# 1 Asia Afghanistan <tibble [12 × 4]>
# 2 Asia Bahrain <tibble [12 × 4]>
解决方案正在按预期进行,但没有简单的方法按国家/地区进行过滤,因为该信息嵌套在第2级。
我提出解决方案2,基于@ aosmith对第一个问题的解决方案:
sol2<-nested_again %>% mutate(by_continent = map(by_continent, ~.x %>%
mutate(models = map(by_country, ~lm(lifeExp ~ year, data = .x) )) ))
sol2
# # A tibble: 5 × 2
# continent by_continent
# <fctr> <list>
# 1 Asia <tibble [33 × 4]>
# 2 Europe <tibble [30 × 4]>
# 3 Africa <tibble [52 × 4]>
# 4 Americas <tibble [25 × 4]>
# 5 Oceania <tibble [2 × 4]>
sol2 %>% unnest %>% slice(1:2)
# # A tibble: 2 × 4
# continent country by_country models
# <fctr> <fctr> <list> <list>
# 1 Asia Afghanistan <tibble [12 × 4]> <S3: lm>
# 2 Asia Bahrain <tibble [12 × 4]> <S3: lm>
sol2 %>% unnest %>% unnest(by_country) %>% colnames
# [1] "continent" "country" "year" "lifeExp" "pop"
# [6] "gdpPercap"
# get model by specific country
sol2 %>% unnest %>% filter(country == "Brazil") %$% models %>% extract2(1)
# Call:
# lm(formula = lifeExp ~ year, data = .x)
#
# Coefficients:
# (Intercept) year
# -709.9427 0.3901
# summary with broom::tidy
sol2 %>% unnest %>% filter(country == "Brazil") %$% models %>%
extract2(1) %>% tidy
# term estimate std.error statistic p.value
# 1 (Intercept) -709.9426860 10.801042821 -65.72909 1.617791e-14
# 2 year 0.3900895 0.005456243 71.49417 6.990433e-15
我们可以整理所有模型并保存在数据中以用于绘图或过滤
sol2 %<>% mutate(by_continent = map(by_continent, ~.x %>%
mutate(tidymodels = map(models, tidy )) ))
sol2 %>% unnest %>% unnest(tidymodels) %>%
ggplot(aes(country,p.value,colour=continent))+geom_point()+
facet_wrap(~continent)+
theme(axis.text.x = element_blank())
selc <- sol2 %>% unnest %>% unnest(tidymodels) %>% filter(p.value > 0.05) %>%
select(country) %>% unique %>% extract2(1)
gapminder %>% filter(country %in% selc ) %>%
ggplot(aes(year,lifeExp,colour=continent))+geom_line(aes(group=country))+
facet_wrap(~continent)
aaaaand,我们可以使用模型
m1 <- sol2 %>% unnest %>% slice(1) %$% models %>% extract2(1)
x <- sol2 %>% unnest %>% slice(1) %>% unnest(by_country) %>% select(year)
pred1 <- data.frame(year = x, lifeExp = predict.lm(m1,x))
sol2 %>% unnest %>% slice(1) %>% unnest(by_country) %>%
ggplot(aes(year, lifeExp )) + geom_point() +
geom_line(data=pred1)
在这种情况下,没有充分的理由使用这种双重嵌套(当然除了学习如何使用它),但我在工作中发现了一个非常有价值的案例,特别是当你需要一个功能工作时在第3级,按级别1和2分组,并保存在级别2中 - 当然为此我们也可以在级别1上使用for
循环,但有什么好玩的;)我不是真的确定这个“嵌套”map
与for
循环+ map
相比有何表现,但接下来我会对其进行测试。
看起来他们差别不大
# comparison map_map with for_map
map_map<-function(nested_again){
nested_again %>% mutate(by_continent = map(by_continent, ~.x %>%
mutate(models = map(by_country, ~lm(lifeExp ~ year, data = .x) )) )) }
for_map<-function(nested_again){ for(i in 1:length(nested_again[[1]])){
nested_again$by_continent[[i]] %<>%
mutate(models = map(by_country, ~lm(lifeExp ~ year, data = .x) )) }}
res<-microbenchmark::microbenchmark(
mm<-map_map(nested_again), fm<-for_map(nested_again) )
res
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# mm <- map_map(nested_again) 121.0033 144.5530 160.6785 155.2389 174.2915 240.2012 100 a
# fm <- for_map(nested_again) 131.4312 148.3329 164.7097 157.6589 173.6480 455.7862 100 a
autoplot(res)