Data.table içindeki bir satırı referans ile nasıl silebilirim?


150

Sorum, kopyalamaya karşı referans ile atama ile ilgili data.table. Birinin referansla benzer şekilde satırları silip silemeyeceğini bilmek istiyorum

DT[ , someCol := NULL]

Hakkında bilmek istiyorum

DT[someRow := NULL, ]

Sanırım bu işlevin var olmamasının iyi bir nedeni var, bu yüzden belki de aşağıdaki gibi olağan kopyalama yaklaşımına iyi bir alternatif gösterebilirsiniz. Özellikle, örneğin favorim ile gitmek (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

Diyelim ki bu satırdaki ilk satırı silmek istiyorum. Tablo. Bunu yapabileceğimi biliyorum:

DT <- DT[-1, ]

ancak genellikle bundan kaçınmak isteyebiliriz, çünkü nesneyi kopyalıyoruz (ve bu, N object.size(DT), burada işaret edildiği gibi yaklaşık 3 * N bellek gerektirir . Şimdi buldum set(DT, i, j, value). Belirli değerleri nasıl ayarlayacağımı biliyorum (burada olduğu gibi: tümünü ayarla) satır 1 ve 2'deki değerler ve sütun 2 ve 3 sıfıra)

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

Ama ilk iki satırı nasıl silebilirim? iş

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

DT'nin tamamını NULL olarak ayarlar.

SQL bilgim çok sınırlı, bu yüzden bana söylüyorsunuz: data.table verilen SQL teknolojisini kullanıyor, SQL komutuna eşdeğer mi

DELETE FROM table_name
WHERE some_column=some_value

data.table içinde?


17
Ben bir data.table()SQL farklı işlemler ve çeşitli argümanlar arasında bir paralel çizebilirsiniz kadar SQL teknolojisi kullanan olduğunu sanmıyorum data.table. Bana göre, "teknolojiye" yapılan atıf data.table, bir yerde bir SQL veritabanının üstünde otururken AFAIK'ın böyle olmadığı anlamına geliyor.
Chase,

1
teşekkürler kovalamaca. evet, sanırım sql benzetmesi vahşi bir tahmindi.
Florian Oswald

1
Çoğu zaman DT[ , keep := .I > 1], daha sonra yapılacak işlemler için alt küme gibi satırları tutmak için bir bayrak tanımlamak yeterli olmalıdır : DT[(keep), ...]belki de setindex(DT, keep)bu alt kümenin hızı. Her derde deva değil, ancak iş akışınızda bir tasarım seçeneği olarak görülmeye değer - tüm bu satırları bellekten gerçekten silmek istiyor musunuz yoksa hariç tutmayı mı tercih edersiniz? Cevap kullanım durumuna göre değişir.
MichaelChirico

Yanıtlar:


125

İyi soru. data.tablehenüz referansla satırları silemiyor.

data.tableBildiğiniz gibi sütun işaretçileri vektörünü aşırı tahsis ettiğinden, başvuru ile sütun ekleyebilir ve silebilir . Plan, satırlar için benzer bir şey yapmak ve hızlı insertve izin vermektir delete. memmoveSilinmiş satırlardan sonra öğeleri (her sütunda) düzenlemek için C'de bir satır silme kullanılır . Tablonun ortasındaki bir satırın silinmesi, SQL gibi bir satır deposu veritabanına kıyasla hala oldukça verimsiz olacaktır; bu, bu satırların tablodaki her yere hızlı satır ekleme ve silme için daha uygundur. Ancak yine de, silinmiş satırlar olmadan yeni büyük bir nesneyi kopyalamaktan çok daha hızlı olurdu.

Öte yandan, kolon vektörleri olacaktır, çünkü aşırı tahsis satır eklenir (ve silinmiş) olabilir sonunda anında; örneğin, büyüyen bir zaman dizisi.


Sorun olarak dosyalandı: Satırları referans ile silin .


1
@Matthew Dowle Bu konuda bir haber var mı?
Nisan'ta geçerli

15
@statquant Sanırım 37 hata düzeltmeli ve freadönce bitirmeliyim . Bundan sonra oldukça yüksek.
Matt Dowle

15
@MatthewDowle, yaptığınız her şey için tekrar teşekkürler.
Nisan'ta

1
@rbatt Doğru. DT[b<8 & a>3]yeni bir data.table döndürür. Eklemek istiyoruz delete(DT, b>=8 | a<=3)ve DT[b>=8 | a<=8, .ROW:=NULL]. İkincisinin avantajı, []satır numaraları i, katılma ive optimizasyondan rollyararlanma gibi diğer özelliklerle birleştirmek olacaktır [i,j,by].
Matt Dowle

2
@charliealpha Güncelleme yok. Katkıları bekliyoruz. Rehberlik etmeye hazırım. C becerilerine ihtiyacı var - yine rehberlik etmeye hazırım.
Matt Dowle

29

bellek kullanımını yerinde silmeye benzer hale getirmek için almış olduğum yaklaşım, bir seferde bir sütunu alt kümeye koymak ve silmektir. uygun bir C memmove çözümü kadar hızlı değil, ama bellek kullanımı burada tüm umurumda. böyle bir şey:

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
}

5
+1 Güzel bellek verimli yaklaşım. İdeal olarak referans olarak bir dizi satırı silmeliyiz aslında değil mi, bunu düşünmemiştim. memmoveBoşlukları güçlendirmek için bir dizi s olmalı, ama sorun değil.
Matt Dowle

Bu bir işlev olarak çalışır mı, yoksa işlev ve geri dönüşte kullanım onu ​​bellek kopyaları yapmaya zorlar mı?
russellpierce

1
data.tables her zaman referans olduğu için bir işlevde çalışır.
vc273

1
teşekkürler, iyi biri. Değiştirmek (özellikle birçok sütunlu) biraz hızlandırmak için DT[, col:= NULL, with = F]deset(DT, NULL, col, NULL)
Michele

2
Değişen deyim ve uyarı ışığında güncellenmesi "= FALSE ile birlikte: = Ekim 2014'te piyasaya sürülen v1.9.4'te kaldırıldı. Lütfen LHS'yi: = parantez ile sarın; örneğin, DT [, (myVar): = toplam (b) , = a] değişkeniyle myVar değişkeninde tutulan sütun adlarına atamak için. Diğer örnekler için bkz.? ': ='. 2014'te uyarıldığı gibi, bu bir uyarıdır. "
Frank

6

@ Vc273'ün cevabı ve @ Frank'in geri bildirimlerine dayanan bir çalışma fonksiyonu.

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);
}

