用前一行的值填充数据框

10

我有一个数据框,其中有2列。

column1里是随机数, column2是一个占位符列,用来展示我想让column3看起来像什么。

  random    temp
0.502423373 1
0.687594055 0
0.741883739 0
0.445364032 0
0.50626137  0.5
0.516364981 0
...

我希望填充列3,使其使用最后一个非零数字(在此示例中为1或.5)并连续填充以下行,直到遇到不同的数字为止。然后它会为整个列重复此过程。

random     temp state
0.502423373 1   1
0.687594055 0   1
0.741883739 0   1
0.445364032 0   1
0.50626137  0.5 0.5
0.516364981 0   0.5
0.807804708 0   0.5
0.247948445 0   0.5
0.46573337  0   0.5
0.103705154 0   0.5
0.079625868 1   1
0.938928944 0   1
0.677713019 0   1
0.112231619 0   1
0.165907178 0   1
0.836195267 0   1
0.387712998 1   1
0.147737077 0   1
0.439281543 0.5 0.5
0.089013503 0   0.5
0.84174743  0   0.5
0.931738707 0   0.5
0.807955172 1   1

感谢任何和所有的帮助

7个回答

12

假设您的data.frame被称为“mydf”,在将值设置为NA之后,也许您可以使用“zoo”包中的na.locf

mydf$state <- mydf$temp
mydf$state[mydf$state == 0] <- NA

library(zoo)
mydf$state <- na.locf(mydf$state)
#      random temp state
# 1 0.5024234  1.0   1.0
# 2 0.6875941  0.0   1.0
# 3 0.7418837  0.0   1.0
# 4 0.4453640  0.0   1.0
# 5 0.5062614  0.5   0.5
# 6 0.5163650  0.0   0.5
如果你的原始数据框中"temp"列有缺失值,并且你想让新生成的"state"列中也保留这些缺失值,那么很容易处理。只需要再添加一行代码来重新引入这些缺失值即可:
mydf$state[is.na(mydf$temp)] <- NA

我认为如果数据中已经存在NA值,那么这样做可能会有问题。但是如果它能够正常工作,那也是好的。 - Neal Fultz
@NealFultz,这个评论值得一个踩吗?很容易解决你对评论的担忧。(我假设你希望在生成的“state”变量中,如果“temp”变量为“NA”,则该值也为“NA”。请注意,我不会触及“temp”变量,因此仍然可以轻松访问该信息。) - A5C1D2H2I1M1N2O1R2T1
如果您的数据中既有NA又有0怎么办? - Neal Fultz
3
@NealFultz,我怎么知道呢?这不是我的数据,而且这些条件在问题中也没有指定。我猜测,在零旁边的“NA”应该被替换为最后已知的值,根据当前的数据集,我认为这不会有问题。或者当遇到“NA”时,您是否希望继续填充数据的“NA”?请随意分享您所感知到的条件以及您建议如何处理它。我发现您目前的解决方案无法处理“NA”值,所以我渴望学习。 - A5C1D2H2I1M1N2O1R2T1
1
只是为了澄清一下,这个解决方案没有任何缺失值,所以它起到了作用! - user2813055

5
受 @Ananda Mahto 解决方案的启发,这是对 na.locf 内部代码的调整,直接使用0而不是NA。然后您就不需要使用 zoo 包,也不需要进行将值更改为 NA 的预处理。基准测试表明,这比原始版本快约10倍。
locf.0 <- function(x) {
  L <- x!=0
  idx <- c(0, which(L))[cumsum(L) + 1]
  return(x[idx])
} 
mydf$state <- locf.0(mydf$temp)

聪明的想法来修改 na.locf。+1 - A5C1D2H2I1M1N2O1R2T1

3

这是一个与Reduce函数相关的有趣方法。

temp = c(1,0,0,0,.5,0,0,0,0,0,1,0,0,0,0,0,1,0,0.5,0,0,0,1)
fill_zero = function(x,y) if(y==0) x else y
state = Reduce(fill_zero, temp, accumulate=TRUE)

