【问题标题】:Automatic column generation from levels based on conditional agggregation in groups using r使用 r 基于组中的条件聚合从级别自动生成列
【发布时间】:2021-10-12 23:11:02
【问题描述】:

对于下面的数据框,我正在尝试从 FactorCol1 有条件地创建八个额外的列 Last1Col7activ 到 Last10Col7inactive:

library(tidyverse)
Data_Frame <- data.frame(Col1 = c("A1", "A1", "A1", "A2", "A2", "A2", "A3", "A3", "A3"),
                         
                         Col2 = c("2011-03-11", "2014-08-21", "2016-01-17", "2017-06-30", "2018-07-11", "2018-11-28", "2019-09-04", "2020-02-29", "2020-07-12"),
                         
                         Col3 = c("2018-10-22", "2019-05-24", "2020-12-25", "2018-10-12", "2019-09-24", "2020-12-19", "2018-10-22", "2019-06-14", "2020-12-20"),
                         
                         Col4 = c(4, 2, 2, 1, 4, 4, 4, 4, 4),
                         
                         Col5 = c(7, 6, 3, 1, 3, 2, 5, 1, 2),
                         
                         FactorCol1 = c("active", "inactive", "inactive", "active", "active", "inactive", "inactive", "active", "inactive"),
                         
                         FactorCol2 = c("Level2", "Level2", "Level3", "Level1", "Level3", "Level1", "Level2", "Level1", "Level3"))

Data_Frame$Col1 <- as.factor(Data_Frame$Col1)
Data_Frame$Col2 <- as.Date(Data_Frame$Col2)
Data_Frame$Col3 <- as.Date(Data_Frame$Col3)
Data_Frame$FactorCol1 <- as.factor(Data_Frame$FactorCol1)
Data_Frame$FactorCol2 <- as.factor(Data_Frame$FactorCol2)

Data_Frame <- Data_Frame %>% group_by(Col1) %>% mutate(Col6 = lubridate::time_length(lubridate::interval(Col2, max(Col3)), "years"))
Data_Frame <- Data_Frame %>% group_by(Col1) %>% dplyr::mutate(Col7 = ifelse(Col6 <= 1, 1, ifelse(Col6 >1 & Col6 <=2, 2, ifelse(Col6 >2 & Col6 <=5, 5, ifelse(Col6 >5 & Col6 <=10, 10, 11)))))

Data_Frame <- Data_Frame %>% group_by(Col1) %>% dplyr::mutate(Col8 = ifelse(FactorCol1 == 'active', 1, 0))
Data_Frame <- Data_Frame %>% group_by(Col1) %>% dplyr::mutate(Col9 = ifelse(FactorCol1 == 'inactive', 1, 0))

Data_Frame <- as.data.frame(Data_Frame)

Data_Frame <- map_dfc(c(1, 2, 5, 10), ~ Data_Frame %>%
                          group_by(Col1) %>% 
                          transmute(!! sprintf("Last%dCol7active", .x) := sum(Col8[Col7 <= .x]),
                                    !! sprintf("Last%dCol7inactive", .x) := sum(Col9[Col7 <= .x])) %>% 
                          ungroup %>%
                          select(-Col1)) %>% 
bind_cols(Data_Frame, .)


  Col1       Col2       Col3 Col4 Col5 FactorCol1 FactorCol2      Col6 Col7 Col8 Col9 Last1Col7active Last1Col7inactive Last2Col7active
1   A1 2011-03-11 2018-10-22    4    7     active     Level2 9.7917808   10    1    0               0                 0               0
2   A1 2014-08-21 2019-05-24    2    6   inactive     Level2 6.3452055   10    0    1               0                 0               0
3   A1 2016-01-17 2020-12-25    2    3   inactive     Level3 4.9371585    5    0    1               0                 0               0
4   A2 2017-06-30 2018-10-12    1    1     active     Level1 3.4712329    5    1    0               0                 0               0
5   A2 2018-07-11 2019-09-24    4    3     active     Level3 2.4410959    5    1    0               0                 0               0
6   A2 2018-11-28 2020-12-19    4    2   inactive     Level1 2.0575342    5    0    1               0                 0               0
7   A3 2019-09-04 2018-10-22    4    5   inactive     Level2 1.2931507    2    0    1               1                 1               1
8   A3 2020-02-29 2019-06-14    4    1     active     Level1 0.8060109    1    1    0               1                 1               1
9   A3 2020-07-12 2020-12-20    4    2   inactive     Level3 0.4410959    1    0    1               1                 1               1
  Last2Col7inactive Last5Col7active Last5Col7inactive Last10Col7active Last10Col7inactive
1                 0               0                 1                1                  2
2                 0               0                 1                1                  2
3                 0               0                 1                1                  2
4                 0               2                 1                2                  1
5                 0               2                 1                2                  1
6                 0               2                 1                2                  1
7                 2               1                 2                1                  2
8                 2               1                 2                1                  2
9                 2               1                 2                1                  2

