规范化大型表中的多值列

时间:2014-03-23 10:29:02

标签: r data.table

我在将此this post中看到的VBA代码转换为R脚本时遇到问题。

问题如下,我有一个列(来自源数据库,而不是选择)包含属性的多个值。我想规范化这个表并保留每个单元格中每个值出现的顺序。

示例数据集:

dat <- data.frame(
  ID = c(1:3),
  Multi = c("VAL1 VAL2 VAL3","VAL2 VAL3","VAL3 VAL1")
  ,stringsAsFactors=FALSE)

  ID          Multi
1  1 VAL1 VAL2 VAL3
2  2      VAL2 VAL3
3  3 VAL2 VAL3 VAL1

伪代码类似于:

  1. 遍历每一行
  2. 拆分字符串Multi,空格作为分隔符
  3. 对于每个拆分字符串,在一个单独的data.frame中添加新行,其中包含ID,总字符串中的Order和值。
  4. 结果如下:

          ID   Order    Multi
    1      1       1     VAL1
    2      1       2     VAL2
    3      1       3     VAL3
    4      2       1     VAL2
    5      2       2     VAL3
    6      3       1     VAL2
    7      3       2     VAL3
    8      3       3     VAL1
    

    我目前正在考虑使用data.frame,我认为data.table更合适,因为我的表将有大约400.000这些行。

    我为没有准备任何代码而道歉,我仍在考虑是否需要使用apply系列,data.table或简单的for循环。我将随着我的进展更新此帖子。

1 个答案:

答案 0 :(得分:5)

以下是两种方式......

在基地R:

X <- setNames(strsplit(as.character(dat$Multi), " "), dat$ID)
X1 <- stack(X)
X1$order <- ave(X1$ind, X1$ind, FUN = seq_along)
X1
#   values ind order
# 1   VAL1   1     1
# 2   VAL2   1     2
# 3   VAL3   1     3
# 4   VAL2   2     1
# 5   VAL3   2     2
# 6   VAL2   3     1
# 7   VAL3   3     2
# 8   VAL1   3     3

或(更好):

X <- strsplit(as.character(dat[, "Multi"]), " ", fixed = TRUE)
len <- vapply(X, length, 1L)
data.frame(ID = rep(dat[, "ID"], len), order = sequence(len), 
           Multi = unlist(X, use.names=FALSE))

使用我的“splitstackshape”软件包中的concat.split.multiple(虽然可能在400,000行上效率不高)。

library(splitstackshape)
out <- concat.split.multiple(dat, "Multi", " ", "long")
out[order(out$ID, out$time), ]
#   ID time Multi
# 1  1    1  VAL1
# 4  1    2  VAL2
# 7  1    3  VAL3
# 2  2    1  VAL2
# 5  2    2  VAL3
# 8  2    3  <NA>
# 3  3    1  VAL2
# 6  3    2  VAL3
# 9  3    3  VAL1

并且,因为您请求了“data.table”:

library(data.table)
DT <- data.table(dat)
DTL <- DT[, list(unlist(strsplit(as.character(Multi), " "))), by = ID]
DTL[, order := sequence(.N), by = ID]
DTL
#    ID   V1 order
# 1:  1 VAL1     1
# 2:  1 VAL2     2
# 3:  1 VAL3     3
# 4:  2 VAL2     1
# 5:  2 VAL3     2
# 6:  3 VAL2     1
# 7:  3 VAL3     2
# 8:  3 VAL1     3

更新:某些时间

我没有费心去测试我的“splitstackshape”方法,因为它使用了read.table,因此我知道它无法满足性能要求。

然而,基地R似乎仍然胜出!

示例“大”数据:

dat_big <- do.call(rbind, replicate(floor(4000/3), dat, simplify = FALSE))
dat_big <- do.call(rbind, replicate(100, dat_big, simplify = FALSE))
dat_big$ID <- make.unique(as.character(dat_big$ID))

DT <- data.table(dat)
DT_big <- data.table(dat_big)

要测试的功能:

fun1 <- function(inDF) {
  X <- strsplit(as.character(inDF[, "Multi"]), " ", fixed = TRUE)
  len <- vapply(X, length, 1L)
  data.frame(ID = rep(inDF[, "ID"], len), order = sequence(len), 
             Multi = unlist(X, use.names=FALSE))
}

fun2 <- function(inDT) {
  DTL <- inDT[, list(unlist(strsplit(as.character(Multi), " ", fixed = TRUE))), by = ID]
  DTL[, order := sequence(.N), by = ID]
  DTL
}

基础R的结果:

system.time(outDF <- fun1(dat_big))
#    user  system elapsed 
#   6.418   0.000   6.454 
dim(outDF)
# [1] 1066400       3
head(outDF)
#   ID order Multi
# 1  1     1  VAL1
# 2  1     2  VAL2
# 3  1     3  VAL3
# 4  2     1  VAL2
# 5  2     2  VAL3
# 6  3     1  VAL2
tail(outDF)
#               ID order Multi
# 1066395 1.133299     3  VAL3
# 1066396 2.133299     1  VAL2
# 1066397 2.133299     2  VAL3
# 1066398 3.133299     1  VAL2
# 1066399 3.133299     2  VAL3
# 1066400 3.133299     3  VAL1

“data.table”的结果:

system.time(outDT <- fun2(DT_big))
#    user  system elapsed 
#  14.035   0.000  14.057 
dim(outDT)
# [1] 1066400       3
outDT
#                ID   V1 order
#       1:        1 VAL1     1
#       2:        1 VAL2     2
#       3:        1 VAL3     3
#       4:        2 VAL2     1
#       5:        2 VAL3     2
#      ---                    
# 1066396: 2.133299 VAL2     1
# 1066397: 2.133299 VAL3     2
# 1066398: 3.133299 VAL2     1
# 1066399: 3.133299 VAL3     2
# 1066400: 3.133299 VAL1     3
相关问题