163

我的问题与引用分配与复制有关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, ]

但通常我们可能希望避免这种情况,因为我们正在复制对象(并且需要大约 3*N 内存,如果 N object.size(DT)正如在此处指出的那样。现在我发现set(DT, i, j, value)了。我知道如何设置特定值(例如这里:set all第 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

在数据表中?

4

6 回答 6

129

好问题。data.table还不能通过引用删除行。

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

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


它作为一个问题提交:Delete rows by reference

于 2012-05-29T00:20:29.977 回答
29

为了使内存使用类似于就地删除,我采取的方法是一次子集一列并删除。不如适当的 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
}
于 2014-01-21T18:39:18.060 回答
7

这是基于@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 万行中删除 14000 行需要 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 的线程添加评论:-(

于 2016-11-18T08:29:35.497 回答
4

而是尝试设置为 NULL,尝试设置为 NA(匹配第一列的 NA 类型)

set(DT,1:2, 1:3 ,NA_character_)
于 2012-05-28T22:33:18.413 回答
4

这个话题对很多人(包括我)来说仍然很有趣。

那个怎么样?我曾经assign替换glovalenv和 前面描述的代码。捕获原始环境会更好,但至少globalenv它是内存高效的,并且就像 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)
于 2017-08-27T21:52:22.860 回答
3

以下是我使用的一些策略。我相信 .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 为最小值,选择所有物种的前三个(按萼片长度)并返回所有随附数据:

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

您可能只想删除可以使用或不使用 Key 的重复行:

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),]
于 2018-01-29T01:47:34.007 回答