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

683

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

df$x <- NULL

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

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

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

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

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

  • 7
    Может кто-нибудь объяснить мне, почему в R нет чего-то простого, например df#drop(var_name) , и вместо этого нам нужно выполнить эти сложные обходные пути?
  • 0
    @ ifly6 Функция 'subset ()' в R примерно такая же экономная, как и функция 'drop ()' в Python, за исключением того, что вам не нужно указывать аргумент оси ... Я согласен, что раздражает то, что быть всего лишь одним, конечным, простым ключевым словом / синтаксисом, реализованным по всем направлениям, для чего-то такого простого, как удаление столбца.
Теги:
dataframe
r-faq

21 ответ

770
Лучший ответ

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

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.

  • 12
    функция подмножества работает лучше, так как она не будет преобразовывать фрейм данных с одним столбцом в вектор
  • 2
    @ mut1na проверьте аргумент drop = FALSE функции индексирования.
Показать ещё 4 комментария
362

Там также команда 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
    Я действительно хотел бы, чтобы функция subset R имела опцию, подобную «allbut = FALSE», которая «инвертирует» выборку, когда установлена в «TRUE», то есть сохраняет все столбцы, кроме тех, которые находятся в списке select .
  • 4
    @prasad, смотрите ответ @joris ниже. Подмножество без каких-либо критериев подмножества немного излишне. Попробуйте просто: df[c("a", "c")]
Показать ещё 7 комментариев
123
within(df, rm(x))

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

within(df, rm(x, y))

Или, если вы имеете дело с 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")]
  • 17
    within(df, rm(x)) является безусловно самым чистым решением. Учитывая, что это возможно, любой другой ответ кажется излишне сложным на порядок.
  • 2
    Обратите внимание, что within(df, rm(x)) не будет работать, если в df есть повторяющиеся столбцы с именем x .
Показать ещё 4 комментария
95

Вы можете использовать %in% следующим образом:

df[, !(colnames(df) %in% c("x","bar","foo"))]
  • 1
    Я что-то упустил, или это фактически то же решение, что и первая часть ответа Джорис? DF[ , !(names(DF) %in% drops)]
  • 8
    @DanielFletcher: это то же самое. Посмотрите на отметки времени в ответах. Мы ответили одновременно ... 5 лет назад. :)
Показать ещё 2 комментария
41

(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
    Brilliant! Это естественным образом расширяет NULL-присваивание для одного столбца и (по-видимому) избегает копирования (хотя я не знаю, что происходит под капотом, так что это может быть не более эффективным в использовании памяти ... но мне кажется, ясно более эффективно синтаксически.)
  • 5
    Вам не нужен список (NULL), достаточно NULL. например: dat [, 4] = NULL
Показать ещё 4 комментария
36

Существует потенциально более мощная стратегия, основанная на том, что grep() вернет числовой вектор. Если у вас есть длинный список переменных, как в одном из моих наборов данных, некоторые переменные, которые заканчиваются на ".A" и другие, которые заканчиваются на ".B", и вам нужны только те, которые заканчиваются на ".A" (вдоль со всеми переменными, которые не соответствуют ни одному шаблону, выполните следующее:

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

В данном случае, используя пример Joris Meys, он может быть не таким компактным, но это будет:

DF <- DF[, -grep( paste("^",drops,"$", sep="", collapse="|"), names(DF) )]
  • 1
    Если мы определим drops в первую очередь , как paste0("^", drop_cols, "$") , это становится гораздо лучше (читай: более компактный) с sapply : DF[ , -sapply(drops, grep, names(DF))]
35

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

Другой ответ 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
  • 0
    Не забывайте обо всех других возможностях, которые предоставляются с select() , таких как contains() или matches() , которые также принимают регулярные выражения.
19

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

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

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

subset(df, select=-y)

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

df[,-2]

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

18
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
18

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

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

или

df <- df[, grep('^(a|c)$', names(df), invert=TRUE)]
  • 1
    Жаль, что за это больше не голосуют, потому что использование setdiff является оптимальным, особенно в случае очень большого количества столбцов.
  • 0
    Еще один df <- df[ , -which(grepl('a|c', names(df)))] : df <- df[ , -which(grepl('a|c', names(df)))]
16

Вот способ 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
    Кроме того, что (1) пользователь хочет заменить исходный df (2) magrittr имеет оператор %<>% для замены входного объекта, его можно упростить до df %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)
  • 1
    Если у вас есть длинный список столбцов для удаления, с помощью dplyr может быть проще сгруппировать их и поставить только один минус: df.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n))
11

Я продолжаю думать, что должна быть лучшая идиома, но для вычитания столбцов по имени я стараюсь сделать следующее:

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
  • 4
    Не очень хорошая идея отрицать соответствие - df[,-match(c("e","f"),names(df))]
  • 0
    . @ JDLong - Что делать, если я хочу удалить столбец, имя которого начинается с - ?
10

Решение 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

  • 0
    Решение очень компактное, но для данных изменений необходимо назначить фрейм данных, например, mtcars = mtcars%>% select (-one_of (нежелательно))
10

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

BBmisc::dropNamed(df, "x")

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

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

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

df[,-which(names(df) == "COLUMN_NAME")]
  • 1
    (1) Проблема заключается в удалении нескольких столбцов одновременно. (2) Это не будет работать, если COLUMN_NAME не находится в df (проверьте себя: df<-data.frame(a=1,b=2) ). (3) df[,names(df) != "COLUMN_NAME"] проще и не страдает от (2)
  • 0
    Можете ли вы дать больше информации об этом ответе?
2

Помимо select(-one_of(drop_col_names)) продемонстрированного в предыдущих ответах, есть несколько других опций dplyr для отбрасывания столбцов с использованием select() которые не включают в себя определение всех конкретных имен столбцов (с использованием данных примера звездных звезд dplyr для некоторого разнообразия в имена столбцов):

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 
2

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

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

Изображение 4270

1

мы также можем использовать следующее:

library(dplyr)
a <- rnorm(10)
b <- rnorm(10)
c <- rnorm(10)
d <- rnorm(10)
e <- data.frame(a,b,c,d)
e
             a          b            c          d
1   1.43302370 -0.7432732 -1.253633400  0.5939462
2   1.98039990  0.1887923  0.291446236  0.3329504
3  -0.36722148 -1.8049586 -0.443291873  1.0630998
4  -1.04413463  1.4655549  0.001105352 -0.3041839
5   0.56971963  0.1532533  0.074341324  0.3700188
6  -0.13505460  2.1726117 -0.589520946  0.2670988
7   2.40161776  0.4755095 -0.568668733 -0.5425200
8  -0.03924000 -0.7099464 -0.135178615  1.2078678
9   0.68973936  0.6107264  1.178086997  1.1604026
10  0.02800216 -0.9340976 -1.523566800  0.7002136

e <- select(e, -c(a,b))
e
              c          d
1  -1.253633400  0.5939462
2   0.291446236  0.3329504
3  -0.443291873  1.0630998
4   0.001105352 -0.3041839
5   0.074341324  0.3700188
6  -0.589520946  0.2670988
7  -0.568668733 -0.5425200
8  -0.135178615  1.2078678
9   1.178086997  1.1604026
10 -1.523566800  0.7002136
0

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

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

>g = 1:10
>r = 11:20
>h = data.frame(g,r)
>h
  g  r
1   1  1
2   2  2
3   3  3
4   4  4
5   5  5
6   6  6
7   7  7
8   8  8
9   9  9
10 10 10

>h = h[,-2]
>h
[1]  1  2  3  4  5  6  7  8  9 10

Надеюсь, что это поможет:)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню