데이터 프레임에서 이름별로 열을 삭제하는 방법


304

큰 데이터 세트가 있으며 특정 열을 읽거나 다른 열을 모두 삭제하고 싶습니다.

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

관심이없는 열을 선택합니다.

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

그리고 내가하고 싶은 것보다 :

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

원하지 않는 모든 열을 삭제합니다. 이것이 최적의 솔루션입니까?


1
문제를 자고 나는 subset(data, select=c(...))내 경우에 vars를 떨어 뜨릴 수 있다고 생각했습니다 . 그러나 문제는 주로 paste("data$",var.out[i],sep="")루프 내부에서 관심있는 열에 액세스 하는 부분에 관한 것 입니다. 열 이름을 붙여 넣거나 어떻게 작성합니까? 관심과 도움을 주신 모든 분들께 감사드립니다
leroux

답변:


380

인덱싱 또는 subset함수를 사용해야합니다 . 예를 들면 다음과 같습니다.

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

그런 다음 열 인덱싱에서 which함수와 -연산자를 사용할 수 있습니다 .

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

또는 훨씬 더 간단하게 함수 의 select인수 subset를 사용하십시오. 그런 다음 -열 이름의 벡터에서 직접 연산자 를 사용할 수 있으며 이름 주위에 따옴표를 생략 할 수도 있습니다!

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

다른 열을 삭제하지 않고 원하는 열을 선택할 수도 있습니다.

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
함수 의 select주장은 subset완벽하게 작동했습니다! 주바 감사합니다!
leroux

2
which필요하지 않다, Ista의 답변을 참조하십시오. 그러나 부분 집합 -은 훌륭합니다! 몰랐어!
TMS

5
subset좋아 보이지만 누락 된 값을 자동으로 삭제하는 방식은 꽤 위험합니다.
static_rtti 2016 년

2
subset실제로 매우 편리하지만 대화 형으로 R을 사용하지 않는 한 사용하지 않는 것이 좋습니다. 자세한 내용 은 함수 설명서의 경고이 SO 질문 을 참조하십시오.
Waldir Leoncio

4
"이름 주위에 따옴표를 생략 할 수도 있습니다!", 실제로 따옴표를 생략해야합니다. 그렇지 않으면 단항 연산자에 잘못된 인수가 표시됩니다. 이름에 특정 문자 (예 : "-")가있는 경우 따옴표를 삭제하면 R이 코드를 올바르게 구문 분석 할 수 없으므로이 방법을 전혀 사용할 수 없습니다.
oh54

122

이것을 사용하지 마십시오 -which(). 매우 위험합니다. 치다:

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

대신 부분 집합 또는 !함수를 사용하십시오.

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

나는 고통스러운 경험에서 이것을 배웠다. 남용하지 마십시오 which()!


31
setdiff유용하다 :setdiff(names(dat), c("foo", "bar"))
hadley

setdiff@hadley 의 제안은 긴 이름 목록에 매우 좋습니다.
JASC

48

먼저 동일한 데이터 프레임으로 작업하는 경우 열 이름에 다시 액세스하는 대신 부울 벡터를 사용하여 직접 인덱싱을 사용할 수 있습니다. Ista가 지적한대로 더 안전하고 작성하고 실행하는 것이 더 빠릅니다. 따라서 필요한 것은 다음과 같습니다.

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

그런 다음 간단히 데이터를 다시 할당하십시오.

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

둘째 , 더 빨리 작성하려면 제거하려는 열에 NULL을 직접 지정할 수 있습니다.

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

마지막으로 서브셋 ()을 사용할 수 있지만 실제로는 코드에서 사용할 수 없습니다 (도움말 파일에서도 경고). 특히 나에게 문제는 susbset ()의 드롭 기능을 직접 사용하려면 열 이름에 해당하는 표현식을 따옴표없이 작성해야한다는 것입니다.

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

보너스로 , 여기에 다른 옵션의 작은 벤치 마크가 있는데, 이는 하위 집합이 느리고 첫 번째 재 할당 방법이 더 빠름을 분명히 보여줍니다.

                                        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

마이크로 벤치 그래프

코드 는 다음과 같습니다.

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
나는 두 번째 대안을 사용하는 것을 좋아 NULL하지만 왜 두 개 이상의 이름을 지정할 때 이름을 할당해야 list(NULL)합니까? 나는 오직 하나의 이름으로 시도 때문에, 그것이 어떻게 작동하는지 알고 만 호기심 그리고 난 필요가 없습니다list()
다윈 PC

