Veri çerçevesindeki sütunları ada göre bırakma


304

Büyük bir veri setim var ve belirli sütunları okumak veya diğerlerini bırakmak istiyorum.

data <- read.dta("file.dta")

İlgilenmediğim sütunları seçiyorum:

var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]

ve böyle bir şey yapmak istiyorum:

for(i in 1:length(var.out)) {
   paste("data$", var.out[i], sep="") <- NULL
}

tüm istenmeyen sütunları bırakmak için. Bu en uygun çözüm müdür?


1
sorun üzerinde uyku subset(data, select=c(...)), benim durumumda vars bırakarak yardımcı olduğunu düşünüyordum . bununla birlikte, soru esas paste("data$",var.out[i],sep="")olarak döngü içindeki ilgili sütunlara erişme kısmıydı. nasıl bir sütun adı yapıştırabilir veya oluşturabilirim? İlginiz ve yardımınız için herkese teşekkürler
leroux

Yanıtlar:


380

İndekslemeyi veya subsetişlevi kullanmalısınız. Örneğin :

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8

Ardından , sütun dizinlemesinde whichişlevi ve -işleci kullanabilirsiniz :

R> df[ , -which(names(df) %in% c("z","u"))]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

Ya da, çok daha basit bir şekilde, fonksiyonun selectargümanını kullanın subset: daha sonra -operatörü doğrudan bir sütun isimleri vektörü üzerinde kullanabilirsiniz ve hatta isimlerin etrafındaki tırnak işaretlerini atlayabilirsiniz!

R> subset(df, select=-c(z,u))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

Diğerlerini bırakmak yerine istediğiniz sütunları da seçebileceğinizi unutmayın:

R> df[ , c("x","y")]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

R> subset(df, select=c(x,y))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

2
selectargümanı subsetfonksiyonu mükemmel bir iş yaptı! Teşekkürler juba!
leroux

2
whichgerekli değildir, bkz. İsta'nın cevabı. Ama ile alt küme -güzel! Bunu bilmiyordum!
TMS

5
subsetiyi görünüyor, ama eksik değerleri sessizce düşürme şekli benim için oldukça tehlikeli görünüyor.
static_rtti

2
subsetgerçekten çok kullanışlı, ancak R'yi etkileşimli olarak kullanmadığınız sürece kullanmaktan kaçınmayı unutmayın. Daha fazla bilgi için işlevin belgelerindeki Uyarı'ya ve bu SO sorusuna bakın.
Waldir Leoncio

4
"Hatta isimleri tırnak içine alabilirsiniz!", aslında tırnak atlamak gerekir, aksi takdirde tekli operatöre geçersiz argüman alırsınız. Adlarınızda belirli karakterler varsa (örneğin "-") bu yöntemi kullanamazsınız, çünkü tırnak işareti bırakmak R'nin kodunuzu düzgün şekilde ayrıştırmamasına neden olur.
oh54

122

Bunun için kullanmayın -which(), son derece tehlikelidir. Düşünmek:

dat <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
dat[ , -which(names(dat) %in% c("z","u"))] ## works as expected
dat[ , -which(names(dat) %in% c("foo","bar"))] ## deletes all columns! Probably not what you wanted...

Bunun yerine altkümeyi veya !işlevi kullanın :

dat[ , !names(dat) %in% c("z","u")] ## works as expected
dat[ , !names(dat) %in% c("foo","bar")] ## returns the un-altered data.frame. Probably what you want

Bunu acı dolu bir deneyimle öğrendim. Aşırı kullanmayın which()!


31
setdiffayrıca yararlıdır:setdiff(names(dat), c("foo", "bar"))
hadley

setdiff@Hadley önerisi isimlerin uzun listeleri için çok iyidir.
JASC

48

İlk olarak , aynı veri çerçevesiyle çalışıyorsanız sütun adlarına yeniden erişmek yerine doğrudan dizin oluşturmayı (booleans vektörleriyle) kullanabilirsiniz; Ista'nın işaret ettiği gibi daha güvenli olacak ve yazmak ve yürütmek daha hızlı olacaktır. Tek ihtiyacınız olan şey:

var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")

ve daha sonra, verileri yeniden atamanız yeterlidir:

data <- data[,var.out.bool] # or...
data <- data[,var.out.bool, drop = FALSE] # You will need this option to avoid the conversion to an atomic vector if there is only one column left

İkincisi , yazmak daha hızlı, kaldırmak istediğiniz sütunlara doğrudan NULL atayabilirsiniz:

data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.

Son olarak , subset () kullanabilirsiniz, ancak kodda gerçekten kullanılamaz (yardım dosyası bile uyarır). Özellikle, benim için bir sorun, susbset () 'in drop özelliğini doğrudan kullanmak istiyorsanız, sütun adlarına karşılık gelen ifadeyi tırnak işaretleri olmadan yazmanız gerektiğidir:

subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL

