用最接近的值和因子替换 NA 值

标签 r dataframe dplyr data.table na

我已经解决了我的问题,但我想知道是否有更省时的方法来解决它。

我有一个 2200 万行 x 9 列的数据框,其中的列具有以下结构:

factorID = 99000 个级别的因子
日期 = 日期
RDate = 数字(由 R 计算的渐进数字日期,自 1970 年 1 月 1 日以来的天数)
V1:V6 = 整数

每个因子水平均由涵盖 40 年时间跨度的 231 个年度内观测值的时间序列组成。由于功能失调,一些观测值呈现 NA 值,这些值可以在所有 6 个变量之间共享,或者仅限于 1 个。我想将这些 NA 值替换为最接近的观测值时域,主要是上一个或下一个(最简单的情况,但有时上一个或下一个也是NA)。

为了解决我的问题,我尝试使用嵌套 for 循环并成功:

## Isolating one factor at a time with the first loop, since NA amount and position 
## differ for each level
for (i in 1:length(levels(df$factorID))){
  ID = levels(df$factorID)[i]
  Point_df <- subset(df, df$factorID == ID) 
## Calculating total amount and position of NA and integer values by column,
## and identify them by their RDate
## If NA values are present in the column, execute the third loop
  for (j in 1:6){
    ID_column = j+3
    NAcheck <- is.na(Point_df[[ID_column]])
    difference_table <- cbind.data.frame(Point_df$RDate, NAcheck)
    NoNA <- subset(difference_table, difference_table$NAcheck == FALSE)
    True_NA <- subset(difference_table, difference_table$NAcheck == TRUE)
    colnames(True_NA)[1] <- "RDate"
    colnames(NoNA)[1] <- "RDate"
    if (length(True_NA$RDate) > 0){
## With the third loop I compute the nearest not NA observation based on the
## minimum absolute value difference in the time domain (treating the date as a progressive number),
## then I replace one NA at a time
      for (k in 1:length(True_NA$NAcheck)){
        difference <- abs(True_NA$RDate[k]-NoNA$RDate)
        difference_list <- cbind.data.frame(NoNA$RDate, difference)
        replacing_difference <- min(difference)
        replacing_date <- subset(difference_list, difference_list$difference==replacing_difference)
        NA_tochange <- subset(Point_df, Point_df$RDate == True_NA$RDate[k])
        replacing_value <- subset(Point_df, Point_df$RDate == replacing_date[1,1])
        NA_tochange[[ID_column]] <- replacing_value[[ID_column]]
        row <- as.numeric(rownames(True_NA)[k])
        Point_df[row] <- NA_tochange
      }
    }
  }
## Writing the new dataframe one level at a time
  fwrite(Point_df, "B:/Point-predictors_NA-replaced.csv", append=TRUE, sep=",")
}

正如你所想象的,以这种方式解决问题是极其耗时的(在我的笔记本上使用带有2个线程的data.table花了12个小时左右:整个数据帧超过1 GB 以及整个循环的每次迭代都会写入大约 15-30 KB 的数据)。 正如我提到的,由于每个 ID 都有其自身的特殊性,因此我无法想出任何方法来更好地自动化任务。您认为如何才能加快整个操作的速度?

非常感谢。

编辑根据要求,我附上一些示例;我不想让帖子变得比原来更长。

示例数据:

factorID   Date         RDate   V1   V2   V3   V4   V5   V6
1          1989-02-06   6976    318  351  172  570  260  108
1          1989-05-13   7072    77   NA   591  NA   801  550
1          1989-05-29   7088    NA   NA   NA   NA   NA   NA
1          1989-06-14   7104    252  305  286  835  271  85
.
2          1989-02-06   6976    236  389  323  2078 908  373
2          1989-05-13   7072    77   NA   591  NA   801  550
2          1989-05-29   7088    55   62   410  2001 NA   NA
2          1989-06-14   7104    351  508  456  1618 780  421

期望的结果:

factorID   Date         RDate   V1   V2   V3   V4   V5   V6
1          1989-02-06   6976    318  351  172  570  260  108
1          1989-05-13   7072    77   351  591  570  801  550
1          1989-05-29   7088    77   351  591  570  801  550
1          1989-06-14   7104    252  305  286  835  271  85
.
2          1989-02-06   6976    236  389  323  2078 908  373
2          1989-05-13   7072    77   62   591  2001 801  550
2          1989-05-29   7088    55   62   410  2001 801  550
2          1989-06-14   7104    351  508  456  1618 780  421

我希望这会有所帮助。

最佳答案

使用data.table中最近滚动的选项:

cols <- paste0("V", 1L:6L)
for (x in cols) {
    DT[is.na(get(x)), (x) := 
        DT[!is.na(get(x))][.SD, on=.(factorID, RDate), roll="nearest", get(paste0("x.",x))]]
}

输出:

   factorID       Date RDate  V1  V2  V3   V4  V5  V6
1:        1 1989-02-06  6976 318 351 172  570 260 108
2:        1 1989-05-13  7072  77 305 591  835 801 550
3:        1 1989-05-29  7088  77 305 591  835 801 550
4:        1 1989-06-14  7104 252 305 286  835 271  85
5:        2 1989-02-06  6976 236 389 323 2078 908 373
6:        2 1989-05-13  7072  77  62 591 2001 801 550
7:        2 1989-05-29  7088  55  62 410 2001 801 550
8:        2 1989-06-14  7104 351 508 456 1618 780 421

数据:

library(data.table)
DT <- fread("factorID   Date         RDate   V1   V2   V3   V4   V5   V6
1          1989-02-06   6976    318  351  172  570  260  108
1          1989-05-13   7072    77   NA   591  NA   801  550
1          1989-05-29   7088    NA   NA   NA   NA   NA   NA
1          1989-06-14   7104    252  305  286  835  271  85
2          1989-02-06   6976    236  389  323  2078 908  373
2          1989-05-13   7072    77   NA   591  NA   801  550
2          1989-05-29   7088    55   62   410  2001 NA   NA
2          1989-06-14   7104    351  508  456  1618 780  421")

请注意,对于 factorID=1,对于 V2,1989-06-14 是最近的日期 1989-05-13 和 1989-05-29,因此应使用 305 来填充这些日期NA 行。

关于用最接近的值和因子替换 NA 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60913126/

相关文章:

r - 计算月份平均值并替换其他列的值

r - 将函数应用于 R 中数据框中的每一行

python - Pandas 如何用数据帧中的前一个值填充行序列

r - 通过使用 tibble 中不同行的值来改变值

r - Sparklyr - 无法实例化 SessionHiveMetaStoreClient

r - R data.table `.BY` 运算符如何使用?

python - 将标题转换为行

r - dplyr - 将分组变量与分组变量的子集进行比较

r - 如何用组内以前的非 NaN 替换 NaN 值

r - 使用 R 估计滚动风险值(value) (VaR)