Veri çerçevesi sütunlarını ada göre bırakın


874

Bir veri çerçevesinden kaldırmak istediğiniz sütunlar bir dizi var. Bunları tek tek silebileceğimizi biliyorum:

df$x <- NULL

Ama bunu daha az komutla yapmayı umuyordum.

Ayrıca, ben böyle tamsayı indeksleme kullanarak sütun bırakabileceğini biliyorum:

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

Fakat değişkenlerimin göreceli konumunun değişebileceğinden endişeliyim.

R'nin ne kadar güçlü olduğu göz önüne alındığında, her sütunu tek tek bırakmaktan daha iyi bir yol olabileceğini düşündüm.


13
Birisi bana R'nin neden böyle basit bir şeye sahip olmadığını açıklayabilir mi df#drop(var_name), bunun yerine bu karmaşık çözümleri yapmamız gerekiyor mu?
ifly6

2
@ ifly6 R'deki 'subset ()' işlevi, Python'daki 'drop ()' işlevi kadar cimri bir değerdir, ancak eksen bağımsız değişkenini belirtmenize gerek yoktur ... bir sütunu düşürmek kadar temel bir şey için tahta boyunca uygulanan tek bir, nihai, kolay anahtar kelime / sözdizimi olun.
Paul Sochacki

Yanıtlar:


912

Basit bir ad listesi kullanabilirsiniz:

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

Alternatif olarak, saklanacakların bir listesini yapabilir ve adlarına göre bunlara başvurabilirsiniz:

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

DÜZENLEME: dropDizin oluşturma işlevinin bağımsız değişkeniyle hala tanışmamış olanlar için, bir sütunu veri çerçevesi olarak tutmak istiyorsanız, şunları yaparsınız:

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

drop=TRUE(veya bahsetmemek) gereksiz boyutları düşürür ve bu nedenle sütun değerlerini içeren bir vektör döndürür y.


19
bir sütun içeren bir veri çerçevesini bir vektöre dönüştürmeyeceğinden alt küme işlevi daha iyi çalışır
mut1na

3
@ mut1na index = işlevinin drop = FALSE argümanını kontrol edin.
Joris Meys

4
Bunun DF[,keeps]yerine olmamalı mı DF[keeps]?
lindelof

8
@lindelof Hayır. Olabilir, ancak yalnızca tek bir sütun seçerseniz R'nin veri çerçevenizi bir vektöre dönüştürmesini önlemek için drop = FALSE eklemeniz gerekir. Veri çerçevelerinin listeler olduğunu unutmayın, bu nedenle liste seçimi (yaptığım gibi tek boyutlu) mükemmel bir şekilde çalışır ve her zaman bir liste döndürür. Ya da bu durumda bir veri çerçevesi, bu yüzden kullanmayı tercih ediyorum.
Joris Meys

7
@AjayOhri Evet, olur. Virgül kullanmadan "liste" seçme yöntemini kullanırsınız, yani tek bir sütunu çıkardığınızda bile yine de bir veri çerçevesi döndürülür. "Matris" yolunu kullanırsanız, yaptığınız gibi, yalnızca tek bir sütun seçerseniz veri çerçevesi yerine bir vektör alacağınızın farkında olmalısınız. Bundan kaçınmak için drop = FALSE eklemeniz gerekir.
Cevabımda

453

Ayrıca subsethangi sütunları istediğinizi biliyorsanız yararlı bir komut da vardır :

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

@Hadley tarafından yapılan yorumdan sonra GÜNCELLENDİ: a, c sütunlarını bırakmak için şunları yapabilirsiniz:

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

3
Gerçekten R subsetişlevi "allbut = FALSE" gibi bir seçenek olsaydı "DOĞRU olarak ayarlandığında seçimi tersine çevirir" yani listede olanlar dışındaki tüm sütunları korur select.
Prasad Chalasani

4
@prasad, aşağıdaki @joris yanıtına bakın. Herhangi bir alt küme kriteri olmayan bir alt küme biraz fazladır. Basitçe deneyin:df[c("a", "c")]
JD Long

@JD Bunu biliyordum, ama subsetsütun isimlerinin etrafına tırnak koymanız gerekmeyen komutun sözdizimsel kolaylığını seviyorum - Sanırım sadece isimleri alıntılamaktan kaçınmak için birkaç ekstra karakter yazmayı umursamıyorum :)
Prasad Chalasani

11
subsetDiğer işlevlerin içinde kullanmamanız gerektiğini unutmayın .
Ari B. Friedman


196
within(df, rm(x))

muhtemelen en kolay olanı veya birden çok değişken için:

within(df, rm(x, y))

Veya data.tables ile uğraşıyorsanız ( bir sütunu data.table içindeki ada göre nasıl silebilirsiniz? ):

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

dt[, !"x"]   # Selects all but x into a new data.table.

veya birden çok değişken için

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

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

