按名称删除数据框列

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

df$x <- NULL

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

此外,我知道我可以像这样使用整数索引删除列:

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

但我担心我的变量的相对位置可能会改变。

考虑到R的强大功能,我想可能有比逐一删除每列更好的方法。

1857217 次浏览

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

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(或不提它)将删除不必要的维度,因此返回一个包含第y列值的向量。

你可以像这样使用%in%

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

还有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))

我一直在想一定有一个更好的习惯用法,但是对于按名称减去列,我倾向于这样做:

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)
# return everything except a and cdf <- df[,-match(c("a","c"),names(df))]df

基于grep()将返回一个数字向量的事实,有一个潜在的更强大的策略。如果你有一个很长的变量列表,就像我在我的一个数据集中所做的那样,一些变量以“. A”结尾,其他变量以“. B”结尾,你只想要以“. A”结尾的变量(以及所有不匹配任何模式的变量,请这样做:

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

对于手头的案例,使用Joris Meys的例子,它可能没有那么紧凑,但它会是:

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

另一种可能:

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

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

如果您想通过引用删除列并避免与data.frames关联的内部复制,那么您可以使用data.table包和函数:=

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

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.

您也可以使用set,这避免了[.data.table也为#2工作!的开销

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)

出于兴趣,这标志着R的一个奇怪的多重语法不一致。例如,给定一个两列数据帧:

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

这给出了一个数据帧

subset(df, select=-y)

但这给出了一个向量

df[,-2]

这一切都在?[中解释过,但这并不完全是预期的行为。至少对我来说不是…

within(df, rm(x))

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

within(df, rm(x, y))

或者,如果您正在处理data.table(per如何在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")]

list(NULL)也可以:

dat <- mtcarscolnames(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"
DF <- data.frame(x=1:10,y=10:1,z=rep(5,10),a=11:20)DF

输出:

    x  y z  a1   1 10 5 112   2  9 5 123   3  8 5 134   4  7 5 145   5  6 5 156   6  5 5 167   7  4 5 178   8  3 5 189   9  2 5 1910 10  1 5 20

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

输出:

        y z1  10 52   9 53   8 54   7 55   6 56   5 57   4 58   3 59   2 510  1 5

这里有一个dplyr的方法来解决这个问题:

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

我喜欢这个,因为它无需注释即可直观地阅读和理解,并且对数据帧中更改位置的列具有健壮性。它还遵循使用-删除元素的矢量化习惯用法。

答案dplyr使用select(-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

如果你想在数据帧中删除一系列变量,你可以使用:。例如,如果你想删除var2var3所有之间的变量,你只剩下var1

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

在Bernd Bischl的BBmisc包中有一个名为dropNamed()的函数就是这样做的。

BBmisc::dropNamed(df, "x")

优点是它避免了重复数据帧参数,因此适用于magrittr管道(就像dplyr方法一样):

df %>% BBmisc::dropNamed("x")

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

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

Dplyr解决方案

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

这是一个简单的,可复制的例子:

undesired <- c('mpg', 'cyl', 'hp')
mtcars <- mtcars %>%select(-one_of(undesired))

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

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

提供数据帧逗号分隔的名称的字符串以删除:

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")

在此处输入图片描述

除了在前面的答案中展示的select(-one_of(drop_col_names))之外,还有一些使用select()删除列的其他dplyr选项,这些选项不涉及定义所有特定的列名(使用dplyr starwar示例数据来获取各种列名):

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 patternselect_if(~!is.list(.)) %>%     # not by column name but by data typehead(2)
# A tibble: 2 x 2homeworld species<chr>     <chr>1 Tatooine  Human2 Tatooine  Droid

如果您需要删除数据框中可能存在也可能不存在的列,这里使用select_if()略有改动,与使用one_of()不同,如果列名不存在,则不会抛出Unknown columns:警告。在这个例子中,bad_column不是数据框中的列:

starwars %>%select_if(!names(.) %in% c('height', 'mass', 'bad_column'))

使用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

如果你有一个大的data.frame并且内存不足,请使用[...[0或[1到[2,因为subset目前(R 3.6.2)使用更多内存-除了手册的提示[3。

getData <- function() {n <- 1e7set.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))]  #Alternativesum(.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

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

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

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

有很多方法可以做…

备选1:

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

备选2:

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

备选3:

subset(df, select=-c(name1,name2))

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

A <- df[ , c("Name","Name1","Name2","Name3")]
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)+ )> dfa  b  c     d     e     f     g     h1 1  6 22 76.31 39.96 66.62 72.75 73.142 2  7 22 53.41 94.85 96.02 97.31 85.323 3  8 22 98.29 38.95 12.61 29.67 88.454 4  9 22 20.04 53.53 83.07 77.50 94.995 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]> newDfa  b  c     e1 1  6 22 39.962 2  7 22 94.853 3  8 22 38.954 4  9 22 53.535 5 10 22  0.42

使用崩溃包中的函数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,带有reprex v2.0.2