Drop dataframekolommen op naam

Ik heb een aantal kolommen die ik uit een dataframe wil verwijderen. Ik weet dat we ze afzonderlijk kunnen verwijderen met iets als:

df$x <- NULL

Maar ik hoopte dit met minder opdrachten te doen.

Ik weet ook dat ik kolommen kan laten vallen met behulp van integer indexeren als volgt:

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

Maar ik ben bang dat de relatieve positie van mijn variabelen kan veranderen.

Gezien hoe krachtig R is, dacht ik dat er misschien een betere manier was dan elke kolom één voor één te laten vallen.


Antwoord 1, autoriteit 100%

U kunt een eenvoudige lijst met namen gebruiken:

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

Of u kunt ook een lijst maken van degenen die u wilt bewaren en er bij naam naar verwijzen:

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

BEWERKEN:
Voor degenen die nog steeds niet bekend zijn met het argument dropvan de indexeringsfunctie, als u één kolom als gegevensframe wilt behouden, doet u het volgende:

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

drop=TRUE(of niet vermelden) zal onnodige dimensies laten vallen en dus een vector retourneren met de waarden van kolom y.


Antwoord 2, autoriteit 50%

Er is ook de opdracht subset, handig als je weet welke kolommen je wilt:

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

GE-UPDATE na opmerking van @hadley: kolommen a,c laten vallenzou je kunnen doen:

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

Antwoord 3, autoriteit 24%

within(df, rm(x))

is waarschijnlijk het gemakkelijkst, of voor meerdere variabelen:

within(df, rm(x, y))

Of als je te maken hebt met data.tables (per Hoe verwijder je een kolom op naam in data.table?):

dt[, x := NULL]   # Deletes column x by reference instantly.
dt[, !"x"]   # Selects all but x into a new data.table.

of voor meerdere variabelen

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

Antwoord 4, autoriteit 13%

Je zou %in%als volgt kunnen gebruiken:

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

Antwoord 5, autoriteit 6%

list(NULL) werkt ook:

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"

Antwoord 6, autoriteit 5%

Als u de kolommen per referentie wilt verwijderen en het interne kopiëren van data.frameswilt vermijden, dan kunt u het pakket data.tableen de functie :=

Je kunt tekenvectornamen doorgeven aan de linkerkant van de operator :=en NULLals de 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]

Als u de namen vooraf wilt definiëren als tekenvector buiten de aanroep van [, zet u de naam van het object in ()of {}om te forceren dat de LHS wordt geëvalueerd in het aanroepende bereik, niet als een naam binnen het bereik van 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.   

U kunt ook setgebruiken, wat de overhead van [.data.table, vermijdt en ook werkt voor 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)

Antwoord 7, autoriteit 4%

Er is een potentieel krachtigere strategie gebaseerd op het feit dat grep() een numerieke vector retourneert. Als je een lange lijst met variabelen hebt zoals ik in een van mijn datasets, sommige variabelen die eindigen op “.A” en andere die eindigen op “.B” en je wilt alleen de variabelen die eindigen op “.A” (samen met doe dit met alle variabelen die niet overeenkomen met een van beide patronen:

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

Voor het onderhavige geval, met het voorbeeld van Joris Meys, is het misschien niet zo compact, maar het zou zijn:

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

Antwoord 8, autoriteit 3%

Nog een dplyrantwoord. Als uw variabelen een gemeenschappelijke naamgevingsstructuur hebben, kunt u starts_with()proberen. Bijvoorbeeld

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

Als u een reeks variabelen in het dataframe wilt neerzetten, kunt u :gebruiken. Als u bijvoorbeeld var2, var3en allevariabelen ertussen wilt laten vallen, houdt u gewoon var1:

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

Antwoord 9, autoriteit 3%

Dplyr-oplossing

Ik betwijfel of dit hier veel aandacht zal krijgen, maar als je een lijst met kolommen hebt die je wilt verwijderen, en je wilt het in een dplyr-keten doen, gebruik ik one_of()in de select-clausule:

Hier is een eenvoudig, reproduceerbaar voorbeeld:

undesired <- c('mpg', 'cyl', 'hp')
mtcars <- mtcars %>%
  select(-one_of(undesired))

Documentatie kan worden gevonden door ?one_ofuit te voeren of hier:

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


Antwoord 10, autoriteit 3%

Een andere mogelijkheid:

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

of

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

Antwoord 11, autoriteit 2%

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

Uitvoer:

   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)

Uitvoer:

       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

Antwoord 12, autoriteit 2%

Uit interesse signaleert dit een van de vreemde meervoudige syntaxisinconsistenties van R. Bijvoorbeeld gegeven een gegevensframe met twee kolommen:

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

Dit geeft een dataframe

subset(df, select=-y)

maar dit geeft een vector

df[,-2]

Dit wordt allemaal uitgelegd in ?[maar het is niet echt verwacht gedrag. In ieder geval niet voor mij…


Antwoord 13, autoriteit 2%

Hier is een dplyrmanier om dit aan te pakken:

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

Ik vind dit leuk omdat het intuïtief is om & begrijpen zonder annotatie en robuust voor kolommen die van positie veranderen binnen het dataframe. Het volgt ook het gevectoriseerde idioom dat -gebruikt om elementen te verwijderen.


Antwoord 14

Ik blijf denken dat er een beter idioom moet zijn, maar voor het aftrekken van kolommen op naam, heb ik de neiging om het volgende te doen:

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

Antwoord 15

Er is een functie genaamd dropNamed()in het BBmisc-pakket van Bernd Bischl die precies dit doet.

BBmisc::dropNamed(df, "x")

Het voordeel is dat het herhaling van het dataframe-argument voorkomt en dus geschikt is voor piping in magrittr(net als de dplyr-benaderingen):

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

Antwoord 16

Een andere oplossing als u de bovenstaande gegevens van @hadley niet wilt gebruiken: Als “COLUMN_NAME” de naam is van de kolom die u wilt verwijderen:

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

Antwoord 17

Naast select(-one_of(drop_col_names))gedemonstreerd in eerdere antwoorden, zijn er een paar andere dplyropties voor het neerzetten van kolommen met behulp van select()waarbij niet alle specifieke kolomnamen moeten worden gedefinieerd (met behulp van de dplyr starwars-voorbeeldgegevens voor enige variatie in kolomnamen):

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 

Als u een kolom moet verwijderen die al dan niet in het dataframe bestaat, is hier een kleine draai aan het gebruik van select_if()die, in tegenstelling tot het gebruik van one_of()gooi geen Unknown columns:waarschuwing als de kolomnaam niet bestaat. In dit voorbeeld is ‘bad_column’ geen kolom in het dataframe:

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

Antwoord 18

Geef het gegevensframeen een reeks door komma’s gescheiden namenop om te verwijderen:

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

Gebruik:

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


Antwoord 19

Zoek de index van de kolommen die u wilt verwijderen met which. Geef deze indexen een minteken (*-1). Subset vervolgens op die waarden, waardoor ze uit het dataframe worden verwijderd. Dit is een voorbeeld.

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

Antwoord 20

Als je een groot data.framehebt en weinig geheugen hebt, gebruik dan [....of rmen withinom kolommen van een data.framete verwijderen, als subsetgebruikt momenteel (R 3.6.2) meer geheugen – naast de hint van de handleiding om subsetinteractief te gebruiken.

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

Other episodes