purrr 映射不产生整洁的数据
purrr mapping not producing tidy data
感谢这个网站,我使用 R purrr
包来聚合基于多列的数据。聚合按我想要的方式工作,但输出不是。这是使用 mtcars
数据集的示例。
library(dplyr)
library(purrr)
#pull in data
data <- mtcars
#get colnames
variable1 <- colnames(data)
#map the variables
t1 <- map(variable1, ~ data %>%
group_by_at(.x) %>%
summarize(number = mean(mpg))) %>%
set_names(variable1) %>%
bind_rows(., .id = 'variable')
如果我期望三列(预测变量、每个变量中的水平、聚合),我有 8 个。请参见下图:
如何将我的代码放在顶部并生成整洁的数据集?
在 map
内对数据进行分组时,您可以将分组变量重命名为 "level"
,因为这些值将形成包含最终数据集中分组变量水平的列。
当您有混合类型的分组变量(例如,数字和字符)时,您还需要将分组变量强制转换为字符,以便能够将结果绑定在一起。
添加这些内容后,您应该会得到预期的结果。 (您也可以使用 map_df
而不是 map
来跳过 bind_rows
,以节省一些代码,就像我在下面所做的那样。)
reprex::reprex_info()
#> Created by the reprex package v0.1.1.9000 on 2018-02-09
library(purrr)
library(dplyr)
data <- iris
vars <- names(data)
set_names(vars) %>%
map_df(function(var) {
var <- set_names(var, "level")
data %>%
group_by_at(var) %>%
summarize_at("Sepal.Length", "mean") %>%
mutate_at("level", as.character)
}, .id = "variable")
#> # A tibble: 126 x 3
#> variable level Sepal.Length
#> <chr> <chr> <dbl>
#> 1 Sepal.Length 4.3 4.3
#> 2 Sepal.Length 4.4 4.4
#> 3 Sepal.Length 4.5 4.5
#> 4 Sepal.Length 4.6 4.6
#> 5 Sepal.Length 4.7 4.7
#> 6 Sepal.Length 4.8 4.8
#> 7 Sepal.Length 4.9 4.9
#> 8 Sepal.Length 5 5.0
#> 9 Sepal.Length 5.1 5.1
#> 10 Sepal.Length 5.2 5.2
#> # ... with 116 more rows
你也可以将过程包裹在一个函数中,让多个变量总结成多个函数。不过,您必须花点时间想出一个令人回味的名字(我作弊,只是在这里使用了 foo
)。
foo <- function(data, vars, funs) {
grps <- names(data)
set_names(grps) %>%
map_df(function(grp) {
grp <- set_names(grp, "level")
data %>%
group_by_at(grp) %>%
summarize_at(vars, funs) %>%
mutate_at("level", as.character)
}, .id = "variable")
}
foo(iris, vars(Sepal.Length, Sepal.Width), funs(mean, sd))
#> # A tibble: 126 x 6
#> variable level Sepal.Length_mean Sepal.Width_mean Sepal.Length_sd
#> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Sepal.Length 4.3 4.3 3.000000 NaN
#> 2 Sepal.Length 4.4 4.4 3.033333 0
#> 3 Sepal.Length 4.5 4.5 2.300000 NaN
#> 4 Sepal.Length 4.6 4.6 3.325000 0
#> 5 Sepal.Length 4.7 4.7 3.200000 0
#> 6 Sepal.Length 4.8 4.8 3.180000 0
#> 7 Sepal.Length 4.9 4.9 2.950000 0
#> 8 Sepal.Length 5 5.0 3.120000 0
#> 9 Sepal.Length 5.1 5.1 3.477778 0
#> 10 Sepal.Length 5.2 5.2 3.425000 0
#> # ... with 116 more rows, and 1 more variables: Sepal.Width_sd <dbl>
一个简单的方法是将数据重塑为长格式,这样您就可以使用普通的 dplyr 进行聚合:
library(tidyverse)
mpg_means <- mtcars %>%
gather(variable, value, -mpg) %>%
group_by(variable, value) %>%
summarise(mean_mpg = mean(mpg))
mpg_means
#> # A tibble: 146 x 3
#> # Groups: variable [?]
#> variable value mean_mpg
#> <chr> <dbl> <dbl>
#> 1 am 0. 17.1
#> 2 am 1. 24.4
#> 3 carb 1. 25.3
#> 4 carb 2. 22.4
#> 5 carb 3. 16.3
#> 6 carb 4. 15.8
#> 7 carb 6. 19.7
#> 8 carb 8. 15.0
#> 9 cyl 4. 26.7
#> 10 cyl 6. 19.7
#> # ... with 136 more rows
请注意,虽然 mtcars
完全是数字,但如果您有不同的类型,转换为长格式将强制转换为变量类型。计算将是相同的,但稍后可能会导致问题。要解决它,请使用可以处理不同类型的输出格式,例如
mpg_means_in_list_cols <- mtcars %>%
as_tibble() %>% # compact printing for list columns
summarise_all(list) %>% # collapse each column into a list of itself
gather(group, group_values, -mpg) %>%
mutate(mpg_means = map2(mpg, group_values, # for each mpg/value pair, ...
~tibble(mpg = .x, group_value = .y) %>% # ...reconstruct a data frame...
group_by(group_value) %>%
summarise(mean_mpg = mean(mpg)))) # ...and aggregate
mpg_means_in_list_cols
#> # A tibble: 10 x 4
#> mpg group group_values mpg_means
#> <list> <chr> <list> <list>
#> 1 <dbl [32]> cyl <dbl [32]> <tibble [3 × 2]>
#> 2 <dbl [32]> disp <dbl [32]> <tibble [27 × 2]>
#> 3 <dbl [32]> hp <dbl [32]> <tibble [22 × 2]>
#> 4 <dbl [32]> drat <dbl [32]> <tibble [22 × 2]>
#> 5 <dbl [32]> wt <dbl [32]> <tibble [29 × 2]>
#> 6 <dbl [32]> qsec <dbl [32]> <tibble [30 × 2]>
#> 7 <dbl [32]> vs <dbl [32]> <tibble [2 × 2]>
#> 8 <dbl [32]> am <dbl [32]> <tibble [2 × 2]>
#> 9 <dbl [32]> gear <dbl [32]> <tibble [3 × 2]>
#> 10 <dbl [32]> carb <dbl [32]> <tibble [6 × 2]>
虽然这绝对不是那么漂亮,但它能够整齐地容纳多种类型。要提取上面的结果,只需添加 %>% unnest(mpg_means)
。按原样,每个分组变量都保存在 group_values
的列表元素中,并以聚合形式保存在每个 mpg_means
tibble 的第一列中。
感谢这个网站,我使用 R purrr
包来聚合基于多列的数据。聚合按我想要的方式工作,但输出不是。这是使用 mtcars
数据集的示例。
library(dplyr)
library(purrr)
#pull in data
data <- mtcars
#get colnames
variable1 <- colnames(data)
#map the variables
t1 <- map(variable1, ~ data %>%
group_by_at(.x) %>%
summarize(number = mean(mpg))) %>%
set_names(variable1) %>%
bind_rows(., .id = 'variable')
如果我期望三列(预测变量、每个变量中的水平、聚合),我有 8 个。请参见下图:
如何将我的代码放在顶部并生成整洁的数据集?
在 map
内对数据进行分组时,您可以将分组变量重命名为 "level"
,因为这些值将形成包含最终数据集中分组变量水平的列。
当您有混合类型的分组变量(例如,数字和字符)时,您还需要将分组变量强制转换为字符,以便能够将结果绑定在一起。
添加这些内容后,您应该会得到预期的结果。 (您也可以使用 map_df
而不是 map
来跳过 bind_rows
,以节省一些代码,就像我在下面所做的那样。)
reprex::reprex_info()
#> Created by the reprex package v0.1.1.9000 on 2018-02-09
library(purrr)
library(dplyr)
data <- iris
vars <- names(data)
set_names(vars) %>%
map_df(function(var) {
var <- set_names(var, "level")
data %>%
group_by_at(var) %>%
summarize_at("Sepal.Length", "mean") %>%
mutate_at("level", as.character)
}, .id = "variable")
#> # A tibble: 126 x 3
#> variable level Sepal.Length
#> <chr> <chr> <dbl>
#> 1 Sepal.Length 4.3 4.3
#> 2 Sepal.Length 4.4 4.4
#> 3 Sepal.Length 4.5 4.5
#> 4 Sepal.Length 4.6 4.6
#> 5 Sepal.Length 4.7 4.7
#> 6 Sepal.Length 4.8 4.8
#> 7 Sepal.Length 4.9 4.9
#> 8 Sepal.Length 5 5.0
#> 9 Sepal.Length 5.1 5.1
#> 10 Sepal.Length 5.2 5.2
#> # ... with 116 more rows
你也可以将过程包裹在一个函数中,让多个变量总结成多个函数。不过,您必须花点时间想出一个令人回味的名字(我作弊,只是在这里使用了 foo
)。
foo <- function(data, vars, funs) {
grps <- names(data)
set_names(grps) %>%
map_df(function(grp) {
grp <- set_names(grp, "level")
data %>%
group_by_at(grp) %>%
summarize_at(vars, funs) %>%
mutate_at("level", as.character)
}, .id = "variable")
}
foo(iris, vars(Sepal.Length, Sepal.Width), funs(mean, sd))
#> # A tibble: 126 x 6
#> variable level Sepal.Length_mean Sepal.Width_mean Sepal.Length_sd
#> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Sepal.Length 4.3 4.3 3.000000 NaN
#> 2 Sepal.Length 4.4 4.4 3.033333 0
#> 3 Sepal.Length 4.5 4.5 2.300000 NaN
#> 4 Sepal.Length 4.6 4.6 3.325000 0
#> 5 Sepal.Length 4.7 4.7 3.200000 0
#> 6 Sepal.Length 4.8 4.8 3.180000 0
#> 7 Sepal.Length 4.9 4.9 2.950000 0
#> 8 Sepal.Length 5 5.0 3.120000 0
#> 9 Sepal.Length 5.1 5.1 3.477778 0
#> 10 Sepal.Length 5.2 5.2 3.425000 0
#> # ... with 116 more rows, and 1 more variables: Sepal.Width_sd <dbl>
一个简单的方法是将数据重塑为长格式,这样您就可以使用普通的 dplyr 进行聚合:
library(tidyverse)
mpg_means <- mtcars %>%
gather(variable, value, -mpg) %>%
group_by(variable, value) %>%
summarise(mean_mpg = mean(mpg))
mpg_means
#> # A tibble: 146 x 3
#> # Groups: variable [?]
#> variable value mean_mpg
#> <chr> <dbl> <dbl>
#> 1 am 0. 17.1
#> 2 am 1. 24.4
#> 3 carb 1. 25.3
#> 4 carb 2. 22.4
#> 5 carb 3. 16.3
#> 6 carb 4. 15.8
#> 7 carb 6. 19.7
#> 8 carb 8. 15.0
#> 9 cyl 4. 26.7
#> 10 cyl 6. 19.7
#> # ... with 136 more rows
请注意,虽然 mtcars
完全是数字,但如果您有不同的类型,转换为长格式将强制转换为变量类型。计算将是相同的,但稍后可能会导致问题。要解决它,请使用可以处理不同类型的输出格式,例如
mpg_means_in_list_cols <- mtcars %>%
as_tibble() %>% # compact printing for list columns
summarise_all(list) %>% # collapse each column into a list of itself
gather(group, group_values, -mpg) %>%
mutate(mpg_means = map2(mpg, group_values, # for each mpg/value pair, ...
~tibble(mpg = .x, group_value = .y) %>% # ...reconstruct a data frame...
group_by(group_value) %>%
summarise(mean_mpg = mean(mpg)))) # ...and aggregate
mpg_means_in_list_cols
#> # A tibble: 10 x 4
#> mpg group group_values mpg_means
#> <list> <chr> <list> <list>
#> 1 <dbl [32]> cyl <dbl [32]> <tibble [3 × 2]>
#> 2 <dbl [32]> disp <dbl [32]> <tibble [27 × 2]>
#> 3 <dbl [32]> hp <dbl [32]> <tibble [22 × 2]>
#> 4 <dbl [32]> drat <dbl [32]> <tibble [22 × 2]>
#> 5 <dbl [32]> wt <dbl [32]> <tibble [29 × 2]>
#> 6 <dbl [32]> qsec <dbl [32]> <tibble [30 × 2]>
#> 7 <dbl [32]> vs <dbl [32]> <tibble [2 × 2]>
#> 8 <dbl [32]> am <dbl [32]> <tibble [2 × 2]>
#> 9 <dbl [32]> gear <dbl [32]> <tibble [3 × 2]>
#> 10 <dbl [32]> carb <dbl [32]> <tibble [6 × 2]>
虽然这绝对不是那么漂亮,但它能够整齐地容纳多种类型。要提取上面的结果,只需添加 %>% unnest(mpg_means)
。按原样,每个分组变量都保存在 group_values
的列表元素中,并以聚合形式保存在每个 mpg_means
tibble 的第一列中。