Подтвердить что ты не робот

Как удалить строку по ссылке в 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?

4b9b3361

Ответ 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)