【问题标题】:Indexing sequence chunks using data.table使用 data.table 索引序列块
【发布时间】:2023-12-17 03:03:01
【问题描述】:

假设我有一个数据集,其中长度为 1 的序列是非法的,长度为 2 的序列是合法的,大于长度 5 的序列是非法的,但允许将较长的序列分解为

set.seed(1)
DT1 <- data.table(smp = 1, R=sample(0:1, 20000, rep=TRUE), Seq = 0L)
DT1[, smp:=1:length(smp)]
DT1[, Seq:=seq(.N), by=list(cumsum(c(0, abs(diff(R)))))]

最后一行直接来自: Creating a sequence in a data.table depending on a column

DT1[, fix_min:=ifelse((R==TRUE & Seq==1) | (R==FALSE), FALSE, TRUE)]
fixmin_idx2 <- which(DT1[, fix_min==TRUE])
DT1[fixmin_idx2 -1, fix_min:=TRUE]

现在我的长度为 2 的合法标记已正确标记。打破>5s。

DT1[R==1 & Seq==6, fix_min:=FALSE]
DT1[,Seq2:=seq(.N), by=list(cumsum(c(0, abs(diff(fix_min)))))]
DT1[R==1 & Seq2==6, fix_min:=FALSE]
fixSeq2_idx7 <- which(DT1[,fix_min==TRUE] & DT1[,Seq2==7])
fixSeq2_idx7
[1] 10203 13228
DT1[fixSeq2_idx7,]
 smp R Seq fix_min Seq2
1: 10203 1  13    TRUE    7
2: 13228 1  13    TRUE    7
DT1[fixSeq2_idx7 + 1,]
 smp R Seq fix_min Seq2
1: 10204 1  14    TRUE    8
2: 13229 0   1   FALSE    1

现在来测试一个 Seq2==7 后面是否跟着一个 Seq2==8,这将是一个合法的 2 长度。我是一个 7,然后是一个 8,一个不是一个 8。我被卡住了。我尝试过的所有操作要么将所有 fix_min 设置为 TRUE,要么将 TRUE 和 FALSE 交替设置。

非常感谢任何指导。

【问题讨论】:

  • 次要修复:ifelse((R==TRUE &amp; Seq==1) | (R==FALSE), FALSE, TRUE) 应该只是 !(R==1 &amp; Seq==1)。请注意,R 是 0/1 而不是 FALSE/TRUE。在其他地方,我强烈怀疑您不需要这么多括号。例如,在by= 中,您不需要将单个向量包装在list() 中。
  • 不确定,但这是否满足您的期望? DT1[, if (.N &gt; 1L) .SD[rep(seq_len(min(.N, 5L)), length.out=.N)], by=.(rleid(R), R)]。它删除了Seq 只是1 的行,如果1:9 将其更改为1:5, 1:4.. 这将在您的第一个代码块之后执行。
  • @Arun - 是的,只是此时我不想删除数据中的行,因为非法数据代表了数据中另一个感兴趣的条件。
  • 在这种情况下,使用 := 代替 .SD 并通过检查适当的条件来更新 Seq。我认为从之前的评论中可以很简单地理解逻辑?
  • @Arun - 我会按照你的建议处理它,但我想我也会挠头。

标签: r indexing data.table sequence chunks


【解决方案1】:

如果我正确理解了您的问题,您希望在 R == 0R == 1 &amp; (1 =&lt; Seq &lt; 6 | Seq &gt; 6) 时将 fix_min 设置为 FALSE。那么以下内容应该可以满足您的需求:

# recreating the data from your first code block
set.seed(1)
DT1 <- data.table(R=sample(0:1, 20000, rep=TRUE))[, smp:=.I
                                                  ][, Seq:=seq(.N), by=rleid(R)
                                                    ][, Seq2 := Seq[.N], by=rleid(R)]

# adding the needed 'fix_min' column
DT1[, fix_min := (R==1 & Seq[.N] > 1 & Seq%%6!=0), by=rleid(R)
    ][R==1 & Seq%%6==1 & Seq2%%6==1 & Seq==Seq2, fix_min := FALSE]

解释

  • data.table(R=sample(0:1, 20000, rep=TRUE)) 创建 data.table 的基础
  • [, smp:=.I] 创建一个索引并将其添加到 data.table
  • by=rleid(R) 识别序列;看看它做了什么尝试:data.table(R=sample(0:1, 20000, rep=TRUE))[, seq.id:=rleid(R)]
  • [, Seq:=seq(.N), by=rleid(R)] 为每个序列创建一个索引并将其添加到 data.table;序列由rleid(R) 标识
  • [, Seq2 := Seq[.N], by=rleid(R)] 创建一个变量,其中包含一个表示序列长度的值
  • fix_min := (R==1 &amp; Seq[.N] &gt; 1 &amp; Seq%%6!=0) 创建一个具有TRUE 值的逻辑向量,其中R==1 和序列的长度大于一(Seq[.N] &gt; 1),不包括序列号是6 倍数的值(Seq%%6!=0
  • R==1 &amp; Seq%%6==1 &amp; Seq2%%6==1 &amp; Seq==Seq2 过滤 data.table 如下:仅在 R==1 和序列值为 71319 等 (Seq%%6==1) 和长度的行序列为71319等,仅从满足其他条件的序列中选择最后一行(Seq==Seq2)。使用 fix_min := FALSE 将它们设置为 FALSE

【讨论】:

  • 嗯,不。如果您查看 DT1[19950:20000] 我看到一个案例,从 19989 年开始,对于 19989:19993 应该是 fix_min TRUE,对于 19994 应该是 FALSE,然后对于 1995:19997 应该是 TRUE。这就是为什么在我最初的方法中,我选择将 Fix_min 再次索引为 Seq2,而不是依赖于 Seq,尽管坦率地说我是在猜测。
  • @Chris 查看新更新。对于您提到的情况,它现在给出了正确的结果。你能检查一下这是不是你想要的吗?
  • @Jaap- 写出来并目视检查我发现 21 个尾随单例 7 在 6 之后(2103/4.4834/5,5703/4,5802/3、8468/9、9275/ 6、9956/7、10493/4、10822/3、11835/6、12618/9、13055/6、13353/4、13551/2、14308/9、14423/4、16389/90、17449/50、 17834/5, 19803/4),其中 6 正确设置为 FALSE,两种情况 8869-8680 允许第二次运行 6 和 13216-13228,其中允许第二次运行 7,均在将 6 设置为 FALSE 之后。
  • @Chris 查看更新。对于 7 的第二次运行,它仍然为 Seq==13 提供 TRUE(我猜这不是你想要的)。我正在努力寻找解决方案。
  • @Chris 我想我找到了正确的解决方案。可以查一下吗?