Arhn - архитектура программирования

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

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

df$x <- NULL

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

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

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

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

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

05.01.2011

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

Ответы:


1

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

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.

05.01.2011
  • функция подмножества работает лучше, поскольку она не преобразует фрейм данных с одним столбцом в вектор 28.06.2013
  • @ mut1na проверяет аргумент drop = FALSE функции индексации. 28.06.2013
  • Разве это не должно быть DF[,keeps] вместо DF[keeps]? 28.10.2014
  • @lindelof Нет. Может, но тогда вам нужно добавить drop = FALSE, чтобы R не преобразовывал ваш фрейм данных в вектор, если вы выбираете только один столбец. Не забывайте, что фреймы данных - это списки, поэтому выбор списка (одномерный, как я) работает отлично и всегда возвращает список. Или в данном случае фрейм данных, поэтому я предпочитаю его использовать. 28.10.2014
  • @AjayOhri Да, было бы. Без запятой вы используете способ выбора списка, что означает, что даже когда вы извлекаете один столбец, вы все равно получаете возвращаемый фрейм данных. Если вы используете матричный способ, как и вы, вы должны знать, что если вы выберете только один столбец, вы получите вектор вместо фрейма данных. Чтобы этого избежать, вам нужно добавить drop = FALSE. Как объяснено в моем ответе и в комментарии прямо над вашим ... 07.07.2015
  • Почему бы не использовать DF[!(names(DF) %in% drops)]? Судя по тому, что было сказано в этих комментариях, это должно быть эквивалентно использованию drop=FALSE, что, вероятно, в любом случае мы хотим. 20.03.2021

  • 2

    Также есть команда 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))
    
    05.01.2011
  • Мне действительно хотелось бы, чтобы у функции R subset была опция вроде allbut = FALSE, которая инвертирует выбор, когда установлено значение TRUE, т.е. сохраняет все столбцы кроме тех, что в списке select. 05.01.2011
  • @prasad, см. ответ @joris ниже. Подмножество без каких-либо критериев подмножества - это немного излишне. Попробуйте просто: df[c("a", "c")] 05.01.2011
  • @JD Я знал об этом, но мне нравится синтаксическое удобство команды subset, когда вам не нужно заключать в кавычки имена столбцов - думаю, я не против набрать несколько дополнительных символов, чтобы не цитировать имена :) 05.01.2011
  • о, это хороший момент. Я не думал о цитате. 05.01.2011
  • Обратите внимание, что вы не должны использовать subset внутри других функций. 03.10.2012
  • @mac stackoverflow.com/questions/12850141/ 30.09.2013
  • подмножество (df, select = -c (b)) @spacetyper 26.04.2019
  • Я использовал это, но теперь получаю сообщение об ошибке: Error: unexpected '=' in "DT <- subset(DT, select=" Что-то изменилось? 30.07.2020

  • 3
    within(df, rm(x))
    

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

    within(df, rm(x, y))
    

    Или, если вы имеете дело с data.tables (согласно как удалить столбец по имени в 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")]
    
    28.09.2013
  • within(df, rm(x)) является безусловно самым чистым решением. Учитывая, что это возможно, любой другой ответ кажется излишне сложным на порядок. 02.10.2015
  • Обратите внимание, что within(df, rm(x)) не будет работать, если есть повторяющиеся столбцы с именем x в df. 15.07.2016
  • @MichaelChirico, чтобы уточнить, он ничего не удаляет, но, похоже, меняет значения данных. В этом случае возникают более серьезные проблемы, но вот пример: df <- data.frame(x = 1, y = 2); names(df) <- c("x", "x"); within(df, rm(x)) возвращает data.frame(x = 2, x = 2). 11.03.2017
  • @MilesErickson Проблема в том, что вы полагаетесь на функцию within(), которая является мощной, но также использует NSE. В примечании на странице справки четко указано, что при программировании следует проявлять достаточную осторожность. 13.12.2018
  • @MilesErickson Как часто можно встретить фрейм данных с повторяющимися именами в нем? 03.01.2019
  • @HSchmale df<-data.frame(x=1:3,y=4:6,x=7:9) является допустимым, но последний столбец становится x.1. Я видел только повторяющиеся имена в списках. Чтобы получить его во фрейме данных, вам нужно передать аргумент check.names = FALSE. 18.03.2021
  • Два других важных преимущества within: вам не нужно передавать аргумент drop=FALSE, и rm предупредит вас, если столбец, который вы пытаетесь удалить, отсутствует. [ не такой добрый. 04.04.2021

  • 4

    Вы можете использовать %in% так:

    df[, !(colnames(df) %in% c("x","bar","foo"))]
    
    05.01.2011
  • Я что-то упустил, или это фактически то же самое решение, что и первая часть ответа Джориса? DF[ , !(names(DF) %in% drops)] 28.04.2016
  • @DanielFletcher: это то же самое. Посмотрите на отметки времени в ответах. Мы ответили одновременно ... 5 лет назад. :) 28.04.2016
  • Чокнутый. identical(post_time_1, post_time_2) [1] TRUE = D 30.04.2016
  • Почему бы не опустить запятую? Я не вижу причин, по которым df[!(colnames(df) %in% c("x","bar","foo"))] не был бы эквивалентен. 20.03.2021
  • почему он возвращает вектор FALSE / TRUE, когда я запускаю фрейм данных data.table. Почему? 07.07.2021

  • 5

    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"
    
    12.02.2014
  • Блестяще! Это естественным образом расширяет назначение NULL до одного столбца и (по-видимому) позволяет избежать копирования (хотя я не знаю, что происходит под капотом, поэтому он может быть не более эффективным в использовании памяти ... но мне кажется ясно синтаксически более эффективен.) 20.05.2014
  • Вам не нужен список (NULL), достаточно NULL. например: dat [, 4] = NULL 07.07.2014
  • Вопрос OP заключался в том, как удалить несколько столбцов. dat [, 4: 5] ‹- NULL работать не будет. Вот тут-то и появляется список (NULL). Он работает для 1 или более столбцов. 16.09.2014
  • Это также не работает при попытке удалить повторяющееся имя столбца. 15.07.2016
  • @MichaelChirico У меня отлично работает. Либо укажите метку, если вы хотите удалить первый столбец с тем же именем, либо укажите индексы для каждого столбца, который вы хотите удалить. Если у вас есть пример, когда это не работает, мне было бы интересно его увидеть. Возможно, разместите это как новый вопрос? 16.07.2016
  • Этот синтаксис также будет работать для data.table. dat[,c("mpg","cyl","wt")] <- NULL 20.11.2018

  • 6

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

    7

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

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

    В нашем случае, используя пример Джориса Мейса, он может быть не таким компактным, но будет:

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

  • 8

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

  • 9

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

    31.01.2018

    10

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

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

    or

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

  • 11
    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
    
    20.06.2014

    12

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

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

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

    subset(df, select=-y)
    

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

    df[,-2]
    

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

    02.05.2013

    13

    Вот способ 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()
    

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

    27.08.2014
  • Добавив к этому, что (1) пользователь хочет заменить исходный df (2) magrittr имеет оператор %<>% для замены входного объекта, его можно упростить до df %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6) 23.11.2016
  • Если вам нужно удалить длинный список столбцов с dplyr, может быть проще сгруппировать их и поставить только один минус: df.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n)) 04.05.2017

  • 14

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

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

  • 15

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

    BBmisc::dropNamed(df, "x")
    

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

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

    16

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

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

  • 17

    Помимо 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:, если имя столбца не существует. В этом примере bad_column не является столбцом во фрейме данных:

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

    18

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

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

    введите здесь описание изображения

    15.06.2018

    19

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

    20

    Если у вас большой 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
    
    16.12.2019
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..