避免合并和临时 data.tables:改进我对 data.table 代码的草率使用
Avoiding merges and temporary data.tables: Refining my sloppy use of data.table code
我正在列出一项纵向研究的回复,在该研究中,参与者被要求自愿回应 4 项调查。每个参与者都有一个唯一的 PartID。每个参与者都分配有一个 SectionID(字母)。已尝试并已完成的调查由 StatusID="Complete" 指示。已尝试但未完成的调查由 StatusID="Incomplete" 指示。未尝试参与调查的参与者不会有记录,但在结果列表中对该调查计为“0”。
输入数据示例:
PartID SectionID Status SurveyID
1: 100 A Complete 1
2: 100 A Complete 2
3: 100 A Complete 3
4: 100 A Complete 4
5: 101 B Incomplete 1
6: 101 B Complete 2
7: 101 B Complete 3
8: 101 B Complete 4
9: 102 A Incomplete 1
10: 103 B Incomplete 4
11: 104 B Incomplete 2
12: 105 A Complete 1
13: 105 A Complete 1
14: 105 A Complete 3
下面的代码可以工作,但是非常草率。我假设有一种更简洁、更优雅的方式使用 data.table 来完成此数据处理?特别是,我想避免临时变量,以及合并两个 data.table 的需要。
library(data.table)
DT <- fread ("PartID,SectionID,Status,SurveyID
100,A,Complete,1
100,A,Complete,2
100,A,Complete,3
100,A,Complete,4
101,B,Incomplete,1
101,B,Complete,2
101,B,Complete,3
101,B,Complete,4
102,A,Incomplete,1
103,B,Incomplete,4
104,B,Incomplete,2
105,A,Complete,1
105,A,Complete,1
105,A,Complete,3\n")
setkey(DT, PartID)
DT2<-DT
setkey(DT2,PartID, SectionID)
DT2<-DT2[Status=="Complete",.(c1=sum(SurveyID==1),c2=sum(SurveyID==2),c3=sum(SurveyID==3), c4=sum(SurveyID==4)), by=.(PartID,SectionID)]
DT3<-DT
setkey(DT3,PartID, SectionID)
DT3<-DT3[Status=="Incomplete",.(i1=sum(SurveyID==1),i2=sum(SurveyID==2),i3=sum(SurveyID==3), i4=sum(SurveyID==4)), by=.(PartID,SectionID)]
DT4<-merge(DT2,DT3, all=TRUE )
DT4[is.na(DT4)] <- 0
DT4
上面代码的输出是正确的,并且是(注意:c1 表示已完成调查 #1,i1 表示调查 #1 未完成。另请注意,参与者可以在每个调查中提交多个回复) :
PartID SectionID c1 c2 c3 c4 i1 i2 i3 i4
1: 100 A 1 1 1 1 0 0 0 0
2: 101 B 0 1 1 1 1 0 0 0
3: 102 A 0 0 0 0 1 0 0 0
4: 103 B 0 0 0 0 0 0 0 1
5: 104 B 0 0 0 0 0 1 0 0
6: 105 A 2 0 1 0 0 0 0 0
你可以使用 dcast
library(data.table)#v1.9.5+
dcast(DT[, N :=.N,list(PartID, SectionID, SurveyID)][,
Status1:= paste0(tolower(substr(Status,1,1)), SurveyID)],
PartID+SectionID~Status1, value.var='N', length)
# PartID SectionID c1 c2 c3 c4 i1 i2 i4
#1: 100 A 1 1 1 1 0 0 0
#2: 101 B 0 1 1 1 1 0 0
#3: 102 A 0 0 0 0 1 0 0
#4: 103 B 0 0 0 0 0 0 1
#5: 104 B 0 0 0 0 0 1 0
#6: 105 A 2 0 1 0 0 0 0
如果您需要 i3
DT1 <- DT[, N :=.N,list(PartID, SectionID, SurveyID)][,
Status1:= paste0(tolower(substr(Status,1,1)), SurveyID)]
DT2 <- data.table(Status1=paste0(rep(c('c', 'i'),each=4), 1:4))
na.omit(dcast(setkey(DT1, Status1)[DT2],
PartID+SectionID~Status1, value.var='N', length))
# PartID SectionID c1 c2 c3 c4 i1 i2 i3 i4
#1: 100 A 1 1 1 1 0 0 0 0
#2: 101 B 0 1 1 1 1 0 0 0
#3: 102 A 0 0 0 0 1 0 0 0
#4: 103 B 0 0 0 0 0 0 0 1
#5: 104 B 0 0 0 0 0 1 0 0
#6: 105 A 2 0 1 0 0 0 0 0
另一个不会改变您的 DT
-Table 的解决方案是:
dt2 <- DT[,.(.N), by=.(PartID,SectionID, SurveyID, Status)]
dcast.data.table(dt2,PartID + SectionID ~ Status + SurveyID, value.var='N', sum)
导致
PartID SectionID Complete_1 Complete_2 Complete_3 Complete_4 Incomplete_1 Incomplete_2 Incomplete_4
1: 100 A 1 1 1 1 0 0 0
2: 101 B 0 1 1 1 1 0 0
3: 102 A 0 0 0 0 1 0 0
4: 103 B 0 0 0 0 0 0 1
5: 104 B 0 0 0 0 0 1 0
6: 105 A 2 0 1 0 0 0 0
akrun 的解决方案还展示了如何在需要时重命名列。
我正在列出一项纵向研究的回复,在该研究中,参与者被要求自愿回应 4 项调查。每个参与者都有一个唯一的 PartID。每个参与者都分配有一个 SectionID(字母)。已尝试并已完成的调查由 StatusID="Complete" 指示。已尝试但未完成的调查由 StatusID="Incomplete" 指示。未尝试参与调查的参与者不会有记录,但在结果列表中对该调查计为“0”。
输入数据示例:
PartID SectionID Status SurveyID
1: 100 A Complete 1
2: 100 A Complete 2
3: 100 A Complete 3
4: 100 A Complete 4
5: 101 B Incomplete 1
6: 101 B Complete 2
7: 101 B Complete 3
8: 101 B Complete 4
9: 102 A Incomplete 1
10: 103 B Incomplete 4
11: 104 B Incomplete 2
12: 105 A Complete 1
13: 105 A Complete 1
14: 105 A Complete 3
下面的代码可以工作,但是非常草率。我假设有一种更简洁、更优雅的方式使用 data.table 来完成此数据处理?特别是,我想避免临时变量,以及合并两个 data.table 的需要。
library(data.table)
DT <- fread ("PartID,SectionID,Status,SurveyID
100,A,Complete,1
100,A,Complete,2
100,A,Complete,3
100,A,Complete,4
101,B,Incomplete,1
101,B,Complete,2
101,B,Complete,3
101,B,Complete,4
102,A,Incomplete,1
103,B,Incomplete,4
104,B,Incomplete,2
105,A,Complete,1
105,A,Complete,1
105,A,Complete,3\n")
setkey(DT, PartID)
DT2<-DT
setkey(DT2,PartID, SectionID)
DT2<-DT2[Status=="Complete",.(c1=sum(SurveyID==1),c2=sum(SurveyID==2),c3=sum(SurveyID==3), c4=sum(SurveyID==4)), by=.(PartID,SectionID)]
DT3<-DT
setkey(DT3,PartID, SectionID)
DT3<-DT3[Status=="Incomplete",.(i1=sum(SurveyID==1),i2=sum(SurveyID==2),i3=sum(SurveyID==3), i4=sum(SurveyID==4)), by=.(PartID,SectionID)]
DT4<-merge(DT2,DT3, all=TRUE )
DT4[is.na(DT4)] <- 0
DT4
上面代码的输出是正确的,并且是(注意:c1 表示已完成调查 #1,i1 表示调查 #1 未完成。另请注意,参与者可以在每个调查中提交多个回复) :
PartID SectionID c1 c2 c3 c4 i1 i2 i3 i4
1: 100 A 1 1 1 1 0 0 0 0
2: 101 B 0 1 1 1 1 0 0 0
3: 102 A 0 0 0 0 1 0 0 0
4: 103 B 0 0 0 0 0 0 0 1
5: 104 B 0 0 0 0 0 1 0 0
6: 105 A 2 0 1 0 0 0 0 0
你可以使用 dcast
library(data.table)#v1.9.5+
dcast(DT[, N :=.N,list(PartID, SectionID, SurveyID)][,
Status1:= paste0(tolower(substr(Status,1,1)), SurveyID)],
PartID+SectionID~Status1, value.var='N', length)
# PartID SectionID c1 c2 c3 c4 i1 i2 i4
#1: 100 A 1 1 1 1 0 0 0
#2: 101 B 0 1 1 1 1 0 0
#3: 102 A 0 0 0 0 1 0 0
#4: 103 B 0 0 0 0 0 0 1
#5: 104 B 0 0 0 0 0 1 0
#6: 105 A 2 0 1 0 0 0 0
如果您需要 i3
DT1 <- DT[, N :=.N,list(PartID, SectionID, SurveyID)][,
Status1:= paste0(tolower(substr(Status,1,1)), SurveyID)]
DT2 <- data.table(Status1=paste0(rep(c('c', 'i'),each=4), 1:4))
na.omit(dcast(setkey(DT1, Status1)[DT2],
PartID+SectionID~Status1, value.var='N', length))
# PartID SectionID c1 c2 c3 c4 i1 i2 i3 i4
#1: 100 A 1 1 1 1 0 0 0 0
#2: 101 B 0 1 1 1 1 0 0 0
#3: 102 A 0 0 0 0 1 0 0 0
#4: 103 B 0 0 0 0 0 0 0 1
#5: 104 B 0 0 0 0 0 1 0 0
#6: 105 A 2 0 1 0 0 0 0 0
另一个不会改变您的 DT
-Table 的解决方案是:
dt2 <- DT[,.(.N), by=.(PartID,SectionID, SurveyID, Status)]
dcast.data.table(dt2,PartID + SectionID ~ Status + SurveyID, value.var='N', sum)
导致
PartID SectionID Complete_1 Complete_2 Complete_3 Complete_4 Incomplete_1 Incomplete_2 Incomplete_4
1: 100 A 1 1 1 1 0 0 0
2: 101 B 0 1 1 1 1 0 0
3: 102 A 0 0 0 0 1 0 0
4: 103 B 0 0 0 0 0 0 1
5: 104 B 0 0 0 0 0 1 0
6: 105 A 2 0 1 0 0 0 0
akrun 的解决方案还展示了如何在需要时重命名列。