26
within(df, rm(x))açık ara en temiz çözümdür. Bunun bir olasılık olduğu göz önüne alındığında, diğer tüm cevaplar bir büyüklük sırası ile gereksiz yere karmaşık görünüyor.
Miles Erickson

2
Not within(df, rm(x))olacak değil adlandırılmış yinelenen sütun olup olmadığını kontrol çalışması xiçinde df.
MichaelChirico

2
@MichaelChirico açıklığa kavuşturur, hiçbirini kaldırmaz, ancak verilerin değerlerini değiştirir gibi görünüyor. Bu durumda birinin daha büyük sorunları vardır, ama işte bir örnek: df <- data.frame(x = 1, y = 2); names(df) <- c("x", "x"); within(df, rm(x))döner data.frame(x = 2, x = 2).
Max Ghenis

1
@MilesErickson Sorun within()güçlü ama aynı zamanda NSE kullanan bir işleve güvenmenizdir . Yardım sayfasındaki notta, programlama için yeterli özen gösterilmesi gerektiği açıkça belirtilmektedir.
Joris Meys

@MilesErickson Yinelenen adlar içeren bir veri çerçevesiyle ne sıklıkta karşılaşılır?
HSchmale

115

Bunun %in%gibi kullanabilirsiniz :

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

1
Bir şey mi kaçırıyorum, yoksa bu etkili bir şekilde Joris'in cevabının ilk kısmı mı? DF[ , !(names(DF) %in% drops)]
Daniel Fletcher

9
@DanielFletcher: aynı. Cevaplardaki zaman damgalarına bakın. Aynı anda cevap verdik ... 5 yıl önce. :)
Joshua Ulrich

5
Çatlak. identical(post_time_1, post_time_2) [1] TRUE = D
Daniel Fletcher

54

list (NULL) da çalışır:

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
Parlak! Bu, NULL atamasını doğal bir şekilde tek bir sütuna genişletir ve (görünüşte) kopyalamayı önler (başlık altında ne olduğunu bilmememe rağmen, bellek kullanımında daha verimli olmayabilir ... ama bana açıkça görünüyor) sözdizimsel olarak daha verimli.)
c-urchin

6
Listeye (NULL) ihtiyacınız yok, NULL yeterlidir. örneğin: dat [, 4] = NULL
CousinCocaine

8
OP'nin sorusu, birden çok sütunun nasıl silineceği idi. dat [, 4: 5] <- NULL çalışmaz. Liste (NULL) devreye girer. 1 veya daha fazla sütun için çalışır.
Vincent

Bu, yinelenen bir sütun adını kaldırmaya çalışırken de çalışmaz.
MichaelChirico

@MichaelChirico Benim için iyi çalışıyor. Aynı ada sahip ilk sütunları kaldırmak istiyorsanız bir etiket verin veya kaldırmak istediğiniz her sütun için dizinler verin. Eğer işe yaramadığı bir örnek varsa onu görmek isterim. Belki de yeni bir soru olarak yayınlayabilirsin?
Vincent

42

Sütunları referansla kaldırmak ve ilişkili iç kopyadan kaçınmak data.framesistiyorsanız, data.tablepaketi ve işlevi kullanabilirsiniz.:=

Bir karakter vektörü isimlerini :=operatörün sol tarafına NULLve RHS olarak iletebilirsiniz .

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]

Adları çağrının dışında karakter vektörü olarak önceden tanımlamak istiyorsanız [, nesnenin adını kaydırın ()veya {}LHS'yi çağrı kapsamı içinde bir ad olarak değil, çağrı kapsamında değerlendirilmeye zorlayın 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.   

Ayrıca kullanabilirsiniz setyükü kaçınan, [.data.table, ve ayrıca için çalışıyor 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)

41

Grep () 'in sayısal bir vektör döndüreceği gerçeğine dayanan potansiyel olarak daha güçlü bir strateji vardır. Veri kümemden birinde yaptığım gibi uzun bir değişkenler listeniz varsa, ".A" ile biten bazı değişkenler ve ".B" ile biten bazı değişkenler varsa ve yalnızca ".A" ile biten değişkenleri ( her iki kalıpla eşleşmeyen tüm değişkenlerle bunu yapın:

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

Eldeki durum için, Joris Meys örneğini kullanarak, o kadar kompakt olmayabilir, ancak şu olurdu:

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

1
dropsİlk etapta şu şekilde tanımlarsak paste0("^", drop_cols, "$"), bu çok daha güzel olur (okuyun: daha kompakt) sapply:DF[ , -sapply(drops, grep, names(DF))]
MichaelChirico

30

Başka bir dplyrcevap. Değişkenlerinizde bazı ortak adlandırma yapıları varsa, deneyebilirsiniz starts_with(). Örneğin

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

Veri çerçevesinde bir değişkenler dizisi bırakmak istiyorsanız, kullanabilirsiniz :. Eğer bırakmak istedim Örneğin var2, var3ve tüm aradaki değişkenler, sadece sol olurdu var1:

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

1
Regex'i de kabul eden veya select()gibi diğer tüm fırsatları unutmamak . contains()matches()
ha_pu

23

Bir başka olasılık:

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

veya

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

2
setdiffÖzellikle çok sayıda sütun söz konusu olduğunda bunun kullanımı en uygun olanı olduğu için bunun daha fazla değerlendirilmemesi çok kötü .
ctbrown

Bu konuda başka bir açı:df <- df[ , -which(grepl('a|c', names(df)))]
Joe

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

Çıktı:

    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)

