【问题标题】:group_by, look up value for group, return value and original data.frame? (efficiently, using dplyr)group_by,查找组的值,返回值和原始data.frame? (有效地,使用 dplyr)
【发布时间】:2020-04-11 04:39:04
【问题描述】:

我正在尝试group_by,找到符合条件的行的第一次出现,并创建一个新列,其值基于从每个组中选择的行。

示例

这很容易用一个例子来演示。一个新列应该由

生成
  1. 按 transaction_id 分组
  2. 寻找第一次出现的icecream_bool (= 1)(注意第二笔交易有两行符合这个条件,所以应该取第一笔)
  3. 使用“item”列中的值创建新列

我们从这个data.frame开始

df <- data.frame(
  transaction_id=as.integer(c(1,1,1,2,2,2,2,2,3,3,3)),
  item=as.character(c("crisps", "magnum", "gum",
                      "jerky", "cheese", "snickers", "ben&jerry", "magnum",
                      "halo", "crisps", "mars")),
  icecream_bool=as.integer(c(0,1,0,
                             0,0,0,1,1,
                             1,0,0)),
  stringsAsFactors = F
)

#    transaction_id      item icecream_bool
# 1               1    crisps             0
# 2               1    magnum             1
# 3               1       gum             0
# 4               2     jerky             0
# 5               2    cheese             0
# 6               2  snickers             0
# 7               2 ben&jerry             1
# 8               2    magnum             1
# 9               3      halo             1
# 10              3    crisps             0
# 11              3      mars             0

期望的输出

像这样生成 ice_cream 列

   transaction_id      item icecream_bool ice_cream
1               1    crisps             0    magnum
2               1    magnum             1    magnum
3               1       gum             0    magnum
4               2     jerky             0 ben&jerry
5               2    cheese             0 ben&jerry
6               2  snickers             0 ben&jerry
7               2 ben&jerry             1 ben&jerry
8               2    magnum             1 ben&jerry
9               3      halo             1      halo
10              3    crisps             0      halo
11              3      mars             0      halo

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    如果icecream_bool 中只有 1 和 0,另一种方法是使用 which.max

    library(dplyr)
    df %>%
      group_by(transaction_id) %>%
      mutate(ice_cream = item[which.max(icecream_bool)])
    
    
    #   transaction_id item      icecream_bool ice-cream
    #            <int> <chr>             <int> <chr>    
    # 1              1 crisps                0 magnum   
    # 2              1 magnum                1 magnum   
    # 3              1 gum                   0 magnum   
    # 4              2 jerky                 0 ben&jerry
    # 5              2 cheese                0 ben&jerry
    # 6              2 snickers              0 ben&jerry
    # 7              2 ben&jerry             1 ben&jerry
    # 8              2 magnum                1 ben&jerry
    # 9              3 halo                  1 halo     
    #10              3 crisps                0 halo     
    #11              3 mars                  0 halo     
    

    这个也可以写成data.table

    library(data.table)
    setDT(df)[, ice_cream := item[which.max(icecream_bool)], transaction_id]
    

    如果有大于 1 的值,我们需要与 1 进行比较,我们可以使用match

    df %>%
      group_by(transaction_id) %>%
      mutate(ice_cream = item[match(1,icecream_bool)])
    

    【讨论】:

    • 谢谢!我很惊讶which.max 没有偶然发现最大值 >1 的组(即示例中的第二笔交易)
    • which.max 默认返回向量中第一个最大值的索引。
    【解决方案2】:

    使用 dplyr 的一种方式

    library(dplyr)
    df%>%
      group_by(transaction_id, icecream_bool) %>%
      mutate(mr = row_number()) %>%
      group_by(transaction_id) %>%
      mutate(ice_cream=item[mr==1 & icecream_bool==1]) %>%
      select(-mr)
    

    # A tibble: 11 x 4
    # Groups:   transaction_id [3]
       transaction_id item      icecream_bool ice_cream
                <int> <chr>             <int> <chr>    
     1              1 crisps                0 magnum   
     2              1 magnum                1 magnum   
     3              1 gum                   0 magnum   
     4              2 jerky                 0 ben&jerry
     5              2 cheese                0 ben&jerry
     6              2 snickers              0 ben&jerry
     7              2 ben&jerry             1 ben&jerry
     8              2 magnum                1 ben&jerry
     9              3 halo                  1 halo     
    10              3 crisps                0 halo     
    11              3 mars                  0 halo 
    

    【讨论】:

    • 谢谢!在您发布之前,我想出了一个类似的方法。我会为它们计时,因为实际用例有很多行(循环需要 91 个小时!)
    • 在这种情况下 data.table 可能是更好的选择。
    • 难道他们(dplyr 和 data.table)都在后台使用 c++。还是 data.table 做了额外的事情?
    • 我知道 data.table 可以。但是dplyr?没有把握。我跟不上每月的变化。
    【解决方案3】:

    你可以这样做:

    library(dplyr)
    
    df %>%
      group_by(transaction_id) %>%
      mutate(icecream = first(item[icecream_bool == 1]))
    
    # A tibble: 11 x 4
    # Groups:   transaction_id [3]
       transaction_id item      icecream_bool ice      
                <int> <chr>             <int> <chr>    
     1              1 crisps                0 magnum   
     2              1 magnum                1 magnum   
     3              1 gum                   0 magnum   
     4              2 jerky                 0 ben&jerry
     5              2 cheese                0 ben&jerry
     6              2 snickers              0 ben&jerry
     7              2 ben&jerry             1 ben&jerry
     8              2 magnum                1 ben&jerry
     9              3 halo                  1 halo     
    10              3 crisps                0 halo     
    11              3 mars                  0 halo   
    

    【讨论】:

    • 非常优雅!
    【解决方案4】:

    我不确定它是否有效,但我找到了一种方法

    df <- data.frame(
      transaction_id=as.integer(c(1,1,1,2,2,2,2,2,3,3,3)),
      item=as.character(c("crisps", "magnum", "gum",
                          "jerky", "cheese", "snickers", "ben&jerry", "magnum",
                          "halo", "crisps", "mars")),
      icecream_bool=as.integer(c(0,1,0,
                                 0,0,0,1,1,
                                 1,0,0)),
      stringsAsFactors = F
    )
    
    df %>% 
      group_by(transaction_id) %>% 
      filter(icecream_bool == 1) %>% 
      distinct(transaction_id, icecream_bool, .keep_all = T) %>% 
      select(-icecream_bool) %>% 
      rename(icecream=item) %>% 
      left_join(df, ., by=c("transaction_id"="transaction_id"))
    
    
       transaction_id      item icecream_bool  icecream
    1               1    crisps             0    magnum
    2               1    magnum             1    magnum
    3               1       gum             0    magnum
    4               2     jerky             0 ben&jerry
    5               2    cheese             0 ben&jerry
    6               2  snickers             0 ben&jerry
    7               2 ben&jerry             1 ben&jerry
    8               2    magnum             1 ben&jerry
    9               3      halo             1      halo
    10              3    crisps             0      halo
    11              3      mars             0      halo
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多