Cách thả cột theo tên trong khung dữ liệu


304

Tôi có một bộ dữ liệu lớn và tôi muốn đọc các cột cụ thể hoặc bỏ tất cả các cột khác.

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

Tôi chọn các cột mà tôi không quan tâm:

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

và hơn tôi muốn làm một cái gì đó như:

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

để thả tất cả các cột không mong muốn. Đây có phải là giải pháp tối ưu?


1
Ngủ qua vấn đề, tôi đã nghĩ rằng subset(data, select=c(...))sẽ giúp trong trường hợp của tôi để thả bình. câu hỏi mặc dù chủ yếu là về paste("data$",var.out[i],sep="")phần để truy cập các cột quan tâm trong vòng lặp. Làm thế nào tôi có thể dán hoặc bằng cách nào đó soạn một tên cột? Cảm ơn tất cả mọi người vì sự quan tâm và giúp đỡ của bạn
leroux

7
Có thể trùng lặp các cột Thả trong khung dữ liệu R
jangorecki

Câu trả lời:


380

Bạn nên sử dụng lập chỉ mục hoặc subsetchức năng. Ví dụ :

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

Sau đó, bạn có thể sử dụng whichhàm và -toán tử trong chỉ mục cột:

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

Hoặc, đơn giản hơn nhiều, sử dụng selectđối số của subsethàm: sau đó bạn có thể sử dụng -toán tử trực tiếp trên một vectơ tên cột và thậm chí bạn có thể bỏ qua các trích dẫn xung quanh tên!

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

Lưu ý rằng bạn cũng có thể chọn các cột bạn muốn thay vì bỏ các cột khác:

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
các selectđối số của các subsetchức năng đã làm việc một cách hoàn hảo! Cảm ơn bạn juba!
leroux

2
whichkhông cần thiết, xem câu trả lời của Ista. Nhưng tập hợp con -là tốt! Không biết điều đó!
TMS

5
subsetCó vẻ tốt, nhưng cách nó âm thầm giảm giá trị thiếu có vẻ khá nguy hiểm đối với tôi.
static_rtti

2
subsetthực sự rất tiện lợi, nhưng hãy nhớ tránh sử dụng trừ khi bạn sử dụng R tương tác. Xem Cảnh báo trong tài liệu của hàmcâu hỏi SO này để biết thêm.
Waldir Leoncio

4
"Bạn thậm chí có thể bỏ qua các trích dẫn xung quanh tên!", bạn thực sự phải bỏ qua các trích dẫn, nếu không bạn sẽ nhận được đối số không hợp lệ cho toán tử đơn nguyên. Nếu bạn có một số ký tự nhất định (ví dụ "-") trong tên của bạn, bạn hoàn toàn không thể sử dụng phương thức này vì việc bỏ dấu ngoặc kép sẽ khiến R không thể phân tích cú pháp mã của bạn một cách chính xác.
oh54

122

Không sử dụng -which()cho việc này, nó cực kỳ nguy hiểm. Xem xét:

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

Thay vào đó sử dụng tập hợp con hoặc !hàm:

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

Tôi đã học được điều này từ kinh nghiệm đau đớn. Đừng lạm dụng which()!


31
setdiffcũng hữu ích:setdiff(names(dat), c("foo", "bar"))
hadley

Các setdiffđề nghị của @hadley là rất tốt cho các danh sách dài các tên.
JASC

48

Đầu tiên , bạn có thể sử dụng lập chỉ mục trực tiếp (với vectơ booleans) thay vì truy cập lại tên cột nếu bạn đang làm việc với cùng một khung dữ liệu; Nó sẽ an toàn hơn như được chỉ ra bởi Ista, và nhanh hơn để viết và thực hiện. Vì vậy, những gì bạn sẽ chỉ cần là:

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

và sau đó, chỉ cần gán lại dữ liệu:

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

Thứ hai , viết nhanh hơn, bạn có thể gán trực tiếp NULL cho các cột bạn muốn xóa:

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

Cuối cùng , bạn có thể sử dụng tập hợp con (), nhưng nó thực sự không thể được sử dụng trong mã (ngay cả tệp trợ giúp cũng cảnh báo về nó). Cụ thể, một vấn đề với tôi là nếu bạn muốn sử dụng trực tiếp tính năng thả xuống của susbset () bạn cần viết mà không trích dẫn biểu thức tương ứng với tên cột:

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

Như một phần thưởng , đây là điểm chuẩn nhỏ của các tùy chọn khác nhau, điều đó cho thấy rõ rằng tập hợp con chậm hơn và phương pháp gán lại đầu tiên là nhanh hơn:

                                        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

Đồ thị vi sinh

