按名称删除数据框列

Drop data frame columns by name

提问人:Btibert3 提问时间:1/5/2011 最后编辑:HenrikBtibert3 更新时间:8/27/2022 访问量:1904272

问:

我想从数据框中删除许多列。我知道我们可以使用以下方法单独删除它们:

df$x <- NULL

但我希望用更少的命令来做到这一点。

另外,我知道我可以使用整数索引删除列,如下所示:

df <- df[ -c(1, 3:6, 12) ]

但我担心我的变量的相对位置可能会发生变化。

鉴于 R 的强大功能,我认为可能有比逐一删除每一列更好的方法。

数据帧 R-常见问题解答

评论

34赞 ifly6 4/21/2018
有人可以向我解释为什么 R 没有像 这样简单的东西,相反,我们需要做这些复杂的解决方法吗?df#drop(var_name)
2赞 Paul Sochacki 9/3/2018
@ifly6 R 中的“subset()”函数与 Python 中的“drop()”函数一样简单,只是您不需要指定轴参数......我同意,对于像删除列这样基本的事情,不可能只有一个终极的、简单的关键字/语法全面实现,这很烦人。

答:

1042赞 Joris Meys 1/5/2011 #1

您可以使用一个简单的名称列表:

DF <- data.frame(
  x=1:10,
  y=10:1,
  z=rep(5,10),
  a=11:20
)
drops <- c("x","z")
DF[ , !(names(DF) %in% drops)]

或者,或者,您可以列出要保留的那些并按名称引用它们:

keeps <- c("y", "a")
DF[keeps]

编辑: 对于那些仍然不熟悉索引函数参数的用户,如果要将一列保留为数据框,请执行以下操作:drop

keeps <- "y"
DF[ , keeps, drop = FALSE]

drop=TRUE(或不提及它)将删除不必要的维度,因此返回一个值为 column 的向量。y

评论

25赞 mut1na 6/28/2013
子集函数效果更好,因为它不会将包含一列的数据框转换为向量
5赞 Joris Meys 6/28/2013
@mut1na检查索引函数的参数 drop=FALSE。
6赞 lindelof 10/28/2014
那不应该是代替吗?DF[,keeps]DF[keeps]
8赞 Joris Meys 10/29/2014
@lindelof号可以,但是如果只选择一列,则必须添加 drop=FALSE,以防止 R 将数据框转换为向量。不要忘记数据框是列表,因此列表选择(像我一样的一维)运行良好,并且始终返回列表。或者在这种情况下是一个数据框,这就是我更喜欢使用它的原因。
7赞 Joris Meys 7/7/2015
@AjayOhri 是的,它会的。如果没有逗号,则使用“列表”方式进行选择,这意味着即使提取单个列,仍会返回数据框。如果您使用“矩阵”方式,您应该知道,如果您只选择单个列,则会得到一个向量而不是数据框。为了避免这种情况,您需要添加 drop=FALSE。正如我的回答中所解释的,以及在你上面的评论中......
137赞 Joshua Ulrich 1/5/2011 #2

你可以这样使用:%in%

df[, !(colnames(df) %in% c("x","bar","foo"))]

评论

1赞 Daniel Fletcher 4/28/2016
我是否遗漏了什么,或者这实际上与乔里斯答案的第一部分相同?DF[ , !(names(DF) %in% drops)]
11赞 Joshua Ulrich 4/28/2016
@DanielFletcher:是一样的。查看答案上的时间戳。我们同时回答......5年前。:)
7赞 Daniel Fletcher 4/30/2016
坚果。 =Didentical(post_time_1, post_time_2) [1] TRUE
0赞 J. Mini 3/20/2021
为什么不去掉逗号呢?我看不出有什么理由不等同。df[!(colnames(df) %in% c("x","bar","foo"))]
0赞 Amer 7/7/2021
为什么当我在data.table数据帧上运行时它返回FALSE / TRUE向量。为什么?
535赞 Prasad Chalasani 1/5/2011 #3

还有一个命令,如果你知道你想要哪些列,它很有用:subset

df <- data.frame(a = 1:10, b = 2:11, c = 3:12)
df <- subset(df, select = c(a, c))

@hadley评论后更新: 要删除 a,c 列,您可以执行以下操作:

df <- subset(df, select = -c(a, c))

评论

