¿Cómo eliminar una fila por referencia en data.table?

Mi pregunta está relacionada con la asignación por referencia frente a la copia en data.table . Quiero saber si uno puede eliminar filas por referencia, similar a

 DT[ , someCol := NULL] 

Quiero saber sobre

 DT[someRow := NULL, ] 

Supongo que hay una buena razón para que esta función no exista, así que tal vez podrías señalar una buena alternativa al enfoque de copia habitual, como se muestra a continuación. En particular, yendo con mi favorito del ejemplo (data.table),

 DT = data.table(x = rep(c("a", "b", "c"), each = 3), y = c(1, 3, 6), v = 1:9) # xyv # [1,] a 1 1 # [2,] a 3 2 # [3,] a 6 3 # [4,] b 1 4 # [5,] b 3 5 # [6,] b 6 6 # [7,] c 1 7 # [8,] c 3 8 # [9,] c 6 9 

Supongamos que quiero eliminar la primera fila de esta tabla de datos. Sé que puedo hacerlo:

 DT <- DT[-1, ] 

pero a menudo podemos querer evitar eso, porque estamos copiando el objeto (y eso requiere alrededor de 3 * N de memoria, si N object.size(DT) , como se señala aquí . Ahora encontré set(DT, i, j, value) . Sé cómo establecer valores específicos (como aquí: establecer todos los valores en las filas 1 y 2 y las columnas 2 y 3 en cero)

 set(DT, 1:2, 2:3, 0) DT # xyv # [1,] a 0 0 # [2,] a 0 0 # [3,] a 6 3 # [4,] b 1 4 # [5,] b 3 5 # [6,] b 6 6 # [7,] c 1 7 # [8,] c 3 8 # [9,] c 6 9 

Pero, ¿cómo puedo borrar las dos primeras filas, por ejemplo? Obra

 set(DT, 1:2, 1:3, NULL) 

establece TODO DT en NULL.

Mi conocimiento SQL es muy limitado, así que me dicen: dado que data.table usa tecnología SQL, ¿existe un equivalente al comando SQL?

 DELETE FROM table_name WHERE some_column=some_value 

en data.table?

Buena pregunta. data.table no puede eliminar filas por referencia todavía.

data.table puede agregar y eliminar columnas por referencia, ya que data.table el vector de punteros de columna, como usted sabe. El plan es hacer algo similar para las filas y permitir insert y delete rápidamente. Una eliminación de fila usaría memmove en C para mover los elementos (en todas y cada una de las columnas) después de las filas eliminadas. Eliminar una fila en el medio de la tabla aún sería bastante ineficiente en comparación con una base de datos de la tienda de filas como SQL, que es más adecuada para la inserción rápida y la eliminación de filas dondequiera que esas filas estén en la tabla. Pero aún así, sería mucho más rápido que copiar un nuevo objeto grande sin las filas eliminadas.

Por otro lado, dado que los vectores de columna se asignarían en exceso, las filas podrían insertarse (y eliminarse) al final , al instante; por ejemplo, una serie de tiempo creciente.

El enfoque que he tomado para hacer que el uso de la memoria sea similar a la eliminación in situ es subconjunto de una columna a la vez y eliminar. no tan rápido como una solución apropiada de memmove C, pero el uso de la memoria es todo lo que me importa aquí. algo como esto:

 DT = data.table(col1 = 1:1e6) cols = paste0('col', 2:100) for (col in cols){ DT[, (col) := 1:1e6] } keep.idxs = sample(1e6, 9e5, FALSE) # keep 90% of entries DT.subset = data.table(col1 = DT[['col1']][keep.idxs]) # this is the subsetted table for (col in cols){ DT.subset[, (col) := DT[[col]][keep.idxs]] DT[, (col) := NULL] #delete } 

Aquí hay una función de trabajo basada en la respuesta de @ vc273 y la retroalimentación de @ Frank.

 delete <- function(DT, del.idxs) { # pls note 'del.idxs' vs. 'keep.idxs' keep.idxs <- setdiff(DT[, .I], del.idxs); # select row indexes to keep cols = names(DT); DT.subset <- data.table(DT[[1]][keep.idxs]); # this is the subsetted table setnames(DT.subset, cols[1]); for (col in cols[2:length(cols)]) { DT.subset[, (col) := DT[[col]][keep.idxs]]; DT[, (col) := NULL]; # delete } return(DT.subset); } 

Y un ejemplo de su uso:

 dat <- delete(dat,del.idxs) ## Pls note 'del.idxs' instead of 'keep.idxs' 

Donde "dat" es una tabla de datos. Eliminar 14k filas de 1.4M filas lleva 0.25 segundos en mi computadora portátil.

 > dim(dat) [1] 1419393 25 > system.time(dat <- delete(dat,del.idxs)) user system elapsed 0.23 0.02 0.25 > dim(dat) [1] 1404715 25 > 

PD. Como soy nuevo en SO, no pude agregar comentario al hilo de @ vc273 🙁

En lugar de intentar establecer NULL, intente configurar NA (que coincida con el tipo de NA para la primera columna)

 set(DT,1:2, 1:3 ,NA_character_) 

El tema sigue siendo interesante para muchas personas (yo incluido).

¿Qué hay de eso? Utilicé assign para reemplazar el código glovalenv y el descrito anteriormente. Sería mejor capturar el entorno original, pero al menos en globalenv es eficiente desde el punto de vista de la memoria y actúa como un cambio por ref.

 delete <- function(DT, del.idxs) { varname = deparse(substitute(DT)) keep.idxs <- setdiff(DT[, .I], del.idxs) cols = names(DT); DT.subset <- data.table(DT[[1]][keep.idxs]) setnames(DT.subset, cols[1]) for (col in cols[2:length(cols)]) { DT.subset[, (col) := DT[[col]][keep.idxs]] DT[, (col) := NULL]; # delete } assign(varname, DT.subset, envir = globalenv()) return(invisible()) } DT = data.table(x = rep(c("a", "b", "c"), each = 3), y = c(1, 3, 6), v = 1:9) delete(DT, 3) 

Aquí hay algunas estrategias que he usado. Creo que una función .ROW puede estar llegando. Ninguno de estos enfoques a continuación es rápido. Estas son algunas estrategias un poco más allá de los subconjuntos o el filtrado. Traté de pensar como dba tratando de limpiar los datos. Como se indicó anteriormente, puede seleccionar o eliminar filas en data.table:

 data(iris) iris <- data.table(iris) iris[3] # Select row three iris[-3] # Remove row three You can also use .SD to select or remove rows: iris[,.SD[3]] # Select row three iris[,.SD[3:6],by=,.(Species)] # Select row 3 - 6 for each Species iris[,.SD[-3]] # Remove row three iris[,.SD[-3:-6],by=,.(Species)] # Remove row 3 - 6 for each Species 

Nota: .SD crea un subconjunto de los datos originales y le permite hacer bastante trabajo en j o en la tabla de datos subsiguiente. Ver https://stackoverflow.com/a/47406952/305675 . Aquí pedí mis lirios por Sepal Length, tomé un Sepal.Length específico como mínimo, seleccioné los tres mejores (por Sepal Length) de todas las especies y devolví todos los datos que los acompañaban:

 iris[order(-Sepal.Length)][Sepal.Length > 3,.SD[1:3],by=,.(Species)] 

Los enfoques sobre todo reordenan una tabla de datos secuencialmente al eliminar filas. Puede transponer una tabla de datos y eliminar o reemplazar las filas antiguas que ahora son columnas transpuestas. Cuando se usa ': = NULL' para eliminar una fila transpuesta, el siguiente nombre de columna también se elimina:

 m_iris <- data.table(t(iris))[,V3:=NULL] # V3 column removed d_iris <- data.table(t(iris))[,V3:=V2] # V3 column replaced with V2 

Cuando transpone el archivo data.frame a una tabla de datos, es posible que desee cambiar el nombre de la data.table original y restaurar los atributos de la clase en el caso de eliminación. Aplicando ": = NULL" a una data.table ahora transpuesta crea todas las clases de caracteres.

 m_iris <- data.table(t(d_iris)); setnames(d_iris,names(iris)) d_iris <- data.table(t(m_iris)); setnames(m_iris,names(iris)) 

Es posible que desee eliminar filas duplicadas que puede hacer con o sin una clave:

 d_iris[,Key:=paste0(Sepal.Length,Sepal.Width,Petal.Length,Petal.Width,Species)] d_iris[!duplicated(Key),] d_iris[!duplicated(paste0(Sepal.Length,Sepal.Width,Petal.Length,Petal.Width,Species)),] 

También es posible agregar un contador incremental con '.I'. Luego puede buscar claves duplicadas o campos y eliminarlos eliminando el registro con el contador. Esto es computacionalmente costoso, pero tiene algunas ventajas ya que puede imprimir las líneas que se eliminarán.

 d_iris[,I:=.I,] # add a counter field d_iris[,Key:=paste0(Sepal.Length,Sepal.Width,Petal.Length,Petal.Width,Species)] for(i in d_iris[duplicated(Key),I]) {print(i)} # See lines with duplicated Key or Field for(i in d_iris[duplicated(Key),I]) {d_iris <- d_iris[!I == i,]} # Remove lines with duplicated Key or any particular field. 

También puede completar una fila con 0 o NA y luego usar una consulta i para eliminarlos:

  X xv foo 1: c 8 4 2: b 7 2 X[1] <- c(0) X xv foo 1: 0 0 0 2: b 7 2 X[2] <- c(NA) X xv foo 1: 0 0 0 2: NA NA NA X <- X[x != 0,] X <- X[!is.na(x),]