使用 data.table 按组删除特定列中带有前导缺失值的行

5

我有一个数据表,像这样:

DT <- data.table(id = c(rep("a", 3), rep("b", 3)),
                 col1 = c(NA,1,2,NA,3,NA), col2 = c(NA,NA,5,NA,NA,NA))
   id col1 col2
1:  a   NA   NA
2:  a    1   NA
3:  a    2    5
4:  b   NA   NA
5:  b    3   NA
6:  b   NA   NA

对于每个id,我想使用zoo::na.trim删除'col1'中的前导NA行。这里是我期望的结果:

   id col1 col2
1:  a    1   NA
2:  a    2    5
3:  b    3   NA
4:  b   NA   NA

这是我目前尝试过的方法。它确实删除了“col1”中前导的NA,但是结果中省略了“col2”:

DT[ , na.trim(col1), by = id]
   id V1
1:  a  1
2:  a  2
3:  b  3

这也不起作用:
DT[ , .SD[na.trim(col1)], by = id]
   id col1 col2
1:  a   NA   NA
2:  a    1   NA
3:  b   NA   NA

请指定包含 na.trim 的软件包。 - akrun
抱歉,它是“zoo”。 - Pierre Lapointe
有一个sides参数,即DT[, zoo::na.trim(col1, sides = "left"), by = id] - akrun
@akrun 是的,但这也会删除 col2。 - Pierre Lapointe
3个回答

6
不使用zoo包的一种可能解决方案:
DT[DT[, .I[!!cumsum(!is.na(col1))], by = id]$V1]

您将获得:

   id col1 col2
1:  a    1   NA
2:  a    2    5
3:  b    3   NA
4:  b   NA   NA

这段代码的作用:
  • 使用DT[, .I[!!cumsum(!is.na(col1))], id]$V1创建一个行号向量以保留。通过使用!!cumsum(!is.na(col1)),确保仅省略了col1前导的缺失值。
  • 接下来使用该向量对数据表进行子集筛选。
  • !!cumsum(!is.na(col1))cumsum(!is.na(col1))!=0相同。使用!!将所有大于零的数字转换为TRUE,将所有零转换为FALSE
  • .I不一定需要,也可以使用:DT[DT[, !!cumsum(!is.na(col1)), by = id]$V1],它使用逻辑向量对数据表进行子集筛选。

@lmo在评论中提供了两个使用cummax的替代方法:
# alternative 1:
DT[DT[, !!(cummax(!is.na(col1))), by = id]$V1]

# alternative 2:
DT[as.logical(DT[, cummax(!is.na(col1)), by = id]$V1)]

另一种选择是由@jogo提供的:

DT[, .SD[!!cumsum(!is.na(col1))], by = id]

@Frank提供的另一种选择:

DT[, .SD[ rleid(col1) > 1L | !is.na(col1) ], by = id]

你能解释一下 "!!" 这部分吗?它在 data.table 参考手册中没有提到。 - Pierre Lapointe
还可以使用 cummax 函数,如下所示:DT[DT[, !!(cummax(!is.na(col1))), by=id]$V1]) 或者 DT[as.logical(DT[, cummax(!is.na(col1)), by=id]$V1)] - lmo
变量:DT[, .SD[!!cumsum(!is.na(col1))], id]DT[, .SD[as.logical(cumsum(!is.na(col1)))], id] - jogo

4

na.trim 在使用 data.table 时如下使用。有关其参数的更多信息,请参见?na.trim

DT[, na.trim(.SD, sides = "left", is.na = "all"), by = id]

提供:

   id col1 col2
1:  a    1   NA
2:  a    2    5
3:  b    3   NA
4:  b   NA   NA

新增:

在评论中,发帖人澄清只有第一列存在NA值需要使用na.trim。在这种情况下,添加一列行号.I,然后在调用na.trim之后使用这些行号进行子集筛选。

DT[DT[, na.trim(data.table(col1, .I), "left"), by = id]$.I, ]

@GGrothendieck 我不明白这个解决方案如何仅修剪col1中的NAs。在这个例子中,col2中没有NAs,它似乎不起作用:DT <- data.table(id=c(rep("a",5),rep("b",5)),col1=c(NA,1,NA,2,NA,NA,3,4,NA,5),col2=1:10) - Pierre Lapointe
不理解。我已经在末尾添加了代码,仅对col1应用na.trim,然后取剩下的行。 - G. Grothendieck

0
我们可以使用 1:.N >= which.max(...) 来选择所需的行
> DT[, .SD[1:.N >= which.max(!is.na(col1))], id]
   id col1 col2
1:  a    1   NA
2:  a    2    5
3:  b    3   NA
4:  b   NA   NA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接