【问题标题】:generate random sequences of NA of random lengths in a vector在向量中生成随机长度的 NA 随机序列
【发布时间】:2017-11-19 07:09:06
【问题描述】:

我想在向量中生成缺失值,以便将缺失值按顺序分组,以模拟不同长度的缺失数据周期。

假设我有一个包含 10 000 个值的向量,我想在向量中的随机位置生成 12 个 NA 序列,每个序列的随机长度 L 介于 1 和 144 之间(144 模拟 2 天的缺失值在时间步长 10 分钟)。序列必须不重叠

我该怎么做?谢谢。

我尝试将lapplyseq 组合在一起,但没有成功。

具有 3 个不同序列的示例预期输出:

# 1 2 3 5 2 NA NA 5 4 6 8 9 10 11 NA NA NA NA NA NA 5 2 NA NA NA...

编辑

我正在处理季节性时间序列,因此 NA 必须覆盖值并且不能插入作为新元素。

【问题讨论】:

    标签: r vector random missing-data seq


    【解决方案1】:

    如果每个 NA 序列的起始位置和运行长度都应该是随机的,我认为您不能确定立即找到合适的解决方案,因为您的限制是序列不能重叠。

    因此,我提出以下解决方案,该解决方案最多尝试有限次数 (max_iter) 以找到起始位置和 NA 运行长度的合适组合。如果找到,则返回,如果在定义的最大迭代次数内没有找到,则只会返回通知。

    x = 1:1000
    n = 3
    m = 1:144
    
    f <- function(x, n, m, max_iter = 100) {
      i = 0
      repeat {
        i = i+1
        idx <- sort(sample(seq_along(x), n))        # starting positions
        dist <- diff(c(idx, length(x)))             # check distance inbetween 
        na_len <- sample(m, n, replace = TRUE) - 1L # lengths of NA-runs
        ok <- all(na_len < dist)                    # check overlap
        if(ok | i == max_iter) break 
      }
    
      if(ok) {
        replace(x, unlist(Map(":", idx, idx+na_len)), NA)
      } else {
          cat("no solution found in", max_iter, "iterations")
        }
    }
    
    f(x, n, m, max_iter = 20)
    

    当然,您可以轻松地增加迭代次数,并且您应该注意,n 越大,找到解决方案就越困难(需要更多迭代)。

    【讨论】:

    • 不错,感谢这个功能,看来符合我的条件
    • @agenis 一个小修正:我认为您需要从每个 na_len 中减去 1,否则 NA 运行可能会在 2 和 145 之间而不是 1 和 144 之间变化。
    【解决方案2】:

    所有其他答案或多或少都遵循“条件规范”,其中模拟了 NA 块的起始索引和运行长度。但是,由于必须满足非重叠条件,因此必须逐个确定这些块。这种依赖禁止向量化,必须使用for 循环或lapply / sapply

    然而,这个问题只是另一个运行长度问题。 12 个不重叠的 NA 块会将整个序列分成 13 个不丢失的块(是的,我猜这是 OP 想要的,因为丢失的块作为第一个块或最后一个块并不有趣)。那么为什么不考虑以下几点:

    • 生成 12 个缺失块的运行长度;
    • 生成 13 个非缺失块的运行长度;
    • 交错这两种类型的块。

    第二步看起来很困难,因为它必须满足所有块的长度总和为一个固定数字。嗯,多项分布就是为了这个。

    所以这是一个完全矢量化的解决方案:

    # run length of 12 missing chunks, with feasible length between 1 and 144
    k <- sample.int(144, 12, TRUE)
    
    # run length of 13 non-missing chunks, summing up to `10000 - sum(k)`
    # equal probability is used as an example, you may try something else
    m <- c(rmultinom(1, 10000 - sum(k), prob = rep.int(1, 13)))
    
    # interleave `m` and `k`
    n <- c(rbind(m[1:12], k), m[13])
    
    # reference value: 1 for non-missing and NA for missing, and interleave them
    ref <- c(rep.int(c(1, NA), 12), 1)
    
    # an initial vector
    vec <- rep.int(ref, n)
    
    # missing index
    miss <- is.na(vec)
    

    我们可以验证sum(n) 是 10000。下一步是什么?随意用随机整数填写非缺失条目吗?


    我最初的答案可能太短而无法理解,因此采用上述扩展。

    使用用户输入来代替示例参数值 12、144、10000 来编写实现上述功能的函数很简单。

    注意,多项式的唯一潜在问题是,在一些不好的prob 下,它可能会生成一些零。因此,一些 NA 块实际上会连接在一起。为了解决这个问题,一个稳健的检查是这样的:将所有 0 替换为 1,并从 max(m) 中减去这种变化的通货膨胀。

    【讨论】:

      【解决方案3】:

      编辑:只是为了好玩,下面是我的解决方案的较短递归版本

      add_nas <- function(v,n_seq = 12,min_l_seq = 1,max_l_seq = 144){
        insert_length  <- sample(min_l_seq:max_l_seq,1)
        insert_pos     <- sample(length(v)-insert_length,1)
        v <- v[-(insert_pos+(1:insert_length)-1)]
        if(n_seq > 1){v <- add_nas(v,n_seq-1,min_l_seq,max_l_seq)}
        append(v,rep(NA,insert_length),insert_pos-1)
      }
      

      旧答案:

      # we build a vextor of 20 values
      v <- sample(1:100,20,replace=TRUE) # your vector
      # your parameters
      n_seq <- 3     # you put 12 here
      min_l_seq <- 1 #
      max_l_seq <- 5 # you put 144 here
      
      # first we will delete items, then we add NAs where we deleted instead
      insert_lengths <- sample(min_l_seq:max_l_seq,n_seq,replace=TRUE)
      lengths_before_deletion <- length(v)- c(0,insert_lengths[-length(insert_lengths)])
      insert_pos <- sapply(lengths_before_deletion-insert_lengths+1,function(x){sample(1:x,1)})
      
      v2 <- v
      print(v)
      for (i in 1:n_seq){
        v2 <- v2[-(insert_pos[i]:(insert_pos[i]+insert_lengths[i]-1))]
        print(v2)
      }
      
      for (i in n_seq:1){
        v2 <- c(v2[1:(insert_pos[i]-1)],rep(NA,insert_lengths[i]),v2[insert_pos[i]:length(v2)])
        print(v2)
      }
      

      这是日志

      > print(v)
       [1] 75 11  4 19 55 20 65 48 85 20 61 16 75 31 50 10 30 61  4 32
      > for (i in 1:n_seq){
      +   v2 <- v2[-(insert_pos[i]:(insert_pos[i]+insert_lengths[i]-1))]
      +   print(v2)
      + }
       [1] 75 11 55 20 65 48 85 20 61 16 75 31 50 10 30 61  4 32
       [1] 75 11 55 20 65 48 85 20 61 16 75 50 10 30 61  4 32
       [1] 75 11 55 20 65 48 85 20 61 16 75 50 10 30 32
      > 
      > for (i in n_seq:1){
      +   v2 <- c(v2[1:(insert_pos[i]-1)],rep(NA,insert_lengths[i]),v2[insert_pos[i]:length(v2)])
      +   print(v2)
      + }
       [1] 75 11 55 20 65 48 85 20 61 16 75 50 10 30 NA NA 32
       [1] 75 11 55 20 65 48 85 20 61 16 75 NA 50 10 30 NA NA 32
       [1] 75 11 NA NA 55 20 65 48 85 20 61 16 75 NA 50 10 30 NA NA 32
      

      【讨论】:

      • 注意:由于请求的 NA 序列不会重叠,但它们可能会一个接一个地出现,尽管这在您的数据集中不太可能
      • 解决方案不是完全随机的,连续的删除可能会删除先前切割的两侧,从而人为地创建更多的 288 序列案例(尽管仍然很少见!)。不过它可能仍然对您有用,每个输出都符合您的要求,只是某些可能性会有额外的出现概率。
      【解决方案4】:

      这是我的修改版:

      while(1){
        na_span_vec <- sample((10000-143), 12) %>% sort 
        if(min(na_span_vec - lag(na_span_vec), na.rm = T) > 144) break
      }
      na_idx <- na_span_vec %>% as.list %>% 
        lapply(function(x) seq(x, x + sample(143, 1))) %>% unlist
      original_vec[na_idx] <- NA
      

      【讨论】:

      • 当我运行你的代码时,它似乎生成了超过 144 的序列。
      • 代码中的很酷的技巧...我没想到对起点和终点都进行采样。然而,使用这种方法,我得到一个平均 50% NA 的向量。不幸的是,这对我的算法来说太多了,这就是为什么我想将长度限制为 144
      • 哎呀,我误解了你的意思。我会修改
      • 添加这个位? insert_data &lt;- data.frame(size = sample(1:144, 12), start = sample(seq(1,10000, by = 144),12))insert_data$end &lt;- insert_data$start + insert_data$sizena_span_matrix &lt;- insert_data[,2:3] %&gt;% t
      【解决方案5】:

      你可以使用这个功能:

      genVecLength<-function(vec,namin,namax,nanumber) {
          nalengths<-sample(namin:namax,nanumber,replace=TRUE)
          vec[sort(sample(nanumber*2+1,length(vec),replace=TRUE))%%2==0]<-NA
          vec
      }
      

      其中vec 是您的原始向量,naminnamaxNA 序列的最小和最大长度,nanumber 是序列数。

      一个例子:

      set.seed(1)
      genVecLength(1:30,namin=1,namax=5,nanumber=3)
      #[1]  1  2  3 NA NA NA NA NA  9 10 11 12 13 NA NA NA 17 18 19 20 21 NA NA NA 25
      #[26] 26 27 28 29 30
      

      对于你的例子,如果vec&lt;-runif(10000),你可以试试:

      genVecLength(vec,1,144,12)
      

      【讨论】:

      • 据我了解,OP 想要模拟完整数据的间隙,而不是像您那样插入 NA
      • 你可能是对的。我要删除这个答案,看看能不能找到解决办法。
      【解决方案6】:

      这是一个简单的想法。将 non-na 部分随机切割成 13 段(有些段可能长度为 0,没关系,因为我们可以为每个 11 NA 序列在末尾保留一个 non-na 位置),并在它们之间插入生成的 12 NA 序列。所以 12 NA seq 在长度为 10000 的向量中没有重叠意味着有10000 - sum(length(NA.seq)) - 11 non-na 位置(11 是在 11 NA 序列末尾保留的 non-na 位置。

      orig.seq = 1:10000
      na.len = sapply(1:12, function(x) sample(1:144, 1)) # na sequence length
      na.len[1:11] = na.len[1:11] + 1 #reserve one non-na position for first 11 NA seq
      avail.space = 10000 - sum(na.len) # number of non-na position to cut (sum(na.len) includes the reserved one non-na position)
      avail.space.loc = sample(0:avail.space, 12) %>% sort # find 12 cut point to split it into 13 piece
      end = avail.space.loc + cumsum(na.len)
      start = end - na.len
      for (i in 1:12) {
          if (i != 12) {
              orig.seq[start[i]:end[i]-1] <- NA # recover the reserved non-na position
          } else orig.seq[start[i]:end[i]] <- NA
      }
      

      【讨论】:

      • 好主意!似乎它符合所有标准
      【解决方案7】:
       #just a vector of 10000 values (uniform distribution)
       initVec <- runif(10000)
      
       #12 sequences of NA's with length 1:144 (randomly picked)
       naVecList<-lapply(sample(c(1:144),12,replace = T),function(x) rep(NA,x))
      
       #random positions (along the whole length of initVec)
       (randomPositions<-sort(unlist(lapply(seq_along(1:length(naVecList)), function(x) sample(c(1:(length(initVec)-144)),x,replace = T)[1]))))#added safenet
      
      
       #insert the NA elements at random places.
        for(i in 1:length(randomPositions))
          initVec[randomPositions[i]:(randomPositions[i]+length(naVecList[[i]]))]<-naVecList[[i]]
      

      【讨论】:

      • @agenis 现在有更好的分布式
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-16
      • 1970-01-01
      • 2018-01-25
      • 2017-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多