Преобразование * некоторых * классов столбцов в data.table

Я хочу преобразовать подмножество data.table cols в новый класс. Здесь есть популярный вопрос (Преобразование классов столбцов в data.table), но ответ создает новый объект, а не работает на стартовом объекте.

Возьмем этот пример:

dat <- data.frame(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
cols <- c('ID', 'Quarter')

Как лучше всего преобразовать только столбцы cols в (например) фактор? В нормальном data.frame вы можете сделать это:

dat[, cols] <- lapply(dat[, cols], factor)

но это не работает для таблицы data.table, и это также не работает

dat[, .SD := lapply(.SD, factor), .SDcols = cols]

Комментарий в связанном вопросе от Matt Dowle (с декабря 2013 года) предлагает следующее, которое прекрасно работает, но кажется немного менее элегантным.

for (j in cols) set(dat, j = j, value = factor(dat[[j]]))

Есть ли в настоящее время лучший ответ data.table(т.е. более короткий + не генерирует переменную счетчика), или я должен просто использовать выше + rm(j)?

2 ответа

Помимо использования опции, предложенной Мэттом Доуле, другой способ изменения классов столбцов выглядит следующим образом:

dat[, (cols) := lapply(.SD, factor), .SDcols=cols]

Используя оператор :=, вы обновляете данные по ссылке. Проверьте, работает ли это:

> sapply(dat,class)
 ID Quarter value 
 "factor" "factor" "numeric"

Как было предложено @MattDowle в комментариях, вы также можете использовать комбинацию for(...) set(...) следующим образом:

for (col in cols) set(dat, j = col, value = factor(dat[[col]]))

который даст тот же результат. Третий вариант:

for (col in cols) dat[, (col) := factor(dat[[col]])]

В меньших наборах данных параметр for(...) set(...) примерно в три раза быстрее, чем параметр lapply (но это не имеет особого значения, поскольку это небольшой набор данных). В больших наборах данных (например, 2 миллиона строк) каждый из этих подходов занимает примерно столько же времени. Для тестирования на более крупном наборе данных я использовал:

dat <- data.table(ID=c(rep("A", 1**), rep("B",1**)),
 Quarter=c(1:1**, 1:1**),
 value=rnorm(10))

Иногда вам придется делать это по-другому (например, когда числовые значения сохраняются как фактор). Тогда вы должны использовать что-то вроде этого:

dat[, (cols) := lapply(.SD, function(x) as.integer(as.character(x))), .SDcols=cols]

ПРЕДУПРЕЖДЕНИЕ: Следующее объяснение - не data.table - способ выполнения действий. Данному не обновляется по ссылке, потому что копия создается и сохраняется в памяти (как указано в @Frank), что увеличивает использование памяти. Это больше подходит для объяснения работы with=FALSE.

Если вы хотите изменить классы столбцов так же, как и с фреймворком данных, вы должны добавить with = FALSE следующим образом:

dat[, cols] <- lapply(dat[, cols, with = FALSE], factor)

Проверьте, работает ли это:

> sapply(dat,class)
 ID Quarter value 
 "factor" "factor" "numeric"

Если вы не добавите with = FALSE, datatable будет оценивать dat[, cols] как вектор. Проверьте разницу в выходе между dat[, cols] и dat[, cols, with=FALSE]:

> dat[, cols]
[1] "ID" "Quarter"
> dat[, cols, with=FALSE]
 ID Quarter
 1: A 1
 2: A 2
 3: A 3
 4: A 4
 5: A 5
 6: B 1
 7: B 2
 8: B 3
 9: B 4
10: B 5


Вы можете использовать .SDcols:

dat[, cols] <- dat[, lapply(.SD, factor), .SDcols=cols]

licensed under cc by-sa 3.0 with attribution.