3赞 Prasad Chalasani 1/5/2011
我真的希望 R 函数有一个像“allbut = FALSE”这样的选项,当设置为 TRUE 时,它会“反转”选择,即保留除列表中的列之外的所有列。subsetselect
4赞 JD Long 1/5/2011
@prasad,请参阅下面的@joris答案。没有任何子集条件的子集有点矫枉过正。尝试简单:df[c("a", "c")]
1赞 Prasad Chalasani 1/5/2011
@JD我知道这一点,但我喜欢命令的语法便利性,你不需要在列名周围加上引号——我想我不介意输入一些额外的字符,只是为了避免引用名称:)subset
17赞 Ari B. Friedman 10/3/2012
请注意,不应在其他函数中使用。subset
2赞 Ari B. Friedman 10/1/2013
@mac stackoverflow.com/questions/12850141/......
16赞 JD Long 1/6/2011 #4

我一直认为一定有更好的成语,但是对于按名称减去列,我倾向于执行以下操作:

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)

# return everything except a and c
df <- df[,-match(c("a","c"),names(df))]
df

评论

5赞 hadley 1/6/2011
否定匹配不是一个好主意 -df[,-match(c("e","f"),names(df))]
0赞 Chetan Arvind Patil 1/23/2019
.@JDLong - 如果我想删除列名开头的列怎么办?-
47赞 IRTFM 1/6/2011 #5

基于grep()将返回一个数字向量这一事实,可能有一个更强大的策略。如果你有一个很长的变量列表,就像我在我的一个数据集中所做的那样,一些以“.A“和其他以”.B“,你只想要以”.A“(连同所有与任一模式不匹配的变量,执行以下操作:

dfrm2 <- dfrm[ , -grep("\\.B$", names(dfrm)) ]

对于手头的情况,以 Joris Meys 为例,它可能没有那么紧凑,但它会是:

DF <- DF[, -grep( paste("^",drops,"$", sep="", collapse="|"), names(DF) )]

评论

2赞 MichaelChirico 4/14/2016
如果我们首先将 定义为 ,这将变得更好(阅读:更紧凑):dropspaste0("^", drop_cols, "$")sapplyDF[ , -sapply(drops, grep, names(DF))]
28赞 scentoni 1/11/2012 #6

另一种可能性:

df <- df[, setdiff(names(df), c("a", "c"))]

df <- df[, grep('^(a|c)$', names(df), invert=TRUE)]

评论

2赞 ctbrown 3/26/2014
太糟糕了,这没有被点赞,因为使用 是最佳的,尤其是在列数量非常多的情况下。setdiff
0赞 Joe 4/21/2016
从另一个角度来看:df <- df[ , -which(grepl('a|c', names(df)))]
47赞 mnel 11/14/2012 #7

如果要通过引用删除列并避免与之关联的内部复制,则可以使用包和函数data.framesdata.table:=

您可以将字符向量名称传递到运算符的左侧,并作为 RHS。:=NULL

library(data.table)

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)
DT <- data.table(df)
# or more simply  DT <- data.table(a=1:10, b=1:10, c=1:10, d=1:10) #

DT[, c('a','b') := NULL]

