如何在数据框中按名称删除列

我有一个大型的数据集,我想阅读特定的列或删除所有其他人。

data <- read.dta("file.dta") 

我select我不感兴趣的列:

 var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")] 

比我想要做的事情如:

 for(i in 1:length(var.out)) { paste("data$", var.out[i], sep="") <- NULL } 

删除所有不需要的列。 这是最佳的解决scheme吗?

您应该使用索引或subset函数。 例如 :

 R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8) R> df xyzu 1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 5 6 7 8 

那么你可以在列索引中使用which函数和-操作符:

 R> df[ , -which(names(df) %in% c("z","u"))] xy 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 

或者更简单一点,使用subset函数的select参数:然后可以直接在列名称向量上使用-运算符,甚至可以省略名称周围的引号!

 R> subset(df, select=-c(z,u)) xy 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 

请注意,您也可以select所需的列而不是删除其他列:

 R> df[ , c("x","y")] xy 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 R> subset(df, select=c(x,y)) xy 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 

不要使用-which() ,这是非常危险的。 考虑:

 dat <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8) dat[ , -which(names(dat) %in% c("z","u"))] ## works as expected dat[ , -which(names(dat) %in% c("foo","bar"))] ## deletes all columns! Probably not what you wanted... 

而是使用子集或! function:

 dat[ , !names(dat) %in% c("z","u")] ## works as expected dat[ , !names(dat) %in% c("foo","bar")] ## returns the un-altered data.frame. Probably what you want 

我从痛苦的经历中学到了这一点。 不要过度使用which()

首先 ,如果您使用相同的数据框架,则可以使用直接索引(使用布尔值向量)而不是重新访问列名称; 如Ista指出的那样更安全,写和执行更快。 所以你只需要:

 var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv") 

然后,只需重新分配数据:

 data <- data[,var.out.bool] # or... data <- data[,var.out.bool, drop = FALSE] # You will need this option to avoid the conversion to an atomic vector if there is only one column left 

其次 ,编写起来更快,可以直接将NULL分配给要删除的列:

 data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure. 

最后 ,你可以使用subset(),但是它不能真正在代码中使用(即使是帮助文件也会警告)。 具体来说,对我来说一个问题是,如果要直接使用susbset()的拖放function,则需要在不带引号的情况下写入与列名相对应的expression式:

 subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL 

作为一个奖励 ,这里是不同选项的小基准,清楚地表明子集是较慢的,而第一个重新分配方法是更快的:

  re_assign(dtest, drop_vec) 46.719 52.5655 54.6460 59.0400 1347.331 null_assign(dtest, drop_vec) 74.593 83.0585 86.2025 94.0035 1476.150 subset(dtest, select = !names(dtest) %in% drop_vec) 106.280 115.4810 120.3435 131.4665 65133.780 subset(dtest, select = names(dtest)[!names(dtest) %in% drop_vec]) 108.611 119.4830 124.0865 135.4270 1599.577 subset(dtest, select = -c(x, y)) 102.026 111.2680 115.7035 126.2320 1484.174 

Microbench图表

代码如下:

 dtest <- data.frame(x=1:5, y=2:6, z = 3:7) drop_vec <- c("x", "y") null_assign <- function(df, names) { df[names] <- list(NULL) df } re_assign <- function(df, drop) { df <- df [, ! names(df) %in% drop, drop = FALSE] df } res <- microbenchmark( re_assign(dtest,drop_vec), null_assign(dtest,drop_vec), subset(dtest, select = ! names(dtest) %in% drop_vec), subset(dtest, select = names(dtest)[! names(dtest) %in% drop_vec]), subset(dtest, select = -c(x, y) ), times=5000) plt <- ggplot2::qplot(y=time, data=res[res$time < 1000000,], colour=expr) plt <- plt + ggplot2::scale_y_log10() + ggplot2::labs(colour = "expression") + ggplot2::scale_color_discrete(labels = c("re_assign", "null_assign", "subset_bool", "subset_names", "subset_drop")) + ggplot2::theme_bw(base_size=16) print(plt) 

您也可以尝试dplyr软件包:

 R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8) R> df xyzu 1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 5 6 7 8 R> library(dplyr) R> dplyr::select(df2, -c(x, y)) # remove columns x and y zu 1 3 4 2 4 5 3 5 6 4 6 7 5 7 8 

我试图删除一列,而使用包data.table并得到意外的结果。 我认为以下可能值得张贴。 只是一点警示。

[由马修编辑…]

 DF = read.table(text = " fruit state grade y1980 y1990 y2000 apples Ohio aa 500 100 55 apples Ohio bb 0 0 44 apples Ohio cc 700 0 33 apples Ohio dd 300 50 66 ", sep = "", header = TRUE, stringsAsFactors = FALSE) DF[ , !names(DF) %in% c("grade")] # all columns other than 'grade' fruit state y1980 y1990 y2000 1 apples Ohio 500 100 55 2 apples Ohio 0 0 44 3 apples Ohio 700 0 33 4 apples Ohio 300 50 66 library('data.table') DT = as.data.table(DF) DT[ , !names(dat4) %in% c("grade")] # not expected !! not the same as DF !! [1] TRUE TRUE FALSE TRUE TRUE TRUE DT[ , !names(DT) %in% c("grade"), with=FALSE] # that's better fruit state y1980 y1990 y2000 1: apples Ohio 500 100 55 2: apples Ohio 0 0 44 3: apples Ohio 700 0 33 4: apples Ohio 300 50 66 

基本上, data.table的语法与data.table不完全相同。 事实上有很多不同之处,请参阅FAQ 1.1和FAQ 2.17。 你被警告了!

这是一个快速的解决scheme。 说,你有一个数据框X有三列A,B和C:

 > X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6)) > X ABC 1 1 3 5 2 2 4 6 

如果我想删除一个列,比如说B,只需要在列名上使用grep来获得列索引,然后可以使用它来省略列。

 > X<-X[,-grep("B",colnames(X))] 

您的新X数据框将如下所示(这次没有B列):

 > X AC 1 1 5 2 2 6 

grep的美妙之处在于你可以指定多个与正则expression式匹配的列。 如果我有五列(A,B,C,D,E):

 > X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10)) > X ABCDE 1 1 3 5 7 9 2 2 4 6 8 10 

拿出B和D列:

 > X<-X[,-grep("B|D",colnames(X))] > X ACE 1 1 5 9 2 2 6 10 

编辑:考虑下面的评论马修·伦德伯格的greplbuild议:

 > X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10)) > X ABCDE 1 1 3 5 7 9 2 2 4 6 8 10 > X<-X[,!grepl("B|D",colnames(X))] > X ACE 1 1 5 9 2 2 6 10 

如果我尝试删除不存在的列,则不会发生任何事情:

 > X<-X[,!grepl("G",colnames(X))] > X ACE 1 1 5 9 2 2 6 10 

这是另一个可能对别人有帮助的解决scheme。 下面的代码从大型数据集中selectless量的行和列。 除了我使用粘贴function来select一系列名称按顺序编号的列之外,这些列被选中为juba的答案之一:

 df = read.table(text = " state county city region mmatrix X1 X2 X3 A1 A2 A3 B1 B2 B3 C1 C2 C3 1 1 1 1 111010 1 0 0 2 20 200 4 8 12 NA NA NA 1 2 1 1 111010 1 0 0 4 NA 400 5 9 NA NA NA NA 1 1 2 1 111010 1 0 0 6 60 NA NA 10 14 NA NA NA 1 2 2 1 111010 1 0 0 NA 80 800 7 11 15 NA NA NA 1 1 3 2 111010 0 1 0 1 2 1 2 2 2 10 20 30 1 2 3 2 111010 0 1 0 2 NA 1 2 2 NA 40 50 NA 1 1 4 2 111010 0 1 0 1 1 NA NA 2 2 70 80 90 1 2 4 2 111010 0 1 0 NA 2 1 2 2 10 100 110 120 1 1 1 3 010010 0 0 1 10 20 10 200 200 200 1 2 3 1 2 1 3 001000 0 0 1 20 NA 10 200 200 200 4 5 9 1 1 2 3 101000 0 0 1 10 10 NA 200 200 200 7 8 NA 1 2 2 3 011010 0 0 1 NA 20 10 200 200 200 10 11 12 ", sep = "", header = TRUE, stringsAsFactors = FALSE) df df2 <- df[df$region == 2, names(df) %in% c(paste("C", seq_along(1:3), sep=''))] df2 # C1 C2 C3 # 5 10 20 30 # 6 40 50 NA # 7 70 80 90 # 8 100 110 120 
 df2 <- df[!names(df) %in% c("c1", "c2")] 

我将代码更改为:

 # read data dat<-read.dta("file.dta") # vars to delete var.in<-c("iden", "name", "x_serv", "m_serv") # what I'm keeping var.out<-setdiff(names(dat),var.in) # keep only the ones I want dat <- dat[var.out] 

无论如何,朱巴的答案是我的问题的最佳解决scheme!

由于信誉评分低,我不能在评论中回答你的问题。

下一个代码会给你一个错误,因为粘贴函数返回一个string

 for(i in 1:length(var.out)) { paste("data$", var.out[i], sep="") <- NULL } 

这是一个可能的解决scheme:

 for(i in 1:length(var.out)) { text_to_source <- paste0 ("data$", var.out[i], "<- NULL") # Write a line of your # code like a character string eval (parse (text=text_to_source)) # Source a text that contains a code } 

或者只是做:

 for(i in 1:length(var.out)) { data[var.out[i]] <- NULL }