Cómo soltar columnas por nombre en un marco de datos
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?
Debe utilizar la indexación o la subset
funció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 which
funció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 select
argumento de la subset
funció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
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()
!
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
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)
También puedes probar el dplyr
paquete:
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