想象一下,我有一个带有1和0的向量
我写得很紧凑:
1111111100001111111111110000000001111111111100101
我需要获得一个新的向量,将零后的“N”替换为新的零。
例如,对于N = 3。
1111111100001111111111110000000001111111111100101成为 1111111100000001111111110000000000001111111100000
我可以用for循环来做,但我读过这不是一个好习惯,我怎么能这样做呢?
欢呼声
我的矢量确实是一个动物园系列,但我想它没有任何区别。 如果我想要零结束,我会使用cumprod。
答案 0 :(得分:5)
您也可以使用rle
执行此操作。您需要做的就是将n添加到值为0的所有长度,并在值为1时减去n(当行中少于n个时稍微小心)。 (使用Greg的方法构建样本)
rr <- rle(tmp)
## Pad so that it always begins with 1 and ends with 1
if (rr$values[1] == 0) {
rr$values <- c(1, rr$values)
rr$lengths <- c(0, rr$lengths)
}
if (rr$values[length(rr$values)] == 0) {
rr$values <- c(rr$values, 1)
rr$lengths <- c(rr$lengths, 0)
}
zero.indices <- seq(from=2, to=length(rr$values), by=2)
one.indices <- seq(from=3, to=length(rr$values), by=2)
rr$lengths[zero.indices] <- rr$lengths[zero.indices] + pmin(rr$lengths[one.indices], n)
rr$lengths[one.indices] <- pmax(0, rr$lengths[one.indices] - n)
inverse.rle(rr)
答案 1 :(得分:3)
如何只循环(假设很少)N个实例:
addZeros <- function(x, N = 3) {
xx <- x
z <- x - 1
for (i in 1:N) {
xx <- xx + c(rep(0, i), z[-c((NROW(x) - i + 1):NROW(x))])
}
xx[xx<0] <- 0
xx
}
只需将所有零实例转换为-1,即可减去N个后续值。
> x <- c(1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1)
> x
[1] 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1
[39] 1 1 1 1 1 1 0 0 1 0 1
> addZeros(x)
[1] 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1
[39] 1 1 1 1 1 1 0 0 0 0 0
编辑:
在阅读R-help邮件列表中的数据描述之后,这显然不是小N的情况。因此,您可能需要为此考虑C函数。
在“addZeros.c”文件中:
void addZeros(int *x, int *N, int *n)
{
int i, j;
for (i = *n - 1; i > 0; i--)
{
if ((x[i - 1] == 0) && (x[i] == 1))
{
j = 0;
while ((j < *N) && (i + j < *n) && (x[i + j] == 1))
{
x[i + j] = 0;
j++;
}
}
}
}
在命令提示符下(Windows中的MS DOS,按Win + r并写入cmd),写入“R CMD SHLIB addZeros.c”。如果无法获得R的路径(即“未知的kommand R”),则需要说明完整的地址(在我的系统上:
"c:\Program Files\R\R-2.10.1\bin\R.exe" CMD SHLIB addZeros.c
在Windows上,这应该生成一个DLL(Linux中的.so),但是如果你还没有R-toolbox,你应该下载并安装它(它是一组工具,比如Perl和Mingw)。从中下载最新版本 http://www.murdoch-sutherland.com/Rtools/
这个的R包装函数是:
addZeros2 <- function(x, N) {
if (!is.loaded("addZeros"))
dyn.load(file.path(paste("addZeros", .Platform$dynlib.ext, sep = "")))
.C("addZeros",
x = as.integer(x),
as.integer(N),
as.integer(NROW(x)))$x
}
请注意,在第一次调用addZeros R函数之前,R中的工作目录应该与DLL(在我的系统setwd("C:/Users/eyjo/Documents/Forrit/R/addZeros")
上)相同(或者,在dyn.load
中只包含完整的dll文件的路径)。最好将它们保存在项目下的子目录中(即“c”),然后在文件路径中的“addZeros”前添加“c /”。
举例说明:
> x <- rbinom(1000000, 1, 0.9)
>
> system.time(addZeros(x, 10))
user system elapsed
0.45 0.14 0.59
> system.time(addZeros(x, 400))
user system elapsed
15.87 3.70 19.64
>
> system.time(addZeros2(x, 10))
user system elapsed
0.01 0.02 0.03
> system.time(addZeros2(x, 400))
user system elapsed
0.03 0.00 0.03
>
其中“addZeros”是我原来的建议只有内部R,而addZeros2正在使用C函数。
答案 2 :(得分:2)
x <- c(1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,1)
n <- 3
z<-rle(x)
tmp <- cumsum(z$lengths)
for (i in seq(which.min(z$values),max(which(z$values==1)),2)) {
if (z$lengths[i+1] < n) x[tmp[i]:(tmp[i] + z$lengths[i+1])] <- 0
else x[tmp[i]:(tmp[i]+n)] <- 0
}
答案 3 :(得分:1)
这是一种方式:
> tmp <- strsplit('1111111100001111111111110000000001111111111100101','')
> tmp <- as.numeric(unlist(tmp))
>
> n <- 3
>
> tmp2 <- embed(tmp, n+1)
>
> tmp3 <- tmp
> tmp3[ which( apply( tmp2, 1, function(x) any(x==0) ) ) + n ] <- 0
>
> paste(tmp3, collapse='')
[1] "1111111100000001111111110000000000001111111100000"
这是否优于循环取决于你。
如果那里有0,这也不会改变前n个元素。
这是另一种方式:
> library(gtools)
>
> tmpfun <- function(x) {
+ if(any(x==0)) {
+ 0
+ } else {
+ x[length(x)]
+ }
+ }
>
> tmp4 <- running( tmp, width=4, fun=tmpfun,
+ allow.fewer=TRUE )
>
> tmp4 <- unlist(tmp4)
> paste(tmp4, collapse='')
[1] "1111111100000001111111110000000000001111111100000"
>
答案 4 :(得分:1)
要跟进我之前的评论,如果速度实际上是一个问题 - 将矢量转换为字符串并使用正则表达式可能比其他解决方案更快。首先是一个功能:
replaceZero <- function(x,n){
x <- gsub(paste("01.{",n-1,"}", sep = "") , paste(rep(0,n+1),collapse = ""), x)
}
生成数据
z <- sample(0:1, 1000000, replace = TRUE)
z <- paste(z, collapse="")
repz <- replaceZero(z,3)
repz <- as.numeric(unlist(strsplit(repz, "")))
崩溃的系统时间,运行正则表达式,并拆分回向量:
Regex method
user system elapsed
2.39 0.04 2.39
Greg's method
user system elapsed
17.m39 0.17 18.30
Jonathon's method
user system elapsed
2.47 0.02 2.31
答案 5 :(得分:1)
我非常喜欢使用“正则表达式”的想法,所以我对此进行了投票。 (希望我也得到了答案,并从嵌入和运行答案中学到了一些东西。整洁!)这是Chase的答案的变体,我认为可以解决所提出的问题:
replaceZero2 <- function(x, n) {
if (n == 0) {
return(x)
}
xString <- paste(x, collapse="")
result <- gsub(paste("(?<=",
paste("01{", 0:(n - 1), "}", sep="", collapse="|"),
")1", sep=""),
"0", xString, perl=TRUE)
return(as.numeric(unlist(strsplit(result, ""))))
}
对于gd047的示例输入中的n = 1,2,3,4,5,这似乎与Chang的rle方法产生相同的结果。
也许你可以用\ K?
更干净地写这个答案 6 :(得分:0)
我自己找到了解决方案。 我认为这很容易而且不是很慢。 我想如果有人能用C ++编译它会非常快,因为它只有一个循环。
f5 <- function(z, N) {
x <- z
count <- 0
for (i in 1:length(z)) {
if (z[i]==0) { count <- N }
else {
if (count >0) {
x[i] <- 0
count <- count-1 }
}
}
x
}
答案 7 :(得分:0)
使用移动最小函数非常快速,简单,并且不依赖于跨度的分布:
x <- rbinom(1000000, 1, 0.9)
system.time(movmin(x, 3, na.rm=T))
# user system elapsed
# 0.11 0.02 0.13
以下对movmin的简单定义就足够了(完整的函数有一些功能对于这种情况是多余的,例如对于大N使用van Herk / Gil-Werman算法)
movmin = function(x, n, na.rm=F) {
x = c(rep.int(NA, n - 1), x) # left pad
do.call(pmin, c(lapply(1:n, function(i) x[i:(length(x) - n + i)]), na.rm=na.rm))
}
实际上,您需要窗口大小为4,因为您会影响零后的3个值。这符合你的f5:
x <- rbinom(1000000, 1, 0.9)
all.equal(f5(x, 3), movmin(x, 4, na.rm=T))
# [1] TRUE