Bir bonus olarak , alt seçeneklerin daha yavaş olduğunu ve ilk yeniden atama yönteminin daha hızlı olduğunu açıkça gösteren farklı seçeneklerin küçük bir ölçütü:

                                        re_assign(dtest, drop_vec)  46.719  52.5655  54.6460  59.0400  1347.331
                                      null_assign(dtest, drop_vec)  74.593  83.0585  86.2025  94.0035  1476.150
               subset(dtest, select = !names(dtest) %in% drop_vec) 106.280 115.4810 120.3435 131.4665 65133.780
 subset(dtest, select = names(dtest)[!names(dtest) %in% drop_vec]) 108.611 119.4830 124.0865 135.4270  1599.577
                                  subset(dtest, select = -c(x, y)) 102.026 111.2680 115.7035 126.2320  1484.174

Mikrobenç grafiği

Kod aşağıdadır:

dtest <- data.frame(x=1:5, y=2:6, z = 3:7)
drop_vec <- c("x", "y")

null_assign <- function(df, names) {
  df[names] <- list(NULL)
  df
}

re_assign <- function(df, drop) {
  df <- df [, ! names(df) %in% drop, drop = FALSE]
  df
}

res <- microbenchmark(
  re_assign(dtest,drop_vec),
  null_assign(dtest,drop_vec),
  subset(dtest, select = ! names(dtest) %in% drop_vec),
  subset(dtest, select = names(dtest)[! names(dtest) %in% drop_vec]),
  subset(dtest, select = -c(x, y) ),
times=5000)

plt <- ggplot2::qplot(y=time, data=res[res$time < 1000000,], colour=expr)
plt <- plt + ggplot2::scale_y_log10() + 
  ggplot2::labs(colour = "expression") + 
  ggplot2::scale_color_discrete(labels = c("re_assign", "null_assign", "subset_bool", "subset_names", "subset_drop")) +
  ggplot2::theme_bw(base_size=16)
print(plt)

2
İkinci alternatifinizi kullanmayı seviyorum NULL, ama neden ikiden fazla isim koyduğunuzda onu atamak için neden gereklidir list(NULL)? Sadece nasıl çalıştığını merak ediyorum, çünkü sadece bir isim ile denedim ve ihtiyacım yoklist()
Darwin PC

3
@DarwinPC Evet. Doğrudan bir vektör öğesine ( $veya ile [[) erişirseniz , kullanmak <- list(NULL)aslında yanlış sonuçlara yol açar. Veri çerçevesinin bir alt kümesine bir veya birden çok sütunla erişirseniz, tek <- list(NULL)bir sütun veri çerçevesi için gerekli olmasa bile gitmenin yolu budur (çünkü df['myColumns']gerekirse bir vektöre dökülür ).
Antoine Lizée

27

dplyrPaketi de deneyebilirsiniz :

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8
R> library(dplyr)
R> dplyr::select(df2, -c(x, y))  # remove columns x and y
  z u
1 3 4
2 4 5
3 5 6
4 6 7
5 7 8

4
Kullanılması dplyr::select(df2, -one_of(c('x','y')))adlı sütunların bazıları mevcut olmasa bile hala (bir uyarı ile) çalışmaları olacak
divibisan

13

İşte bunun için hızlı bir çözüm. Diyelim ki üç sütun A, B ve C olan bir veri kareniz X var:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6))
> X
  A B C
1 1 3 5
2 2 4 6

B gibi bir sütunu kaldırmak istersem, sütun dizini almak için daha sonra sütunu atlamak için kullanabileceğiniz colnames üzerinde grep kullanın.

> X<-X[,-grep("B",colnames(X))]

Yeni X veri çerçeveniz aşağıdaki gibi görünecektir (bu sefer B sütunu olmadan):

> X
  A C
1 1 5
2 2 6

Grep'in güzelliği, normal ifadeyle eşleşen birden çok sütun belirtebilmenizdir. Beş sütunlu (A, B, C, D, E) X varsa:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10

B ve D sütunlarını çıkarın:

