我已经解决了我的问题,但我想知道是否有更省时的方法来解决它。
我有一个 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/