如果您担心速度问题,可以尝试使用Rcpp。
library(Rcpp)
cppFunction('
  NumericVector fill_zeros( NumericVector x ) {
    for( int i=1; i<x.size(); i++ )
     if( x[i]==0 ) x[i] = x[i-1];
    return x;
  }
')
state = fill_zeros(temp)

3
此外,除非我忽略了什么,否则这似乎可以起作用:
DF$state2 <- ave(DF$temp, cumsum(DF$temp), FUN = function(x) x[x != 0])
DF
#       random temp state state2
#1  0.50242337  1.0   1.0    1.0
#2  0.68759406  0.0   1.0    1.0
#3  0.74188374  0.0   1.0    1.0
#4  0.44536403  0.0   1.0    1.0
#5  0.50626137  0.5   0.5    0.5
#6  0.51636498  0.0   0.5    0.5
#7  0.80780471  0.0   0.5    0.5
#8  0.24794844  0.0   0.5    0.5
#9  0.46573337  0.0   0.5    0.5
#10 0.10370515  0.0   0.5    0.5
#11 0.07962587  1.0   1.0    1.0
#12 0.93892894  0.0   1.0    1.0
#13 0.67771302  0.0   1.0    1.0
#14 0.11223162  0.0   1.0    1.0
#15 0.16590718  0.0   1.0    1.0
#16 0.83619527  0.0   1.0    1.0
#17 0.38771300  1.0   1.0    1.0
#18 0.14773708  0.0   1.0    1.0
#19 0.43928154  0.5   0.5    0.5
#20 0.08901350  0.0   0.5    0.5
#21 0.84174743  0.0   0.5    0.5
#22 0.93173871  0.0   0.5    0.5
#23 0.80795517  1.0   1.0    1.0

我认为 ave(DF$temp, cumsum(DF$temp), FUN = sum) 也应该可以工作。 - kdauria
@Kevin:是的,你说得对!在这种情况下,对值进行求和也可以。而且,也许它更快,因为它避免了在索引之前转换为逻辑?尽管如此,我仍然可能更喜欢x[x != 0],因为它确切地声明了平均函数是什么。 - alexis_laz

0
一个类似下面的循环应该能为您解决问题 -
for(i in seq(nrow(df)))
{
  if (df[i,"v1"] == 0) df[i,"v1"] <- df[i-1,"v1"]
}

输出 -

> df
   v1 somedata
1   1       33
2   2       24
3   1       36
4   0       49
5   2       89
6   2       48
7   0        4
8   1       98
9   1       60
10  2       76
> 
> for(i in seq(nrow(df)))
+ {
+   if (df[i,"v1"] == 0) df[i,"v1"] <- df[i-1,"v1"]
+ }
> df
   v1 somedata
1   1       33
2   2       24
3   1       36
4   1       49
5   2       89
6   2       48
7   2        4
8   1       98
9   1       60
10  2       76

0

我建议使用运行长度编码函数,这是处理数据集中的“连续段”(steaks)的一种自然方式。以@Kevin的示例向量为例:

temp = c(1,0,0,0,.5,0,0,0,0,0,1,0,0,0,0,0,1,0,0.5,0,0,0,1)
y <- rle(temp)
#str(y)
#List of 2
# $ lengths: int [1:11] 1 3 1 5 1 5 1 1 1 3 ...
# $ values : num [1:11] 1 0 0.5 0 1 0 1 0 0.5 0 ...
# - attr(*, "class")= chr "rle"


for( i in seq(y$values)[-1] ) {
   if(y$values[i] == 0) {
      y$lengths[i-1] = y$lengths[i] + y$lengths[i-1]
      y$lengths[i] = 0
   }
}

#str(y)
#List of 2
# $ lengths: num [1:11] 4 0 6 0 6 0 2 0 4 0 ...
# $ values : num [1:11] 1 0 0.5 0 1 0 1 0 0.5 0 ...
# - attr(*, "class")= chr "rle"

inverse.rle(y)
#  [1] 1.0 1.0 1.0 1.0 0.5 0.5 0.5 0.5 0.5 0.5 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.5
# [20] 0.5 0.5 0.5 1.0

1
你需要一些A-1数据酱来搭配这些牛排吗? :-0 - Carl Witthoft

-1

只需使用一个带有全局变量的循环,

这里使用的全局变量是mr是一个具有两列AB的数据框。

r$B = c(1,NA, NA, NA, 3, NA,6)


m=1

for( i in 1:nrow(r) ){

  if(is.na(r$B[i])==FALSE ){

    m <<- i # please note the assign sign ,  " <<- "
    next()

  } else {

    r$B[i] = r$B[m]

  }

}

执行后: r$B = 1 1 1 1 3 3 6


首先,这是一种非常糟糕和不符合R语言风格的方法来实现OP想要的功能。还有更好(而且向量化)的替代方案,请参见此帖子的其他答案。其次,您提供的代码实际上无法重现。r没有定义在任何地方,您将R作为data.frame提到,但是R区分大小写。在这种情况下使用<<-恰恰是如何使用<<-的示例之一:邪恶和错误的用法是修改全局环境中的变量 - Maurits Evers
最后,next是一个控制流语句next不返回任何值,应该使用next而不是next()。我认为这个答案对本帖子的贡献很小(如果有的话),因此应该删除,因为它促进了不良的R编码实践。 - Maurits Evers

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