Cómo colocar columnas por nombre en un dataframe

Tengo un gran conjunto de datos y me gustaría leer columnas específicas o soltar todas las demás.

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

Selecciono las columnas que no me interesan:

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

y de lo que me gustaría hacer algo como:

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

para soltar todas las columnas no deseadas. ¿Es esta la solución óptima?

Debería usar indización o la función de subset . Por ejemplo :

 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 

Luego puede usar la función which y el operador - en indexación de columna:

 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 

O, mucho más simple, use el argumento de select de la función subset : puede usar el operador - directamente en un vector de nombres de columna, ¡e incluso puede omitir las comillas alrededor de los nombres!

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

Tenga en cuenta que también puede seleccionar las columnas que desea en lugar de descartar las demás:

 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 

No use -which() para esto, es extremadamente peligroso. 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... 

En lugar de usar subconjunto o el ! funció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 

Lo aprendí de una experiencia dolorosa. No use en exceso which() ¡ which() !

En primer lugar , puede usar la indexación directa (con vectores booleanos) en lugar de volver a acceder a los nombres de columna si está trabajando con el mismo dataframe; será más seguro como señala Ista, y más rápido para escribir y ejecutar. Entonces, lo que solo necesitarás es:

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

y luego, simplemente reasignar datos:

 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 

En segundo lugar , más rápido de escribir, puede asignar directamente NULL a las columnas que desea eliminar:

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

Finalmente , puede usar el subconjunto (), pero realmente no se puede usar en el código (incluso el archivo de ayuda lo advierte). Específicamente, un problema para mí es que si quieres usar directamente la función de soltar de susbset () necesitas escribir sin comillas la expresión correspondiente a los nombres de las columnas:

 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 beneficio adicional , aquí hay un pequeño punto de referencia de las diferentes opciones, que muestra claramente que el subconjunto es el más lento, y que el primero, el método de reasignación es el más 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

El código está abajo:

 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) 

También puedes probar el paquete 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 

Aquí hay una solución rápida para esto. Diga, usted tiene un dataframe X con tres columnas A, B y 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 

Si quiero eliminar una columna, digamos B, simplemente use grep en colnames para obtener el índice de la columna, que luego puede usar para omitir la columna.

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

Su nuevo dataframe X se vería como el siguiente (esta vez sin la columna B):

 > X AC 1 1 5 2 2 6 

La belleza de grep es que puedes especificar múltiples columnas que coincidan con la expresión regular. Si tuviera X con cinco columnas (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 

Saca las columnas B y D:

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

EDIT: considerando la sugerencia grepl de Matthew Lundberg en los comentarios a continuación:

 > 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 

Si trato de eliminar una columna que no existe, no debería pasar nada:

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

Intenté eliminar una columna mientras usaba el paquete data.table y obtuve un resultado inesperado. Creo que lo siguiente vale la pena publicar. Solo una pequeña nota de advertencia.

[Editado por 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 

Básicamente, la syntax de data.table NO es exactamente lo mismo que data.frame . De hecho, hay muchas diferencias, consulte la Pregunta frecuente 1.1 y la Pregunta frecuente 2.17. ¡Usted ha sido advertido!

Aquí hay otra solución que puede ser útil para otros. El siguiente código selecciona una pequeña cantidad de filas y columnas de un gran conjunto de datos. Las columnas se seleccionan como en una de las respuestas de juba, excepto que utilizo una función de pegar para seleccionar un conjunto de columnas con nombres numerados secuencialmente:

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

Cambié el código a:

 # 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 todos modos, ¡la respuesta de Juba es la mejor solución para mi problema!

No puedo responder su pregunta en los comentarios debido a su bajo puntaje de reputación.

El siguiente código le dará un error porque la función de pegar devuelve una cadena de caracteres

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

Aquí hay una posible solución:

 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 } 

o simplemente hacer:

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