2

我正在尝试使用 dplyr::summarize() 和 dplyr::across() 来获得一个小标题,其中包含行中的几个汇总统计信息和列中的变量。我只能通过使用 dplyr::bind_rows() 来实现这个结果,但我想知道是否有更优雅的方式来获得相同的输出。

> library(tidyverse)
── Attaching packages ────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.3     ✔ purrr   0.3.4
✔ tibble  3.1.1     ✔ dplyr   1.0.6
✔ tidyr   1.1.3     ✔ stringr 1.4.0
✔ readr   1.4.0     ✔ forcats 0.5.1
── Conflicts ───────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
> 
> bind_rows(min = summarize(starwars, across(where(is.numeric), min, 
+       na.rm = TRUE)), 
+   median = summarize(starwars, across(where(is.numeric), median, 
+       na.rm = TRUE)), 
+   mean = summarize(starwars, across(where(is.numeric), mean, na.rm = TRUE)), 
+   max = summarize(starwars, across(where(is.numeric), max, na.rm = TRUE)), 
+   sd = summarize(starwars, across(where(is.numeric), sd, na.rm = TRUE)), 
+   .id = "statistic")
# A tibble: 5 x 4
  statistic height   mass birth_year
  <chr>      <dbl>  <dbl>      <dbl>
1 min         66     15          8  
2 median     180     79         52  
3 mean       174.    97.3       87.6
4 max        264   1358        896  
5 sd          34.8  169.       155. 

为什么不能直接用summary来做呢?正如colwise vignette所建议的那样,似乎比使用函数列表更优雅。这是否违反了整洁数据框的原则?(在我看来,将一堆数据帧一个接一个地放样远不那么整洁。)

4

4 回答 4

6

这是一种purrr用于迭代函数列表的方法。这实际上是您使用 . 所做的事情bind_rows(),但代码更少。

library(dplyr)
library(purrr)

funs <- lst(min, median, mean, max, sd)

map_dfr(funs,
        ~ summarize(starwars, across(where(is.numeric), .x, na.rm = TRUE)),
        .id = "statistic")

# # A tibble: 5 x 4
#   statistic height   mass birth_year
#   <chr>      <dbl>  <dbl>      <dbl>
# 1 min         66     15          8  
# 2 median     180     79         52  
# 3 mean       174.    97.3       87.6
# 4 max        264   1358        896  
# 5 sd          34.8  169.       155.
于 2021-05-18T16:20:07.533 回答
2

我会这样做:

starwars %>%
    summarise(across(where(is.numeric), stat_funs,
        na.rm = TRUE, .names = "{.col}__{.fn}")) %>%
    pivot_longer(everything()) %>%
    separate(name, c('v', 'f'), sep = '__') %>%
    pivot_wider(names_from = v, values_from = value)

#  f      height   mass birth_year
#   <chr>   <dbl>  <dbl>      <dbl>
# 1 min      66     15          8  
# 2 median  180     79         52  
# 3 mean    174.    97.3       87.6
# 4 max     264   1358        896  
# 5 sd       34.8  169.       155. 
于 2021-05-18T16:10:16.930 回答
2

这可以解决您想要的输出,但它并不是那么花哨。

starwars %>% 
  summarise(across(
    where(is.numeric),
    .fns = list(
       min = min,
       median = median, 
       mean = mean, 
       max = max, 
       sd = sd
    ), 
    na.rm = TRUE, 
    .names = "{.col}_{.fn}")) %>% 
  pivot_longer(cols = everything()) %>% 
  mutate(statistic = str_match(name, pattern = ".+_(.+)")[,2],
         name = str_match(name, pattern = "(.+)_.+")[,2]) %>% 
  pivot_wider(names_from = name, values_from = value)
于 2021-05-18T15:59:45.787 回答
1

您可以gtsummary用来汇总数据。下面我将子集设置为数字列(尽管gtsummary处理许多不同的数据类型。然后我告诉 type 参数将我的摘要统计信息放在不同的行上,最后告诉 statistics 参数我要显示哪些摘要。

library(tidyverse)
library(gtsummary)

starwars[sapply(starwars, is.numeric)] %>% 
    tbl_summary(type = all_continuous() ~ "continuous2",
                statistic = all_continuous() ~ c("{median} ({p25}, {p75})",
                                                 "{min}, {max}",
                                                 "{mean},{sd}"))
于 2021-05-18T16:08:10.227 回答