【问题标题】:Change value of column based on criteria and by group根据条件和按组更改列的值
【发布时间】:2019-03-31 06:00:07
【问题描述】:

我的数据如下所示:

     year month flag group
 1: 1992     6    1     8
 2: 1992     7    0     8
 3: 1992     8    0     8
 4: 1992     9    0     8
 5: 1992    10    0     8
 6: 1992    11    0     8
 7: 1992    12    0     8
 8: 1995     6    0    10
 9: 1995     7    0    11
10: 1995     8    0    11
11: 1995     9    1    11
12: 1995    10    0    11
13: 1995    11    0    11
14: 1995    12    0    11
15: 1998     6    0    13
16: 1998     7    0    13
17: 1998     8    0    13
18: 1998     9    0    13
19: 1998    10    0    13
20: 1998    11    0    13
21: 1998    12    0    13

我需要做的是为 flag 列中第一次观察到 1 之后的所有行分配一个值 1,但是这也需要由 group 完成。

作为一个具体的例子,我想要这个:

     year month flag group
 1: 1992     6    1     8
 2: 1992     7    1     8
 3: 1992     8    1     8
 4: 1992     9    1     8
 5: 1992    10    1     8
 6: 1992    11    1     8
 7: 1992    12    1     8
 8: 1995     6    0    10
 9: 1995     7    0    11
10: 1995     8    0    11
11: 1995     9    1    11
12: 1995    10    1    11
13: 1995    11    1    11
14: 1995    12    1    11
15: 1998     6    0    13
16: 1998     7    0    13
17: 1998     8    0    13
18: 1998     9    0    13
19: 1998    10    0    13
20: 1998    11    0    13
21: 1998    12    0    13

请注意第 1:7 行和 11:14 行现在是 1 的情况,还请注意第 15:21 行没有变化,看看最初是如何没有 1 的。

我的大部分想法都围绕着使用which 来按组找出前 1 的索引,但我遇到了一些麻烦。

如果有人有任何基于 data.table() 的解决方案,那就太好了。

感谢您的帮助!

如果有帮助,这是我的基本数据的dput()

library(data.table)

