我有多个带有NA的向量,并且打算填充NA,它们距有效数据点0大于2个间隔。例如:
x <- c(3, 4, NA, NA, NA, 3, 3)
Run Code Online (Sandbox Code Playgroud)
预期的输出是
3, 4, NA, 0, NA, 3, 3
Run Code Online (Sandbox Code Playgroud)
Shr*_*ree 13
更新-
这可能是最简单,最快的解决方案之一(感谢G. Grothendieck的回答)。只需知道该值是否NA在任意值的两侧就可以了NA。因此,使用lead与lag从dplyr包-
na2zero <- function(x) {
x[is.na(lag(x, 1, 0)) & is.na(lead(x, 1, 0)) & is.na(x)] <- 0
x
}
na2zero(x = c(3, 4, NA, NA, NA, 3, 3))
[1] 3 4 NA 0 NA 3 3
na2zero(x = c(3, 4, NA, NA, NA, NA, NA, 3, 3))
[1] 3 4 NA 0 0 0 NA 3 3
na2zero(x = c(3, 4, NA, NA, NA, 3, 3, NA, NA, 1, NA, 0, 0, rep(NA, 4L)))
[1] 3 4 NA 0 NA 3 3 NA NA 1 NA 0 0 NA 0 0 NA
Run Code Online (Sandbox Code Playgroud)
上一个答案(也很快)-
这是使用rle和replace从基数R开始的一种方法。此方法将每个NA(不是运行长度的终点)变成0-
na2zero <- function(x) {
run_lengths <- rle(is.na(x))$lengths
replace(x,
sequence(run_lengths) != 1 &
sequence(run_lengths) != rep(run_lengths, run_lengths) &
is.na(x),
0)
}
na2zero(x = c(3, 4, NA, NA, NA, 3, 3))
[1] 3 4 NA 0 NA 3 3
na2zero(x = c(3, 4, NA, NA, NA, NA, NA, 3, 3))
[1] 3 4 NA 0 0 0 NA 3 3
Run Code Online (Sandbox Code Playgroud)
更新基准-
set.seed(2)
x <- c(3, 4, NA, NA, NA, 3, 3)
x <- sample(x, 1e5, T)
microbenchmark(
Rui(x),
Shree_old(x), Shree_new(x),
markus(x),
IceCreamT(x),
Uwe1(x), Uwe2(x), Uwe_Reduce(x),
Grothendieck(x),
times = 50
)
all.equal(Shree_dplyr(x), Rui(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Shree_rle(x)) # [1] TRUE
all.equal(Shree_dplyr(x), markus(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe1(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe2(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe_Reduce(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Grothendieck(x)) # [1] TRUE
Unit: milliseconds
expr min lq mean median uq max neval
Rui(x) 286.026540 307.586604 342.620266 318.404731 363.844258 518.03330 50
Shree_rle(x) 51.556489 62.038875 85.348031 65.012384 81.882141 327.57514 50
Shree_dplyr(x) 3.996918 4.258248 17.210709 6.298946 10.335142 207.14732 50
markus(x) 853.513854 885.419719 1001.450726 919.930389 1018.353847 1642.25435 50
IceCreamT(x) 12.162079 13.773873 22.555446 15.021700 21.271498 199.08993 50
Uwe1(x) 162.536980 183.566490 225.801038 196.882049 269.020395 439.17737 50
Uwe2(x) 83.582360 93.136277 115.608342 99.165997 115.376903 309.67290 50
Uwe_Reduce(x) 1.732195 1.871940 4.215195 2.016815 4.842883 25.91542 50
Grothendieck(x) 620.814291 688.107779 767.749387 746.699435 850.442643 982.49094 50
Run Code Online (Sandbox Code Playgroud)
PS:请检查出TiredSquirell的答案,这似乎是Uwe超前-落后答案的基本版本,但速度更快(未在上面进行基准测试)。
也许有更简单的解决方案,但这是可行的。
na2zero <- function(x){
ave(x, cumsum(abs(c(0, diff(is.na(x))))), FUN = function(y){
if(anyNA(y)){
if(length(y) > 2) y[-c(1, length(y))] <- 0
}
y
})
}
na2zero(x)
#[1] 3 4 NA 0 NA 3 3
X <- list(x, c(x, x), c(3, 4, NA, NA, NA, NA, 3, 3))
lapply(X, na2zero)
Run Code Online (Sandbox Code Playgroud)
这是一个data.table选项
library(data.table)
na0_dt <- function(x){
replace(x, rowid(r <- rleid(xna <- is.na(x))) > 1 & rev(rowid(rev(r))) > 1 & xna, 0)
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这里还有其他三种data.table方法:
x <- c(3, 4, NA, NA, NA, 3, 3, NA, NA, 1, NA, 0, 0, rep(NA, 4L))
library(data.table)
data.table(x)[, x := replace(x, which(is.na(x))[-c(1L, .N)], 0), by =.(rleid(is.na(x)))]$x
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)[1] 3 4 NA 0 NA 3 3 NA NA 1 NA 0 0 NA 0 0 NA
x[data.table(x)[, .I[is.na(x)][-c(1L, .N)], by =.(rleid(is.na(x)))]$V1] <- 0
x
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)[1] 3 4 NA 0 NA 3 3 NA NA 1 NA 0 0 NA 0 0 NA
shift() 和 Reduce()我非常专注于找到创建小组的正确方法,以至于我开始考虑直接方法的时间很晚。规则很简单:
将所有NA替换为另一个NA之前和之后的零。
这可以通过以下方式实现zoo::rollapply()在G.格罗腾迪克的回答,或者使用lag()与lead()像斯里最新的编辑。
然而,我自己的基准(这里就不贴避免重复与斯里”基准)显示,data.table::shift()并且Reduce()是迄今为止最快的方法。
isnax <- is.na(x)
x[Reduce(`&`, data.table::shift(isnax, -1:1))] <- 0
x
Run Code Online (Sandbox Code Playgroud)
这也是稍比使用快lag()&lead()(请注意,与此不同斯里的版本,如is.na()只调用一次):
isnax <- is.na(x)
x[isnax & dplyr::lag(isnax) & dplyr::lead(isnax)] <- 0
x
Run Code Online (Sandbox Code Playgroud)
基于该示例,我假设您的意思是,如果该值为NA,并且两个方向上的相邻值均为NA(或者,一个方向为第一个或最后一个,则为一个方向),然后将该值替换为0。使用居中滚动窗口长度为3的值如果全部为NA,则返回TRUE,然后将TRUE位置替换为0。这将提供以下一线
library(zoo)
replace(x, rollapply(c(TRUE, is.na(x), TRUE), 3, all), 0)
## [1] 3 4 NA 0 NA 3 3
Run Code Online (Sandbox Code Playgroud)
这是一个“非常简单”的解决方案:
is_na <- is.na(x) # Vector telling you whether each position in x is NA
na_before <- c(F,is_na[1:(length(x)-1)]) # Whether each position has an NA before it
na_after <- c(is_na[2:length(x),F) # Whether each position has an NA after it
x[is_na & na_before & na_after] <- 0 # Set to 0 if all three are true
Run Code Online (Sandbox Code Playgroud)
na_before和na_after的创建基于向右移动一个或向左移动一个。为了说明这是如何工作的,请考虑以下字母(我将T和F分别写为1和0,以便于区分):
美国广播公司 is_vowel 1 0 0 0 1 元音前0 1 0 0 0 元音后0 0 0 1 0
制作vowel_before时,采用is_vowel的“ 10001”序列并将其向右移动一个(因为每个字母现在都指向其左侧的字母)。您删除最后一个1(您不必担心F之前是否有元音,因为不包括F),并且在开头添加0(第一个字母之前没有字母,因此不能元音之前)。使用相同的逻辑创建vowel_after。
编辑。(由Rui Barradas添加)
根据我的基准,该解决方案是最快的。
作为功能:
TiredSquirrel <- function(x){
is_na <- is.na(x)
na_before <- c(FALSE, is_na[1:(length(x) - 1)])
na_after <- c(is_na[2:length(x)], FALSE)
x[is_na & na_before & na_after] <- 0
x
}
Run Code Online (Sandbox Code Playgroud)
和基准。
x <- c(3, 4, NA, NA, NA, 3, 3)
r <- na2zero(x)
all.equal(r, TiredSquirrel(x))
#[1] TRUE
x <- sample(x, 1e3, TRUE)
r <- na2zero(x)
all.equal(r, TiredSquirrel(x))
#[1] TRUE
microbenchmark(
Rui = na2zero(x),
Uwe_Reduce = Uwe_Reduce(x),
TiredSquirrel = TiredSquirrel(x)
)
#Unit: microseconds
# expr min lq mean median uq max neval cld
# Rui 3134.293 3198.8180 3365.70736 3263.7980 3391.7900 5593.111 100 b
# Uwe_Reduce 99.895 104.3510 125.81417 113.9995 146.7335 244.280 100 a
# TiredSquirrel 65.205 67.4365 72.41129 70.6430 75.8315 122.061 100 a
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1028 次 |
| 最近记录: |