Ve kullanım örneği:

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

Burada "dat" bir data.table dosyasıdır. 1.4M satırlardan 14k satırları kaldırmak, dizüstü bilgisayarımda 0.25 saniye sürüyor.

> 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
> 

PS. SO için yeni olduğum için, @ vc273 adlı konuya yorum ekleyemedim :-(


(Col): = için değişen sözdizimini açıklayan vc'nin cevabı altında yorum yaptım. "Delete" adlı bir işleve sahip olmakla ilgili bir argümana sahip olmak biraz garip. Btw, genellikle kendi verileriniz için loş göstermek yerine tekrarlanabilir bir örnek kullanılması tercih edilir. Örneğin, DT'yi sorudan tekrar kullanabilirsiniz.
Frank

Referans olarak neden yaptığınızı anlamıyorum ama daha sonra bir ödev kullanın dat <-
skan

1
@skan, Bu atama, değiştirilmiş data.table öğesini özgün data.table alt kümesi kullanılarak oluşturulduğunu göstermek için "dat" atar. <- Değerlendirme dönüş verilerinin kopyasını yapmaz, sadece yeni bir ad atar. bağlantı
Jarno P.

@Frank, işaret ettiğin tuhaflık için işlevi güncelledim.
Jarno P.

Tamam teşekkürler. Yorumdan ayrılıyorum çünkü hala tekrarlanabilir bir örnek yerine konsol çıktısının gösterilmesinin burada teşvik edilmediğini belirtmeye değer. Ayrıca, tek bir kriter o kadar bilgilendirici değildir. Alt küme için harcanan zamanı da ölçtüyseniz, daha bilgilendirici olurdu (çoğumuz sezgisel olarak ne kadar sürdüğünü, comp'inizde ne kadar sürdüğünü bilmiyoruz). Her neyse, bunun kötü bir cevap olduğunu söylemek istemiyorum; Ben onun yükselticilerinden biriyim.
Frank

4

Bunun yerine veya NULL olarak ayarlamaya çalışırken, NA olarak ayarlamayı deneyin (ilk sütun için NA türüyle eşleşir)

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

3
evet, bu çalışıyor sanırım. Benim sorunum, çok fazla veri var ve ben muhtemelen bu satırlardan kurtulmak için DT kopyalamak zorunda kalmadan, NA ile tam olarak bu satırlardan kurtulmak istiyorum. Yine de yorumunuz için teşekkürler!
Florian Oswald

4

Konu hala birçok kişi (ben dahil) ilginç.

Buna ne dersin? Kullandığım assignyerine glovalenvve kod, daha önce tarif edildiği. Orijinal ortamı yakalamak daha iyi olur, ancak en azından içinde globalenvbellek verimlidir ve ref ile bir değişiklik gibi davranır.

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)

Açıkça söylemek gerekirse, bu referans yoluyla silinmez ( address(DT); delete(DT, 3); address(DT)bir anlamda), ancak bir anlamda etkili olabilir.
Frank

1
Hayır değil. Davranışı öykünür ve bellek verimlidir. Bu yüzden dedim: gibi davranıyor . Ama kesinlikle doğru konuştuğunuzda adres değişti.
JRR

3

İşte kullandığım bazı stratejiler. Bir .ROW işlevinin gelebileceğine inanıyorum. Aşağıdaki bu yaklaşımların hiçbiri hızlı değildir. Bunlar, altkümelerin veya filtrelemenin biraz ötesinde bazı stratejilerdir. Sadece verileri temizlemeye çalışırken dba gibi düşünmeye çalıştım. Yukarıda belirtildiği gibi, verilerdeki satırları seçebilir veya kaldırabilirsiniz. Tablo:

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

Not: .SD orijinal verilerin bir alt kümesini oluşturur ve j veya sonraki verilerde oldukça fazla iş yapmanızı sağlar. Tablo. Bkz. Https://stackoverflow.com/a/47406952/305675 . Burada süsenimi Sepal Length'e göre sipariş ettim, minimum olarak belirli bir Sepal.Length alın, tüm Türlerin ilk üçünü (Sepal Length'e göre) seçin ve eşlik eden tüm verileri döndürün:

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

Her şeyden önce yaklaşımlar, satırları kaldırırken bir data.table dosyasını yeniden sıralar. Bir data.table dosyasını aktarabilir ve şimdi aktarılan sütunlar olan eski satırları kaldırabilir veya değiştirebilirsiniz. Aktarılan bir satırı kaldırmak için ': = NULL' kullanılırken, sonraki sütun adı da kaldırılır:

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

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

Data.frame öğesini bir data.table dosyasına geri aktardığınızda, orijinal data.table öğesinden yeniden adlandırmak ve silme durumunda sınıf özniteliklerini geri yüklemek isteyebilirsiniz. Şimdi aktarılan bir data.table öğesine ": = NULL" uygulamak tüm karakter sınıflarını oluşturur.

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

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

Bir Anahtarla veya Anahtarsız yapabileceğiniz yinelenen satırları kaldırmak isteyebilirsiniz:

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

'.I' ile artımlı bir sayaç eklemek de mümkündür. Daha sonra çoğaltılan anahtarları veya alanları arayabilir ve sayaçla kaydı kaldırarak bunları kaldırabilirsiniz. Bu, hesaplama açısından pahalıdır, ancak kaldırılacak çizgileri yazdırabileceğiniz için bazı avantajları vardır.

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.

Ayrıca bir satırı 0 veya NA ile doldurabilir ve ardından bunları silmek için bir i sorgusu kullanabilirsiniz:

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

Bu gerçekten soruyu cevaplamıyor (referansla kaldırma hakkında) ve tbir data.frame üzerinde kullanmak genellikle iyi bir fikir değildir; str(m_iris)tüm verilerin dize / karakter haline geldiğini kontrol edin . Btw, d_iris[duplicated(Key), which = TRUE]sayaç sütunu yapmadan kullanarak satır numaralarını da alabilirsiniz .
Frank

1
Evet haklısın. Soruyu özellikle cevaplamıyorum. Ancak bir satırın referans olarak kaldırılması henüz resmi işlevselliğe veya belgelere sahip değildir ve birçok kişi tam olarak bunu yapmak için genel işlevsellik arayan bu gönderiye gelecektir. Bir satırın nasıl kaldırılacağı sorusunu cevaplamak için bir yayın oluşturabiliriz. Yığın taşması çok faydalıdır ve soruların cevaplarını tam olarak tutmanın gerekliliğini gerçekten anlıyorum. Bazen olsa da, SO'nun bu konuda biraz faşist olabileceğini düşünüyorum ... ama belki de bunun için iyi bir neden var.
rferrisx

Tamam, açıkladığın için teşekkürler. Şimdilik buradaki tartışmamızın, bu durumda kafası karışan herkes için bir tabela olduğunu düşünüyorum.
Frank
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.