我想从数据框中删除许多列。我知道我们可以使用类似的方法分别删除它们:
df$x <- NULL
但是我希望用更少的命令来做到这一点。
另外,我知道我可以使用整数索引删除列,如下所示:
df <- df[ -c(1, 3:6, 12) ]
但是我担心我的变量的相对位置可能会改变。
考虑到R的强大功能,我认为可能有比逐一删除每一列更好的方法。
我想从数据框中删除许多列。我知道我们可以使用类似的方法分别删除它们:
df$x <- NULL
但是我希望用更少的命令来做到这一点。
另外,我知道我可以使用整数索引删除列,如下所示:
df <- df[ -c(1, 3:6, 12) ]
但是我担心我的变量的相对位置可能会改变。
考虑到R的强大功能,我认为可能有比逐一删除每一列更好的方法。
Answers:
您可以使用简单的名称列表:
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
。
DF[,keeps]
不是应该DF[keeps]
吗?
还有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))
subset
函数具有“ allbut = FALSE”之类的选项,该选项在设置为TRUE时“反转”选择,即保留除select
列表中的所有列之外的所有列。
df[c("a", "c")]
subset
命令的语法方便性,在这里您不需要在列名周围加上引号-我想我不介意输入一些额外的字符只是为了避免引述名称:)
subset
在其他函数内部使用。
within(df, rm(x))
是最简单的,或者对于多个变量:
within(df, rm(x, y))
或者,如果您要处理data.table
s(请参见如何在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")]
within(df, rm(x))
是迄今为止最干净的解决方案。鉴于这是可能的,其他所有答案似乎都不必要地复杂一个数量级。
within(df, rm(x))
会不会,如果有重复的命名工作列x
在df
。
df <- data.frame(x = 1, y = 2); names(df) <- c("x", "x"); within(df, rm(x))
return data.frame(x = 2, x = 2)
。
within()
强大但还使用NSE的功能。帮助页面上的注释清楚地表明,在编程时应格外小心。
您可以这样使用%in%
:
df[, !(colnames(df) %in% c("x","bar","foo"))]
DF[ , !(names(DF) %in% drops)]
identical(post_time_1, post_time_2) [1] TRUE
= D
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"
如果要通过引用删除列并避免与之关联的内部复制,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
,也可以使用data.frames
!
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”结尾的变量(以及对于所有与任一模式都不匹配的变量,请执行以下操作:
dfrm2 <- dfrm[ , -grep("\\.B$", names(dfrm)) ]
对于当前情况,以Joris Meys为例,它可能不那么紧凑,但可能是:
DF <- DF[, -grep( paste("^",drops,"$", sep="", collapse="|"), names(DF) )]
drops
首先将定义为paste0("^", drop_cols, "$")
,则使用以下命令会变得更好(阅读:更紧凑)sapply
:DF[ , -sapply(drops, grep, names(DF))]
另一个dplyr
答案。如果您的变量具有某些通用的命名结构,则可以尝试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
如果要在数据框中删除一系列变量,可以使用:
。例如,如果您想删除var2
,var3
以及介于两者之间的所有变量,则只剩下var1
:
df2 <- df1 %>% select(-c(var2:var3) )
df2
# var1
#1 0.75879754
#2 0.31168919
#3 0.03983268
select()
,例如contains()
或matches()
,它们也接受正则表达式。
另一种可能性:
df <- df[, setdiff(names(df), c("a", "c"))]
要么
df <- df[, grep('^(a|c)$', names(df), invert=TRUE)]
setdiff
最佳,尤其是在列数非常多的情况下。
df <- df[ , -which(grepl('a|c', names(df)))]
Dplyr解决方案
我怀疑这会在这里引起很多关注,但是如果您有要删除的列列表,并且想在dplyr
链中使用one_of()
它,select
子句中:
这是一个简单的可复制示例:
undesired <- c('mpg', 'cyl', 'hp')
mtcars <- mtcars %>%
select(-one_of(undesired))
可以通过运行?one_of
或此处找到文档:
http://genomicsclass.github.io/book/pages/dplyr_tutorial.html
这是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()
我之所以喜欢它,是因为它无需注释即可阅读和理解,并且对更改数据框内的列的鲁棒性很直观。它还遵循矢量化习惯用法,-
用于删除元素。
%<>%
运算符来替换输入对象,可以简化为df %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)
dplyr
,将它们分组并仅放置一个减号会更容易:df.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n))
我一直认为必须有一个更好的习惯用法,但是为了按名称减去列,我倾向于执行以下操作:
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
df[,-match(c("e","f"),names(df))]
-
怎么办?
如果您不想使用上面的@hadley,则可以采用另一种解决方案:如果“ COLUMN_NAME”是您要删除的列的名称:
df[,-which(names(df) == "COLUMN_NAME")]
COLUMN_NAME
不在,则无法使用df
(请检查您的:)df<-data.frame(a=1,b=2)
。(3)df[,names(df) != "COLUMN_NAME"]
更简单,不会遭受(2)
除了select(-one_of(drop_col_names))
前面的答案中展示的以外,还有其他一些dplyr
用于删除列的选项,这些选项select()
不涉及定义所有特定的列名称(使用dplyr starwars示例数据来获取某些列名称):
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
如果您需要删除数据帧中可能存在或可能不存在的列,则使用select_if()
此方法one_of()
会稍有不同,与之不同的是,Unknown columns:
如果列名不存在,则使用using 不会发出警告。在此示例中,“ bad_column”不是数据框中的列:
starwars %>%
select_if(!names(.) %in% c('height', 'mass', 'bad_column'))
如果您的data.frame
内存很大而内存不足[
。 。 。 。或rm
和within
,如目前(R 3.6.2)所述,使用更多内存来删除a的列data.frame
subset
-在手册的提示下可以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
df#drop(var_name)
,而是需要做这些复杂的变通方法吗?