组不为 NA 时的汇总统计

summary statistics when group is not NA

我想计算第 1 组、第 2 组和第 3 组的体重和身高汇总统计数据(均值、范围)。我正在专门寻找一种方法,通过在第 1 组不可用时计算汇总统计数据来实现此目的NA(计算第 1 组) 当列不是 NA 时,第 2 组也是如此。

在下面的示例中,第 1 组的权重为 3,2,第 2 组的权重为 3,5

dt <- tibble(
  group1 = c(1, 1, NA, NA, NA, NA),
  group2 = c(NA, NA, 2, 2, NA, NA),
  group3 = c(NA, NA, NA, NA, 3, 3),
  weight = c(3, 2, 3, 5, NA, 7),
  height = c(10, NA, 14, 15, 11, 20)
)

您可以找到每个组的摘要统计信息,然后过滤掉 NA。以group1为例

dt %>%
  group_by(group1) %>%
  summarise(mean_weight = mean(weight, na.rm=T),
            mean_height = mean(height, na.rm=T),
            .groups = 'drop') %>%
  filter(!(is.na(group1)))

你可以试试

library(dplyr)
library(tidyr)

dt %>% 
  group_by(group = coalesce(group1, group2, group3)) %>% 
  summarize(
    mean_weight = mean(weight, na.rm = TRUE),
    mean_height = mean(height, na.rm = TRUE)
    )

哪个returns

# A tibble: 3 x 3
  group mean_weight mean_height
  <dbl>       <dbl>       <dbl>
1     1         2.5        10  
2     2         4          14.5
3     3         7          15.5

如果每行有多个组,您可以使用

dt %>% 
  pivot_longer(
    starts_with("group"),
    values_drop_na = TRUE,
    values_to = "group"
    ) %>%
  group_by(group) %>% 
  summarize(
    mean_weight = mean(weight, na.rm = TRUE),
    mean_height = mean(height, na.rm = TRUE)
  )

其中returns基本相同

# A tibble: 3 x 3
  group mean_weight mean_height
  <dbl>       <dbl>       <dbl>
1     1         2.5        10  
2     2         4          14.5
3     3         7          15.5

这是如何工作的?

  1. 首先,我们使用 pivot_longer 将数据转换为“长”格式。我们获取以“组”(starts_with("group")) 开头的每一列。这些列名称进入新列 name(默认名称,您可以使用 names_to = "YourNewColumnNameHere" 更改它)。这些值使用 values_to = "group" 放入新列 group。如果不使用此参数,默认情况下,值存储在 value 列中。 values_drop_na = TRUE 处理包含 NA 值的每个单元格。这些被删除。 所以在使用 pivot_longer 之后,转换后的数据看起来像
# A tibble: 6 x 4
  weight height name   group
   <dbl>  <dbl> <chr>  <dbl>
1      3     10 group1     1
2      2     NA group1     1
3      3     14 group2     2
4      5     15 group2     2
5     NA     11 group3     3
6      7     20 group3     3
  1. 接下来我们 group_by(group) 因此接下来的转换将应用于每个独立的组并且不会相互影响。
  2. summarize 进行分组并计算每个组的新列。删除所有其他列。 mean()na.rm = TRUE 参数处理 NA 值:那些被忽略。如果没有这个参数,组 3mean_weight 将是 NA.

编辑

多亏了 akruns 的评论,这可以推广到多列,而无需使用 big-bang-operator:

进行重塑
dt %>% 
  group_by(group = coalesce(!!! select(., starts_with('group')))) %>% 
  summarise(across(c(weight, height), mean, na.rm = TRUE))

akrun 使用更高级的方法:

  1. 核心是复杂的group_by语句。
  2. coalesce() 是一个函数,用于按参数顺序获取第一个非 NA 元素。例如:coalesce(NA_real_, 1, 2) returns 1 因为这是第一个非 NA 值。由于 coalesce() 已矢量化,因此 coalesce(group1, group2, group3) 并且您的组列每行仅包含一个值
dt %>% 
  group_by(group = coalesce(group1, group2, group3))

returns 一个已经分组的小标题。

# A tibble: 6 x 6
# Groups:   group [3]
  group1 group2 group3 weight height group
   <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl>
1      1     NA     NA      3     10     1
2      1     NA     NA      2     NA     1
3     NA      2     NA      3     14     2
4     NA      2     NA      5     15     2
5     NA     NA      3     NA     11     3
6     NA     NA      3      7     20     3
  1. 如果有 many/multiple 列名为 "group",我们不想键入 coalesce(group1, ... , group100)。所以我们正在使用一个函数,它选择所有这些列。这里 !!! select(., starts_with('group')) 是最好的方法:获取 data.frame 的每一列以“组”开头。不幸的是, select returns a data.frame (这是向量列表的特殊版本)。我们需要提供 coalesce 多个向量作为参数。 矢量列表 无法完成工作:
dt %>% 
  group_by(group = coalesce(select(., starts_with('group')))) 

returns

# Groups:   group [3]
  group1 group2 group3 weight height group$group1 $group2 $group3
   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>        <dbl>   <dbl>   <dbl>
1      1     NA     NA      3     10            1      NA      NA
2      1     NA     NA      2     NA            1      NA      NA
3     NA      2     NA      3     14           NA       2      NA
4     NA      2     NA      5     15           NA       2      NA
5     NA     NA      3     NA     11           NA      NA       3
6     NA     NA      3      7     20           NA      NA       3

这不是我们要找的。 big-bang-operator !!! 将这个 向量列表 分成多个单个向量,这些向量作为参数提供给 coalesce。所以

dt %>% 
  group_by(group = coalesce(!!! select(., starts_with('group')))) 

returns

# A tibble: 6 x 6
# Groups:   group [3]
  group1 group2 group3 weight height group
   <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl>
1      1     NA     NA      3     10     1
2      1     NA     NA      2     NA     1
3     NA      2     NA      3     14     2
4     NA      2     NA      5     15     2
5     NA     NA      3     NA     11     3
6     NA     NA      3      7     20     3
  1. 所以现在我们有一个分组数据。frame/tibble 我们可以应用 summarise 函数:summarise(across(c(weight, height), mean, na.rm = TRUE))across() 告诉 summarise 将带有参数 na.rm = TRUE 的函数 mean 应用于列 weightheight。这是
  2. 的更优雅的版本
summarize(
    mean_weight = mean(weight, na.rm = TRUE),
    mean_height = mean(height, na.rm = TRUE)
    )

没有重命名(也可以用 across 完成)。

选项data.table

library(data.table)
setDT(dt)[, lapply(.SD, mean, na.rm = TRUE),
        .(group = fcoalesce(group1, group2, group3)),
        .SDcols = c('weight', 'height')]
   group weight height
1:     1    2.5   10.0
2:     2    4.0   14.5
3:     3    7.0   15.5