7

R 中数据操作的一项常见任务是通过删除符合特定条件的行来对数据帧进行子集化。但是,在 R 中执行此操作的简单方法似乎在逻辑上不一致,甚至对于没有经验的人(比如我自己)来说也是危险的。

假设我们有一个数据框,我们想要排除属于“G1”处理的行:

Treatment=c("G1","G1","G1","G1","G1","G1","G2","G2","G2","G2","G2",
"G2","G3","G3","G3","G3","G3","G3")
Vals=c(runif(6),runif(6)+0.9,runif(6)-0.3)
data=data.frame(Treatment)
data=cbind(data, Vals)  

正如预期的那样,下面的代码删除了与第一行条件匹配的数据帧行

to_del=which(data$Treatment=="G1")
new_data=data[-to_del,]
new_data

但是,与预期相反,如果“which”命令未找到任何匹配的行,则使用此方法,此代码将删除所有行而不是单独保留它们

to_del=which(data$Treatment=="G4")
new_data=data[-to_del,]
new_data

上面的代码导致数据框没有剩余行,这是没有意义的(即,由于 R 没有找到符合我的删除条件的行,因此它删除了所有行)。我的解决方法可以完成这项工作,但我想有一种更简单的方法可以在没有所有这些条件语句的情况下做到这一点

###WORKAROUND
to_del=which(data$Treatment=="G4") #no G4 treatment in this particular data frame
if (length(to_del)>0){
  new_data=data[-to_del,]  
}else{
  new_data=data
}
new_data

有没有人有一个简单的方法来做到这一点,即使没有行匹配指定的条件?

4

4 回答 4

6

您偶然发现了使用which. 改为使用!=

new_data <- data[data$Treatment!="G4",]

问题是which如果integer(0)所有元素都是FALSE. 即使which返回,这仍然是一个问题,0因为零子集也返回integer(0)

R> # subsetting by zero (positive or negative)
R> (1:3)[0]  # same as (1:3)[-0]
integer(0)

如果您按以下方式进行子集化,您也会遇到问题NA

R> # subsetting by NA
R> (1:3)[NA]
[1] NA NA NA
于 2013-02-15T21:40:33.537 回答
3

为什么不使用subset

subset(data,  ! rownames(data) %in% to_del )

(无论如何,您在示例中隐式匹配行名data[-to_del, ]。)当然,一旦可行,您可以返回仅使用“[”

data[  ! rownames(data) %in% to_del , ]
于 2013-02-15T22:09:39.340 回答
3

我喜欢使用data.table子集,因为它更直观、更短,并且在大型数据集上运行更快。

library(data.table)
data.dt<-as.data.table(data)
setkey(data.dt, Treatment)

data.dt[!"G1",]
##     Treatment        Vals
##  1:        G2  0.90264622
##  2:        G2  1.47842130
##  3:        G2  1.52494735
##  4:        G2  1.46373958
##  5:        G2  1.12850658
##  6:        G2  1.46705561
##  7:        G3  0.58451869
##  8:        G3 -0.20231228
##  9:        G3  0.52519475
## 10:        G3  0.62956475
## 11:        G3 -0.06655426
## 12:        G3  0.56814703

data.dt[!"G4",]
##    Treatment        Vals
## 1         G1  0.93411692
## 2         G1  0.60153972
## 3         G1  0.28147464
## 4         G1  0.97264924
## 5         G1  0.50804831
## 6         G1  0.48273876
## 7         G2  0.90264622
## 8         G2  1.47842130
## 9         G2  1.52494735
## 10        G2  1.46373958
## 11        G2  1.12850658
## 12        G2  1.46705561
## 13        G3  0.58451869
## 14        G3 -0.20231228
## 15        G3  0.52519475
## 16        G3  0.62956475
## 17        G3 -0.06655426
## 18        G3  0.56814703

请注意,如果您在未设置为键的列上设置子集,则需要使用子集中的列名(例如data.dt[Vals<0,]

我认为的创建者data.table可能正在研究一种直接从原始表中删除行的方法,而不是必须将所有未删除的行复制到新表中然后删除原始表。当您遇到内存限制时,这将是一个很大的帮助。

于 2013-02-16T14:03:31.213 回答
2

问题是您没有选择要删除的行, 而是选择要保留的行。正如您所发现的,您通常可以互换这些概念,但有时会出现问题。

具体来说,当您使用时,which您是在询问 R“该向量的哪些元素是真实的”。但是,当它没有找到时,它会通过返回来表明这一点integer(0)

Integer(0) 不是一个实际数字,因此取 Integer(0) 的负数仍然给出 Integer(0)。

但是,如果您只想使用它进行过滤,则无需使用 which。

取而代之的是,将您传递给的语句which作为过滤器直接传递给 data[..]. 回想一下,您可以将逻辑向量用作索引,也可以将整数向量用作索引。

于 2013-02-15T21:44:16.020 回答