如何在data.table中按引用删除行?

时间:2012-05-28 20:41:08

标签: r data.table

我的问题与data.table中的引用分配和复制有关。我想知道是否可以通过引用删除行,类似于

DT[ , someCol := NULL]

我想了解

DT[someRow := NULL, ]

我想有一个很好的理由说明为什么这个功能不存在,所以也许你可以指出一个很好的替代通常的复制方法,如下所示。特别是,从示例(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)as pointed out here,则需要大约3 * N内存。 现在我找到了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

但是,如何删除前两行呢?做

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

6 个答案:

答案 0 :(得分:112)

好问题。 data.table无法通过引用删除行。

data.table可以通过引用添加和删除,因为它会过度分配列指针的向量,如您所知。该计划是针对行执行类似操作,并允许快速insertdelete。行删除将使用C中的memmove来删除已删除行之后的项目(在每一列中)。与诸如SQL之类的行存储数据库相比,删除表中间的行仍然是非常低效的,这更适合于快速插入和删除表中这些行的任何位置。但是,它仍然比复制没有删除行的新大对象快得多。

另一方面,由于列向量将被过度分配,因此可以立即在末尾插入(和删除);例如,一个不断增长的时间序列。


这是一个问题:Delete rows by reference

答案 1 :(得分:28)

为了使内存使用类似于就地删除,我采取的方法是一次对列进行子集并删除。没有正确的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
}

答案 2 :(得分:5)

这是一个基于@ 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'

哪里&#34; dat&#34;是一个data.table。从我的笔记本电脑上删除1.4M行中的14k行需要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的帖子中添加评论: - (

答案 3 :(得分:4)

相反或尝试设置为NULL,尝试设置为NA(匹配第一列的NA类型)

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

答案 4 :(得分:4)

很多人(包括我在内)的主题仍然很有趣。

那怎么样?我使用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)

答案 5 :(得分:2)

以下是我使用的一些策略。我相信.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。在这里,我按照Sepal Length命令我的虹膜,将指定的Sepal.Length作为最小值,选择所有物种的前三个(通过Sepal Length)并返回所有附带的数据:

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重命名并恢复类属性。将“:= NULL”应用于现在转置的data.table会创建所有字符类。

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