Как удалить строку по ссылке в 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
Скажем, я хочу удалить первую строку из этой таблицы данных. Я знаю, что могу это сделать:
DT <- DT[-1, ]
но часто мы можем избежать этого, потому что мы копируем объект (и для этого требуется около 3 * N памяти, если N object.size(DT)
, как указано здесь.
Теперь я нашел 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?
Ответы
Ответ 1
Хороший вопрос. data.table
пока не удаляет строки по ссылке.
data.table
может добавлять и удалять столбцы по ссылке, поскольку, как вы знаете, перераспределяет вектор указателей столбцов. План состоит в том, чтобы сделать что-то подобное для строк и позволить быстро insert
и delete
. Удаление строки будет использовать memmove
в C для упорядочивания элементов (в каждом столбце) после удаленных строк. Удаление строки в середине таблицы будет по-прежнему неэффективным по сравнению с базой данных хранилища строк, такой как SQL, которая больше подходит для быстрой вставки и удаления строк, где бы ни находились эти строки в таблице. Но все же, это было бы намного быстрее, чем копирование нового большого объекта без удаленных строк.
С другой стороны, поскольку векторы столбцов были бы перераспределены, строки могут быть вставлены (и удалены) в конце, мгновенно; например, растущий временной ряд.
Ответ 2
подход, который я принял, чтобы сделать использование памяти похожим на удаление на месте, заключается в подмножестве столбца за раз и удалении. не так быстро, как правильное решение 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
}
Ответ 3
Вот рабочая функция, основанная на ответе @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" - это таблица данных. Удаление 14k строк из строк 1.4M занимает 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: - (
Ответ 4
Вместо или попытаться установить значение NULL, попробуйте установить значение NA (сопоставление типа NA для первого столбца)
set(DT,1:2, 1:3 ,NA_character_)
Ответ 5
Тема по-прежнему интересна многим людям (включая меня).
Как насчет этого? Я использовал 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)