关于r:按名称删除数据框列

Drop data frame columns by name

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

1
df$x <- NULL

但我希望用更少的命令来完成这个任务。

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

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

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

考虑到R有多强大,我认为有一种方法可能比逐个删除每一列要好。


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

1
2
3
4
5
6
7
8
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)]

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

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

编辑:对于那些还不熟悉indexing函数的drop参数的用户,如果希望将一列作为数据帧,则需要:

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

drop=TRUE将删除不必要的维,因此返回一个具有列y值的向量。


还有subset命令,如果您知道需要哪些列,它很有用:

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

@hadley:在评论后更新:要删除A、C列,可以执行以下操作:

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


1
within(df, rm(x))

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

1
within(df, rm(x, y))

或者,如果您正在处理data.tables(如何在data.table中按名称删除列?):

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

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

或者对于多个变量

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

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


您可以这样使用%in%

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


列表(空)也有效:

1
2
3
4
5
6
7
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"


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

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

1
2
3
4
5
6
7
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进行评估,而不是将lhs作为DT范围内的名称。

1
2
3
4
5
6
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的开销,而且还可以用于data.frames

1
2
3
4
5
6
7
8
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)


基于grep()将返回一个数字向量这一事实,有一种潜在的更强大的策略。如果你有一个长长的变量列表,就像我在我的数据集中所做的那样,一些变量以".a"结尾,而另一些变量以".b"结尾,你只需要以".a"结尾的变量(以及所有与这两种模式都不匹配的变量,请这样做:

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

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

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


另一个dplyr回答。如果您的变量有一些通用的命名结构,您可以尝试使用starts_with()。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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


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

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

这给出了一个数据帧

1
subset(df, select=-y)

但这给出了一个向量

1
df[,-2]

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


另一种可能性:

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

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


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

输出:

1
2
3
4
5
6
7
8
9
10
11
    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
1
DF[c("a","x")] <- list(NULL)

输出:

1
2
3
4
5
6
7
8
9
10
11
        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


下面是一个dplyr方法:

1
2
#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()

我喜欢这一点,因为阅读和理解无需注释,并且对数据帧中改变位置的列具有鲁棒性。它还遵循了使用-删除元素的矢量化习惯用法。


DPLYR溶液

我怀疑下面会引起很大的注意,但是如果您有一个要删除的列列表,并且您想在dplyr链中删除,我在select子句中使用one_of()

下面是一个简单的、可复制的示例:

1
2
3
4
undesired <- c('mpg', 'cyl', 'hp')

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

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

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


我一直认为必须有一个更好的习惯用法,但对于按名称减去列,我倾向于执行以下操作:

1
2
3
4
5
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


在BerndBischl的BBmisc包中有一个名为dropNamed()的函数可以做到这一点。

1
BBmisc::dropNamed(df,"x")

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

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

如果不想使用上面的@hadley's,另一个解决方案是:如果"columnu name"是要删除的列的名称:

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

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

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

用途:

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

enter image description here


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

1
2
3
4
5
6
7
8
9
10
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