首页 > 解决方案 > 如何在 R 中使用 dplyr 有条件地总结不同的数据列?

问题描述

我需要在 R 中聚合数据。我有 8 列,其中 3 列是分类的,其中 5 列是数字的,需要根据来自 2 个分类变量的条件组合有条件地求和。我的数据如下所示:

df <- structure(list(Color = c("Red", "Blue", "Blue", "Red", "Yellow"
), Weekend = c(1L, 0L, 1L, 0L, 1L), LeapYear = c(1L, 1L, 0L, 
0L, 0L), Length = c(15L, 20L, 10L, 15L, 15L), Height = c(50L, 
70L, 35L, 28L, 80L), Weight = c(120L, 130L, 120L, 105L, 140L), 
    Cost = c(25L, 50L, 55L, 65L, 80L), Purchases = c(5L, 10L, 
    5L, 10L, 15L)), class = "data.frame", row.names = c(NA, -5L
))

> df
   Color Weekend LeapYear Length Height Weight Cost Purchases
1    Red       1        1     15     50    120   25         5
2   Blue       0        1     20     70    130   50        10
3   Blue       1        0     10     35    120   55         5
4    Red       0        0     15     28    105   65        10
5 Yellow       1        0     15     80    140   80        15

我想用条件总和聚合这个表,

例如,总长度和高度,但仅适用于闰年,总和高度和成本,但仅适用于闰年和周末。

我希望这些按颜色分组的条件总和如下所示:

颜色 长度 高度 重量 成本 购买 Length_LeapYear 身高_闰年 身高_闰年_周末 成本_闰年_周末 采购_周末
红色的 30 78 225 90 15 15 50 50 25 5
蓝色的 30 105 250 105 15 20 70 0 0 5
黄色 15 80 140 80 15 0 0 0 0 15

我在 dplyr 工作,并有以下工作使用 summarise_at() 在相​​同条件下对多个字段求和:

df %>% 
group_by(Color, Weekend, LeapYear) %>% 
summarise_at(c(Length_LeapYear == "Length", Height_LeapYear == "Height"), ~sum(.[LeapYear==1]))

但是当我尝试为剩余的条件求和变量添加条件时,这会删除我之前的总结。这是我想象代码如何工作的想法。

df %>% 
group_by(Color, Weekend, LeapYear) %>% 
summarise_at(c("Length", "Height", "Weight", "Cost", "Purchases"), sum) %>%
summarise_at(c(Length_LeapYear == "Length", Height_LeapYear == "Height"), ~sum(.[LeapYear==1])) %>%
summarise_at(c(Height_LeapYear_Weekend == "Height", Cost_LeapYear_Weekend == "Cost"), ~sum(.[LeapYear==1 & Weekend ==1])) %>%
summarise(Purchases_Weekend = sum(Purchases)) %>%
group_by(Color)

最终,我觉得必须有一种方法可以将这些不同条件的总和中的每一个都放入一个 summarise_at() 调用中。我也不确定对列(周末和闰年)进行有条件求和然后从最终表格中省略这些列的最佳实践。因此,我们也将不胜感激。

作为记录,我确实知道我可以通过对 summarise() 的一次长时间调​​用来执行这些操作,其中我单独调整每个派生列。然而,在实践中,我的数据集比这要广泛得多,尝试通过对类似条件进行分组来压缩数据操作更有意义。

标签: rdplyrconditional-statementstidyversedata-manipulation

解决方案


更新再三考虑,我知道您需要立即执行此操作。我认为下面的语法将完成通过四种类型的聚合一次汇总整个数据集(在示例 cols 3 到 col7 中)的工作

df %>% group_by(Color) %>%
  summarise(across(3:7, ~sum(.))) %>%
  left_join(df %>% group_by(Color) %>% summarise(across(3:7, ~sum(.*LeapYear), .names= "{.col}_LeapYear"))) %>%
  left_join(df %>% group_by(Color) %>% summarise(across(3:7, ~sum(.*Weekend), .names= "{.col}_Weekend"))) %>%
  left_join(df %>% group_by(Color) %>% summarise(across(3:7, ~sum(.*LeapYear*Weekend), .names= "{.col}_LeapYear_Weekend")))

# A tibble: 3 x 21
  Color Length Height Weight  Cost Purchases Length_LeapYear Height_LeapYear Weight_LeapYear Cost_LeapYear
  <chr>  <int>  <int>  <int> <int>     <int>           <int>           <int>           <int>         <int>
1 Blue      30    105    250   105        15              20              70             130            50
2 Red       30     78    225    90        15              15              50             120            25
3 Yell~     15     80    140    80        15               0               0               0             0
# ... with 11 more variables: Purchases_LeapYear <int>, Length_Weekend <int>, Height_Weekend <int>,
#   Weight_Weekend <int>, Cost_Weekend <int>, Purchases_Weekend <int>, Length_LeapYear_Weekend <int>,
#   Height_LeapYear_Weekend <int>, Weight_LeapYear_Weekend <int>, Cost_LeapYear_Weekend <int>,
#   Purchases_LeapYear_Weekend <int>

您也可以像这样在列表中传递完整的功能(这将进一步缩短您的代码)

df %>% group_by(Color) %>%
  summarise(across(3:7, list(sum= ~sum(.), 
                             leapyear = ~sum(.*LeapYear), 
                             weekend = ~sum(.*Weekend), 
                             leapyear_weekend = ~sum(.*Weekend*LeapYear))))

# A tibble: 3 x 21
  Color Length_sum Length_leapyear Length_weekend Length_leapyear~ Height_sum Height_leapyear Height_weekend
  <chr>      <int>           <int>          <int>            <int>      <int>           <int>          <int>
1 Blue          30              20             10                0        105              70             35
2 Red           30              15             15               15         78              50             50
3 Yell~         15               0             15                0         80               0             80
# ... with 13 more variables: Height_leapyear_weekend <int>, Weight_sum <int>, Weight_leapyear <int>,
#   Weight_weekend <int>, Weight_leapyear_weekend <int>, Cost_sum <int>, Cost_leapyear <int>,
#   Cost_weekend <int>, Cost_leapyear_weekend <int>, Purchases_sum <int>, Purchases_leapyear <int>,
#   Purchases_weekend <int>, Purchases_leapyear_weekend <int>

dput(df)我已包含在您的问题中的示例。

旧答案这样做

df %>% 
  group_by(Color) %>% 
  summarise(Length_s = sum(Length),
            Height_s = sum(Height),
            Weight_s = sum(Weight),
            Cost_s = sum(Cost),
            Purchases_s = sum(Purchases),
            Length_Leap_year = sum(Length * LeapYear),
            Height_Leap_year = sum(Height * LeapYear),
            Height_Leap_year_Weekend = sum(Height * LeapYear * Weekend),
            Purchases_Weekend = sum(Purchases * Weekend))

# A tibble: 3 x 10
  Color  Length_s Height_s Weight_s Cost_s Purchases_s Length_Leap_year Height_Leap_year Height_Leap_year_Weekend Purchases_Weeke~
  <chr>     <int>    <int>    <int>  <int>       <int>            <int>            <int>                    <int>            <int>
1 Blue         30      105      250    105          15               20               70                        0                5
2 Red          30       78      225     90          15               15               50                       50                5
3 Yellow       15       80      140     80          15                0                0                        0               15

推荐阅读