R 用最近的值和因子替换NA值
我已经解决了我的问题,但我想知道是否有一种更省时的方法来解决它 我有一个2200万行x 9列的数据框架,其中列具有以下结构:R 用最近的值和因子替换NA值,r,dataframe,dplyr,data.table,na,R,Dataframe,Dplyr,Data.table,Na,我已经解决了我的问题,但我想知道是否有一种更省时的方法来解决它 我有一个2200万行x 9列的数据框架,其中列具有以下结构: factorID=具有99000个级别的因子 Date=日期 RDate=数字(日期为R计算的累进数,自1970年1月1日起的天数) V1:V6=整数 每个因子水平由231个年内观测值的时间序列组成,时间跨度为40年。由于故障,一些观察值出现了NA值,这些值可在所有6个变量中共享,或仅限于1个变量。我想用时域中最近的观测值替换那些NA值,主要是前一个或下一个(最简单的情况
factorID
=具有99000个级别的因子Date
=日期RDate
=数字(日期为R计算的累进数,自1970年1月1日起的天数)V1:V6
=整数
每个因子水平由231个年内观测值的时间序列组成,时间跨度为40年。由于故障,一些观察值出现了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=",")
}
预期结果:
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
我希望这会有所帮助。以下是一个与您期望的输出相匹配的尝试,但它没有像我希望的那样工作
library(zoo)
library(dplyr)
df2 <- df %>%
group_by(ID) %>%
mutate(next_date_closer = as.Date(Date)-lag(as.Date(Date)) >= lead(as.Date(Date)) - as.Date(Date))
df2 %>%
gather(key, value, -ID, -Date, -RDate, -next_date_closer) %>%
group_by(ID) %>%
mutate(
new_val = ifelse(is.na(next_date_closer), value, na.locf(value, fromLast = next_date_closer[which(is.na(value))]))
) %>%
select(ID, Date, key, new_val) %>%
spread(key, new_val)
# A tibble: 8 x 8
# Groups: ID [2]
ID Date V1 V2 V3 V4 V5 V6
<int> <fct> <int> <int> <int> <int> <int> <int>
1 1 1989-02-06 318 351 172 570 260 108
2 1 1989-05-13 77 305 591 835 801 550
3 1 1989-05-29 252 305 286 835 271 85
4 1 1989-06-14 252 305 286 835 271 85
5 2 1989-02-06 236 389 323 2078 908 373
6 2 1989-05-13 77 62 591 2001 801 550
7 2 1989-05-29 55 62 410 2001 780 421
8 2 1989-06-14 351 508 456 1618 780 421
图书馆(动物园)
图书馆(dplyr)
df2%
分组依据(ID)%>%
变异(下一个日期\u closer=as.date(date)-lag(as.date(date))>=lead(as.date(date))-as.date(date))
df2%>%
聚集(键、值、-ID、-Date、-RDate、-next\u Date\u closer)%>%
分组依据(ID)%>%
变异(
new\u val=ifelse(is.na(next\u date\u closer),value,na.locf(value,fromLast=next\u date\u closer[is.na(value))))
) %>%
选择(ID、日期、键、新值)%>%
排列(键,新值)
#一个tibble:8x8
#组别:ID[2]
ID日期V1 V2 V3 V5 V6
1 1 1989-02-06 318 351 172 570 260 108
2 1 1989-05-13 77 305 591 835 801 550
3 1 1989-05-29 252 305 286 835 271 85
4 1 1989-06-14 252 305 286 835 271 85
5 2 1989-02-06 236 389 323 2078 908 373
6 2 1989-05-13 77 62 591 2001 801 550
7 2 1989-05-29 55 62 410 2001 780 421
8 2 1989-06-14 351 508 456 1618 780 421
使用玩具数据集,您可以使用tidyr::fill
填充数据集。按因素和日期排列数据。按因素分组。然后应用tidyr::fill
。至少对于玩具数据集,当NAs恰好是某个因子的第一个OB时,仍有一些剩余的NAs,但这可以通过应用tidyr::fill
和argument.direction=“up”
向上填充来解决
库(dplyr)
图书馆(tidyr)
种子集(123)
日期因子v1 v2
#>1 2020-01-01 a NA 2
#>2 2020-02-01不适用
#>3 2020-03-01不适用
#>4 2020-04-01 a 2 1
#>5 2020-01-01 b不适用
#>6 2020-02-01 b 2 1
df_填充率%
安排(系数、日期)%>%
分组依据(因子)%>%
填充(v1:v2)
df_填充
#>#A tibble:40 x 4
#>#群体:系数[10]
#>日期因子v1 v2
#>
#>1 2020-01-01 a NA 2
#>2 2020-02-01 a NA 2
#>3 2020-03-01 a NA 2
#>4 2020-04-01 a 2 1
#>5 2020-01-01 b不适用
#>6 2020-02-01 b 2 1
#>7 2020-03-01 b 2 1
#>8 2020-04-01 b 2 2
#>9 2020-01-01 c NA 1
#>10 2020-02-01 c 1 2
#> # ... 还有30行
#检查
df_填充%>%
左联接(df,by=c(“日期”=“日期”、“系数”、“系数”),后缀=c(“\u填充”,“原始”))
#>#A tible:40 x 6
#>#群体:系数[10]
#>日期系数v1\u填充v2\u填充v1\u原始v2\u原始
#>
#>1 2020-01-01 a NA 2 NA 2
#>2 2020-02-01 a NA 2 NA NA
#>3 2020-03-01 a NA 2 NA NA
#>4 2020-04-01 a 2 1
#>5 2020-01-01 b不适用
#>6 2020-02-01 b 2 1
#>7 2020-03-01 b 2 1 2 NA
#>8 2020-04-01 b 2
#>9 2020-01-01 c NA 1 NA 1
#>10 2020-02-01 C1 2
#> # ... 还有30行
由(v0.3.0)创建于2020-03-29,该选项使用从
数据最近的滚动。表:
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))]]
}
数据:
库(data.table)
DTAdata.table
alternative使用连接。如果您的内存能够处理熔化/凝固,则速度应该很快
DT_long <- melt(DT, id.vars = c("factorID", "Date", "RDate"))
DT_long[is.na(value),
value := DT_long[!is.na(value)
][.SD,
on = .(factorID, variable, RDate),
j = value,
roll = "nearest",
mult = "first"]]
dcast(DT_long, factorID + Date + RDate ~ variable, value.vars = "value")
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
DT_long您能提供一些最基本的样本数据来突出问题和期望的结果吗?但感觉确实像zoo::na。fill
可能会有帮助。您可能想做的是,首先使用complete.cases
或anyNA
来确定哪些行不需要修改。也有专门设计用于插补缺失值的包。我认为这里有一些问题,但如果没有一些样本数据,就很难知道什么是最好的。也就是说,通过在循环之外重复一些计算,您将获得一些收益,例如,levels(df$factorID)
——只做一次,而不是多次。我想你可以得到它,这样你只需要第三个循环,然后你就可以使用*apply函数或dplyr或purrr函数,是的,你要找的是zoo::na.locf
,无论是使用fromLast=F
还是fromLast=T
我都选择了这个答案,因为这是提出的最快、更精确的解决方案,尽管其他人也帮了我的忙
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")
DT_long <- melt(DT, id.vars = c("factorID", "Date", "RDate"))
DT_long[is.na(value),
value := DT_long[!is.na(value)
][.SD,
on = .(factorID, variable, RDate),
j = value,
roll = "nearest",
mult = "first"]]
dcast(DT_long, factorID + Date + RDate ~ variable, value.vars = "value")
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