> X<-X[,-grep("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

EDIT: Matthew Lundberg'in grepl önerisini göz önünde bulundurarak:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10
> X<-X[,!grepl("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

Var olmayan bir sütunu düşürmeye çalışırsam hiçbir şey olmamalı:

> X<-X[,!grepl("G",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

3
X[,-grep("B",colnames(X))]Btüm sütunları istenildiği gibi döndürmek yerine sütun adının bulunmadığı durumlarda sütun döndürmez. X <- irisBir örnek düşünün . Bu, hesaplanan değerlerle negatif indekslerin kullanılması sorunudur. greplBunun yerine düşünün .
Matthew Lundberg

6

Paketi kullanırken bir sütunu silmeye çalıştım data.tableve beklenmedik bir sonuç aldım. Sanırım aşağıdakiler göndermeye değer olabilir. Sadece küçük bir uyarıcı not.

[Matthew tarafından düzenlendi ...]

DF = read.table(text = "
     fruit state grade y1980 y1990 y2000
     apples Ohio   aa    500   100   55
     apples Ohio   bb      0     0   44
     apples Ohio   cc    700     0   33
     apples Ohio   dd    300    50   66
", sep = "", header = TRUE, stringsAsFactors = FALSE)

DF[ , !names(DF) %in% c("grade")]   # all columns other than 'grade'
   fruit state y1980 y1990 y2000
1 apples  Ohio   500   100    55
2 apples  Ohio     0     0    44
3 apples  Ohio   700     0    33
4 apples  Ohio   300    50    66

library('data.table')
DT = as.data.table(DF)

DT[ , !names(dat4) %in% c("grade")]    # not expected !! not the same as DF !!
[1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE

DT[ , !names(DT) %in% c("grade"), with=FALSE]    # that's better
    fruit state y1980 y1990 y2000
1: apples  Ohio   500   100    55
2: apples  Ohio     0     0    44
3: apples  Ohio   700     0    33
4: apples  Ohio   300    50    66

Temel olarak, sözdizimi data.tabletam olarak aynı DEĞİLDİR data.frame. Aslında birçok fark vardır, bkz. SSS 1.1 ve SSS 2.17. Uyarıldın!


1
Veya bunu DT[,var.out := NULL]yapmak istediğiniz sütunları silmek için kullanabilirsiniz .
mnel

Alt kümesi (x, seçmek = ...) yöntemi hem de eserleri data.frameve data.tablesınıflar
momeara

3

Kodu şu şekilde değiştirdim:

# read data
dat<-read.dta("file.dta")

# vars to delete
var.in<-c("iden", "name", "x_serv", "m_serv")

# what I'm keeping
var.out<-setdiff(names(dat),var.in)

# keep only the ones I want       
dat <- dat[var.out]

Her neyse, juba'nın cevabı sorunum için en iyi çözüm!


Bunu neden bir döngüde yapmak istiyorsunuz? Cevaplar juba'nın cevabı size bunu tek adımda nasıl yapacağınızı gösterir. Neden daha karmaşık hale getirelim?
Ista

tabii ki benim kodunda fonksiyon selectargümanını kullanın subset. i sadece sütun bırakarak daha başka bir şey yapmak istedim durumunda nasıl bir döngüde rasgele sütunlar erişebildiğini görmek istedim. orijinal veri seti yaklaşık 1200 vars ve sadece tam olarak nerede olduklarını bilmeden 4 tanesi kullanmakla ilgileniyorum.
leroux

2

İşte başkalarına yardımcı olabilecek başka bir çözüm. Aşağıdaki kod, büyük bir veri kümesinden az sayıda satır ve sütun seçer. Sütunları, sırayla numaralandırılmış adları olan bir sütun kümesi seçmek için bir Yapıştır işlevini kullanmak dışında juba yanıtlarından birinde olduğu gibi seçilir:

df = read.table(text = "

state county city  region  mmatrix  X1 X2 X3    A1     A2     A3      B1     B2     B3      C1      C2      C3

  1      1     1      1     111010   1  0  0     2     20    200       4      8     12      NA      NA      NA
  1      2     1      1     111010   1  0  0     4     NA    400       5      9     NA      NA      NA      NA
  1      1     2      1     111010   1  0  0     6     60     NA      NA     10     14      NA      NA      NA
  1      2     2      1     111010   1  0  0    NA     80    800       7     11     15      NA      NA      NA

  1      1     3      2     111010   0  1  0     1      2      1       2      2      2      10      20      30
  1      2     3      2     111010   0  1  0     2     NA      1       2      2     NA      40      50      NA
  1      1     4      2     111010   0  1  0     1      1     NA      NA      2      2      70      80      90
  1      2     4      2     111010   0  1  0    NA      2      1       2      2     10     100     110     120

  1      1     1      3     010010   0  0  1    10     20     10     200    200    200       1       2       3
  1      2     1      3     001000   0  0  1    20     NA     10     200    200    200       4       5       9
  1      1     2      3     101000   0  0  1    10     10     NA     200    200    200       7       8      NA
  1      2     2      3     011010   0  0  1    NA     20     10     200    200    200      10      11      12

", sep = "", header = TRUE, stringsAsFactors = FALSE)
df

df2 <- df[df$region == 2, names(df) %in% c(paste("C", seq_along(1:3), sep=''))]
df2

#    C1  C2  C3
# 5  10  20  30
# 6  40  50  NA
# 7  70  80  90
# 8 100 110 120


-1

Düşük itibar puanı nedeniyle sorunuza yorumunuzu cevaplayamıyorum.

Yapıştırma işlevi bir karakter dizesi döndürdüğü için sonraki kod size bir hata verecektir

for(i in 1:length(var.out)) {
   paste("data$", var.out[i], sep="") <- NULL
}

İşte olası bir çözüm:

for(i in 1:length(var.out)) {

  text_to_source <- paste0 ("data$", var.out[i], "<- NULL") # Write a line of your
                                                  # code like a character string
  eval (parse (text=text_to_source)) # Source a text that contains a code
}

ya da sadece yap:

for(i in 1:length(var.out)) {
  data[var.out[i]] <- NULL
}

-1
df = mtcars 
kaldır vs ve am kategorik oldukları için. Veri kümesinde vs sütun 8'de, am sütun 9'da

dfnum = df[,-c(8,9)]

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.