【问题标题】:Create duplicate rows based on conditions in R根据 R 中的条件创建重复行
【发布时间】:2015-05-11 18:09:22
【问题描述】:

我有一个如下所示的 data.table

dt <- data.table(ID=c("A","A","B","B"),Amount1=c(100,200,300,400),
                 Amount2=c(1500,1500,2400,2400),Dupl=c(1,0,1,0))

   ID Amount1 Amount2 Dupl
1:  A     100    1500    1
2:  A     200    1500    0
3:  B     300    2400    1
4:  B     400    2400    0

我需要复制 Dupl 列中具有 1 的每一行,并将该复制行中的 Amount1 值替换为 Amount2 值。除此之外,我需要在 Dupl 中为该重复行赋予值 2。这意味着它应该如下所示:

   ID Amount1 Amount2 Dupl
1:  A     100    1500    1
2:  A    1500    1500    2
3:  A     200    1500    0
4:  B     300    2400    1
5:  B    2400    2400    2
6:  B     400    2400    0

非常感谢任何帮助! 亲切的问候,

提姆

【问题讨论】:

  • 我们还需要了解其他规则吗?给定的 ID 可以有四行吗? “Dupl”列是否有“1”和“2”以外的值?

标签: r duplicates conditional data.table


【解决方案1】:

使用 dplyr

library("data.table")
library("dplyr")

#data
dt <- data.table(ID = c("A", "A", "B", "B"),
                 Amount1 = c(100, 200, 300, 400),
                 Amount2 = c(1500, 1500, 2400, 2400),
                 Dupl = c(1, 0, 1, 0))
#result
rbind(dt,
      dt %>% 
        filter(Dupl == 1) %>% 
        mutate(Dupl = 2,
               Amount1 = Amount2))

#    ID Amount1 Amount2 Dupl
# 1:  A     100    1500    1
# 2:  A     200    1500    0
# 3:  B     300    2400    1
# 4:  B     400    2400    0
# 5:  A    1500    1500    2
# 6:  B    2400    2400    2

【讨论】:

  • 我总是尽量避免使用 plyr,因为我使用的是大型数据集,但这也很有效。非常感谢。
【解决方案2】:

这里有偏见,但我认为这个 dplyr 解决方案很优雅,而且它的可扩展性也很好,特别是只要 Dupl 始终 tidyr::uncount,它说,“基于给定列的值 (x),每行重复 x 次,从而拉长 df。一旦我们延长了 df,我们就可以使用dplyr::mutate_at 来替换与滞后值相同的单元格。

library(tidyverse)
dt %>%
    uncount(Dupl + 1) %>%
    mutate_at(vars(Amount1),
              ~case_when(. == lag(.) ~ Amount2, TRUE ~.)) %>%
    mutate_at(vars(Dupl),
              ~case_when(. == lag(.) ~ 2, TRUE ~.))

#    ID Amount1 Amount2 Dupl
# 1:  A     100    1500    1
# 2:  A    1500    1500    2
# 3:  A     200    1500    0
# 4:  B     300    2400    1
# 5:  B    2400    2400    2
# 6:  B     400    2400    0

【讨论】:

    【解决方案3】:

    使用 dplyr 的 left_join 进行复制工作。也许不优雅,但应该很容易理解。

    library(data.table)
    library(dplyr)
    
    joiner <- data.frame(Dupl = 1, helper_col= 1:2)
    
    dt <- left_join(dt, joiner) %>%
      mutate(Dupl = ifelse(helper_col == 2 & !is.na(helper_col), 2, Dupl)) %>%
      select(-helper_col) %>%
      mutate(Amount1 = ifelse(Dupl == 2, Amount2, Amount1))
    
    > dt
      ID Amount1 Amount2 Dupl
    1  A     100    1500    1
    2  A    1500    1500    2
    3  A     200    1500    0
    4  B     300    2400    1
    5  B    2400    2400    2
    6  B     400    2400    0
    

    【讨论】:

      【解决方案4】:

      您可以rbind 一份已完成正确转换的子集数据副本:

      rbind(dt,copy(dt[Dupl==1])[,Amount1:=Amount2][,Dupl:=Dupl+1])
         ID Amount1 Amount2 Dupl
      1:  A     100    1500    1
      2:  A     200    1500    0
      3:  B     300    2400    1
      4:  B     400    2400    0
      5:  A    1500    1500    2
      6:  B    2400    2400    2
      

      或者,您可以通过子设置获取重复项,然后使用中间步骤转换重复的行。这会将重复的行保留在原始行旁边,如问题中的示例所示:

      x <- dt[rep(seq(dt[,Dupl]),times=dt[,Dupl==1]+1)]
      x[duplicated(x),c("Amount1","Dupl"):=list(Amount2,Dupl+1)]
      x
         ID Amount1 Amount2 Dupl
      1:  A     100    1500    1
      2:  A    1500    1500    2
      3:  A     200    1500    0
      4:  B     300    2400    1
      5:  B    2400    2400    2
      6:  B     400    2400    0
      

      【讨论】:

      • 你可以通过x &lt;- dt[rep(seq_len(.N), Dupl + 1L)]完成第二个解决方案的第一步。如果dt 很大,那么在这一步中调用dt 3 次似乎是一个很大的开销。
      【解决方案5】:

      你可以试试

      rbind(dt,dt[Dupl==1][,c('Amount1', 'Dupl') := list(Amount2, 2)])
      

      【讨论】:

      • @DavidArenburg 谢谢,我也在考虑使用您的模板代码rleid,但发现有点困难。
      • 您如何概括这一点?例如,我的 data.table 有一个定义组的列。我想将整个组复制 N 次,其中 N 是作为某个列的函数计算的。
      • @skan 你能发个问题,这样更容易理解吗
      【解决方案6】:

      这似乎符合您的要求。大概可以细化一点……

      library(splitstackshape)
      expandRows(dt, dt$Dupl+1, count.is.col = FALSE)[
        Dupl != 0, Dupl := cumsum(Dupl), by = ID][
          , Amount1 := ifelse(Dupl > 1, Amount2[-1], Amount1)][]
      #    ID Amount1 Amount2 Dupl
      # 1:  A     100    1500    1
      # 2:  A    1500    1500    2
      # 3:  A     200    1500    0
      # 4:  B     300    2400    1
      # 5:  B    2400    2400    2
      # 6:  B     400    2400    0
      

      【讨论】:

        猜你喜欢
        • 2011-11-01
        • 1970-01-01
        • 2017-02-09
        • 2017-09-12
        • 1970-01-01
        • 2019-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多