Çıktı:

        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

23

Dplyr Çözümü

Bunun burada çok dikkat çekeceğinden şüpheliyim, ancak kaldırmak istediğiniz sütunların bir listeniz varsa ve bu maddede dplyrkullandığım bir zincirde yapmak istiyorsanız :one_of()select

İşte basit, tekrarlanabilir bir örnek:

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

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

Belgeleri çalıştırarak ?one_ofveya buradan bulabilirsiniz:

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


22

İlginin dışında, bu R'nin tuhaf çoklu sözdizimi tutarsızlıklarından birini işaretliyor. Örneğin, iki sütunlu bir veri çerçevesi verildiğinde:

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

Bu bir veri çerçevesi sağlar

subset(df, select=-y)

ama bu bir vektör veriyor

df[,-2]

Bunların hepsi açıklanmaktadır ?[ancak tam olarak beklenen davranış değildir. En azından benim için değil ...


18

İşte bunun için bir dplyryol:

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

Bunu beğendim, çünkü açıklama olmadan okumak ve anlamak sezgiseldir ve veri çerçevesi içindeki konumu değiştiren sütunlara dayanıklıdır. Ayrıca -elemanları çıkarmak için kullanılan vektörlenmiş deyimi takip eder .


Buna (1) kullanıcının orijinal df'yi (2) değiştirmek istediğini ekleyerek, %<>% basitleştirilebilecek giriş nesnesini değiştirmek için operatöre sahiptirdf %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)
Marek

1
dplyrdf.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n))
Bırakılacak

14

Daha iyi bir deyim olması gerektiğini düşünmeye devam ediyorum, ancak sütunları ada göre çıkarmak için aşağıdakileri yapmaya eğilimliyim:

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
Maçı reddetmek için iyi bir fikir değil -df[,-match(c("e","f"),names(df))]
Hadley

. @ JDLong - Sütunu sütun adının başladığı yere bırakmak istersem ne olur -?
Chetan Arvind Patil

12

dropNamed()Bernd Bischl'in BBmiscpaketinde tam olarak bunu yapan bir işlev var .

BBmisc::dropNamed(df, "x")

Avantajı, veri çerçevesi argümanını tekrarlamaktan kaçınması ve bu nedenle magrittr(tıpkı dplyryaklaşımlar gibi ) borular için uygun olmasıdır :

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

9

@ Hadley'nin yukarısında kullanmak istemiyorsanız başka bir çözüm: "COLUMN_NAME", bırakmak istediğiniz sütunun adı ise:

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

1
(1) Sorun aynı anda birden fazla sütunu bırakmaktır. (2) Bu takdirde çalışmaz COLUMN_NAMEdeğil df(Kendini kontrol edin: df<-data.frame(a=1,b=2)). (3) df[,names(df) != "COLUMN_NAME"]daha basit ve (2) 'den muzdarip değil
Marek

Bu cevap hakkında biraz daha bilgi verebilir misiniz?
Akash Nayak

8

select(-one_of(drop_col_names))Daha önceki yanıtlarda gösterilmenin ötesinde , tüm belirli sütun adlarını tanımlamayı içermeyen dplyrsütunları bırakmak için birkaç seçenek daha vardır select()(sütun adlarında bazı çeşitler için dplyr starwars örnek verilerini kullanarak):

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 

Eğer veya veri çerçevesi mevcut olmayabilir bir sütun bırakın gerekiyorsa, burada kullanarak hafif bir büküm select_if()kullanarak aksine o one_of()bir atmaz Unknown columns:sütun adı yaparsa uyarı mevcut değil. Bu örnekte 'bad_column' veri çerçevesinde bir sütun değildir:

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

4

Kaldırılacak veri çerçevesini ve virgülle ayrılmış ad dizesini sağlayın :

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

Kullanımı :

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

resim açıklamasını buraya girin


1

Kullanarak bırakmak istediğiniz sütunların dizinini bulun which. Bu dizinlere negatif işaret ( *-1) verin. Daha sonra bu değerleri veri çerçevesinden kaldıracak olan alt küme. Bu bir örnektir.

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

1

Büyük bir varsa data.frameve hafıza kullanımı hakkında düşüktür [ . . . . ya da rmvewithin için bir sütun kaldırmakdata.frame olarak, subsetel ipucu için yanında - şu anda daha fazla bellek (R 3.6.2) olan kullanımı subsetetkileşimli .

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.