【问题标题】:Summarising multiple variables without iteration无需迭代即可汇总多个变量
【发布时间】:2021-10-01 20:52:54
【问题描述】:

考虑这个data 需要summary 在多个变量上测量meansd

# Create grouping var; ####
mtcars <- mtcars %>% mutate(
        am = case_when(
                am == 0 ~ "Automatic",
                TRUE ~ "Manual"
        )
)

使用以下自定义functionpurrr,我可以创建一个baseline table

# Summarising function; ####
sum_foo <- function(data, var) {
        
        data %>% 
                group_by(am) %>% 
                summarise(
                        mean = mean( !!sym(var) , na.rm = TRUE),
                        sd   = sd( !!sym(var) , na.rm = TRUE)
                ) %>% 
                mutate(across(where(is.double), round, 2)) %>%  
                group_by(am) %>% 
                transmute(
                        value = paste(mean, "(±", sd, ")", sep = ""),
                        variable = var
                ) %>%
                pivot_wider(
                        names_from = "am"
                )
        
        
}


# Execute Function; ####
sum_variables <- c("mpg", "hp", "disp")


sum_variables %>% map(
        sum_foo,
        data = mtcars
) %>% reduce(
        bind_rows
)

这给出了以下output

# A tibble: 3 x 3
  variable Automatic       Manual        
  <chr>    <chr>           <chr>         
1 mpg      17.15(±3.83)    24.39(±6.17)  
2 hp       160.26(±53.91)  126.85(±84.06)
3 disp     290.38(±110.17) 143.53(±87.2) 

我想在不使用mapreduce 的情况下获得output,即。无需使用 rowwisemap 遍历变量。

我正在寻找替代方案 tidyverse-solution!

【问题讨论】:

  • map 和 reduce 来自 purrr,它是 tidyverse 的一部分,那么这不是一个整洁的解决方案吗?

标签: r tidyverse


【解决方案1】:

也许你可以使用这个解决方案:

library(dplyr)
library(tidyr)
library(tibble)

sum_variables %>%
  enframe() %>%
  rowwise() %>%
  mutate(output = list(sum_foo(mtcars, value))) %>%
  select(output) %>%
  unnest(cols = output)

# A tibble: 3 x 3
  variable Automatic       Manual        
  <chr>    <chr>           <chr>         
1 mpg      17.15(±3.83)    24.39(±6.17)  
2 hp       160.26(±53.91)  126.85(±84.06)
3 disp     290.38(±110.17) 143.53(±87.2) 

更新 或者你甚至可以通过以下方式修改你的函数:

sum_foo2 <- function(data, var) {
  data %>% 
    group_by(am) %>% 
    summarise(across(all_of(var), list(Mean = mean, sd = sd))) %>% 
    mutate(across(where(is.double), round, 2)) %>%  
    group_by(am) %>%
    summarise(across(ends_with("Mean"), ~ paste(.x, "(±", get(gsub("_Mean", "_sd", cur_column())), ")", sep = ""))) %>%
    pivot_longer(!am, names_to = "Mean", values_to = "Val") %>%
    pivot_wider(names_from = "am", values_from = "Val")
}

sum_foo2(mtcars, sum_variables)

# A tibble: 3 x 3
  Mean      Automatic       Manual        
  <chr>     <chr>           <chr>         
1 mpg_Mean  17.15(±3.83)    24.39(±6.17)  
2 hp_Mean   160.26(±53.91)  126.85(±84.06)
3 disp_Mean 290.38(±110.17) 143.53(±87.2) 
 

如果我要将上面的函数修剪成更简洁的版本:

sum_foo2 <- function(data, var) {
  data %>%
    group_by(am) %>%
    summarise(across(all_of(var), ~ paste0(round(mean(.x), 2), "(±", round(sd(.x), 2), ")"))) %>%
    pivot_longer(!am, names_to = "Mean", values_to = "Val") %>%
    pivot_wider(names_from = "am", values_from = "Val")
}

sum_foo2(mtcars, sum_variables)

