Эффективен ли мой способ дублирования строк в data.table? - PullRequest
19 голосов
/ 04 ноября 2011

У меня есть месячные данные в одном data.table и годовые данные в другом data.table, и теперь я хочу сопоставить годовые данные с соответствующим наблюдением в ежемесячных данных.

Мой подход заключается в следующем:Дублирование годовых данных за каждый месяц, а затем объединение месячных и годовых данных.А теперь у меня вопрос по поводу дублирования строк.Я знаю, как это сделать, но я не уверен, что это лучший способ сделать это, поэтому некоторые мнения были бы великолепны.

Вот примерное data.table DT для моих годовых данных и как яв настоящее время дубликат:

library(data.table)
DT <- data.table(ID = paste(rep(c("a", "b"), each=3), c(1:3, 1:3), sep="_"),
                    values = 10:15,
                    startMonth = seq(from=1, by=2, length=6),
                    endMonth = seq(from=3, by=3, length=6))
DT
      ID values startMonth endMonth
[1,] a_1     10          1        3
[2,] a_2     11          3        6
[3,] a_3     12          5        9
[4,] b_1     13          7       12
[5,] b_2     14          9       15
[6,] b_3     15         11       18
#1. Alternative
DT1 <- DT[, list(MONTH=startMonth:endMonth), by="ID"]
setkey(DT,  ID)
setkey(DT1, ID)
DT1[DT]
ID MONTH values startMonth endMonth
a_1     1     10          1        3
a_1     2     10          1        3
a_1     3     10          1        3
a_2     3     11          3        6
[...]

Последнее соединение - именно то, что я хочу.Однако DT[, list(MONTH=startMonth:endMonth), by="ID"] уже делает все, что я хочу, кроме добавления других столбцов в DT, поэтому мне было интересно, смогу ли я избавиться от последних трех строк в моем коде, то есть операций setkey и join.Оказывается, вы можете просто сделать следующее:

#2. Alternative: More intuitiv and just one line of code
DT[, list(MONTH=startMonth:endMonth, values, startMonth, endMonth), by="ID"]
 ID MONTH values startMonth endMonth
a_1    1     10          1        3
a_1    2     10          1        3
a_1    3     10          1        3
a_2    3     11          3        6
...

Это, однако, работает только потому, что я жестко закодировал имена столбцов в выражение list.В моих реальных данных я заранее не знаю имен всех столбцов, поэтому мне было интересно, могу ли я просто сказать data.table, чтобы вернуть столбец MONTH, который я вычислял, как показано выше, и все остальные столбцы DT..SD, казалось, был в состоянии сделать трюк, но:

DT[, list(MONTH=startMonth:endMonth, .SD), by="ID"]
Error in `[.data.table`(DT, , list(YEAR = startMonth:endMonth, .SD), by = "ID") : 
  maxn (4) is not exact multiple of this j column's length (3)

Таким образом, чтобы подвести итог, я знаю, как это было сделано, но я просто задавался вопросом, является ли это лучшим способом сделать это, потому что яЯ все еще немного борюсь с синтаксисом data.table и часто читаю в постах и ​​в вики, что есть хорошие и плохие способы ведения дел.Кроме того, я не совсем понимаю, почему я получаю ошибку при использовании .SD.Я подумал, что это простой способ сказать data.table, что вы хотите все столбцы.Что мне не хватает?

Ответы [ 4 ]

13 голосов
/ 04 ноября 2011

Отличный вопрос. То, что вы пытались, было очень разумным. Предполагая, что вы используете v1.7.1, теперь проще создавать list столбцы. В этом случае он пытается сделать один столбец list из .SD (3 элемента) рядом со столбцом MONTH во 2-й группе (4 элемента). Я подниму это как ошибку [РЕДАКТИРОВАТЬ: теперь исправлено в v1.7.5], спасибо.

А пока попробуйте:

DT[, cbind(MONTH=startMonth:endMonth, .SD), by="ID"]
 ID MONTH values startMonth endMonth
