简单使用"if"和"for"

Vin*_*woo 0 r

我一直在努力学习在R中使用"if"和"for".作为一个简单的例子,我设置了数据框

V1<-c(3,2,2,4,5)
V2<-c(3,7,3,5,2)
V3<-c(5,2,5,7,5)
V4<-c(1,1,2,3,4)
V5<-c(1,2,6,7,5)
DF2<-data.frame(V1=V1,V2=V2,V3=V3,V4=V4,V5=V5)
DF2
  V1 V2 V3 V4 V5
1  3  3  5  1  1
2  2  7  2  1  2
3  2  3  5  2  6 
4  4  5  7  3  7
5  5  2  5  4  5
Run Code Online (Sandbox Code Playgroud)

我的目标是设置一个if语句,它将根据行值删除一行中的列.举个例子:

If V1 = 5, drop column V5
If V1 = 4, drop column V4 & V5
Run Code Online (Sandbox Code Playgroud)

所以根据这些规则,我的数据框最终会像这样

  V1 V2 V3 V4 V5
1  3  3  5  1  1
2  2  7  2  1  2
3  2  3  5  2  6 
4  4  5  7  
5  5  2  5  4  
Run Code Online (Sandbox Code Playgroud)

我的第一个想法是我可以写一个简单的if语句来做到这一点:

if(DF2$V1==5){
    DF2[-5]
}else if(DF2$V1==4){
    DF2[-4:5]
}
Run Code Online (Sandbox Code Playgroud)

但我得到一个错误,"如果"条件语句不能> 1.所以我想如果我写了一个"for"循环,它将允许if语句逐行,允许条件语句为== 1.

for(i in 1:length(DF2)){
if(DF2$V1==5){
    DF2[-5]
}else if(DF2$V1==4){
    DF2[-4]
}
} 
Run Code Online (Sandbox Code Playgroud)

但现在我得到了同样的错误,只有x10.所以我显然在吠叫同一棵树.所以我的问题是 - 处理原始数据框架问题的最佳方法是什么?如果没有某种if或for循环的答案,为什么我会收到这个错误?

Tyl*_*ker 14

这不会帮助您使用,if因为您不应该使用if此处,而是对该解决方案进行矢量化.另外,你真的不能在数据帧中放置空洞,因为根据定义,数据帧是等长矢量的列表.我想你可以把它变成一个字符向量,并用""替换你想要的部分,但这可能没用.另一种方法是使用print并将其打印为矩阵,并告诉它不要显示NA或缺失值.

长话短说:

  1. 矢量化
  2. 用NA替换不是空白
  3. 要将NA打印为空白,请将其转换为矩阵并使用参数作为打印功能

这里是...

DF2[DF2$V1==5, 5] <- NA
DF2[DF2$V1==4, 4:5] <- NA
DF2

#If you want blanks printed.
M1 <- as.matrix(DF2)
rownames(M1) <- 1:nrow(M1)
print(M1, na.print="", quote=FALSE)
Run Code Online (Sandbox Code Playgroud)


A5C*_*2T1 5

老实说,我认为@ Tyler的方法更有效 - 对于普通的R用户来说,这肯定是一种更典型的方法 - 但如果你注意使用它if,那么只要想一想你在做什么:

  • 你正在data.frame逐行处理.
  • apply()R中的函数允许您将您指定MARGIN为1(按行应用函数)或2(按列应用函数).
  • 因此,您可以将条件设​​置为"函数",apply()以便在每行上使用,如下所示.

    t(apply(DF2, 1, function(x) { if(x[1] == 5) x[5] <- NA;
                                  if(x[1] == 4) x[4:5] <- NA;
                                  x} ))
    #      V1 V2 V3 V4 V5
    # [1,]  3  3  5  1  1
    # [2,]  2  7  2  1  2
    # [3,]  2  3  5  2  6
    # [4,]  4  5  7 NA NA
    # [5,]  5  2  5  4 NA
    
    Run Code Online (Sandbox Code Playgroud)

该t仅仅是转在最后一步的输出.

基准

一些评论中提出了效率问题.在小数据集的情况下,我怀疑在任何答案中效率会有很大差异,所以我做了一些基准测试,其数据集更大(但仍然非常小).

这是数据集:

set.seed(1)
DF2 = data.frame(V1 = sample(5, 1000, replace = TRUE),
                 V2 = sample(5, 1000, replace = TRUE),
                 V3 = sample(5, 1000, replace = TRUE),
                 V4 = sample(5, 1000, replace = TRUE),
                 V5 = sample(5, 1000, replace = TRUE))
Run Code Online (Sandbox Code Playgroud)

以下是用于运行基准测试和结果的代码.在这里,我们可以很容易地看到Tyler的方法比使用方法快得多if (...) else if (...).

library(rbenchmark)
benchmark(
  Barranka = {
    for(i in seq(1,nrow(DF2))) {
      if(DF2$V1[i] == 5) {
        DF2[i,5] <- NaN
      } else if(DF2$V1[i] == 4) {
        DF2[i,4] <- NaN
        DF2[i,5] <- NaN
      }
    }},
  Tyler = {
    DF2[DF2$V1==5, 5] <- NA
    DF2[DF2$V1==4, 4:5] <- NA },
  mrdwab = {
    t(apply(DF2, 1, function(x) { if(x[1] == 5) x[5] <- NA;
                                  if(x[1] == 4) x[4:5] <- NA;
                                   x })) },
columns = c("test", "replications", "elapsed", "relative"), 
order = "relative")
#       test replications elapsed relative
# 2    Tyler          100   0.378    1.000
# 3   mrdwab          100   2.072    5.481
# 1 Barranka          100  11.885   31.442
Run Code Online (Sandbox Code Playgroud)

当我尝试将行数改为100000时,使用system.time(),Tyler的方法和我的方法能够毫无问题地做必要的事情.Tyler的经过时间是0.315秒,我的是2.773秒,而Barranka的是807.446秒(13+分钟!).这是一个巨大的差异.

如果有人知道更好的基准测试方法,请随时编辑和更新这篇文章.

注意:这不是批评任何人的特定方法,而是为了证明评论中的一些陈述是正确的.关于R我喜欢(并且讨厌)的一件事是,几乎总有不止一种方法可以做某事.