web-dev-qa-db-ja.com

data.tableの参照によって行を削除するにはどうすればよいですか?

私の質問は、参照による割り当てとdata.tableでのコピーに関連しています。参照によって行を削除できるかどうかを知りたい

DT[ , someCol := NULL]

知りたい

DT[someRow := NULL, ]

この関数が存在しない理由には十分な理由があると思うので、以下のように、通常のコピーのアプローチに代わる優れた方法を指摘することができます。特に、example(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

このdata.tableから最初の行を削除するとします。私はこれができることを知っています:

DT <- DT[-1, ]

ただし、オブジェクトをコピーしているため(N object.size(DT)ここで指摘されているように、約3 * Nのメモリが必要です found set(DT, i, j, value)。特定の値を設定する方法を知っています(ここのように、行1と2および列2と3のすべての値をゼロに設定します)

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

しかし、どうすれば最初の2行を消去できますか?やること

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

dT全体をNULLに設定します。

私のSQLの知識は非常に限られているので、皆さんは私に言った:与えられたdata.tableはSQL技術を使用しているので、SQLコマンドに相当するものがある

DELETE FROM table_name
WHERE some_column=some_value

data.tableに?

140
Florian Oswald

良い質問。 data.tableはまだ参照によって行を削除できません。

data.tableは、ご存じのとおり、列ポインターのベクトルを過剰に割り当てるため、参照によってcolumnsを追加および削除できます。行に対して同様のことを行い、insertdeleteを高速にすることを計画しています。行の削除では、Cのmemmoveを使用して、削除された行の後に(各列の)アイテムを調整します。テーブルの中央の行を削除することは、SQLなどの行ストアデータベースと比較して、非常に非効率的です。SQLは、行がテーブル内にある場合に行をすばやく挿入および削除するのに適しています。それでも、削除された行なしで新しいラージオブジェクトをコピーするよりもはるかに高速です。

一方、列ベクトルは過剰に割り当てられるため、行をすぐに挿入(および削除)することができます。例:成長する時系列。


それは問題としてファイルされています: 参照によって行を削除

115
Matt Dowle

メモリの使用をインプレース削除と同様にするために私が取ったアプローチは、一度に列をサブセット化して削除することです。適切なC memmoveソリューションほど高速ではありませんが、ここではメモリの使用のみが重要です。このようなもの:

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
}
28
vc273

以下は、@ vc273の回答と@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);
}

そしてその使用例:

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

「dat」はdata.tableです。 140万行から14,000行を削除するには、ラップトップで0.25秒かかります。

> 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が初めてなので、@ vc273のスレッドにコメントを追加できませんでした:-(

5
Jarno P.

このトピックは、多くの人々にとって興味深いものです(私も含まれています)。

そのことについて何? assignと前述のコードを置き換えるためにglovalenvを使用しました。元の環境をキャプチャすることをお勧めしますが、少なくともglobalenvではメモリ効率が良く、参照による変更のように機能します。

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)
4
JRR

代わりに、またはNULLに設定しようとして、NAに設定してみてください(最初の列のNAタイプと一致)

set(DT,1:2, 1:3 ,NA_character_)
4
42-

ここに私が使用したいくつかの戦略があります。 .ROW関数が登場する可能性があると思います。以下のこれらのアプローチはいずれも高速ではありません。これらは、サブセットやフィルタリングを少し超えた戦略です。データをクリーンアップしようとしているだけのdbaのように考えました。上記のように、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

注:.SDは元のデータのサブセットを作成し、j以降のdata.tableでかなりの作業を行うことができます。 https://stackoverflow.com/a/47406952/305675 を参照してください。ここで、私は虹彩をSep片の長さで注文し、指定されたSep片の長さを最小として、すべての種の上位3つ((片の長さ)を選択し、すべての付随データを返します。

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

上記のアプローチはすべて、行を削除するときにdata.tableを順番に並べ替えます。 data.tableを転置し、転置された列になった古い行を削除または置換できます。 「:= NULL」を使用して転置された行を削除すると、後続の列名も削除されます。

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を転置してdata.tableに戻すとき、元のdata.tableから名前を変更し、削除の場合にクラス属性を復元することができます。現在転置されたdata.tableに「:= NULL」を適用すると、すべての文字クラスが作成されます。

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

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

キーを使用して、または使用せずに実行できる重複した行を削除することができます。

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'で増分カウンタを追加することもできます。次に、重複したキーまたはフィールドを検索し、カウンターでレコードを削除してそれらを削除できます。これは計算コストがかかりますが、削除する行を印刷できるため、いくつかの利点があります。

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.

また、行に0またはNAを入力し、iクエリを使用してそれらを削除することもできます。

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