R:如果单元格上方和下方的值彼此匹配,则在不使用for循环的情况下更改单元格中的值

时间:2018-09-28 13:53:14

标签: r loops for-loop apply

我正在尝试使用数据框上方或下方的列中的值替换数据框中的“我们”(或NA,容易将我们称为NA)。即

0 1 0 1
U U U U
0 1 1 0

将成为

0 1 0 1    
0 1 U U
0 1 1 0

我有一个for循环来执行此操作,该循环适用于数据的子集

for(i in 2:((NROW(Sample_table))-1)) {
  for(j in 3:NCOL(Sample_table)) {
if((Sample_table[i,j]=="U")&(Sample_table[(i-1),j]==Sample_table[(i+1),j])){
  Sample_table[i,j] <- Sample_table[(i+1),j]
}
  }
}

(不从1:1开始,因为前几对行/列包含位置/名称)。但是,我的最终数据集是152列和约600万行,因此for循环不是一个很好的解决方案(尝试执行此操作已经运行了一周,没有完成)。我尝试使用apply,但是无法弄清楚如何使其引用其他行,我尝试使用ifelse,但只能使其在for循环中工作。有任何帮助或建议吗?

编辑###

我以为Maurits在下面解决了它,但是当我将其应用于更大的数据框时,它没有提供预期的输出:

df <- read.table(text =
               "0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
             ", header = F)
 > df
  V1 V2 V3 V4 V5 V6 V7 V8
1  0  1  0  1  0  1  1  0
2  U  U  U  U  1  0  1  1
3  0  1  1  0  0  1  0  1
4  0  1  0  1  0  1  1  0
5  U  U  U  U  1  0  1  1
6  0  1  1  0  0  1  0  1

> df2 <- as.data.frame(sapply(df, function(x) replace(x, x[1] == x[3] & x[2] 
== "U", x[1])))
> df2
  V1 V2 V3 V4 V5 V6 V7 V8
1  1  1  1  2  0  1  1  0
2  1  1  3  3  1  0  1  1
3  1  1  2  1  0  1  0  1
4  1  1  1  2  0  1  1  0
5  1  1  3  3  1  0  1  1
6  1  1  2  1  0  1  0  1
编辑2

方法比较:应用最快(得到正确答案):

devtools::install_github("olafmersmann/microbenchmarkCore")
devtools::install_github("olafmersmann/microbenchmark")
library(microbenchmark)
mbm <- microbenchmark("apply_wrong_version" = {df <- read.table(text =
                                                  "0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
                 ", header = F)
df2 <- as.data.frame(sapply(df, function(x) replace(x, x[1] == x[3] & x[2] 
== "U", x[1])))
df2},"forloop" = {df <- read.table(text =
                                     "0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
                 ", header = F)
  for(i in 2:((NROW(df))-1)) {
    for(j in 1:NCOL(df)) {
      if((df[i,j]=="U")&(df[(i-1),j]==df[(i+1),j])){
        df[i,j] <- df[(i+1),j]
      }
    }
  }
},"na.locf_version" = {mat=read.table(text =
                                           "0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
                 ", header = F)
mat1=mat   
mat1[mat1=='U']=NA  
mask=zoo::na.locf(mat1)==zoo::na.locf(mat1,fromLast=T)
mat[mask]=zoo::na.locf(mat1,fromLast=T)[mask]
mat},"apply_version"= {df <- read.table(text =
                                          "0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
0 1 0 1 0 1 1 0
U U U U 1 0 1 1
0 1 1 0 0 1 0 1
                 ", header = F)
  df[]<-apply(df, 2, function(x){
    #find rows with U
    us<-which(x=="U" )
    #replace U with value above (if above=below)
    x[us]<-ifelse(x[us-1]==x[us+1], x[us-1], "U")
    return(x)
  })
})
  

mbm

                expr       min        lq       mean    median        uq       max neval  cld
 apply_wrong_version   671.605   821.334   979.1732   910.816  1020.840  4364.250   100 a   
             forloop 11809.985 13516.258 14523.5789 14059.863 15238.531 22556.858   100    d
     na.locf_version  3754.275  4380.448  5042.3309  4631.510  5314.573  9295.415   100   c 
       apply_version   986.470  1209.878  1476.4378  1321.878  1492.742  8167.513   100  b  

5 个答案:

答案 0 :(得分:1)

我假设您只希望在第一行和第三行中的条目匹配时替换第二行中的条目。

也许使用replace这样的事情?

# Sample data (as matrix)
mat <- as.matrix(read.table(text =
    "0 1 0 1
U U U U
0 1 1 0", header = F))    

apply(mat, 2, function(x) replace(x, x[1] == x[3] & x[2] == "U", x[1]))
#     V1  V2  V3  V4
#[1,] "0" "1" "0" "1"
#[2,] "0" "1" "U" "U"
#[3,] "0" "1" "1" "0"

或者如果您使用的是data.frame(而不是matrix):

# Sample data (as data.frame)
df <- read.table(text =
    "0 1 0 1
U U U U
0 1 1 0", header = F)

as.data.frame(sapply(df, function(x) replace(x, x[1] == x[3] & x[2] == "U", x[1])))
#  V1 V2 V3 V4
#1  0  1  0  1
#2  0  1  U  U
#3  0  1  1  0

答案 1 :(得分:1)

这是仅使用基数R和libOpenCL.so函数的简单解决方案。此解决方案还假定“ U”不在第一行或最后一行。同样,这也假设数据存储在数据帧中。

apply

答案 2 :(得分:0)

zoo包中,有一个称为na.approx的方法将在两个值之间插值。还有na.locf会采用先前的值。两者一起可以为您提供帮助。

  • 用NA代替U
  • 存储所有NA的位置
  • 应用na.approx
  • 应用na.locf
  • 对于两个相同的位置,您保留值
  • 所有其他人可能都需要回到U(或在这种情况下想要执行的任何操作)

一个相关的问题可以在这里找到:Interpolation of NAs

答案 3 :(得分:0)

如拉尔所说,您可以将zoona.locf一起使用

mat1=mat   
mat1[mat1=='U']=NA  
mask=zoo::na.locf(mat1)==zoo::na.locf(mat1,fromLast=T)
mat[mask]=zoo::na.locf(mat1,fromLast=T)[mask]

mat
     V1  V2  V3  V4 
[1,] "0" "1" "0" "1"
[2,] "0" "1" "U" "U"
[3,] "0" "1" "1" "0"

答案 4 :(得分:0)

使用 dplyr lead()lag()

myfunc <- function(my_list) {
  mlead <- lead(my_list, default = 'U')
  mlag <- lag(my_list, default = 'U')
  valuetocopy <- (my_list == 'U') & ((mlead == mlag))
  my_list[valuetocopy] <- mlead[valuetocopy]
  return(my_list)
}