3
@DarwinPC 예. 당신은 (직접 한 벡터 요소에 액세스하는 경우 $또는 [[)을 사용하는 것은 <- list(NULL)실제로 잘못된 결과로 이어질 것입니다. 하나 또는 여러 개의 열이있는 데이터 프레임의 하위 집합에 액세스하는 <- list(NULL)경우 하나의 열 데이터 프레임에 필요하지 않더라도 이동할 수있는 방법입니다 ( df['myColumns']필요한 경우 벡터로 캐스팅 되기 때문에 ).
Antoine Lizée

27

dplyr패키지를 시험해 볼 수도 있습니다 .

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
dplyr::select(df2, -one_of(c('x','y')))명명 된 열 중 일부가 존재하지 않더라도 사용 은 여전히 ​​작동합니다 (경고와 함께)
divibisan

13

여기에 빠른 해결책이 있습니다. 예를 들어 열 A, B 및 C가 3 개인 데이터 프레임 X가 있다고 가정합니다.

> 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와 같은 열을 제거하려면 colnames에 grep을 사용하여 열 인덱스를 얻은 다음 열을 생략하는 데 사용할 수 있습니다.

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

새 X 데이터 프레임은 다음과 같습니다 (이번에는 B 열이 없음).

> X
  A C
1 1 5
2 2 6

grep의 장점은 정규식과 일치하는 여러 열을 지정할 수 있다는 것입니다. 열이 5 개인 X가있는 경우 (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

열 B와 D를 꺼내십시오.

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

편집 : 아래 의견에서 Matthew Lundberg의 grepl 제안을 고려하십시오.

> 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

존재하지 않는 열을 삭제하려고하면 아무 일도 일어나지 않아야합니다.

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

3
X[,-grep("B",colnames(X))]B원하는대로 모든 열을 반환하는 대신 열 이름이 포함되지 않은 경우 열을 반환하지 않습니다 . 함께 고려 X <- iris에 대한 예. 이것은 계산 된 값과 함께 음의 인덱스를 사용하는 데 문제가 있습니다. grepl대신 고려하십시오 .
Matthew Lundberg

6

패키지를 사용하는 동안 열을 삭제하려고했습니다. data.table 예기치 않은 결과가 발생했습니다. 다음 내용을 게시 할 가치가 있다고 생각합니다. 약간의주의 사항.

[매튜에 의해 편집 ...]

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

기본적으로의 구문 data.table은와 정확히 동일하지 않습니다 data.frame. 실제로 많은 차이점이 있습니다 (FAQ 1.1 및 FAQ 2.17 참조). 경고를 받았습니다!


1
또는 DT[,var.out := NULL]원하는 열을 삭제하는 데 사용할 수 있습니다 .
mnel

서브셋 (x, select = ...) 메소드는 클래스 data.framedata.table클래스 모두에서 작동합니다.
momeara

3

코드를 다음과 같이 변경했습니다.

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

어쨌든 juba의 대답은 내 문제에 대한 최고의 해결책입니다!


왜 루프에서 이것을하고 싶습니까? juba의 답변은 한 단계로 수행하는 방법을 보여줍니다. 왜 더 복잡하게 만드나요?
Ista

물론 나는 select 주장을subset 내 코드 함수 . 방금 열을 삭제하는 것 이외의 작업을 수행하려는 경우 루프에서 임의의 열에 액세스하는 방법을 알고 싶었습니다. 원래 데이터 세트에는 약 1200 가지가 있으며 정확히 어디에 있는지 모르고 4 개만 사용하는 데 관심이 있습니다.
leroux

2

다른 사람들에게 도움이 될 수있는 또 다른 해결책이 있습니다. 아래 코드는 큰 데이터 세트에서 적은 수의 행과 열을 선택합니다. 붙여 넣기 기능을 사용하여 순차적으로 번호가 지정된 이름의 열 집합을 선택한다는 점을 제외하고 juba의 답변 중 하나에서 열이 선택됩니다.

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

평판 점수가 낮아서 의견에 귀하의 질문에 답변을 드릴 수 없습니다.

다음 코드는 paste 함수가 문자열을 반환하기 때문에 오류를 발생시킵니다

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

가능한 해결책은 다음과 같습니다.

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
}

또는 그냥하십시오 :

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

-1
df = mtcars 
vs와 am은 범주 형이므로 제거합니다. 데이터 집합 대 열 번호 8에 있고 am은 열 번호 9에 있습니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.