在哪里: Col6:每组内max(Col3)和Col2之间的时间差

Col7:Col6 中的值的 (

Col8:FactorCol1 中的活动元素设置为 1

Col9:FactorCol1 中的非活动元素设置为 1

Last1Col7active:在每个组内(Col1 中的 A1 到 A3),FactorCol1 中的活动元素数在 Col7 中

Last1Col7inactive:在每个组中,FactorCol1 中的非活动元素数量

Last5Col7active:在每个组内(Col1 中的 A1 到 A3),FactorCol1 中的活动元素数量

Last5Col7inactive:在每个组中,FactorCol1 中的非活动元素数在 Col7 中

Last10Col7active:在每个组内(Col1 中的 A1 到 A3),FactorCol1 中的活动元素数

Last10Col7inactive:在每个组内,FactorCol1 中的非活动元素数在 Col7 中

在尝试根据 FactorCol1 的级别自动生成列时,使用了以下代码,但是,结果显示 ....Col7inactive 的值始终被强制转换为 ...Col7active 的值。出了什么问题?

map_dfc(c(1, 2, 5, 10), function(.x) map_dfc(levels(Data_Frame$FactorCol1), function(.y) Data_Frame %>%
                                               group_by(Col1) %>%
                                               transmute(!! sprintf("Last%dCol7%s", .x, .y) := sum(Col8[Col7 <= .x])
                                                         ,!! sprintf("Last%dCol7%s", .x, .y) := sum(Col9[Col7 <= .x])
                                                         )%>% 
                                               ungroup %>%
                                               select(-Col1))) %>% 
bind_cols(Data_Frame, .)


  Col1       Col2       Col3 Col4 Col5 FactorCol1 FactorCol2      Col6 Col7 Col8 Col9 Last1Col7active Last1Col7inactive Last2Col7active
1   A1 2011-03-11 2018-10-22    4    7     active     Level2 9.7917808   10    1    0               0                 0               0
2   A1 2014-08-21 2019-05-24    2    6   inactive     Level2 6.3452055   10    0    1               0                 0               0
3   A1 2016-01-17 2020-12-25    2    3   inactive     Level3 4.9371585    5    0    1               0                 0               0
4   A2 2017-06-30 2018-10-12    1    1     active     Level1 3.4712329    5    1    0               0                 0               0
5   A2 2018-07-11 2019-09-24    4    3     active     Level3 2.4410959    5    1    0               0                 0               0
6   A2 2018-11-28 2020-12-19    4    2   inactive     Level1 2.0575342    5    0    1               0                 0               0
7   A3 2019-09-04 2018-10-22    4    5   inactive     Level2 1.2931507    2    0    1               1                 1               2
8   A3 2020-02-29 2019-06-14    4    1     active     Level1 0.8060109    1    1    0               1                 1               2
9   A3 2020-07-12 2020-12-20    4    2   inactive     Level3 0.4410959    1    0    1               1                 1               2
  Last2Col7inactive Last5Col7active Last5Col7inactive Last10Col7active Last10Col7inactive
1                 0               1                 1                2                  2
2                 0               1                 1                2                  2
3                 0               1                 1                2                  2
4                 0               1                 1                1                  1
5                 0               1                 1                1                  1
6                 0               1                 1                1                  1
7                 2               2                 2                2                  2
8                 2               2                 2                2                  2
9                 2               2                 2                2                  2

【问题讨论】:

    标签: r dataframe group-by conditional-statements tidyverse


    【解决方案1】:

    我想我通过以下代码得到了预期的输出:

    library(tidyverse)
    
     # 1st step, make the changes to colum types and create Col7 tp 9
    df <- df %>% 
      # Here I just make the changes to colum format as you did in your code ex
      mutate(
        across(.cols = any_of(c("Col1", "FactorCol1", "FactorCol2")), 
               .fns = as.factor),
        across(.cols = any_of(c("Col2", "Col3")),
               .fns = as.Date)
        
      ) %>% 
      group_by(Col1) %>% 
      mutate(
        Col6 = lubridate::time_length(lubridate::interval(Col2, max(Col3)), "years"),
        Col7 = ifelse(Col6 <= 1, 1, ifelse(Col6 >1 & Col6 <=2, 2, ifelse(Col6 >2 & Col6 <=5, 5, ifelse(Col6 >5 & Col6 <=10, 10, 11)))),
        Col8 = ifelse(FactorCol1 == 'active', 1, 0),
        Col9 = ifelse(FactorCol1 == 'inactive', 1, 0),
      )
    # make the counts and merge them with initial df
    df %>% 
      group_by(Col1, FactorCol1) %>% 
      # the trick is to use summarise to get the counts and then use pivot_wider and merge to "set that entire column to those number"
      summarise(
        Last1Col7 = sum(Col7 <= 1),
        Last5Col7 = sum(Col7 <= 5),
        Last10Col7 = sum(Col7 <= 10)
      ) %>% 
      pivot_wider(names_from = FactorCol1, values_from = 3:last_col(), names_sep = "") %>% 
      right_join(df, ., by = "Col1")
    

    数据:

    df <- data.frame(Col1 = c("A1", "A1", "A1", "A2", "A2", "A2", "A3", "A3", "A3"),
                             
                             Col2 = c("2011-03-11", "2014-08-21", "2016-01-17", "2017-06-30", "2018-07-11", "2018-11-28", "2019-09-04", "2020-02-29", "2020-07-12"),
                             
                             Col3 = c("2018-10-22", "2019-05-24", "2020-12-25", "2018-10-12", "2019-09-24", "2020-12-19", "2018-10-22", "2019-06-14", "2020-12-20"),
                             
                             Col4 = c(4, 2, 2, 1, 4, 4, 4, 4, 4),
                             
                             Col5 = c(7, 6, 3, 1, 3, 2, 5, 1, 2),
                             
                             FactorCol1 = c("active", "inactive", "inactive", "active", "active", "inactive", "inactive", "active", "inactive"),
                             
                             FactorCol2 = c("Level2", "Level2", "Level3", "Level1", "Level3", "Level1", "Level2", "Level1", "Level3"))
    

    进一步解释回答“为什么要设置pivot_wider(values_from = 3:last_col())?”

    df %>% group_by(Col1, FactorCol1) %>% summarise(
        Last1Col7 = sum(Col7 <= 1),
        Last5Col7 = sum(Col7 <= 5),
        Last10Col7 = sum(Col7 <= 10))
    

    给你以下小标题:

    # A tibble: 6 x 5
    # Groups:   Col1 [3]
      Col1  FactorCol1 Last1Col7 Last5Col7 Last10Col7
      <fct> <fct>          <int>     <int>      <int>
    1 A1    active             0         0          1
    2 A1    inactive           0         1          2
    3 A2    active             0         2          2
    4 A2    inactive           0         1          1
    5 A3    active             1         1          1
    6 A3    inactive           1         2          2
    

    您希望FactorCol1 的信息采用宽格式,新列包含从第三列 (Last1Col7) 到最后一列的值。从?pivot_wider,我们知道 "names_from, values_from : 一对参数,描述从哪一列(或多列)获取输出列的名称(names_from),以及从哪一列(或多列)获取单元格values from (values_from)。如果 values_from 包含多个值,则该值将添加到输出列的前面。"

    然后,使用%&gt;% pivot_wider(names_from = FactorCol1, values_from = 3:last_col(), names_sep = ""),你会得到:

    # A tibble: 3 x 7
    # Groups:   Col1 [3]
      Col1  Last1Col7active Last1Col7inactive Last5Col7active Last5Col7inactive Last10Col7active Last10Col7inactive
      <fct>           <int>             <int>           <int>             <int>            <int>              <int>
    1 A1                  0                 0               0                 1                1                  2
    2 A2                  0                 0               2                 1                2                  1
    3 A3                  1                 1               1                 2                1                  2
    

    【讨论】:

    • 谢谢。能否请您解释 values_from = 3 的作用:(以及为什么是 3)?
    • 我运行df %&gt;% group_by(Col1, FactorCol1) %&gt;% # the trick is to use summarise to get the counts and then use pivot_wider and merge to "set that entire column to those number" summarise( Last1Col7 = sum(Col7 &lt;= 1), Last5Col7 = sum(Col7 &lt;= 5), Last10Col7 = sum(Col7 &lt;= 10) ),您将在控制台中看到数据框的状态。要将FactorCol1 的级别作为列,您需要枢轴是。并且您希望保留最后 3 列的值。
    • 我希望它更清楚^^顺便说一句,请参阅更新的答案。我虽然可以在一个 dplyr 链中完成所有操作,但我觉得需要保存一个中间 df 以保留您在最终合并中创建的列。
    • 谢谢,我仍然没有理解与 3 相关的要点(它们是最后 3 列)。
    • @Ray 答案已编辑并进一步解释。
    【解决方案2】:

    以下内容可以完成这项工作,但它相当于创建额外的列并删除它们,但使用其他列名

    ABC <- map_dfc(c(1, 2, 5, 10), function(.x) map_dfc(levels(Data_Frame$FactorCol1), function(.y) Data_Frame %>%
                                                                 group_by(Col1) %>%
                                                                 transmute(!! sprintf("Last%dCol7Col8%s", .x, .y) := sum(Col8[Col7 <= .x]),
                                                                           !! sprintf("Last%dCol7Col9%s", .x, .y) := sum(Col9[Col7 <= .x])
                                                                 )%>% 
                                                                 ungroup %>%
                                                                 select(-Col1))) %>%
      bind_cols(Data_Frame, .)
    
    # removing all columns that contain these names
    Data_Frame <- Data_Frame %>% select(-contains(c("Col7Col9active", "Col7Col8inactive")))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 2021-08-18
      • 2015-05-17
      • 2021-10-18
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多