【问题标题】:Index the first and the last rows with NA in a dataframe在数据框中使用 NA 索引第一行和最后一行
【发布时间】:2017-08-25 13:35:29
【问题描述】:

我有一个大型数据集,其中包含许多 NA。我想找到第一个 NA 和最后一个 NA 出现的行。例如,对于 A 列,我希望输出为第二行(数字前的最后一个 NA)和第五行(数字后的第一个 NA)。我的代码(如下所示)不能很好地工作。

nonnaindex <- which(!is.na(df))
firstnonna <- apply(nonnaindex, 2, min)

数据:

ID  A   B   C
1   NA  NA  3
2   NA  2   2
3   3   3   1
4   4   5   NA
5   NA  6   NA

【问题讨论】:

  • 如果你有NA 2 NA NA, 3, NA NA 3怎么办

标签: r dataframe


【解决方案1】:

我相信这个函数可能是你正在寻找的:

first_and_last_non_na <- function(DT, col) {
  library(data.table)
  data.table(DT)[, grp := rleid(is.na(get(col)))][
    , rbind(last(.SD[is.na(get(col)) & grp == min(grp)]),
            first(.SD[is.na(get(col)) & grp == max(grp)]))][
              !is.na(ID)][, grp := NULL][]
}

返回

first_and_last_na_row(DT, "A")
   ID  A B  C
1:  2 NA 2  2
2:  5 NA 6 NA
first_and_last_na_row(DT, "B")
   ID  A  B C
1:  1 NA NA 3
first_and_last_na_row(DT, "C")
   ID A B  C
1:  4 4 5 NA
first_and_last_na_row(DT, "D")
Empty data.table (0 rows) of 4 cols: ID,A,B,C

万一

DT
   ID  A  B  C
1:  1 NA NA  3
2:  2 NA  2  2
3:  3  3  3  1
4:  4  4  5 NA
5:  5 NA  6 NA

first_and_last_na_row(DT2, "D")
   ID  A  B C  D
1:  1 NA NA 3 NA

如果是Akrun's (simplified) example

DT2
   ID  A  B  C  D
1:  1 NA NA  3 NA
2:  2 NA  2  2  2
3:  3  3  3  1 NA
4:  4  4  5 NA NA
5:  5 NA  6 NA  4

编辑:使用melt() 的更快版本

OP 有 commented,他的生产数据集由 4000 列和 192 行组成,他需要索引来清理另一个数据集。他在所有列上尝试了for 循环,这非常慢。

因此,我建议将数据集从宽格式重塑为长格式,并使用data.table的高效分组机制:

# reshape from wide to long format
long <- setDT(DT2)[, melt(.SD, id = "ID")][
  # add grouping variable to distinguish streaks continuous of NA/non-NA values 
  # for each variable
  , grp := rleid(variable, is.na(value))][
    # set sort order just for convenience, not essential
    , setorder(.SD, variable, ID)]
long
    ID variable value grp
 1:  1        A    NA   1
 2:  2        A    NA   1
 3:  3        A     3   2
 4:  4        A     4   2
 5:  5        A    NA   3
 6:  1        B    NA   4
 7:  2        B     2   5
 8:  3        B     3   5
 9:  4        B     5   5
10:  5        B     6   5
11:  1        C     3   6
12:  2        C     2   6
13:  3        C     1   6
14:  4        C    NA   7
15:  5        C    NA   7
16:  1        D    NA   8
17:  2        D     2   9
18:  3        D    NA  10
19:  4        D    NA  10
20:  5        D     4  11

现在,我们通过

获得每个变量(如果有)的开始或结束序列的索引,分别是NA
# starting NA sequence
long[, .(ID = which(is.na(value) & grp == min(grp))), by = variable]
   variable ID
1:        A  1
2:        A  2
3:        B  1
4:        D  1
# ending NA sequence
long[, .(ID = which(is.na(value) & grp == max(grp))), by = variable]
   variable ID
1:        A  5
2:        C  4
3:        C  5

请注意,这将返回开始或结束NA 序列的所有索引,这可能更便于后续清理另一个数据集。如果只需要最后一个和第一个索引,则可以通过

long[long[, is.na(value) & grp == min(grp), by =variable]$V1, .(ID = max(ID)), by = variable]
   variable ID
1:        A  2
2:        B  1
3:        D  1
long[long[, is.na(value) & grp == max(grp), by =variable]$V1, .(ID = min(ID)), by = variable]
   variable ID
1:        A  5
2:        C  4

我使用 192 行乘以 4000 列的虚拟数据集测试了这种方法。整个操作只需要不到一秒

【讨论】:

  • 那行得通。由于我有 4000 多列,因此我必须在函数顶部使用 for 循环。有没有更省时的路线?
  • 如果所有或大多数列具有相同的数据类型,则数据通常应该以长格式(数据库“样式”)而不是宽格式(电子表格样式)存储。在长格式中,列名成为变量,并且更容易处理聚合。最终结果可以重新整形为宽格式。顺便说一句:你有多少行?
  • 我有 192 行。我想要索引它的原因是因为另一个数据集具有相同的行 * 列。一旦我有了这个数据帧的 NA 索引,我就需要用它来清理另一个数据帧。
  • 我添加了一个版本,对于许多列来说,它比重复调用函数要快得多。希望这将与您随后对另一个数据集的清理步骤很好地配合。
猜你喜欢
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2018-01-01
  • 1970-01-01
  • 2020-08-09
  • 2022-01-13
  • 2020-09-26
  • 1970-01-01
相关资源
最近更新 更多