You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用前非NA值起始的递增序列填充向量NA值——求更优实现

问题:替换向量中连续NA为递增整数序列

我有一个由整数和NA值组成的向量,需要将其中连续的NA值替换为以前一个非NA值为起始的递增整数序列。

现有实现方案

方案1:逻辑直观但速度较慢

使用Reduce函数实现,代码简洁但性能表现差:

v = c(NA, NA, 5, NA, NA, 2, 8, NA)
Reduce(\(i,j) if(is.na(j)) i+1 else j, v, accumulate = T)
# 输出结果:[1] NA NA  5  6  7  2  8  9

方案2:速度较快但写法繁琐

基于rle和bit::vecseq实现,基准测试显示比方案1快约10倍,但代码可读性较差:

r = rle(is.na(v)); w = which(r$values[-1]); s = cumsum(r$lengths); v2 = v
v2[bit::vecseq(s[w]+1, s[w+1])] = bit::vecseq(v[s[w]]+1, v[s[w]]+r$lengths[w+1]); v2
# 输出结果:[1] NA NA  5  6  7  2  8  9

是否存在更快或更简洁的实现方案?

补充实现与基准测试

以下是补充的多种实现方案,以及基于microbenchmark的性能对比:

测试代码

set.seed(0); v = sample(2e4); v[sample(2e4, 1e4)] = NA
vnum = as.numeric(v)

# last observation incremented forward
loif = \(x){r = rle(is.na(x)); w = which(r$values[-1]); s = cumsum(r$lengths)
  x[bit::vecseq(s[w]+1, s[w+1])] = bit::vecseq(v[s[w]]+1, v[s[w]]+r$lengths[w+1]); x}

fill_incr <- function(v){
  idx = which(complete.cases(v))
  le = length(v)

  d <- diff(c(idx, le + 1))
  v[idx[1]:le] <- sequence(d, v[complete.cases(v)])
  v
}

library(collapse)
fill_incr_collapse <- function(v){
  idx = whichNA(v, invert = TRUE)
  le = length(v)

  d <- diff(c(idx, le + 1))
  v[idx[1]:le] <- sequence(d, na_rm(v))
  v
}

# 用单次!is.na替换两次complete.cases调用
fill_incr2 = \(x){nn = !is.na(x); w = which(nn); l = length(x); d = diff(c(w, l+1))
  x[w[1]:l] = sequence(d, x[nn]); x}

cpp11::cpp_source(,'#include "cpp11.hpp"
using namespace cpp11;
[[cpp11::register]]
doubles loif_cpp(doubles xs) {
  bool hadnonna = false;
  int n = xs.size();
  writable::doubles out(n) ;
  for (int i = 0; i < n; ++i) {
    if (hadnonna && ISNA(xs[i])) {
      out[i] = out[i-1] + 1;
    } else {
      out[i] = xs[i];
      if (!ISNA(xs[i])) hadnonna = true;
    }
  }
  return out;
}')

cppFunction('NumericVector na_locf_numeric(NumericVector x) {
  int n = x.size();
  LogicalVector ina = is_na(x);

  for(int i = 1; i<n; i++) {
    if(ina[i] == TRUE) {
      x[i] = x[i-1] + 1;
    }
  }
  return x;
}')

b = microbenchmark::microbenchmark(times=100,
  Reduce(\(i,j)if(is.na(j))i+1 else j,v,accumulate=T),
  loif(v),
  fill_incr(v),
  fill_incr2(v),
  fill_incr_collapse(v),
  loif_cpp(vnum),
  na_locf_numeric(vnum))

o = sort(tapply(b$time, gsub("  +"," ",b$expr), median))
writeLines(sprintf("%.2f %s", o/min(o), names(o)))

基准测试结果(100次运行中位数时间,相对最快方案)

1.00 na_locf_numeric(vnum)
2.26 loif_cpp(vnum)
9.80 fill_incr_collapse(v)
11.63 fill_incr2(v)
15.63 fill_incr(v)
20.41 loif(v)
211.83 Reduce(function(i, j) if (is.na(j)) i + 1 else j, v, accumulate = T)

内容的提问来源于stack exchange,提问作者nisetama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:07:03