根据前面的值按组替换一系列值

Djp*_*ngo 10 string replace r sequence data.table

我有一个这种形式的数据表(2000000+行,1000+组):

set.seed(1)    
dt <- data.table(id = rep(1:3, each = 5), values = sample(c("a", "b","c"), 15, TRUE))

> dt
    id values
 1:  1      a
 2:  1      c
 3:  1      a
 4:  1      b
 5:  1      a
 6:  2      c
 7:  2      c
 8:  2      b
 9:  2      b
10:  2      c
11:  3      c
12:  3      a
13:  3      a
14:  3      a
15:  3      b
Run Code Online (Sandbox Code Playgroud)

我想在每个 ID 组中替换字符“b”之前的整个字符“a”序列,并且我想用“b”替换它们。所以条件是,如果 "a" 或一系列 "a" 出现在 "b" 之前,则替换所有的 "a"。(实际上,在我的真实表格中,当“b”前面是“a”、“x”或“y”时,应该替换前面的字符,但我应该能够概括)

在上面的例子中,第 3 行中“a”的值应该被替换(很容易在 data.table 中使用(移位)),以及第 12-14 行中的所有“a”(不知道如何替换)做)。因此,所需的输出是这样的:

> dt
    id values
 1:  1      a
 2:  1      c
 3:  1      b
 4:  1      b
 5:  1      a
 6:  2      c
 7:  2      c
 8:  2      b
 9:  2      b
10:  2      c
11:  3      c
12:  3      b
13:  3      b
14:  3      b
15:  3      b
Run Code Online (Sandbox Code Playgroud)

我想到的是从最后一个索引开始循环,但是如果我有多个分组(比如 ID 和 DATE),我不确定如何做到这一点,无论如何,这似乎不是最快的 dt 解决方案.

tal*_*lat 5

这是另一种 data.table 方法:

dt[, x := rleid(values), by = .(id)]
dt[dt[values == "b", .(id, x=x-1, values="a")], 
   on = .(id, x, values), 
   values := "b"
   ][, x := NULL]
Run Code Online (Sandbox Code Playgroud)
  • 创建一个新列“x”,每个值的运行长度 id 按 id 分组
  • 在将运行长度 ids (x) 修改为前面的值并将值修改为“a”(您要更改的特定值)的同时加入自身,然后使用“b”更新值
  • 之后删除列 x

结果是:

dt
#     id values
#  1:  1      a
#  2:  1      c
#  3:  1      b
#  4:  1      b
#  5:  1      a
#  6:  2      c
#  7:  2      c
#  8:  2      b
#  9:  2      b
# 10:  2      c
# 11:  3      c
# 12:  3      b
# 13:  3      b
# 14:  3      b
# 15:  3      b
Run Code Online (Sandbox Code Playgroud)

以下是您要将值“a”、“x”或“y”后跟“b”替换为“b”的情况的概括:

dt[, x := rleid(values), by = .(id)]
dt[dt[values == "b", .(values=c("a", "x", "y")), by = .(id, x=x-1)], 
   on = .(id, x, values), 
   values := "b"
   ][, x := NULL]
Run Code Online (Sandbox Code Playgroud)


Dar*_*sai 1

您可以使用rle()

注意:为了避免歧义,我将该"values"列重命名为"var"因为该rle()函数还会生成一个包含名为 的向量的列表"values"

dt[, new := with(rle(var), rep(ifelse(values == "a" & c(values[-1], "") == "b", "b", values), lengths)), by = id]
dt

#     id var new
#  1:  1   a   a
#  2:  1   c   c
#  3:  1   a   b
#  4:  1   b   b
#  5:  1   a   a
#  6:  2   c   c
#  7:  2   c   c
#  8:  2   b   b
#  9:  2   b   b
# 10:  2   c   c
# 11:  3   c   c
# 12:  3   a   b
# 13:  3   a   b
# 14:  3   a   b
# 15:  3   b   b
Run Code Online (Sandbox Code Playgroud)