a_1     1     10          1        3
a_1     2     10          1        3
a_1     3     10          1        3
a_2     3     11          3        6
...

Кроме того, просто чтобы проверить, видели ли вы roll=TRUE? Обычно у вас есть только один столбец startMonth (нерегулярный с пробелами), а затем просто roll присоединяется к нему. Данные вашего примера имеют перекрывающиеся диапазоны месяцев, что усложняет его.

11 голосов
/ 16 января 2013

Глядя на это, я понял, что ответ возможен только потому, что ID был уникальным ключом (без дубликатов).Вот еще один ответ с дубликатами.Но, кстати, некоторые NA, кажется, закрадываются. Может ли это быть ошибкой?Я использую v1.8.7 (commit 796).

library(data.table)
DT <- data.table(x=c(1,1,1,1,2,2,3),y=c(1,1,2,3,1,1,2))

DT[,rep:=1L][c(2,7),rep:=c(2L,3L)]   # duplicate row 2 and triple row 7
DT[,num:=1:.N]                       # to group each row by itself

DT
   x y rep num
1: 1 1   1   1
2: 1 1   2   2
3: 1 2   1   3
4: 1 3   1   4
5: 2 1   1   5
6: 2 1   1   6
7: 3 2   3   7

DT[,cbind(.SD,dup=1:rep),by="num"]
    num x y rep dup
 1:   1 1 1   1   1
 2:   2 1 1   1  NA      # why these NA?
 3:   2 1 1   2  NA
 4:   3 1 2   1   1
 5:   4 1 3   1   1
 6:   5 2 1   1   1
 7:   6 2 1   1   1
 8:   7 3 2   3   1
 9:   7 3 2   3   2
10:   7 3 2   3   3

Просто для полноты, более быстрый способ - rep номеров строк и затем выполнить подмножество за один шаг (без группировкии не использовать cbind или .SD):

DT[rep(num,rep)]
    x y rep num
 1: 1 1   1   1
 2: 1 1   2   2
 3: 1 1   2   2
 4: 1 2   1   3
 5: 1 3   1   4
 6: 2 1   1   5
 7: 2 1   1   6
 8: 3 2   3   7
 9: 3 2   3   7
10: 3 2   3   7

, где в данных этого примера столбец rep совпадает с именем базовой функции rep().

0 голосов
/ 02 декабря 2016

Самый быстрый и краткий способ сделать это:

DT[rep(1:nrow(DT), endMonth - startMonth)]

Мы также можем перечислить по группам:

dd <- DT[rep(1:nrow(DT), endMonth - startMonth)]
dd[, nn := 1:.N, by = ID]
dd
0 голосов
/ 04 ноября 2011

Вот функция, которую я написал, которая имитирует disaggregate (мне нужно что-то, что обрабатывает сложные данные).Это может быть полезно для вас, если это не излишне.Чтобы развернуть только строки, установите для аргумента fact значение c (1,12), где 12 будет для 12 строк «месяца» для каждой строки «года».

zexpand<-function(inarray, fact=2, interp=FALSE,  ...)  {
fact<-as.integer(round(fact))
switch(as.character(length(fact)),
        '1' = xfact<-yfact<-fact,
        '2'= {xfact<-fact[1]; yfact<-fact[2]},
        {xfact<-fact[1]; yfact<-fact[2];warning(' fact is too long. First two values used.')})
if (xfact < 1) { stop('fact[1] must be > 0') } 
if (yfact < 1) { stop('fact[2] must be > 0') }
# new nonloop method, seems to work just ducky
bigtmp <- matrix(rep(t(inarray), each=xfact), nrow(inarray), ncol(inarray)*xfact, byr=T)   
#does column expansion
bigx <- t(matrix(rep((bigtmp),each=yfact),ncol(bigtmp),nrow(bigtmp)*yfact,byr=T))
return(invisible(bigx))
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...