仅在特定时间序列对象范围内替换NA

时间:2018-03-10 22:41:48

标签: na zoo

我将以下数据存储为zoo对象:

               A       B       C
2017-05-31     NA      NA      3.1
2017-06-30     2.5     2.4     3.2
2017-07-31     2.5     NA      3.3
2017-08-31     2.6     NA      3.4
2017-09-30     2.8     2.5     3.6
2017-10-31     2.3     NA      3.4
2017-11-30     2.5     NA      3.2
2017-12-31     2.7     2.6     2.9
2018-01-31     2.8     NA      3.0
2018-02-28     2.6     NA      NA
2018-03-31     NA      NA      NA

您可以按如下方式重现此zoo对象:

zoo <- data.frame(A=c(NA, 2.5, 2.5, 2.6, 2.8, 2.3, 2.5, 2.7, 2.8, 2.6, NA), B=c(NA, 2.4, NA, NA, 2.5, NA, NA, 2.6, NA, NA, NA), C=c(3.1, 3.2, 3.3, 3.4, 3.6, 3.4, 3.2, 2.9, 3.0, NA, NA), dates=c('2017-05-31', '2017-06-30', '2017-07-31', '2017-08-31', '2017-09-30', '2017-10-31', '2017-11-30', '2017-12-31', '2018-01-31', '2018-02-28', '2018-03-31'))
zoo <- as.zoo(df, order.by=as.Date(df$dates, format="%Y-%m-%d"))[,-4]

A栏和A栏C有月度观察,B栏有季度观察。专栏A&amp; B在开始时有一些NA,最后都有一些NA。我想只填充间歇性的NA(使用前一个值),但不填写开头或结尾的那些。因此,在我的示例中,只应使用之前的值填充2017-06-30和2017-12-31之间的B列中的NA。结果应如下所示:

               A       B       C
2017-05-31     NA      NA      3.1
2017-06-30     2.5     2.4     3.2
2017-07-31     2.5     2.4     3.3
2017-08-31     2.6     2.4     3.4
2017-09-30     2.8     2.5     3.6
2017-10-31     2.3     2.5     3.4
2017-11-30     2.5     2.5     3.2
2017-12-31     2.7     2.6     2.9
2018-01-31     2.8     NA      3.0
2018-02-28     2.6     NA      NA
2018-03-31     NA      NA      NA

请注意,我的数据总是看起来不同,可能有间歇性的NA,但在开始和结束时也有不同长度的NA。因此,我需要一个通用的解决方案。

我已经使用以下代码实现了预期的结果,但它非常麻烦,我确信有更优雅的解决方案。

min <- sapply(zoo, function(col) min(which(!is.na(col))))
max <- sapply(zoo, function(col) max(which(!is.na(col))))

k <- ncol(zoo)
l <- length(min)

for (i in 1:l){
orig <- colnames(zoo)[i]
temp_repl <- na.locf(zoo[min[1]:max[i],i])
temp_zoo <- rbind(zoo[1:min[i]-1,i], temp_repl, zoo[(1+max[i]):nrow(zoo),i])
zoo <- cbind(zoo,temp_zoo)
colnames(zoo)[i] <- paste(orig, ", orig", sep="")
colnames(zoo)[k+i] <- orig  
i+1
}

zoo <- zoo[,(k+1):ncol(zoo)]

1 个答案:

答案 0 :(得分:0)

这也不优雅,但使用dplyr::bind_rows()tidyr::fill()可能会让它更容易理解:

df <- data.frame(A=c(NA, 2.5, 2.5, 2.6, 2.8, 2.3, 2.5, 2.7, 2.8, 2.6, NA), B=c(NA, 2.4, NA, NA, 2.5, NA, NA, 2.6, NA, NA, NA), C=c(3.1, 3.2, 3.3, 3.4, 3.6, 3.4, 3.2, 2.9, 3.0, NA, NA), dates=c('2017-05-31', '2017-06-30', '2017-07-31', '2017-08-31', '2017-09-30', '2017-10-31', '2017-11-30', '2017-12-31', '2018-01-31', '2018-02-28', '2018-03-31'))
df$dates <- as.Date(df$dates)
min_date='2017-06-30' 
max_date='2017-12-31'

df_new <- dplyr::bind_rows(
  df[df$dates < min_date,],
  tidyr::fill(df[df$dates >= min_date & df$dates <= max_date,], A:C),
  df[df$dates > max_date,]
)

df_new

     A   B   C      dates
1   NA  NA 3.1 2017-05-31
2  2.5 2.4 3.2 2017-06-30
3  2.5 2.4 3.3 2017-07-31
4  2.6 2.4 3.4 2017-08-31
5  2.8 2.5 3.6 2017-09-30
6  2.3 2.5 3.4 2017-10-31
7  2.5 2.5 3.2 2017-11-30
8  2.7 2.6 2.9 2017-12-31
9  2.8  NA 3.0 2018-01-31
10 2.6  NA  NA 2018-02-28
11  NA  NA  NA 2018-03-31

zoo也具有na.locf()功能,但在应用于整个数据框时,它会将所有列转换为字符。

相关问题