data.table - 有效地操纵大型数据集

时间:2017-01-24 22:14:51

标签: r matrix data.table time-series dcast

我对数据速度极快感到惊讶。下面的编码正是我所需要的,但是当在大型表上执行时,它的表现并不是很好。

确信使用data.table可以更快地完成此操作,但我不知道如何。

输出

输出必须是一个矩阵,其中rownames是一个常规的天数序列。 对于每个列分别:

  • 第一个值之前的所有值都需要为NA
  • 最后一个值之后的所有值都需要为NA
  • 在第一个和最后一个值之间,需要添加0,因为输入表中不存在

以下编码显示结果应如何:

M <- 
  matrix(c(NA, NA, NA, 2, 0, 1, 3, 0, 2 , NA,
           NA, NA, 3,  1, 3, 2, 1, 2, NA, NA), 
           ncol = 2, 
           dimnames = list(as.character((Sys.Date() + 0:9)),
                           c("E1", "E2")))

Output example

##            E1 E2
## 2017-01-27 NA NA
## 2017-01-28 NA NA
## 2017-01-29 NA  2
## 2017-01-30  2  2
## 2017-01-31  0  2
## 2017-02-01  3  1
## 2017-02-02  1  3
## 2017-02-03  0  3
## 2017-02-04  2 NA
## 2017-02-05 NA NA

输入

下表显示了编码/功能的来源/输入:

DS <- data.table(
  E = c(rep("E1", 4), rep("E2", 6)),
  C = c(c(Sys.Date() + c(3, 5, 6, 8)),
        c(Sys.Date() + c(2, 3, 4, 5, 6, 7))),
  S = round(runif(n = 10,min = 1, max = 3), 0),
  key = c("E", "C"))

##      E          C S
##  1: E1 2017-01-30 3
##  2: E1 2017-02-01 1
##  3: E1 2017-02-02 2
##  4: E1 2017-02-04 1
##  5: E2 2017-01-29 3
##  6: E2 2017-01-30 2
##  7: E2 2017-01-31 3
##  8: E2 2017-02-01 1
##  9: E2 2017-02-02 2
## 10: E2 2017-02-03 3

Input example

代码工作

以下几行正是我所需要的,而且很简单。但效率不高。
真实表有700个唯一的C值和2百万个E值。

# Create the regular time line per day
CL <- c(C= (Sys.Date() + 0:9))

# Determine first and last per E
DM <- DS[, .(MIN = min(C), MAX = max(C)), by =.(E)]

# Generate all combinations 
CJ <- CJ(E = DS$E, C = CL, unique = TRUE)

# Join 
DC <- DS[CJ, on = .(E, C)][!is.na(E)]

# replace NA by 0
DC[is.na(S), S:=0]

# Lead-in
DC[DM, on=.(E, C<MIN), S:=NA]

# Lead-out
DC[DM, on=.(E, C>MAX), S:=NA]

# Cast to matrix format
DC2 <- dcast(
  data = DC, formula = C ~ E, 
  fun.aggregate = sum, value.var = "S")

# coerce to matrix
M3 <- as.matrix(DC2[, -1])

# add row nanes
rownames(M3) <- format(CL, "%Y-%m-%d")

我做了一些长的,不可读的,笨拙的编码,它在35秒内创建了具有1.2B单元的矩阵。这必须是可以快速但更优雅的data.table,但不是这样。

2 个答案:

答案 0 :(得分:1)

data.table,就像所有内容的data.frame一样是一个列表(长度=列数)

200万列是很多列 - 这将使任何事情变得缓慢。

转换为“wide”的描述会使具有大量NA值的数据膨胀。您几乎可以肯定地在“长形式”和使用键上执行所需的分析。

从您的问题中不清楚您需要什么,但您可以计算各种总和

# convert to an IDate
DT[, CALDAY := as.IDate(CALDAY)]
# get range of dates
rangeDays <- DT[,range(CALDAY)]

all_days <- as.IDate(seq(rangeDays[1],rangeDays[2], by=1)) 
# create sums
DT_sum <- DT[, list(VALUE= sum(VALUE)), keyby = list(ENTITY, CALDAY)]

然后使用实体和日期进行索引。

 DT_sum[.("2a8605e2-e283-11e6-a3bb-bbe3fd226f8d", all_days)]

如果您需要将NA替换为0

na_replace <- function(x,repl=0){x[is.na(x)]<-repl;x}

DT_sum[.("2a8605e2-e283-11e6-a3bb-bbe3fd226f8d", all_days), na_replace(VALUE)]

答案 1 :(得分:0)

这就是诀窍。但表现仍然不好   它需要DS作为输入参数。结果是data.table,应该通过以下方式强制转换为矩阵:

$ env -i "bar=3" "baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is baz
Value is 9
Name is bar
Value is 3
$ env -i "bar=3
> baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is bar
Value is 3
baz=9

功能

M <- as.matrix(build_timeseries_DT(DS))

测试数据

build_timeseries_DT <- function(DS){

  # regular time serie for complete range with index
  dtC <- data.table(
    CAL = seq(min(DS$C), max(DS$C), by = "day"))[, idx:= 1:.N]

  # add row index (idx) to sales
  DQ <- dtC[DS, on = "CAL"]
  setkey(DQ, "ENT")

  # calculate min index per ENT
  DM <- DQ[, .(MIN = min(idx), MAX = max(idx)), by = .(ENT)]

  # allocate memory, assign 0 and set rownames by reference
  DT <- dtC[, .(CAL)][, (DM[, ENT]):= 0L][, CAL:= NULL]
  setattr(DT, "row.names", format(dtC$CAL, "%Y-%m-%d"))

  # Set NA for the Lead-in and out, next populate values by ref
  for(j in colnames(DT)){
    set(x     = DT, 
        i     = c(1L:(DM[j, MIN]), (DM[j, MAX]):DT[, .N]), 
        j     = j, 
        value = NA )
    set(x     = DT, 
        i     = DQ[j, idx], 
        j     = j, 
        value = DQ[j, SLS] )}

  return(DT)
}

结果

DS <- data.table(
  ENT = c("A", "A", "A", "B", "B", "C", "C", "C", "D", "D"),
  CAL = c(Sys.Date() + c(0, 5, 6, 3, 8, 1, 2, 9, 3, 5)),
  SLS = as.integer(c(1, 2, 1, 2, 3, 1, 2, 3, 2, 1)),
  key = c("ENT", "CAL"))

   ENT        CAL SLS
 1:   A 2017-01-28   1
 2:   A 2017-02-02   2
 3:   A 2017-02-03   1
 4:   B 2017-01-31   2
 5:   B 2017-02-05   3
 6:   C 2017-01-29   1
 7:   C 2017-01-30   2
 8:   C 2017-02-06   3
 9:   D 2017-01-31   2
10:   D 2017-02-02   1  

result with colors