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

ler*_*oux 281 r subset dataframe

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

data <- read.dta("file.dta")
Run Code Online (Sandbox Code Playgroud)

我选择了我不感兴趣的列:

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

而且我想做的事情如下:

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

删除所有不需要的列.这是最佳解决方案吗?

jub*_*uba 347

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

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
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
Run Code Online (Sandbox Code Playgroud)

然后你可以在列索引中使用which函数和-运算符:

R> df[ , -which(names(df) %in% c("z","u"))]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Run Code Online (Sandbox Code Playgroud)

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

R> subset(df, select=-c(z,u))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Run Code Online (Sandbox Code Playgroud)

请注意,您还可以选择所需的列,而不是删除其他列:

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

R> subset(df, select=c(x,y))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
Run Code Online (Sandbox Code Playgroud)

  • `subset`看起来不错,但它默默地丢失缺失值的方式对我来说似乎非常危险. (4认同)
  • `subset` 确实非常方便,但请记住避免使用它,除非您以交互方式使用 R。请参阅 [函数文档中的警告](http://stat.ethz.ch/R-manual/R-devel/library/base/html/subset.html) 和 [this SO question](http://stackoverflow .com/q/9860090/1169233)了解更多。 (4认同)
  • `subset` 函数的 `select` 参数完美地完成了这项工作!谢谢朱巴! (3认同)
  • `which` 不是必需的,请参阅 Ista 的回答。但是带有`-`的子集很好!不知道! (2认同)
  • 什么可能导致这个`&gt;子集(testData5,select = -c(“myCol”))错误 -c(“myCol”):一元运算符的无效参数`? (2认同)
  • "你甚至可以省略名字周围的引号!",你实际上必须省略引号,否则你将得到一元运算符的无效参数.如果您的名字中有某些字符(例如" - "),则根本不能使用此方法,因为删除引号将导致R无法正确解析您的代码. (2认同)

Ist*_*sta 114

不要用-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...
Run Code Online (Sandbox Code Playgroud)

而是使用子集或!函数:

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
Run Code Online (Sandbox Code Playgroud)

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

  • `setdiff`也很有用:`setdiff(names(dat),c("foo","bar"))` (30认同)

Ant*_*zée 43

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

其次,写入速度更快,您可以直接为要删除的列分配NULL:

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

最后,您可以使用subset(),但它无法在代码中使用(甚至帮助文件也会对其进行警告).具体来说,一个问题是,如果你想直接使用susbset()的drop特性,你需要在没有引号的情况下编写与列名对应的表达式:

subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL
Run Code Online (Sandbox Code Playgroud)

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

                                        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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

  • @DarwinPC 是的。如果直接访问一个向量元素(使用`$` 或`[[`),使用`&lt;- list(NULL)` 实际上会导致错误的结果。如果您使用一列或多列访问数据框的子集,则可以使用 `&lt;- list(NULL)`,即使单列数据框不需要它(因为 `df['myColumns']` 将如果需要,可以转换为向量)。 (3认同)
  • 我喜欢你使用 `NULL` 的第二个选择,但是为什么当你输入两个以上的名字时需要用 `list(NULL)` 分配它?我只是想知道它是如何工作的,因为我只试过一个名字,我不需要`list()` (2认同)

Meg*_*ron 25

你也可以试试这个dplyr包:

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
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
  z u
1 3 4
2 4 5
3 5 6
4 6 7
5 7 8
Run Code Online (Sandbox Code Playgroud)

  • 我不敢相信更多的人不会使用它.它是迄今为止最简单,最有效的方法. (7认同)
  • 使用```dplyr :: select(df2,-one_of(c('x','y')))```仍然可以工作(带警告),即使某些命名列不存在 (3认同)

Mar*_*n W 10

df2 <- df[!names(df) %in% c("c1", "c2")]
Run Code Online (Sandbox Code Playgroud)


Job*_*gan 9

这是一个快速的解决方案.比如,你有一个数据框X,有三列A,B和C:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6))
> X
  A B C
1 1 3 5
2 2 4 6
Run Code Online (Sandbox Code Playgroud)

如果我想删除一个列,比如B,只需在colnames上使用grep来获取列索引,然后可以使用该索引来省略列.

> X<-X[,-grep("B",colnames(X))]
Run Code Online (Sandbox Code Playgroud)

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

> X
  A C
1 1 5
2 2 6
Run Code Online (Sandbox Code Playgroud)

grep的优点在于您可以指定与正则表达式匹配的多个列.如果我有五列X(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
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10
Run Code Online (Sandbox Code Playgroud)

取出B栏和D栏:

> X<-X[,-grep("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10
Run Code Online (Sandbox Code Playgroud)

编辑:考虑到马修伦德伯格在下面的评论中的grepl建议:

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

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

> X<-X[,!grepl("G",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10
Run Code Online (Sandbox Code Playgroud)

  • `X [, - grep("B",colnames(X))]`在没有列名称包含`B`的情况下将不返回列,而不是根据需要返回所有列.以"X < - iris"为例进行考虑.这是使用具有计算值的负索引的问题.请考虑`grepl`. (3认同)

ler*_*oux 6

我将代码更改为:

# 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]
Run Code Online (Sandbox Code Playgroud)

不管怎样,juba的回答是解决我问题的最好方法!


Mar*_*ler 5

我试图在使用包时删除一个列,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
Run Code Online (Sandbox Code Playgroud)

基本上,语法data.table不完全相同data.frame.实际上存在很多差异,请参阅FAQ 1.1和FAQ 2.17.你被警告了!