Como descartar colunas por nome em um quadro de dados

Eu tenho um grande dataset e gostaria de ler colunas específicas ou descartar todas as outras.

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

Eu seleciono as colunas nas quais não estou interessado:

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

e do que eu gostaria de fazer algo como:

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

para soltar todas as colunas indesejadas. Essa é a solução ideal?

    Você deve usar a indexação ou a function de subset . Por exemplo :

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

    Então você pode usar a function which e o operador - na indexação da coluna:

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

    Ou, muito mais simples, use o argumento select da function de subset : você pode então usar o operador - diretamente em um vetor de nomes de coluna, e você pode até mesmo omitir as aspas em torno dos nomes!

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

    Observe que você também pode selecionar as colunas desejadas em vez de soltar as outras:

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

    Não use -which() para isso, é extremamente perigoso. Considerar:

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

    Em vez disso, use subconjunto ou o ! function:

     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 

    Eu aprendi isso com uma experiência dolorosa. Não use excessivamente o which() !

    Primeiro , você pode usar a indexação direta (com vetores booleanos) em vez de acessar novamente os nomes das colunas se estiver trabalhando com o mesmo quadro de dados; será mais seguro, conforme apontado por Ista, e mais rápido para escrever e executar. Então, o que você só precisa é:

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

    e, em seguida, simplesmente reatribuir dados:

     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 

    Segundo , mais rápido para escrever, você pode atribuir diretamente NULL às colunas que deseja remover:

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

    Finalmente , você pode usar o subconjunto (), mas ele não pode ser realmente usado no código (até mesmo o arquivo de ajuda o avisa). Especificamente, um problema para mim é que, se você quiser usar diretamente o recurso drop de susbset (), é necessário escrever sem aspas a expressão correspondente aos nomes das colunas:

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

    Como um bônus , aqui está uma pequena referência das diferentes opções, que mostram claramente que o subconjunto é mais lento e que o primeiro método de reatribuição é mais rápido:

      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 

    Gráfico de Microbench

    Código está abaixo:

     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) 

    Você também pode tentar o pacote dplyr :

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

    Aqui está uma solução rápida para isso. Digamos que você tenha um data frame X com três colunas A, B e C:

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

    Se eu quiser remover uma coluna, digamos B, apenas use grep em colnames para obter o índice da coluna, que você pode usar para omitir a coluna.

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

    Seu novo quadro de dados X seria semelhante ao seguinte (desta vez sem a coluna B):

     > X AC 1 1 5 2 2 6 

    A beleza do grep é que você pode especificar várias colunas que correspondem à expressão regular. Se eu tivesse X com cinco colunas (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 ABCDE 1 1 3 5 7 9 2 2 4 6 8 10 

    Retire as colunas B e D:

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

    EDIT: Considerando a sugestão grepl de Matthew Lundberg nos comentários abaixo:

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

    Se eu tentar soltar uma coluna inexistente, nada deve acontecer:

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

    Eu tentei apagar uma coluna enquanto usava o pacote data.table e obtive um resultado inesperado. Eu meio que acho que vale a pena postar o seguinte. Apenas uma pequena nota de caucanvas.

    [Editado por Mateus …]

     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 

    Basicamente, a syntax de data.table NÃO é exatamente igual a data.frame . Existem muitas diferenças, veja FAQ 1.1 e FAQ 2.17. Você foi avisado!

    Aqui está outra solução que pode ser útil para os outros. O código abaixo seleciona um pequeno número de linhas e colunas de um grande dataset. As colunas são selecionadas como uma das respostas da juba, exceto pelo fato de eu usar uma function de colar para selecionar um conjunto de colunas com nomes numerados sequencialmente:

     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 
     df2 < - df[!names(df) %in% c("c1", "c2")] 

    Eu mudei o código para:

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

    De qualquer forma, a resposta da juba é a melhor solução para o meu problema!

    Não posso responder a sua pergunta nos comentários devido à baixa pontuação de reputação.

    O próximo código lhe dará um erro porque a function de colar retorna uma cadeia de caracteres

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

    Aqui está uma solução possível:

     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 } 

    ou apenas faça:

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