Cómo soltar columnas por nombre en un marco de datos

Resuelto leroux asked hace 54 años • 12 respuestas

Tengo un gran conjunto de datos y me gustaría leer columnas específicas o eliminar 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 luego me gustaría hacer algo como:

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

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

leroux avatar Jan 01 '70 08:01 leroux
Aceptado

Debe utilizar la indexación o la subsetfunción. Por ejemplo :

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

Luego puedes usar la whichfunción y el -operador en la indexación de columnas:

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

O, mucho más simple, use el selectargumento de la subsetfunción: luego puede usar el -operador directamente en un vector de nombres de columnas, ¡e incluso puede omitir las comillas alrededor de los nombres!

R> subset(df, select=-c(z,u))
  x y
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 desee en lugar de soltar las demás:

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
juba avatar Mar 08 '2011 15:03 juba

No lo utilices -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 su lugar utilice subconjunto o la !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

Esto lo he aprendido de experiencias dolorosas. ¡No abuses which()!

Ista avatar Mar 08 '2011 18:03 Ista

En primer lugar , puede utilizar la indexación directa (con vectores booleanos) en lugar de volver a acceder a los nombres de las columnas si está trabajando con el mismo marco de datos; Será más seguro, como señaló Ista, y más rápido de escribir y ejecutar. Entonces lo único que necesitarás es:

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

y luego, simplemente reasigne los 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 , puedes usar subset(), pero en realidad no se puede usar en el código (incluso el archivo de ayuda advierte al respecto). 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 primer 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 microbanco

El código está a continuación:

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)
Antoine Lizée avatar Jul 22 '2013 20:07 Antoine Lizée

También puedes probar el dplyrpaquete:

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
Megatron avatar Jun 12 '2015 18:06 Megatron