Удалить столбцы фрейма данных по имени

У меня есть ряд столбцов, которые я хотел бы удалить из фрейма данных. Я знаю, что мы можем удалить их по отдельности, используя что-то вроде:

df$x <- NULL

Но я надеялся сделать это с меньшим количеством команд.

Кроме того, я знаю, что я мог бы отбросить столбцы, используя целочисленную индексацию следующим образом:

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

Но я обеспокоен тем, что относительное положение моих переменных может измениться.

Учитывая, насколько мощный R, я подумал, что может быть лучший способ, чем отбрасывать каждый столбец один за другим.

25 ответов

Решение

Вы можете использовать простой список имен:

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,

Там также 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))
within(df, rm(x))

вероятно, проще всего, или для нескольких переменных:

within(df, rm(x, y))

Или если вы имеете дело с data.tables (per Как удалить столбец по имени в data.table?):

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

dt[, !"x", with=FALSE]   # selects all but x into a new data.table

или для нескольких переменных

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

dt[, !c("x", "y"), with=FALSE]

В разрабатываемой версии data.table ( инструкция по установке), with = FALSE больше не нужно:

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

Вы могли бы использовать %in% как это:

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

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

Другая 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

Решение Dplyr

Я сомневаюсь, что это привлечет большое внимание здесь, но если у вас есть список столбцов, которые вы хотите удалить, и вы хотите сделать это в dplyr Я использую цепь one_of() в select пункт:

Вот простой, воспроизводимый пример:

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

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

Документацию можно найти, запустив ?one_of или здесь:

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

Другая возможность:

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

или же

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

Из интереса это указывает на одну из странных множественных синтаксических несоответствий R. Например, дан кадр данных из двух столбцов:

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

Это дает фрейм данных

subset(df, select=-y)

но это дает вектор

df[,-2]

Это все объясняется в ?[ но это не совсем ожидаемое поведение. Ну, по крайней мере, не для меня...

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

Вот 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 <- 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

Там есть функция под названием dropNamed() в Бернда Бишла BBmisc пакет, который делает именно это.

BBmisc::dropNamed(df, "x")

Преимущество состоит в том, что он избегает повторения аргумента фрейма данных и поэтому подходит для magrittr (так же, как dplyr подходы):

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

За 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 

Другое решение, если вы не хотите использовать @adley выше: если "COLUMN_NAME" - это имя столбца, который вы хотите удалить:

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

Укажите фрейм данных и строку имен, разделенных запятыми, для удаления:

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

Есть много способов сделать ...

Опция 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("Имя","Имя1","Имя2","Имя3")]

Если у вас большой data.frame и мало используют память [ . . . . или rm а также within чтобы удалить столбцыdata.frame, как subsetв настоящее время (R 3.6.2) использует больше памяти - помимо подсказки в руководстве по использованию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

Найдите индекс столбцов, которые вы хотите удалить, используя 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

Другой вариант использования функции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

Создано 26 августа 2022 г. с репрексом v2.0.2

      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

Другой вариант 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 можно найти здесь

Другие вопросы по тегам