【问题标题】:Replace sets of rows in a data.table with a single row用单行替换 data.table 中的行集
【发布时间】:2018-05-12 16:41:43
【问题描述】:

我有以下data.table

 DT<- data.table(id=c(1,1,1,1,2,2,2,2),
            place = c("a","b","c","d","a","b","d","e"),
            seq = c(1,2,3,4,1,2,3,4))
 setkey(DT,id)   

data.table按id和seq排序:

setorder(DT,id,seq)

对于每个 id,我想查找序列 b,c,d,如果有这样的事情,我想用单行替换 b 和 c 的行,比如说 z(保留其他列与带有 a) 的行相同。

所以在这种情况下,新的 data.table 应该是

DT.tobe<- data.table(id=c(1,1,1,2,2,2,2),
                     place = c("a","z","d","a","b","d","e"),
                     seq = c(1,2,4,1,2,3,4))
> DT.tobe
   id place seq
1:  1     a   1
2:  1     z   2
3:  1     d   4
4:  2     a   1
5:  2     b   2
6:  2     d   3
7:  2     e   4

我不得不说我不知道​​该尝试什么...我也可以接受 data.frame 解决方案的答案!

【问题讨论】:

  • 为什么 b 对应 id = 2 的行没有被 z 替换?
  • @MKR 因为没有c
  • seq 保留以前与 b 关联的值?
  • @MichaelChirico 是的,所有其他列都保留以前与 b 关联的值

标签: r replace data.table


【解决方案1】:
res = setkey(DT[, {
  w = setDT(shift(place, 0:2, type="lead"))[.("b","c","d"), on=.(V1,V2,V3), which=TRUE, nomatch=0]
  if (length(w)){
    w2 = c(w, w + 1L)
    rbind(
      .SD[-w2],
      copy(.SD[w])[, place := "z"]  
    )
  } else .SD
}, by=id], id, seq)

给了

   id place seq
1:  1     a   1
2:  1     z   2
3:  1     d   4
4:  2     a   1
5:  2     b   2
6:  2     d   3
7:  2     e   4

位置 w 是使用对序列 b、c、d 的连接来找到的。从那里,我们确定要删除哪些行(w 加上它后面的行);保留哪些行(w);以及在其中修改什么(位置:=“z”)。

有太多不同的方向可以概括,所以如果出现更复杂的情况,最好只发布一个新问题。

【讨论】:

  • shift 函数是一个黄金工具!
【解决方案2】:

方法应该是按id 分组并评估条件/标志,其中place == "b"place == "c" 大于0(意味着b 和c 都可用于id)。这个标志(比如ReplB 决定是否将b 替换为z 对于id。此外,相同的标志(ReplB)用于过滤掉带有place == "c" 的行对于id .

library(data.table)

DT<- data.table(id=c(1,1,1,1,2,2,2,2),
                place = c("a","b","c","d","a","b","d","e"),
                seq = c(1,2,3,4,1,2,3,4))
setkey(DT,id) 

setorder(DT,id,seq)

DT[,ReplB := sum(place == "b") > 0 & sum(place == "c") >0 ,by=id][
  !(ReplB & place == "c"),.(id, place = ifelse(place=="b" & ReplB,"z",place),seq)]

更新:要检查的条件place(即a-b-c-d-e)按顺序排列。

Approach :=> 从letters 获取每个位置的位置,位置差应为1 以确保位置顺序。

DT[,ReplB := all(diff(mapply(function(x)which(letters==x),place)) == 1),by=id][
  !(ReplB & place == "c"),.(id, place = ifelse(place=="b" & ReplB,"z",place),seq)]
#    id place seq
# 1:  1     a   1
# 2:  1     z   2
# 3:  1     d   4
# 4:  2     a   1
# 5:  2     b   2
# 6:  2     d   3
# 7:  2     e   4

【讨论】:

  • 感谢@MKR 的回答,但是这样一来,我认为没有检查顺序要求。例如,如果序列是 a-c-b-d,替换仍然会发生,但我们希望它仅在 a-b-c-d 时发生!
  • @User800701 很抱歉,序列的顺序没有很清楚地提及。但是基本解决方案仍然是相同的。现在,问题是如果序列是a-b-c-d-e-f,那么你想做什么?
  • 如果是序列a-b-c-d-e-f我想要a-z-d-e-f
  • @User800701 更新了解决方案以考虑顺序条件。看看吧。
  • @User800701 绝对有可能。您需要做的就是编写一个函数来替换all(diff(mapply(function(x)which(letters==x),place)) == 1),它将确定ReplB 是否为TRUE/FALSE
猜你喜欢
  • 1970-01-01
  • 2021-05-23
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
  • 2015-07-01
  • 1970-01-01
相关资源
最近更新 更多