我一直在努力学习在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或缺失值.
长话短说:
这里是...
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)
老实说,我认为@ 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我喜欢(并且讨厌)的一件事是,几乎总有不止一种方法可以做某事.