在向量中生成随机长度的 NA 随机序列
generate random sequences of NA of random lengths in a vector
我想在向量中生成缺失值,以便将缺失值按顺序分组,以模拟不同长度的缺失数据周期。
假设我有一个包含 10000 个值的向量,我想在向量中的随机位置生成 12 个 NA 序列,每个序列的随机长度 L
在 1 到 144 之间(144 模拟 2时间步长 10 分钟处缺失值的天数)。序列必须不重叠。
我该怎么做?谢谢
我尝试组合 lapply
和 seq
但没有成功。
具有 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 必须覆盖值并且不能插入作为新元素。
这是我修改后的版本:
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
所有其他答案或多或少都遵循 "conditional specification",其中模拟了 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)
.
中减去此类更改的 inflation
你可以使用这个功能:
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
是您的原始向量,namin
和 namax
是 NA
序列的最小和最大长度,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<-runif(10000)
,您可以尝试:
genVecLength(vec,1,144,12)
编辑: 只是为了好玩,下面是我的解决方案的一个较短的递归版本
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 序列的起始位置和 运行 长度都应该是随机的,我认为您不能确定立即找到合适的解决方案,因为您的约束是序列必须不重叠。
因此我提出了以下解决方案,该解决方案最多尝试有限次数 (max_iter
) 以找到起始位置和 NA-运行-长度的合适组合。如果找到一个,则返回它,如果在定义的最大迭代次数内找到 none,您只会收到一条返回通知。
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
的增加,找到解决方案变得越来越困难(需要更多迭代)。
这是一个简单的想法。将non-na部分随机剪成13块(有的块可能长度为0,没关系,因为我们可以在末尾为每11个NA序列保留一个非na位置),并在它们之间插入生成的12个NA序列。因此,在长度为 10000 的向量中没有重叠的 12 NA 序列意味着有 10000 - sum(length(NA.seq)) - 11
非 na 位置(11 是 11 NA 序列末尾保留的非 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
}
#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]]
我想在向量中生成缺失值,以便将缺失值按顺序分组,以模拟不同长度的缺失数据周期。
假设我有一个包含 10000 个值的向量,我想在向量中的随机位置生成 12 个 NA 序列,每个序列的随机长度 L
在 1 到 144 之间(144 模拟 2时间步长 10 分钟处缺失值的天数)。序列必须不重叠。
我该怎么做?谢谢
我尝试组合 lapply
和 seq
但没有成功。
具有 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 必须覆盖值并且不能插入作为新元素。
这是我修改后的版本:
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
所有其他答案或多或少都遵循 "conditional specification",其中模拟了 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)
.
你可以使用这个功能:
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
是您的原始向量,namin
和 namax
是 NA
序列的最小和最大长度,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<-runif(10000)
,您可以尝试:
genVecLength(vec,1,144,12)
编辑: 只是为了好玩,下面是我的解决方案的一个较短的递归版本
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 序列的起始位置和 运行 长度都应该是随机的,我认为您不能确定立即找到合适的解决方案,因为您的约束是序列必须不重叠。
因此我提出了以下解决方案,该解决方案最多尝试有限次数 (max_iter
) 以找到起始位置和 NA-运行-长度的合适组合。如果找到一个,则返回它,如果在定义的最大迭代次数内找到 none,您只会收到一条返回通知。
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
的增加,找到解决方案变得越来越困难(需要更多迭代)。
这是一个简单的想法。将non-na部分随机剪成13块(有的块可能长度为0,没关系,因为我们可以在末尾为每11个NA序列保留一个非na位置),并在它们之间插入生成的12个NA序列。因此,在长度为 10000 的向量中没有重叠的 12 NA 序列意味着有 10000 - sum(length(NA.seq)) - 11
非 na 位置(11 是 11 NA 序列末尾保留的非 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
}
#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]]