首页 > 解决方案 > R:更改单元格中的值,如果其上方和下方的值相互匹配,则不使用 for 循环

问题描述

我正在尝试用它们上方或下方的列中的值替换我的数据框中的 Us(或 NA,很容易将 Us 作为 NA)。IE

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  

标签: rloopsfor-loopapply

解决方案


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

也许像这样使用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

推荐阅读