将前面的零值替换为NA,以代替非零值

6

我是R语言的新手,最近一直在苦恼以下问题,希望有人能够帮助我解决。

示例数据代表股价回报(每行为一个月份)。实际数据集要大得多,结构如下所示:

输入:

stock1 <- c(0.01, -0.02, 0.01, 0.05, 0.04, -0.02)
stock2 <- c(0, 0, 0.02, 0.04, -0.03, 0.02)
stock3 <- c(0, 0, 0.02, 0, -0.01, 0.03)
stock4 <- c(0, -0.02, 0.01, 0, 0, -0.02)
df <- cbind(stock1,stock2,stock3,stock4)

     stock1 stock2 stock3 stock4
[1,]   0.01   0.00   0.00   0.00
[2,]  -0.02   0.00   0.00  -0.02
[3,]   0.01   0.02   0.02   0.01
[4,]   0.05   0.04   0.00   0.00
[5,]   0.04  -0.03  -0.01   0.00
[6,]  -0.02   0.02   0.03  -0.02

任何在给定股票的非零数之前出现的零代表缺失数据,而不是该期间的零回报。我希望将这些值设置为NA,以便实现以下所需输出:
stock1 <- c(0.01, -0.02, 0.01, 0.05, 0.04, -0.02)
stock2 <- c(NA, NA, 0.02, 0.04, -0.03, 0.02)
stock3 <- c(NA, NA, 0.02, 0, -0.01, 0.03)
stock4 <- c(NA, -0.02, 0.01, 0, 0, -0.02)
df <- cbind(stock1,stock2,stock3,stock4)

     stock1 stock2 stock3 stock4
[1,]   0.01     NA     NA     NA
[2,]  -0.02     NA     NA  -0.02
[3,]   0.01   0.02   0.02   0.01
[4,]   0.05   0.04   0.00   0.00
[5,]   0.04  -0.03  -0.01   0.00
[6,]  -0.02   0.02   0.03  -0.02

我尝试了几种方法,但它们似乎只适用于单个向量,而不是具有多个列的数据集。我尝试使用lapply来解决这个问题,但到目前为止还没有成功。我最接近的解决方案如下所示。

我的单个向量解决方案:

stock1[1:min(which(stock1!=0))-1 <- NA

我的多向量解决方案无法工作

lapply(df,function(x) x[1:min(which(x!=0))-1 <- NA]

我非常感谢您的指导!谢谢!

1
只需要更改第一个前导零吗?这意味着,例如,如果 stock1 <- c(0.01, -0.02, 0.01, 0, 0, -0.02),您也希望将它们保留为0,尽管有两个连续的零吗?在您的示例中,其他地方只有单个0,而没有两个连续的零。 - Daniel Fischer
3个回答

7

有三个问题。首先是写作:

df <- cbind(stock1,stock2,stock3,stock4)

不会创建一个数据框。它创建的是矩阵。当你试图使用lapply时,这会成为一个问题,因为它将在数据框的列上操作,但在矩阵的元素上操作。相反,你应该写成:

df <- data.frame(stock1,stock2,stock3,stock4)

其次,您在lapply中使用的函数需要返回修改后的向量。否则,返回值将是意外的(在这种情况下,赋值将返回单个NA,并且lapply将返回一个由NA行组成的数据帧,而不是您想要的数据帧)。
第三,当n可能为零时(即第一个股票报价非零时),需要注意1:n,因为1:0会给出序列c(1,0)而不是空序列。(这可以说是R中最愚蠢的特性之一。)
因此,以下代码将给您所需的结果:
stock1 <- c(0.01, -0.02, 0.01, 0.05, 0.04, -0.02)
stock2 <- c(0, 0, 0.02, 0.04, -0.03, 0.02)
stock3 <- c(0, 0, 0.02, 0, -0.01, 0.03)
stock4 <- c(0, -0.02, 0.01, 0, 0, -0.02)
df <- data.frame(stock1,stock2,stock3,stock4)

as.data.frame(lapply(df, function(x) {
    n <- min(which(x != 0)) - 1
    if (n > 0)
        x[1:n] <- NA
    x
}))

输出结果符合预期:
  stock1 stock2 stock3 stock4
1   0.01     NA     NA     NA
2  -0.02     NA     NA  -0.02
3   0.01   0.02   0.02   0.01
4   0.05   0.04   0.00   0.00
5   0.04  -0.03  -0.01   0.00
6  -0.02   0.02   0.03  -0.02

更新:正如@Daniel_Fischer所指出的那样,有一个巧妙的技巧可以避免1:0问题。你可以改为写成:

as.data.frame(lapply(df, function(x) {
    n <- min(which(x != 0)) - 1
    x[0:n] <- NA    # use 0:n instead of 1:n
    x
}))

这利用了 R 在这种类型的索引操作中忽略零的特性,因此:
x[0:0] <- NA    # same as x[0] <- NA and does nothing
x[0:1] <- NA    # same as x[1] <- NA
x[0:2] <- NA    # same as x[1:2] <- NA, etc.

1
哦,我看到@Daniel_Fischer有一个不错的解决方案来解决n > 0问题。如果你做x[0:n] <- NA,那么无论n是零还是非零,都可以工作,所以你也可以跳过if语句。 - K. A. Buhr
1
非常感谢,真的很感激您的帮助和及时回复!我仍在努力理解各种“数据结构”之间微妙但重要的区别,所以感谢您指出 data.frame 和 as.data.frame 函数! - bubs7

4

这可能不是最优雅的方式,但我认为它有效

changeValues <- function(x){
   place <- min(which(diff(c(0,cumsum(x==0)))==0))-1;
   x[0:place] <- NA
   x
}

apply(df,2,changeValues)

编辑:对此函数的简要解释如下:首先,我创建了一个向量,在每个列中有零的位置上增加,然后检查这个向量在哪个位置不递增(即意味着两个零不相邻),然后仍然取其中的最小值,并确保这些只是前导零(以便不改变矩阵内的值)。


好的,这里还太早了,所以我的回答肯定是把事情复杂化了,min(which(x!=0))-1 是获取“place”的更短的方式... - Daniel Fischer

3
stock1 <- c(0.01, -0.02, 0.01, 0.05, 0.04, -0.02)
stock2 <- c(0, 0, 0.02, 0.04, -0.03, 0.02)
stock3 <- c(0, 0, 0.02, 0, -0.01, 0.03)
stock4 <- c(0, -0.02, 0.01, 0, 0, -0.02)
df <- data.frame(stock1,stock2,stock3,stock4) #the following function only works if df is actually a data.frame

df[] <- lapply(df, function(x) {ifelse(cumsum(x) == 0 & x == 0, NA, x)})

df

  stock1 stock2 stock3 stock4
1   0.01     NA     NA     NA
2  -0.02     NA     NA  -0.02
3   0.01   0.02   0.02   0.01
4   0.05   0.04   0.00   0.00
5   0.04  -0.03  -0.01   0.00
6  -0.02   0.02   0.03  -0.02

一些解释:首先检查每个单元格,是否累积的colSum和当前单元格是否等于0。如果是,则返回NA,否则返回原始值。括号后面的df确保lapply函数再次返回一个分配给dfdataframe
此外,如果您不需要df成为数据帧,则也可以这样做:
df <- cbind(stock1,stock2,stock3,stock4)
apply(df, 2, function(x) {ifelse(cumsum(x) == 0 & x == 0, NA, x)})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接