Правильный / самый быстрый способ изменить данные.стол
у меня есть таблица данных в R:
library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))
DT
x y v
[1,] 1 A 12
[2,] 1 B 62
[3,] 1 A 60
[4,] 1 B 61
[5,] 2 A 83
[6,] 2 B 97
[7,] 2 A 1
[8,] 2 B 22
[9,] 3 A 99
[10,] 3 B 47
[11,] 3 A 63
[12,] 3 B 49
Я могу легко суммировать переменную v по группам в данных.таблица:
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out
x y SUM
[1,] 1 A 72
[2,] 1 B 123
[3,] 2 A 84
[4,] 2 B 119
[5,] 3 A 162
[6,] 3 B 96
тем не менее, я хотел бы иметь группы (y) в виде столбцов, а не строк. Я могу сделать это с помощью reshape
:
out <- reshape(out,direction='wide',idvar='x', timevar='y')
out
x SUM.A SUM.B
[1,] 1 72 123
[2,] 2 84 119
[3,] 3 162 96
есть ли более эффективный способ изменить форму данных после их агрегирования? Есть ли способ объединить эти операции в один шаг, используя данные.операции с таблицами?
4 ответа:
The
data.table
пакет реализует быстрееmelt/dcast
функции (в C). Он также имеет дополнительные функции, позволяя плавить и бросать несколько столбцов. Смотрите новый эффективное изменение формы с использованием данных.таблицы на Github.функции melt / dcast для данных.таблица были доступны с v1.9. 0 и функции включают в себя:
нет необходимости загружать
reshape2
пакет перед литьем. Но если вы хотите его загрузить для других операций, Пожалуйста, загрузите его до загрузкаdata.table
.
dcast
также является общим S3. Нет большеdcast.data.table()
. Просто используйтеdcast()
.
melt
:
способен плавиться на столбцах типа "список".
успехов
variable.factor
иvalue.factor
которые по умолчаниюTRUE
иFALSE
соответственно для совместимости сreshape2
. Это позволяет Для сразу управление типом выводаvariable
иvalue
столбцы (факторы или нет).
melt.data.table
' sna.rm = TRUE
параметр внутренне оптимизирован для того чтобы извлечь NAs сразу во время плавить и поэтому очень более эффективн.NEW:
melt
может принять список дляmeasure.vars
и столбцы, указанные в каждом элементе списка, будут объединены вместе. Это облегчается далее с помощьюpatterns()
. Смотрите виньетка или?melt
.
dcast
:
принимает несколько
fun.aggregate
и несколькоvalue.var
. Смотрите виньетку или?dcast
.использовать
rowid()
функция непосредственно в Формуле для создания id-столбца, который иногда требуется для уникальной идентификации строк. Видишь ?dcast.старый ориентиры:
melt
: 10 миллион строк и 5 столбцов, 61,3 секунды уменьшены до 1,2 секунды.dcast
: 1 миллион строк и 4 столбца, 192 секунды уменьшены до 3,6 секунды.напоминание о Кельне (декабрь 2013) презентация слайд 32:почему бы не представить
dcast
потяните запросreshape2
?
эта функция теперь реализована в данных.таблица (начиная с версии 1.8.11), как видно из ответа Зака выше.
Я только что видел этот большой кусок кода от Arun вот на так. Так что я думаю, что есть
data.table
решение. Применительно к этой проблеме:library(data.table) set.seed(1234) DT <- data.table(x=rep(c(1,2,3),each=1e6), y=c("A","B"), v=sample(1:100,12)) out <- DT[,list(SUM=sum(v)),by=list(x,y)] # edit (mnel) to avoid setNames which creates a copy # when calling `names<-` inside the function out[, as.list(setattr(SUM, 'names', y)), by=list(x)] }) x A B 1: 1 26499966 28166677 2: 2 26499978 28166673 3: 3 26500056 28166650
это дает те же результаты, что и подход DWin:
tapply(DT$v,list(DT$x, DT$y), FUN=sum) A B 1 26499966 28166677 2 26499978 28166673 3 26500056 28166650
кроме того, это быстро:
system.time({ out <- DT[,list(SUM=sum(v)),by=list(x,y)] out[, as.list(setattr(SUM, 'names', y)), by=list(x)]}) ## user system elapsed ## 0.64 0.05 0.70 system.time(tapply(DT$v,list(DT$x, DT$y), FUN=sum)) ## user system elapsed ## 7.23 0.16 7.39
обновление
так что это решение также работает для несбалансированных наборов данных (т. е. некоторые комбинации не существуют), вы должны сначала ввести их в таблицу данных:
library(data.table) set.seed(1234) DT <- data.table(x=c(rep(c(1,2,3),each=4),3,4), y=c("A","B"), v=sample(1:100,14)) out <- DT[,list(SUM=sum(v)),by=list(x,y)] setkey(out, x, y) intDT <- expand.grid(unique(out[,x]), unique(out[,y])) setnames(intDT, c("x", "y")) out <- out[intDT] out[, as.list(setattr(SUM, 'names', y)), by=list(x)]
резюме
объединяя комментарии с вышеизложенным, вот 1-строчное решение:
DT[, sum(v), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][, setNames(as.list(V1), paste(y)), by = x]
это также легко изменить это, чтобы иметь больше, чем просто сумма, например:
DT[, list(sum(v), mean(v)), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][, setNames(as.list(c(V1, V2)), c(paste0(y,".sum"), paste0(y,".mean"))), by = x] # x A.sum B.sum A.mean B.mean #1: 1 72 123 36.00000 61.5 #2: 2 84 119 42.00000 59.5 #3: 3 187 96 62.33333 48.0 #4: 4 NA 81 NA 81.0
данные.объекты таблицы наследуются от ' data.рамы, так что вы можете просто использовать tapply:
> tapply(DT$v,list(DT$x, DT$y), FUN=sum) AA BB a 72 123 b 84 119 c 162 96
можно использовать
dcast
Сreshape2
библиотека. Вот код# DUMMY DATA library(data.table) mydf = data.table( x = rep(1:3, each = 4), y = rep(c('A', 'B'), times = 2), v = rpois(12, 30) ) # USE RESHAPE2 library(reshape2) dcast(mydf, x ~ y, fun = sum, value_var = "v")
Примечание:
tapply
решение было бы гораздо быстрее.