【讨论】:

  • 这是一个很棒的 tidyverse 唯一解决方案。我目前正在测试您的答案,并对其进行了一些修改summarise(across(all_of(sum_variables),~ paste0(mean(.) %&gt;% round(2), "(±", sd(.) %&gt;% round(2), ")") ))。不使用自定义function。你能详细说明这里getgsub的用法,以及我是如何修改的吗?
  • 不客气,很高兴它有帮助。在最后一个 summarize 函数之前,我们有 6 个变量(实际上是 7 个带有 am),其中 3 个是 Mean 后缀,另外 3 个是 sd 后缀。由于命名几乎相同,我首先选择across.cols参数的ends_withMean,然后将它们与以sd为后缀的那些交换,我通过替换来修改名称_Meangsub 然后使用 get 函数获取值。这里cur_column实际上是指那些以Mean为后缀的初始选择。
  • 哦,我没有注意到括号,我编辑了那部分。如果我没记错的话,你想round 将所有输出都设为 2 位数字,但它们实际上四舍五入为 2 位数字。
  • @Anoushiravan R:我的朋友,这对你来说一定很难,但你没有map就做到了!是的。 :-)
  • @TarJae 哈哈哈是的,但我一个赛季只会做一次。下次别指望我了。
【解决方案2】:

有了我得到的强大答案,这是最终的tidyverse-没有迭代的解决方案或诞生的map

mtcars %>% 
        group_by(am) %>% 
        summarise(
                across(
                        all_of(sum_variables),
                        ~ paste0(mean(.) %>% round(2), "(±", sd(.) %>% round(2), ")")
                        )
                ) %>% pivot_longer(
                        cols = -"am"
                ) %>% pivot_wider(
                        names_from = "am"
                )

这给出了以下output

# A tibble: 3 x 3
  name  Automatic       Manual        
  <chr> <chr>           <chr>         
1 mpg   17.15(±3.83)    24.39(±6.17)  
2 hp    160.26(±53.91)  126.85(±84.06)
3 disp  290.38(±110.17) 143.53(±87.2) 

【讨论】:

  • 是的,我认为在这种情况下我们可以用一个pivot_longer 后跟一个pivot_wider 来模拟data.table::transpose
【解决方案3】:

所以:请保持外邦人。我的解决方案不使用mapreduce

library(dplyr)
library(tidyr)
library(stringr)

data %>% 
    group_by(am) %>% 
    summarise(across(c(mpg, hp, disp), list(mean = mean, sd = sd), .names = "{.col}_{.fn}")) %>% 
    pivot_longer (
        cols = 2:7,
        names_to = "variable",
        values_to = "values"
    ) %>% 
    pivot_wider(
        names_from = am,
        values_from = values
    ) %>% 
    mutate(variable = str_extract(variable, "[^_]*")) %>% 
    mutate(across(c(Automatic, Manual), lead, .names = "{.col}_{.fn}")) %>% 
    filter(row_number() %% 2 == 1) %>% 
    mutate(across(where(is.numeric), round, 2)) %>% 
    mutate(Automatic = paste0(Automatic,"(±",Automatic_1,")"), .keep="unused") %>% 
    mutate(Manual = paste0(Manual,"(±",Manual_1,")"), .keep="unused") 

输出:

  variable Automatic       Manual        
  <chr>    <chr>           <chr>         
1 mpg      17.15(±3.83)    24.39(±6.17)  
2 hp       160.26(±53.91)  126.85(±84.06)
3 disp     290.38(±110.17) 143.53(±87.2) 

【讨论】:

  • 这实际上比我第一次尝试要好得多! :-)
【解决方案4】:

不使用您编写的需要迭代的函数,即逐行/映射,您可以简单地这样做:

sum_variables <- c("mpg", "hp", "disp")
mtcars %>%
  group_by(am) %>%
  summarise(across(all_of(sum_variables),
        ~sprintf('%.2f(\u00B1%.2f)', mean(.x), sd(.x))), .groups = 'drop') %>%
  data.table::transpose(keep.names = 'variable', make.names = TRUE)

 variable       Automatic         Manual
1      mpg    17.15(±3.83)   24.39(±6.17)
2       hp  160.26(±53.91) 126.85(±84.06)
3     disp 290.38(±110.17) 143.53(±87.20)

【讨论】:

  • transpose 中是否有不包括 pivot_*transpose 等效项?我喜欢这个解决方案。
  • @Serkan,你必须使用column_to_rownames 然后t 然后as.data.frame 然后type_convert
  • @Serkan 你不需要type_convert。那是因为一切都是字符
猜你喜欢
  • 2020-06-17
  • 2016-04-08
  • 2011-11-08
  • 1970-01-01
  • 2013-07-16
  • 2018-03-27
  • 1970-01-01
  • 2023-01-12
  • 2021-09-27
相关资源
最近更新 更多