如果要将名称预定义为调用 之外的字符向量,请将对象的名称括在 中,或者强制在调用范围内计算 LHS,而不是作为 范围内的名称。[(){}DT

del <- c('a','b')
DT <- data.table(a=1:10, b=1:10, c=1:10, d=1:10)
DT[, (del) := NULL]
DT <-  <- data.table(a=1:10, b=1:10, c=1:10, d=1:10)
DT[, {del} := NULL]
# force or `c` would also work.   

您还可以使用 ,它避免了 的开销,并且也适用于 data.framesset[.data.table

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)
DT <- data.table(df)

# drop `a` from df (no copying involved)

set(df, j = 'a', value = NULL)
# drop `b` from DT (no copying involved)
set(DT, j = 'b', value = NULL)
24赞 jkeirstead 5/3/2013 #8

出于兴趣,这标记了 R 奇怪的多种语法不一致之一。例如,给定一个两列数据框:

df <- data.frame(x=1, y=2)

这给出了一个数据框

subset(df, select=-y)

但这给出了一个向量

df[,-2]

这都在解释中,但并不完全是预期的行为。好吧,至少对我来说不是......?[

259赞 Max Ghenis 9/29/2013 #9
within(df, rm(x))

可能是最简单的,或者对于多个变量:

within(df, rm(x, y))

或者,如果您正在处理 s(根据 如何在 data.table 中按名称删除列?data.table

dt[, x := NULL]   # Deletes column x by reference instantly.

dt[, !"x"]   # Selects all but x into a new data.table.

或多个变量

dt[, c("x","y") := NULL]

dt[, !c("x", "y")]

评论

40赞 Miles Erickson 10/2/2015
within(df, rm(x))迄今为止最干净的解决方案。鉴于这是一种可能性,所有其他答案似乎都不必要地复杂了一个数量级。
2赞 MichaelChirico 7/16/2016
请注意,如果 中命名了重复的列,则该操作将不起作用within(df, rm(x))xdf
2赞 Max Ghenis 3/11/2017
@MichaelChirico澄清一下,它两者都删除了,但似乎改变了数据的值。如果是这种情况,则会遇到更大的问题,但这里有一个示例: 返回 .df <- data.frame(x = 1, y = 2); names(df) <- c("x", "x"); within(df, rm(x))data.frame(x = 2, x = 2)
1赞 Joris Meys 12/13/2018
@MilesErickson 问题是你依赖于一个功能强大但也使用 NSE 的功能。帮助页面上的注释明确指出,对于编程,应充分小心。within()
1赞 HSchmale 1/4/2019
@MilesErickson 多久会遇到一次名称重复的数据帧?
67赞 Vincent 2/12/2014 #10

list(NULL) 也有效:

dat <- mtcars
colnames(dat)
# [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
# [11] "carb"
dat[,c("mpg","cyl","wt")] <- list(NULL)
colnames(dat)
# [1] "disp" "hp"   "drat" "qsec" "vs"   "am"   "gear" "carb"

评论

1赞 c-urchin 5/21/2014
明!这以一种自然的方式将 NULL 赋值扩展到单个列,并且(似乎)避免了复制(尽管我不知道在引擎盖下会发生什么,所以它可能在内存使用方面效率不高......但在我看来,在语法上显然更有效。
6赞 CousinCocaine 7/7/2014
您不需要 list(NULL),NULL 就足够了。例如:dat[,4]=NULL
10赞 Vincent 9/16/2014
OP的问题是如何删除多个列。dat[,4:5] <- NULL 不起作用。这就是 list(NULL) 的用武之地。它适用于 1 列或多列。
0赞 MichaelChirico 7/16/2016
尝试删除重复的列名时,这也不起作用。
0赞 Vincent 7/16/2016
@MichaelChirico对我来说效果很好。如果要删除具有相同名称的第一列,请提供标签,或者为要删除的每个列提供索引。如果你有一个不起作用的例子,我很想看看它。也许将其作为新问题发布?
25赞 Kun Ren 6/20/2014 #11
DF <- data.frame(
  x=1:10,
  y=10:1,
  z=rep(5,10),
  a=11:20
)
DF

输出:

    x  y z  a
1   1 10 5 11
2   2  9 5 12
3   3  8 5 13
4   4  7 5 14
5   5  6 5 15
6   6  5 5 16
7   7  4 5 17
8   8  3 5 18
9   9  2 5 19
10 10  1 5 20

DF[c("a","x")] <- list(NULL)

输出:

        y z
    1  10 5
    2   9 5
    3   8 5
    4   7 5
    5   6 5
    6   5 5
    7   4 5
    8   3 5    
    9   2 5
    10  1 5
22赞 c.gutierrez 8/28/2014 #12

这里有一种方法可以解决它:dplyr

#df[ -c(1,3:6, 12) ]  # original
df.cut <- df %>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)  # with dplyr::select()

我喜欢这个,因为它在没有注释的情况下阅读和理解很直观,并且对列在数据框中改变位置很可靠。它还遵循用于删除元素的矢量化习语。-

评论

0赞 Marek 11/23/2016
除此之外,(1) 用户想要替换原来的 df (2) magrittr 有运算符来替换输入对象,它可以简化为%<>% df %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)
1赞 iNyar 5/4/2017
如果要删除一长串列,则使用 ,对它们进行分组并仅添加一个减号可能会更容易:dplyrdf.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n))
43赞 Pat W. 11/23/2014 #13

另一个答案。 用。dplyrselect(-column)