dưới đây:

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
Tôi thích cách thay thế thứ hai của bạn bằng cách sử dụng NULL, nhưng tại sao khi bạn đặt nhiều hơn hai tên lại cần thiết để gán nó list(NULL)? Tôi chỉ tò mò muốn biết nó hoạt động như thế nào, vì tôi đã thử chỉ với một cái tên và tôi không cầnlist()
Darwin PC

3
@DarwinPC Có. Nếu bạn truy cập trực tiếp một yếu tố vectơ (có $hoặc [[), việc sử dụng <- list(NULL)sẽ thực sự dẫn đến kết quả sai. Nếu bạn truy cập một tập hợp con của khung dữ liệu bằng một hoặc nhiều cột, đó <- list(NULL)là cách để đi, ngay cả khi không cần thiết cho một khung dữ liệu một cột (vì df['myColumns']sẽ được truyền vào một vectơ nếu cần).
Antoine Lizée

27

Bạn cũng có thể thử dplyrgói:

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
Việc sử dụng dplyr::select(df2, -one_of(c('x','y')))vẫn sẽ hoạt động (có cảnh báo) ngay cả khi một số cột được đặt tên không tồn tại
divibisan 23/03/18

13

Đây là một giải pháp nhanh chóng cho việc này. Giả sử, bạn có khung dữ liệu X với ba cột A, B và C:

> 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

Nếu tôi muốn xóa một cột, giả sử B, chỉ cần sử dụng grep trên colnames để lấy chỉ mục cột, sau đó bạn có thể sử dụng để bỏ qua cột.

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

Khung dữ liệu X mới của bạn sẽ trông như sau (lần này không có cột B):

> X
  A C
1 1 5
2 2 6

Cái hay của grep là bạn có thể chỉ định nhiều cột khớp với biểu thức chính quy. Nếu tôi có X với năm cột (A, B, C, D, E):

> 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

Lấy ra cột B và D:

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

EDIT: Xem xét đề nghị grepl của Matthew Lundberg trong các bình luận dưới đây:

> 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

Nếu tôi cố gắng thả một cột không tồn tại, sẽ không có gì xảy ra:

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

3
X[,-grep("B",colnames(X))]sẽ trả về không có cột nào trong trường hợp không có tên cột chứa B, thay vì trả về tất cả các cột như mong muốn. Xem xét với X <- irismột ví dụ. Đây là vấn đề với việc sử dụng các chỉ số âm với các giá trị được tính toán. Hãy xem xét greplthay thế.
Matthew Lundberg

6

Tôi đã cố gắng xóa một cột trong khi sử dụng gói data.table và nhận được một kết quả không mong muốn. Tôi nghĩ rằng những điều sau đây có thể đáng để đăng. Chỉ cần lưu ý một chút.

[Được chỉnh sửa bởi Matthew ...]

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

Về cơ bản, cú pháp cho data.tableKHÔNG hoàn toàn giống như data.frame. Thực tế có rất nhiều sự khác biệt, xem FAQ 1.1 và FAQ 2.17. Bạn đã được cảnh báo!


1
Hoặc bạn có thể sử dụng DT[,var.out := NULL]để xóa các cột bạn muốn làm như vậy.
mnel

Phương thức tập hợp con (x, select = ...) hoạt động cho cả hai data.framedata.tablecác lớp
momeara

3

Tôi đã thay đổi mã thành:

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

Dù sao, câu trả lời của juba là giải pháp tốt nhất cho vấn đề của tôi!


Tại sao bạn muốn làm điều này trong một vòng lặp? Các câu trả lời của juba chỉ cho bạn cách thực hiện trong một bước. Tại sao làm cho nó phức tạp hơn?
Ista

tất nhiên tôi sử dụng selectđối số của subsethàm trong mã của tôi. tôi chỉ muốn xem làm thế nào tôi có thể truy cập các cột tùy ý trong một vòng lặp trong trường hợp tôi muốn làm một cái gì đó khác hơn là chỉ thả cột. bộ dữ liệu gốc có khoảng 1200 vars và tôi chỉ quan tâm đến việc sử dụng 4 trong số chúng mà không biết chính xác chúng ở đâu.
leroux

2

Đây là một giải pháp khác có thể hữu ích cho người khác. Mã dưới đây chọn một số lượng nhỏ hàng và cột từ một tập dữ liệu lớn. Các cột được chọn như một trong những câu trả lời của juba ngoại trừ việc tôi sử dụng chức năng dán để chọn một tập hợp các cột có tên được đánh số liên tục:

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

Tôi có thể trả lời câu hỏi của bạn trong các ý kiến ​​do điểm danh tiếng thấp.

Mã tiếp theo sẽ báo lỗi cho bạn vì hàm dán trả về chuỗi ký tự

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

Đây là một giải pháp có thể:

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
}

hoặc chỉ làm:

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

-1
df = mtcars 
loại bỏ vs và am vì chúng là phân loại. Trong tập dữ liệu vs ở cột số 8, sáng nằm ở cột số 9

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.