我的问题与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中?
答案 0 :(得分:112)
好问题。 data.table
无法通过引用删除行。
data.table
可以通过引用添加和删除列,因为它会过度分配列指针的向量,如您所知。该计划是针对行执行类似操作,并允许快速insert
和delete
。行删除将使用C中的memmove
来删除已删除行之后的项目(在每一列中)。与诸如SQL之类的行存储数据库相比,删除表中间的行仍然是非常低效的,这更适合于快速插入和删除表中这些行的任何位置。但是,它仍然比复制没有删除行的新大对象快得多。
另一方面,由于列向量将被过度分配,因此可以立即在末尾插入(和删除)行;例如,一个不断增长的时间序列。
这是一个问题:Delete rows by reference。
答案 1 :(得分:28)
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),]