DT = setDT(structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
1992, 1992, 1992, 1992, 1992, 1995, 1995, 1995, 1995, 1995, 1995, 
1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 1998, 
1998, 1998, 1998, 1998, 1998, 1998, 1998), month = c(1, 2, 3, 
4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), flag = c(0, 0, 
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 8L, 8L, 
8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 10L, 10L, 10L, 10L, 10L, 
10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 13L, 13L, 13L, 13L, 13L, 
13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -36L), 
class = c("data.table", "data.frame")))

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    对于第一次出现 flag = 1 且组至少有一个 flag = 1 的行,我们返回 1

    library(data.table)
    dt[,flag := +(seq_len(.N)>= which.max(flag == 1) & any(flag == 1)),by = group]
    
    dt
    
    #    year month flag group
    # 1: 1992     6    1     8
    # 2: 1992     7    1     8
    # 3: 1992     8    1     8
    # 4: 1992     9    1     8
    # 5: 1992    10    1     8
    # 6: 1992    11    1     8
    # 7: 1992    12    1     8
    # 8: 1995     6    0    10
    # 9: 1995     7    0    11
    #10: 1995     8    0    11
    #11: 1995     9    1    11
    #12: 1995    10    1    11
    #13: 1995    11    1    11
    #14: 1995    12    1    11
    #15: 1998     6    0    13
    #16: 1998     7    0    13
    #17: 1998     8    0    13
    #18: 1998     9    0    13
    #19: 1998    10    0    13
    #20: 1998    11    0    13
    #21: 1998    12    0    13
    #    year month flag group
    

    dplyr 中的哪个

    library(dplyr)
    dt %>%
       group_by(group) %>%
       mutate(flag = +(row_number() >= which.max(flag == 1) & any(flag == 1)))
    

    在基础 R 中使用 ave 将是

    dt$flag <- with(dt, +(ave(flag == 1, group, FUN = function(x) 
                         seq_along(x) >= which.max(x) & any(x))))
    

    数据

    dt <- structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
    1992, 1992, 1992, 1992, 1992, 1995, 1995, 1995, 1995, 1995, 1995, 
    1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 1998, 
    1998, 1998, 1998, 1998, 1998, 1998, 1998), month = c(1, 2, 3, 
    4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
    11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), flag = c(0, 0, 
    0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 8L, 8L, 
    8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 10L, 10L, 10L, 10L, 10L, 
    10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 13L, 13L, 13L, 13L, 13L, 
    13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -36L), class = 
    c("data.table","data.frame"))
    

    【讨论】:

    • 当使用您的data.table() 答案时,我收到以下错误:Error in [.data.table(stack_dat, , :=(flag, as.integer(seq_len(.N) &gt;= : Type of RHS ('integer') must match LHS ('double'). To check and coerce would impact performance too much for the fastest cases. Either change the type of the target column, or coerce the RHS of := yourself (e.g. by using 1L instead of 1) 此外,您似乎已按year 分组而不是group 列。还是我误解了你的代码?
    • @Gin_Salmon 是的,我首先按year 分组。我现在已将组更改为group,尽管输出没有改变,而且我使用的示例数据没有出错。看起来有类型不匹配尝试使用as.numeric 而不是+,试试dt[,flag := as.numeric(seq_len(.N)&gt;= which.max(flag == 1) &amp; any(flag == 1)),by=group]
    • 谢谢。我有几个关于代码的问题:为什么需要 any(),我最初遇到的错误是什么,+as.numeric 之间有什么区别?
    • @Gin_Salmon which.max 计算传递的向量中的第一个最大值。 flag == 1 返回 TRUE/FALSE 值的向量,具体取决于 flag 是否为 1,当我们执行 which.max(flag == 1) 时,它返回第一个最大值的索引。因此,如果有任何TRUE 值,它将返回第一个TRUE 的索引,否则如果所有值都是FALSE,那么最大值将为FALSE,它会返回1。所以在这种情况下它会给整个group 赋值1,以避免我们使用any,并检查group 中是否有任何flag 的值为1。
    • +as.numeric 而言,+TRUE/FALSE 值转换为整数,而您的列中有数值,因此存在类型不匹配,因此不是@ 987654361@ 我们使用as.numeric 以便类型与原始列匹配。
    【解决方案2】:

    您可以在每个组的第一个月进行非 equi 加入:

    DT[unique(DT[flag==1], by="group"), on=.(group, month >= month), flag := 1]
    

    这是来自 OP 的 dput 的结果:

        year month flag group
     1: 1992     1    0     8
     2: 1992     2    0     8
     3: 1992     3    0     8
     4: 1992     4    0     8
     5: 1992     5    0     8
     6: 1992     6    1     8
     7: 1992     7    1     8
     8: 1992     8    1     8
     9: 1992     9    1     8
    10: 1992    10    1     8
    11: 1992    11    1     8
    12: 1992    12    1     8
    13: 1995     1    0    10
    14: 1995     2    0    10
    15: 1995     3    0    10
    16: 1995     4    0    10
    17: 1995     5    0    10
    18: 1995     6    0    10
    19: 1995     7    0    11
    20: 1995     8    0    11
    21: 1995     9    1    11
    22: 1995    10    1    11
    23: 1995    11    1    11
    24: 1995    12    1    11
    25: 1998     1    0    13
    26: 1998     2    0    13
    27: 1998     3    0    13
    28: 1998     4    0    13
    29: 1998     5    0    13
    30: 1998     6    0    13
    31: 1998     7    0    13
    32: 1998     8    0    13
    33: 1998     9    0    13
    34: 1998    10    0    13
    35: 1998    11    0    13
    36: 1998    12    0    13
        year month flag group
    

    【讨论】:

      【解决方案3】:

      您可以使用dplyrcumsum

      library(dplyr)
      df %>%
        group_by(group) %>%
        mutate(flag = ifelse(cumsum(flag) > 1, 1, 0))
      

      另一种方法是使用lag

      df %>%
        group_by(group) %>%
        mutate(flag = ifelse(flag != 1 & row_number() > 1, lag(flag, 1), flag)) 
      

      或在data.table 中为:

      df[, flag := ifelse(cumsum(flag) > 1, 1, 0), by=group]
      

      【讨论】:

      • 您的data.table 解决方案对我不起作用。它只是将每个值分配为 0。有什么建议吗?
      【解决方案4】:

      使用zoo 包中的na.locf()

      第 1 步:筛选包含至少一个“1”的组,并将其中的“0”替换为 NA

      第 2 步:使用 na.locf() 将最近的非 NA 值拖到下面的所有内容中

      library(zoo)
      library(data.table)
      
      temp[group %in% temp[,max(flag),.(group)][V1==1]$group & flag == 0,flag:= NA][,flag:=na.locf(flag,na.rm = FALSE)]
      

      输入表(温度)

      structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
      1995, 1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 
      1998, 1998, 1998), month = c(6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 
      9, 10, 11, 12, 6, 7, 8, 9, 10, 11, 12), flag = c(1, 0, 0, 0, 
      0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 
      8L, 8L, 8L, 8L, 8L, 8L, 10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 
      13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -21L), class = c("data.table", 
      "data.frame"))
      

      输出表

      structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
      1995, 1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 
      1998, 1998, 1998), month = c(6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 
      9, 10, 11, 12, 6, 7, 8, 9, 10, 11, 12), flag = c(1, 1, 1, 1, 
      1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 
      8L, 8L, 8L, 8L, 8L, 8L, 10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 
      13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -21L), class = c("data.table", 
      "data.frame"))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-30
        • 2022-01-25
        • 1970-01-01
        • 2013-12-27
        • 2021-04-19
        • 2019-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多