dcast 有效地使用多个变量的大型数据集

Posted

技术标签:

【中文标题】dcast 有效地使用多个变量的大型数据集【英文标题】:dcast efficiently large datasets with multiple variables 【发布时间】:2015-02-18 06:52:13 【问题描述】:

我正在尝试dcast 一个大型数据集(数百万行)。我有一排是到达时间和出发地,另一排是出发时间和目的地。在这两种情况下都有一个id 来标识该单元。它看起来像这样:

id  time            movement    origin  dest
1   10/06/2011 15:54    ARR        15    15
1   10/06/2011 16:14    DEP        15    29
2   10/06/2011 17:59    ARR        73    73
2   10/06/2011 18:10    DEP        73    75
2   10/06/2011 21:10    ARR        75    75
2   10/06/2011 21:20    DEP        75    73
3   10/06/2011 17:14    ARR        17    17
3   10/06/2011 18:01    DEP        17    48
4   10/06/2011 17:14    ARR        49    49
4   10/06/2011 17:26    DEP        49    15

所以,我想重新分配对 (ARR-DEP) 并有效地执行此操作(如 here)。因为它是一个非常大的数据集,所以 for loop 在这种情况下不起作用。理想的输出是

  index unitid origin   arr time    dest    dep time
    1    1     15   10/06/2011 14:33    29  10/06/2011 19:24
    2    2     73   10/06/2011 14:59    75  10/06/2011 17:23
    3    2     75   10/06/2011 21:10    73  10/06/2011 23:40

数据:

        df <- structure(list(time = structure(c(7L, 16L, 8L, 11L, 18L, 20L, 
10L, 12L, 3L, 6L, 15L, 19L, 9L, 4L, 5L, 14L, 1L, 2L, 13L, 17L
), .Label = c("10/06/2011 09:08", "10/06/2011 10:54", "10/06/2011 11:38", 
"10/06/2011 12:41", "10/06/2011 12:54", "10/06/2011 14:26", "10/06/2011 14:33", 
"10/06/2011 14:59", "10/06/2011 17:12", "10/06/2011 17:14", "10/06/2011 17:23", 
"10/06/2011 18:56", "10/06/2011 19:03", "10/06/2011 19:04", "10/06/2011 19:16", 
"10/06/2011 19:24", "10/06/2011 20:12", "10/06/2011 21:10", "10/06/2011 22:28", 
"10/06/2011 23:40"), class = "factor"), movement = structure(c(1L, 
2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 3L, 1L, 2L, 2L, 1L, 
2L, 2L, 3L), .Label = c("ARR", "DEP", "ITZ"), class = "factor"), 
    origin = c(15L, 15L, 73L, 73L, 75L, 75L, 17L, 17L, 49L, 49L, 
    15L, 15L, 32L, 10L, 10L, 17L, 76L, 76L, 76L, 76L), dest = c(15L, 
    29L, 73L, 75L, 75L, 73L, 17L, 48L, 49L, 15L, 15L, 49L, 32L, 
    10L, 17L, 10L, 76L, 65L, 76L, 65L), id = c(1L, 1L, 2L, 2L, 
    2L, 2L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, 6L, 6L, 6L, 7L, 7L, 8L, 
    8L)), .Names = c("time", "movement", "origin", "dest", "id"
), row.names = c(NA, -20L), class = "data.frame")

【问题讨论】:

也许你可以从1.9.8 branch of "data.table" 尝试dcast.data.table(但预计情况可能会改变,因为这还不是 CRAN 上的版本。 嗨@AnandaMahto,如果我只是想选择时间,这段代码(@akron)在dcast.data.table(setDT(df)[ ,c('.id', 'Seq'):= list(c('arrival', 'departure'), gl(.N,2, .N))], id+Seq~.id, value.var='time')之前已经工作过了,但是,如果我想添加出发地和目的地信息,我不' t 真的知道如何拿起它。请记住,这是一个非常大的数据集(数百万行) 你能告诉我你正在处理多少百万行吗? 每个文件 7-10 百万,要处理 100 多个文件 @user3507584 我认为这可以使用类似于 docendo discimus 使用 dplyr 显示的方法来解决。你试过dplyr/tidyr吗?最好知道时机,看看这种方法是否真的很好。 【参考方案1】:

这个怎么样?使用data.table

require(data.table)
setorder(setDT(df), id, time)
df[, grp := FALSE][movement == "ARR", grp := TRUE]
df[, .(time[grp], time[!grp], origin[grp], dest[!grp]), by=id]
#    id                  V1                  V2 V3 V4
# 1:  1 10/06/2011 14:33:57 10/06/2011 19:24:16 15 29
# 2:  2 10/06/2011 14:59:14 10/06/2011 17:23:20 73 75
# 3:  2 10/06/2011 21:10:56 10/06/2011 23:40:29 75 73
# 4:  3 10/06/2011 17:14:44 10/06/2011 18:56:39 17 48
# 5:  4 10/06/2011 11:38:43 10/06/2011 14:26:43 49 15
# 6:  4 10/06/2011 19:16:55 10/06/2011 22:28:14 15 49
# 7:  5 10/06/2011 10:41:20 10/06/2011 12:54:26 10 17
# 8:  6 10/06/2011 09:08:05 10/06/2011 10:54:48 76 65

如果您添加另一个值为!grp 的列并使用该列而不是在每个组上执行!grp,则可以稍微加快速度。


这是如何工作的:

setDT通过引用将data.frame转换为data.table。

setorder 根据提供的列(和顺序)通过引用重新排序 data.table。在这里,它根据列idtime 以升序重新排列df 的行。

然后我们使用data.table的sub-assign by reference添加一个额外的列,当movement == "ARR"movement == "DEP"时保存值TRUEFALSE

注意:df$movement 列中的因子水平有一个名为ITZ 的附加水平,它似乎不在此样本数据中。不知道如何处理。

现在我们要做的就是从origin 中选择1,3,5,.. 元素,从dest 中选择2,4,6,... .

只要ARR 时间始终早于DEP 时间(这是一个非常有效的假设),此方法就有效。


在 OP 对数据不一致的 Q 进行编辑之后:

na.omit(df[movement != "ITZ", .(time[grp], time[!grp], origin[grp], dest[!grp]), by=id])

【讨论】:

有 620 万行,时间为:setorder(setDT(df), id, time) 0.25 秒,df[, grp := FALSE][movement == "ARR", grp := TRUE] 0.53 秒,df[, .(time[grp], time[!grp], origin[grp], dest[!grp]), by=id] 6.52 秒。总计:7.3 秒,因此每秒处理近一百万 (900k) 行。非常感谢@Arun @Arun 这是很好的解决方案。【参考方案2】:

如果您的数据集的结构与示例中的一样,即每个 id 和 origin 有一个到达和一个离开时间,那么您可以手动执行此操作,只需重新排序和子集您的数据(当然您对此必须非常小心,并尝试添加尽可能多的检查,如下所示,以捕获错误)

dat <- df[order(df$id, df$origin, df$dest, df$movement), ]
dat.dep <- dat[dat$movement == "DEP", ]
dat.arr <- dat[dat$movement == "ARR", ]
stopifnot(nrow(dat.dep) == nrow(dat.arr) &
    dat.dep$origin == dat.arr$origin & 
    dat.dep$id == dat.arr$id)
result <- dat.dep[c("id", "origin", "dest")]
result$arr.time <- dat.arr$time
result$dep.time <- dat.dep$time
result 
#    id origin dest            arr.time            dep.time
# 2   1     15   29 10/06/2011 14:33:57 10/06/2011 19:24:16
# 4   2     73   75 10/06/2011 14:59:14 10/06/2011 17:23:20
# 6   2     75   73 10/06/2011 21:10:56 10/06/2011 23:40:29
# 8   3     17   48 10/06/2011 17:14:44 10/06/2011 18:56:39
# 12  4     15   49 10/06/2011 19:16:55 10/06/2011 22:28:14
# 10  4     49   15 10/06/2011 11:38:43 10/06/2011 14:26:43
# 14  5     10   17 10/06/2011 10:41:20 10/06/2011 12:54:26
# 16  6     76   65 10/06/2011 09:08:05 10/06/2011 10:54:48

【讨论】:

@Arun 我明白这一点。不是每个数据点都由其 ID、来源和移动唯一确定吗? @Arun aaah 现在我明白了。所以问题在于我的措辞。我没有在解决方案中假设这一点,我的意思是每个 id 和 origin 一个到达和一个离开.. 谢谢 :)

以上是关于dcast 有效地使用多个变量的大型数据集的主要内容,如果未能解决你的问题,请参考以下文章

基于多个字段搜索大型数据集的有效方法

如何使用 PHP 和 MySQL 有效地对大型数据集进行分页?

从大型数据集的数据框有效地创建矩阵

有效地组织大型数据集以进行报告

在 Javascript 中有效地逐步过滤大型数据集

大 HDF5 数据集,如何在每个 epoch 后有效地洗牌