R 基于较大数据帧的多个子集创建多个列表

R 基于较大数据帧的多个子集创建多个列表,r,subset,plyr,R,Subset,Plyr,在R中工作时,我的数据结构与下面类似(代码块1)。我希望创建一个具有以下特征的新data.frame: 对于每个唯一的ID_1值,我希望有两个新列,一个包含(共享ID_1&Direction==1的ID_2)列表,另一个列包含(共享ID_1&Direction==0的ID_2)列表(见下一个代码块2) 数据集块1(首字母): ID_1 ID_2 Direction 100001 1 1 100001 11 1 100001 111

在R中工作时,我的数据结构与下面类似(代码块1)。我希望创建一个具有以下特征的新data.frame:

对于每个唯一的ID_1值,我希望有两个新列,一个包含(共享ID_1&Direction==1的ID_2)列表,另一个列包含(共享ID_1&Direction==0的ID_2)列表(见下一个代码块2)

数据集块1(首字母):

ID_1    ID_2    Direction
100001  1           1
100001  11          1
100001  111         1
100001  1111        0
100001  11111       0
100001  111111      0
100002  2           1
100002  22          1
100002  222         0
100002  2222        0
100003  3           1
100003  33          1
100003  333         1
100003  3333        0
100003  33333       0
100003  333333      1
100004  4           1
100004  44          1
ID_1    ID_2_D1             ID_2_D0
100001  1,11,111            1111,11111,111111
100002  2,22                222,222
100003  3,33,333,333333     3333,33333
100004  4,44    
转换为:

数据集块2(所需输出):

ID_1    ID_2    Direction
100001  1           1
100001  11          1
100001  111         1
100001  1111        0
100001  11111       0
100001  111111      0
100002  2           1
100002  22          1
100002  222         0
100002  2222        0
100003  3           1
100003  33          1
100003  333         1
100003  3333        0
100003  33333       0
100003  333333      1
100004  4           1
100004  44          1
ID_1    ID_2_D1             ID_2_D0
100001  1,11,111            1111,11111,111111
100002  2,22                222,222
100003  3,33,333,333333     3333,33333
100004  4,44    
我有这样做的代码(对子集的子集进行循环),但我在数百万个唯一的“ID_1”上运行它,这非常耗时(我告诉你,几个小时!)

有什么建议吗?也许使用apply()或plyr()包可以让它运行得更快


参考代码:

DF <- data.frame(ID_1=c(100001,100001,100001,100001,100001,100001,100002,100002,100002,100002,100003,100003,100003,100003,100003,100003,100004,100004)
                   ,ID_2=c(1,11,111,1111,11111,111111,2,22,222,2222,3,33,333,3333,33333,333333,4,44)
                   ,Direction=c(1,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1)
                   )

DF您当然可以在这里使用apply函数。我不确定你是否需要,(也就是说,你可以通过子集变得更快),但我想不出你现在会怎么做。你可以像这样实现你想要的:

# Direction = 1
d1 <- lapply( unique( DF$ID_1 ) , function(x){ subset( DF , ID_1== x & Direction == 1)$ID_2 } )
d1 <- sapply( d1 , function(x){ paste0( x , sep = "," , collapse = "" ) } )
# Direction = 0
d0 <- lapply( unique( DF$ID_1 ) , function(x){ subset( DF , ID_1== x & Direction == 0)$ID_2 } )
d0 <- sapply( d0 , function(x){ paste0( x , sep = "," , collapse = "" ) } )


# Results dataframe
resDF <- data.frame(ID_1 = unique(DF$ID_1), d1, d0)
resDF
              d1                 d0                  
[1,] "100001" "1,11,111,"        "1111,11111,111111,"
[2,] "100002" "2,22,"            "222,2222,"         
[3,] "100003" "3,33,333,333333," "3333,33333,"       
[4,] "100004" "4,44,"            "," 
#方向=1
d1像这样:

library(reshape2)
dcast(DF, ID_1 ~ Direction, value.var = "ID_2", list)
#     ID_1                   0                  1
# 1 100001 1111, 11111, 111111         1, 11, 111
# 2 100002           222, 2222              2, 22
# 3 100003         3333, 33333 3, 33, 333, 333333
# 4 100004                                  4, 44    

