用递归值替换零和NA

4
我正在尝试递归替换NA和零值。 我正在处理的是时间序列数据,其中NA或零最好用上一周的值替换(每15分钟测量一次,因此需要往回672个步骤)。 我的数据包含约两年的15分钟值,因此这是一个大型数据集。 不太可能有很多NA或零值,并且也不期望出现相邻的零序列或NA> 672。
我在这个主题 ( R中的递归替换 ) 中找到了一个递归方法,并将其适应于我的问题。
load[is.na(load)] <- 0
o <- rle(load)
o$values[o$values == 0] <- o$values[which(o$values == 0) - 672]
newload<-inverse.rle(o)

现在这是“最好”的方法还是优雅的方法吗?当第一个672个值中出现零值时,我如何保护我的代码免受错误影响? 我习惯于使用Matlab,在那里我会执行以下操作:
% Replace NaN with 0
Load(isnan(Load))=0;
% Find zero values
Ind=find(Load==0);
for f=Ind
    if f>672
    fprintf('Replacing index %d with the load 1 day ago\n', Ind)
    % Replace zero with previous week value
    Load(f)=Load(f-672);
    end
end

由于我不熟悉R语言,所以如何设置if else循环呢?

以下是一个可重现的例子(更改代码,因为其他线程使用的示例无法处理相邻的零):

day<-1:24
load<-rep(day, times=10)
load[50:54]<-0
load[112:115]<-NA
load[is.na(load)] <- 0
load[load==0]<-load[which(load == 0) - 24]

这将提供没有零值和NA值的原始负载数据框。如果在前24个值中存在零,则会出现问题,因为没有值可以替换:

loadtest[c(10,50:54)]<-0 # instead of load[50:54]<-0 gives:

Error in loadtest[which(loadtest == 0) - 24] : 
only 0's may be mixed with negative subscripts

现在可以使用if else语句来解决这个问题,但我不知道如何应用。类似于:
day<-1:24
loadtest<-rep(day, times=10)
loadtest[c(10,50:54)]<-0
loadtest[112:115]<-NA
loadtest[is.na(loadtest)] <- 0 
if(INDEX(loadtest[loadtest==0])<24) {
     # nothing / mean / standard value
    } else {
      loadtest[loadtest==0]<-loadtest[which(loadtest == 0) - 24]
    } 

当然,INDEX不是有效的代码。

如果我没错的话,这会用最后一个非 NA 值替换 NA 值,但这不是我的目标。我想要用递归值替换它。“通用函数,用于将每个 NA 值替换为其之前最近的非 NA 值。” - Peter Nijhuis
哦,我的错误……这是在我喝咖啡之前! - Justin
2
请提供一个简化的示例(无需672个值)和预期结果。示例链接 - Roland
idx <- which(loadtest == 0); idx <- idx[which(idx>24)]; loadtest[idx] <- loadtest[idx-24] - Wojciech Sobala
2个回答

1
你可以使用这个例子:

set.seed(42)

x <- sample(c(0,1,2,3,NA), 100, T)

stepback <- 6

x_old <- x
x_new <- x_old

repeat{
    filter <- x_new==0 | is.na(x_new)
    x_new[filter] <- c(rep(NA, stepback), head(x_new, -stepback))[filter]
    if(identical(x_old,x_new)) break
    x_old <- x_new
}

x
x_new

结果:

> x
  [1] NA NA  1 NA  3  2  3  0  3  3  2  3 NA  1  2 NA NA  0  2  2 NA  0 NA NA  0
 [26]  2  1 NA  2 NA  3 NA  1  3  0 NA  0  1 NA  3  1  2  0 NA  2 NA NA  3 NA  3
 [51]  1  1  1  3  0  3  3  0  1  2  3 NA  3  2 NA  0  1 NA  3  1  0  0  1  2  0
 [76]  3  0  1  2  0  2  0  1  3  3  2  1  0  0  1  3  0  1 NA NA  3  1  2  3  3
> x_new
  [1] NA NA  1 NA  3  2  3 NA  3  3  2  3  3  1  2  3  2  3  2  2  2  3  2  3  2
 [26]  2  1  3  2  3  3  2  1  3  2  3  3  1  1  3  1  2  3  1  2  3  1  3  3  3
 [51]  1  1  1  3  3  3  3  1  1  2  3  3  3  2  1  2  1  3  3  1  1  2  1  2  3
 [76]  3  1  1  2  2  2  3  1  3  3  2  1  3  1  1  3  2  1  3  1  3  1  2  3  3

请注意,某些值仍为“NA”,因为没有先前的信息可用于它们。如果您的数据具有足够的先前信息,则不会发生这种情况。

我在考虑将核心替换为xnew[which(is.na(x)|x==0)]<- x[(which(is.na(x)|x==0)-stepback)],这基本上是一样的。不知道哪个更快。仍然存在一些NA值可能会“回溯”到前几个位置的NA,在这种情况下它们永远无法被替换。这是OP未能正确定义其初始条件的失败,而不是您解决方案中的错误。 - Carl Witthoft
@CarlWitthoft,使用索引(which(is.na(x)|x==0)-stepback)的问题在于它可能会产生负数,这会导致出现错误(如果还有正数索引)或者更糟糕的是,在答案中默默地插入垃圾数据(如果只有负数索引)。 - Ferdinand.kraft
确实如此。我处理任何递归引用问题的方法都是从第一步开始:清理边界条件。或者添加 max(1,which(whateverconditions)) - Carl Witthoft
@CarlWitthoft pmax :-) - Ferdinand.kraft
对不起,我的错。我去拿更多巧克力了。 - Carl Witthoft
显示剩余2条评论

1

一种选择是将您的向量分为一个包含672行的矩阵:

load2 <- matrix(load, nrow=672)

然后将最后观测值向前传递(可以使用zoo、上述方法或其他方法)应用于矩阵的每一行:
load3 <- apply( load2, 1, locf.function )

然后,将结果矩阵转换回长度正确的向量:
load4 <- t(load3)[ seq_along(load) ]

为什么不直接使用load4 <- as.vector(t(load3))呢?只是个人口味问题吗? :-) - Ferdinand.kraft
@Ferdinand.kraft,如果load的长度是672的倍数,那么这将很好地工作,但如果不是,则转换为矩阵(load2)将循环利用一些值来填充最后一列,它们不会破坏应用步骤,但如果它们保留在load4中,那么您将从系列的第一部分添加额外的值到末尾,这可能会真正破坏分析。如果存在这些值,我的版本会将其剥离。 - Greg Snow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接