如果您的变量具有一些通用的命名结构,您可以尝试 .例如starts_with()

library(dplyr)
df <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm (5), 
                 var4 = rnorm(5), char1 = rnorm(5), char2 = rnorm(5))
df
#        var2      char1        var4       var3       char2       var1
#1 -0.4629512 -0.3595079 -0.04763169  0.6398194  0.70996579 0.75879754
#2  0.5489027  0.1572841 -1.65313658 -1.3228020 -1.42785427 0.31168919
#3 -0.1707694 -0.9036500  0.47583030 -0.6636173  0.02116066 0.03983268

df1 <- df %>% select(-starts_with("char"))

df1
#        var2        var4       var3       var1
#1 -0.4629512 -0.04763169  0.6398194 0.75879754
#2  0.5489027 -1.65313658 -1.3228020 0.31168919
#3 -0.1707694  0.47583030 -0.6636173 0.03983268

如果要在数据框中删除变量序列,可以使用 .例如,如果你想删除 、 和介于两者之间的所有变量,你只剩下::var2var3var1

df2 <- df1 %>% select(-c(var2:var3) )  
df2
#        var1
#1 0.75879754
#2 0.31168919
#3 0.03983268

评论

2赞 ha-pu 3/2/2019
不要忘记 ,例如 或 ,它也接受正则表达式。select()contains()matches()
13赞 krlmlr 12/4/2014 #14

Bernd Bischl 的包中有一个函数可以做到这一点。dropNamed()BBmisc

BBmisc::dropNamed(df, "x")

优点是它避免了重复数据帧参数,因此适合管道输入(就像方法一样):magrittrdplyr

df %>% BBmisc::dropNamed("x")
10赞 Nick Keramaris 10/26/2016 #15

如果您不想使用上述@hadley,另一种解决方案: 如果“COLUMN_NAME”是要删除的列的名称:

df[,-which(names(df) == "COLUMN_NAME")]

评论

1赞 Marek 11/23/2016
(1)问题是一次删除多个列。(2)如果不在(检查自己:)。(3)更简单,不会遭受(2)的困扰COLUMN_NAMEdfdf<-data.frame(a=1,b=2)df[,names(df) != "COLUMN_NAME"]
0赞 Akash Nayak 1/17/2018
你能提供更多关于这个答案的信息吗?
37赞 Preston 1/31/2018 #16

Dplyr 解决方案

我怀疑这在这里会引起很多关注,但是如果你有一个要删除的列列表,并且你想在我在子句中使用的链中这样做:dplyrone_of()select

下面是一个简单、可重现的例子:

undesired <- c('mpg', 'cyl', 'hp')

mtcars <- mtcars %>%
  select(-one_of(undesired))

可以通过运行或此处找到文档:?one_of

http://genomicsclass.github.io/book/pages/dplyr_tutorial.html

6赞 Cybernetic 6/16/2018 #17

提供要删除的数据框和逗号分隔的名称字符串:

remove_features <- function(df, features) {
  rem_vec <- unlist(strsplit(features, ', '))
  res <- df[,!(names(df) %in% rem_vec)]
  return(res)
}

用法

remove_features(iris, "Sepal.Length, Petal.Width")

在此处输入图像描述

11赞 sbha 7/3/2018 #18

除了前面的答案中演示之外,还有其他几个选项可用于删除列,这些选项不涉及定义所有特定的列名称(使用 dplyr starwars 示例数据来表示列名称的某些变化):select(-one_of(drop_col_names))dplyrselect()

library(dplyr)
starwars %>% 
  select(-(name:mass)) %>%        # the range of columns from 'name' to 'mass'
  select(-contains('color')) %>%  # any column name that contains 'color'
  select(-starts_with('bi')) %>%  # any column name that starts with 'bi'
  select(-ends_with('er')) %>%    # any column name that ends with 'er'
  select(-matches('^f.+s$')) %>%  # any column name matching the regex pattern
  select_if(~!is.list(.)) %>%     # not by column name but by data type
  head(2)

# A tibble: 2 x 2
homeworld species
  <chr>     <chr>  
1 Tatooine  Human  
2 Tatooine  Droid 

如果您需要删除数据框中可能存在也可能不存在的列,这里有一个轻微的转折,使用与使用不同,如果列名称不存在,则不会引发警告。在此示例中,“bad_column”不是数据框中的列:select_if()one_of()Unknown columns:

starwars %>% 
  select_if(!names(.) %in% c('height', 'mass', 'bad_column'))
1赞 milan 8/4/2018 #19

使用 找到要删除的列的索引。给这些索引一个负号 ()。然后对这些值进行子集,这将从数据帧中删除它们。这是一个示例。which*-1

DF <- data.frame(one=c('a','b'), two=c('c', 'd'), three=c('e', 'f'), four=c('g', 'h'))
DF
#  one two three four
#1   a   d     f    i
#2   b   e     g    j

DF[which(names(DF) %in% c('two','three')) *-1]
#  one four
#1   a    g
#2   b    h
1赞 GKi 12/16/2019 #20

如果你有一个大和低内存使用 rmwithin 删除 data.frame 的列,就像当前 (R 3.6.2) 使用更多内存一样 - 除了手册的提示以交互方式使用子集data.frame[subset

getData <- function() {
  n <- 1e7
  set.seed(7)
  data.frame(a = runif(n), b = runif(n), c = runif(n), d = runif(n))
}

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- DF[setdiff(names(DF), c("a", "c"))] ##
#DF <- DF[!(names(DF) %in% c("a", "c"))] #Alternative
#DF <- DF[-match(c("a","c"),names(DF))]  #Alternative
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- subset(DF, select = -c(a, c)) ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#357 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- within(DF, rm(a, c)) ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF[c("a", "c")]  <- NULL ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used
0赞 andschar 8/12/2021 #21

另一个尚未发布的 data.table 选项是使用特殊动词,它代表数据子集。与参数一起,您可以按名称或索引选择/删除列。.SD.SDcols

require(data.table)
# data
dt = data.table(
  A = LETTERS[1:5],
  B = 1:5,
  C = rep(TRUE, 5)
)
# delete B
dt[ , .SD, .SDcols =! 'B' ]
# delete all matches (i.e. all columns)
cols = grep('[A-Z]+', names(dt), value = TRUE)
dt[ , .SD, .SDcols =! cols ]

可以在此处找到 data.table 中此类任务的所有选项的摘要

4赞 Md. Sahidul Islam 10/24/2021 #22

你可以做很多方法......

选项 1:

df[ , -which(names(df) %in% c("name1","name2"))]

选项 2:

df[!names(df) %in% c("name1", "name2")]

选项 3:

subset(df, select=-c(name1,name2))
3赞 Rupesh Kumar 11/3/2021 #23

在数据框中按列名称删除和删除列。

A <- df[ , c("Name","Name1","Name2","Name3")]
0赞 Rafa 11/30/2021 #24
df <- data.frame(
+   a=1:5,
+   b=6:10,
+   c=rep(22,5),
+   d=round(runif(5)*100, 2),
+   e=round(runif(5)*100, 2),
+   f=round(runif(5)*100, 2),
+   g=round(runif(5)*100, 2),
+   h=round(runif(5)*100, 2)
+ )
> df
  a  b  c     d     e     f     g     h
1 1  6 22 76.31 39.96 66.62 72.75 73.14
2 2  7 22 53.41 94.85 96.02 97.31 85.32
3 3  8 22 98.29 38.95 12.61 29.67 88.45
4 4  9 22 20.04 53.53 83.07 77.50 94.99
5 5 10 22  5.67  0.42 15.07 59.75 31.21

> # remove cols: d g h
> newDf <- df[, c(1:3, 5), drop=TRUE]
> newDf
  a  b  c     e
1 1  6 22 39.96
2 2  7 22 94.85
3 3  8 22 38.95
4 4  9 22 53.53
5 5 10 22  0.42
1赞 Quinten 8/27/2022 #25

使用折叠包中的函数的另一个选项。下面是一个可重现的例子:fselect

DF <- data.frame(
  x=1:10,
  y=10:1,
  z=rep(5,10),
  a=11:20
)

library(collapse)
fselect(DF, -z)
#>     x  y  a
#> 1   1 10 11
#> 2   2  9 12
#> 3   3  8 13
#> 4   4  7 14
#> 5   5  6 15
#> 6   6  5 16
#> 7   7  4 17
#> 8   8  3 18
#> 9   9  2 19
#> 10 10  1 20

创建于 2022-08-26 with reprex v2.0.2