@flodel的答案是迄今为止我能想到的最直接的答案,但在BaseR中有一个选项,使用
aggregate
merge
。它利用
aggregate
步骤中的“
subset
”参数获取“Direction==0”和“Direction==1”时的单独列


当然,这里有一个使用<代码>数据>表< /> >的方法,您可能需要考虑,正如您所提到的,必须处理超过几百万个唯一的“ID1”S*。您不太可能从这个小示例中看到任何速度优势,但您应该使用实际数据

library(data.table)
DT <- data.table(DF, key = "ID_1")
DT0 <- DT[Direction == 0, list(D0 = list(ID_2)), by = key(DT)]
DT1 <- DT[Direction == 1, list(D1 = list(ID_2)), by = key(DT)]
DT0[DT1]
#      ID_1                D0              D1
# 1: 100001 1111,11111,111111        1,11,111
# 2: 100002          222,2222            2,22
# 3: 100003        3333,33333 3,33,333,333333
# 4: 100004                              4,44

这在一个小数据集上执行得非常好。我想知道它的伸缩性有多好?我希望最后一步类似于
data.frame(ID_1=unique(DF$ID_1),d1,d0)
而不是
cbind(…)
+1按照您的建议进行编辑!是的,这个解决方案可能不是最好的。事实上,与弗洛德尔超优雅的一款衬里相比,它看起来确实有些过时。然而,
dcast
cast
确实在内部使用了许多apply函数族。不过,我无法估计有多少实际工作是由
dcast
中的
apply
函数完成的。您的代码非常有用(但我很快就发现flodel的建议)。谢谢大家!@SimonO101,我忘了在前面提到一件事:我对你的答案的一个批评是,用这种方法你实际上只得到一个字符串。其他方法将值保持为
向量的
列表
,因此,如果需要,可以轻松地进一步处理该数据。尽管如此,看到替代方案并获得更多想法还是很棒的!我的上帝。通过测试我的完整数据的一小部分,我的代码(在上面的原始帖子中)在20多小时内完成了完整数据集的测试(因此,我的帖子)。你的代码在5秒内完成了--我想我能分辨出一个好的程序员和我自己之间的区别。谢谢你为我节省了很多时间@出于兴趣的经济法庭什么方法最快?我假设这是一个,但它看起来像是
dcast
正在使用
lappy
和表兄妹,所以我想知道它是否更快?如果此解决方案对您有效,请务必勾选解决方案顶部旁边的绿色箭头,以便将此问题标记为已回答。对于你们来说,这是一个愚蠢的问题——但好奇的是,您是否可以告诉我(或者,给我一个指向维基百科文章或cran文档的链接),为什么dcast比我的循环快得多--我在数百万行上完成了这项工作,dcast在17秒内完成了这项工作(加上一点额外的子集和合并),正如我所说的,我的方法在一天内完成。(只是想了解基本理论)和@SimonO101——在我的数据上(忽略所有额外需要的子集和合并位:)(1)flodel的dcast:7.4秒,(2)Ananda Mahto的聚合在同一个任务上花费了23秒,(3)Arun的补充:我真的无法运行,抱歉,(4)我的循环被记录在190小时内完成(我记录了0.01%的子集,耗时68秒)。+1一组非常好的解决方案。我喜欢这个问题,所以我可以检查你的参考答案。
library(data.table)
DT <- data.table(DF, key = "ID_1")
DT0 <- DT[Direction == 0, list(D0 = list(ID_2)), by = key(DT)]
DT1 <- DT[Direction == 1, list(D1 = list(ID_2)), by = key(DT)]
DT0[DT1]
#      ID_1                D0              D1
# 1: 100001 1111,11111,111111        1,11,111
# 2: 100002          222,2222            2,22
# 3: 100003        3333,33333 3,33,333,333333
# 4: 100004                              4,44
DT[, list(list(D0 = ID_2[Direction==0]), list(D1 = ID_2[Direction == 1])), by=ID_1]