Come eliminare una riga per riferimento in data.table?




(4)

La mia domanda è legata all'assegnazione per riferimento rispetto alla copia in data.table . Voglio sapere se è possibile eliminare righe per riferimento, in modo simile a

DT[ , someCol := NULL]

Voglio sapere

DT[someRow := NULL, ]

Immagino ci sia una buona ragione per cui questa funzione non esiste, quindi potresti semplicemente indicare una buona alternativa al solito approccio di copia, come di seguito. In particolare, andando con il mio preferito dall'esempio (data.table),

DT = data.table(x = rep(c("a", "b", "c"), each = 3), y = c(1, 3, 6), v = 1:9)
#      x y v
# [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

Dire che voglio eliminare la prima riga da questo data.table. So che posso fare questo:

DT <- DT[-1, ]

ma spesso potremmo volerlo evitare, perché stiamo copiando l'oggetto (e questo richiede circa 3 * N di memoria, se N object.size(DT) , come indicato qui . Ora ho trovato il set(DT, i, j, value) . So come impostare valori specifici (come qui: imposta tutti i valori nelle righe 1 e 2 e le colonne 2 e 3 a zero)

set(DT, 1:2, 2:3, 0) 
DT
#      x y v
# [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

Ma come posso cancellare le prime due righe, ad esempio? fare

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

imposta l'intero DT su NULL.

La mia conoscenza di SQL è molto limitata, quindi voi ragazzi mi dite: dato data.table usa la tecnologia SQL, c'è un equivalente al comando SQL

DELETE FROM table_name
WHERE some_column=some_value

in data.table?


Buona domanda. data.table non può ancora cancellare righe per riferimento.

data.table può aggiungere ed eliminare colonne per riferimento poiché sovrasta il vettore dei puntatori di colonne, come sai. Il piano è di fare qualcosa di simile per le righe e consentire l' insert rapido e l' delete . memmove riga utilizza memmove in C per memmove gli elementi (in ogni colonna) dopo le righe eliminate. L'eliminazione di una riga nel mezzo della tabella sarebbe ancora piuttosto inefficiente rispetto a un database di archivio di riga come SQL, che è più adatto per l'inserimento rapido e l'eliminazione di righe ovunque queste righe si trovino nella tabella. Tuttavia, sarebbe molto più veloce della copia di un nuovo oggetto di grandi dimensioni senza le righe eliminate.

D'altra parte, poiché i vettori di colonna sarebbero sovra-allocati, le righe potrebbero essere inserite (e cancellate) alla fine , istantaneamente; ad esempio, una serie temporale crescente.


Ecco alcune strategie che ho usato. Credo che possa esserci una funzione .ROW. Nessuno di questi approcci di seguito è veloce. Queste sono alcune strategie un po 'oltre i sottoinsiemi o il filtraggio. Ho provato a pensare come se dba stesse solo cercando di ripulire i dati. Come indicato sopra, puoi selezionare o rimuovere le righe in 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 sottoinsieme dei dati originali e ti permette di fare un po 'di lavoro in j o data.table successiva. Vedi https://.com/a/47406952/305675 . Qui ho ordinato i miei iris per Sepal Length, prendiamo un Sepal.Length specificato come minimo, selezioniamo i primi tre (in Sepal Length) di tutte le Species e restituiamo tutti i dati di accompagnamento:

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

Gli approcci soprattutto riordinano un data.table in sequenza quando si rimuovono le righe. È possibile trasporre un data.table e rimuovere o sostituire le vecchie righe che ora sono colonne trasposte. Quando si utilizza ': = NULL' per rimuovere una riga trasposta, viene rimosso anche il nome della colonna successiva:

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

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

Quando si traspone il data.frame su un data.table, si potrebbe voler rinominare dal data.table originale e ripristinare gli attributi della classe in caso di cancellazione. Applicando ": = NULL" a un dato ora trasposto.table crea tutte le classi di caratteri.

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

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

Puoi semplicemente rimuovere le righe duplicate che puoi fare con o senza una chiave:

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

È anche possibile aggiungere un contatore incrementale con '.I'. È quindi possibile cercare chiavi o campi duplicati e rimuoverli rimuovendo il record con il contatore. Questo è molto costoso dal punto di vista computazionale, ma presenta alcuni vantaggi dal momento che è possibile stampare le linee da rimuovere.

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.

Puoi anche compilare una riga con 0 o NA e quindi utilizzare una query i per eliminarli:

 X 
   x v foo
1: c 8   4
2: b 7   2

X[1] <- c(0)

X
   x v foo
1: 0 0   0
2: b 7   2

X[2] <- c(NA)
X
    x  v foo
1:  0  0   0
2: NA NA  NA

X <- X[x != 0,]
X <- X[!is.na(x),]

Invece di provare a impostare NULL, prova a impostare su NA (corrispondente al tipo di NA per la prima colonna)

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

L'argomento è ancora interessante per molte persone (me incluso).

Che ne pensi? Ho usato assign per sostituire il glovalenv e il codice descritto in precedenza. Sarebbe meglio catturare l'ambiente originale ma almeno in globalenv è efficiente in termini di memoria e agisce come un cambiamento per 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)




data.table