在帖子底部澄清“地图”或“排序”
想象一下,我们有一个包含多个逻辑列的数据框,以及一个'map',对于这些逻辑列的特定组合,它给出了一个值。
计算与数据帧的每一行相关联的值的最佳/最有效方法是什么。
我有三种可能的解决方案:ifelse(),merge()和table()。我很感激任何评论或替代解决方案。
[道歉,一个相当长的帖子]
考虑以下示例数据框:
# Generate example
#N <- 15
#Data <- data.frame(A=sample(c(FALSE,TRUE),N,TRUE,c(8,2)),
# B=sample(c(FALSE,TRUE),N,TRUE,c(6,4)),
# C=sample(c(FALSE,TRUE),N,TRUE,c(7,3)),
# D=sample(c(FALSE,TRUE),N,TRUE,c(7,3)))
# Specific example used in this question
Data <- structure(list(A = c(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE,
FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE),
B = c(FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE,
FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE), C = c(FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,
FALSE, TRUE, FALSE, FALSE, FALSE), D = c(TRUE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE,
FALSE, TRUE, FALSE)), .Names = c("A", "B", "C", "D"),
class = "data.frame", row.names = c(NA,-15L))
A B C D
1 FALSE FALSE FALSE TRUE
2 FALSE FALSE FALSE FALSE
3 FALSE TRUE FALSE FALSE
4 TRUE FALSE FALSE FALSE
5 FALSE FALSE FALSE FALSE
6 FALSE TRUE FALSE FALSE
7 FALSE TRUE FALSE FALSE
8 FALSE FALSE FALSE FALSE
9 FALSE FALSE FALSE FALSE
10 TRUE FALSE TRUE TRUE
11 FALSE TRUE FALSE TRUE
12 FALSE FALSE TRUE FALSE
13 FALSE TRUE FALSE FALSE
14 FALSE FALSE FALSE TRUE
15 FALSE FALSE FALSE FALSE
结合以下地图:
# A -> B -> C
# \_ D
### To clarify, if someone has both B & D TRUE (with C FALSE), D is higher than B
### i.e. there can be no ties
这定义了逻辑列的排序。我想要的最终值是每行中的“最高”列。这样,如果C列为真,我们总是返回C.如果C为FALSE且D为真,我们只返回“D”。
这种天真的方法是嵌套ifelse语句:
Data$Highest <- with(Data, ifelse( C, "C",
ifelse( D, "D",
ifelse( B, "B",
ifelse( A, "A", "none")
)
)
)
)
但是,对于包含许多列的复杂排序,该代码难以阅读/维护并且变得非常复杂。
我可以快速生成从列组合到所需输出的映射:
Map <- expand.grid( lapply( lapply( Data[c("A","B","C","D")], unique ), sort ) )
Map$Value <- factor(NA, levels=c("A","B","C","D","none"))
Map$Value[which(Map$A)] <- "A"
Map$Value[which(Map$B)] <- "B"
Map$Value[which(Map$D)] <- "D"
Map$Value[which(Map$C)] <- "C"
Map$Value[which(is.na(Map$Value))] <- "none"
A B C D Value
1 FALSE FALSE FALSE FALSE none
2 TRUE FALSE FALSE FALSE A
3 FALSE TRUE FALSE FALSE B
4 TRUE TRUE FALSE FALSE B
5 FALSE FALSE TRUE FALSE C
6 TRUE FALSE TRUE FALSE C
7 FALSE TRUE TRUE FALSE C
8 TRUE TRUE TRUE FALSE C
9 FALSE FALSE FALSE TRUE D
10 TRUE FALSE FALSE TRUE D
11 FALSE TRUE FALSE TRUE D
12 TRUE TRUE FALSE TRUE D
13 FALSE FALSE TRUE TRUE C
14 TRUE FALSE TRUE TRUE C
15 FALSE TRUE TRUE TRUE C
16 TRUE TRUE TRUE TRUE C
可以与merge()一起使用:
merge( Data, Map, by=c("A","B","C","D"), all.y=FALSE )
A B C D Highest Value
1 FALSE FALSE FALSE FALSE none none
2 FALSE FALSE FALSE FALSE none none
3 FALSE FALSE FALSE FALSE none none
4 FALSE FALSE FALSE FALSE none none
5 FALSE FALSE FALSE FALSE none none
6 FALSE FALSE FALSE TRUE D D
7 FALSE FALSE FALSE TRUE D D
8 FALSE FALSE TRUE FALSE C C
9 FALSE TRUE FALSE FALSE B B
10 FALSE TRUE FALSE FALSE B B
11 FALSE TRUE FALSE FALSE B B
12 FALSE TRUE FALSE FALSE B B
13 FALSE TRUE FALSE TRUE D D
14 TRUE FALSE FALSE FALSE A A
15 TRUE FALSE TRUE TRUE C C
但是,merge()函数当前不保留行顺序。虽然有各种各样的方法。
我最后的想法是使用一个4维表格,其中包含与地图相对应的字符条目:
Map2 <- table( lapply( Data[c("A","B","C","D")], unique ) )
Map2[] <- "none"
Map2["TRUE",,,] <- "A"
Map2[,"TRUE",,] <- "B"
Map2[,,,"TRUE"] <- "D"
Map2[,,"TRUE",] <- "C"
但是我发现以上几行不清楚(也许有更好的方法来制作表格?我认为可以将Map转换为Map2,但我看不出怎么样)。
然后我们使用矩阵索引来提取相应的值:
BOB <- as.matrix(Data[c("A","B","C","D")])
cBOB <- matrix(as.character(BOB),nrow=NROW(BOB),ncol=NCOL(BOB),dimnames=dimnames(BOB))
Data$Alt.Highest <- Map2[cBOB]
A B C D Highest Alt.Highest
1 FALSE FALSE FALSE TRUE D D
2 FALSE FALSE FALSE FALSE none none
3 FALSE TRUE FALSE FALSE B B
4 TRUE FALSE FALSE FALSE A A
5 FALSE FALSE FALSE FALSE none none
6 FALSE TRUE FALSE FALSE B B
7 FALSE TRUE FALSE FALSE B B
8 FALSE FALSE FALSE FALSE none none
9 FALSE FALSE FALSE FALSE none none
10 TRUE FALSE TRUE TRUE C C
11 FALSE TRUE FALSE TRUE D D
12 FALSE FALSE TRUE FALSE C C
13 FALSE TRUE FALSE FALSE B B
14 FALSE FALSE FALSE TRUE D D
15 FALSE FALSE FALSE FALSE none none
总而言之,有没有更好的方法来实现这种'映射'类型操作以及对这些方法效率的任何想法?
对于我感兴趣的应用程序,我有九列和一个有三个分支的订购图表,适用于3000行。本质上,我试图构建一个基于笨拙的数据存储格式的因子。所以代码的清晰度是我的第一要务,速度/内存效率是我的第二个。
提前致谢。
P.S。修改问题标题的建议也欢迎。
澄清
真实的申请涉及一份问卷,其中有9个问题,询问受访者是否达到了特定的教育/资格水平。这些是二元是/否响应。
我们想要的是生成一个新变量'达到最高资格'。
问题是9级不形成简单的堆栈。例如,可以在不进入大学的情况下实现专业资格(特别是在年龄较大的受访者中)。
我们设计了一个'地图'或'排序',这样,对于每个响应组合,我们都有一个'最高资格'(这个顺序是主观的,因此希望简化实现替代订单)。
# So given the nine responses: A, B, C, D, E, F, G, H, I
# we define an ordering as:
# D > C > B > A
# F > E
# E > A
# E == B
# I > H
# H == B
# G == B
# which has a set of order relationships. There is equality in this example
# A -> B -> C -> D
# \_ E -> F
# \_ H -> I
# \_ G
# 0 1 2 3 4
# We could then have five levels in out final 'highest' ordered factor: none, 1, 2, 3, 4
# Or we could decide to add more levels to break certain ties.
R问题是,给定一个排序(以及如何处理关系),将逻辑列的组合映射到“最高实现”值。如何最好地在R。
中实现这一点答案 0 :(得分:2)
我想我可能不理解你的订购概念。如果不允许绑定,并且您确切地知道每个字母与其他字母的比较,则表示存在严格的排序,可以将其分解为从最高到最低的简单向量。如果这不是真的,那么也许你可以给出一个更难的例子。如果是真的,那么你可以很容易地编写代码:
order<-c('C','D','B','A')
reordered.Data<-Data[order]
Data$max<-
c(order,'none')[apply(reordered.Data,1,function(x) min(which(c(x,TRUE))))]
# A B C D max
# 1 FALSE FALSE FALSE TRUE D
# 2 FALSE FALSE FALSE FALSE none
# 3 FALSE TRUE FALSE FALSE B
# 4 TRUE FALSE FALSE FALSE A
# 5 FALSE FALSE FALSE FALSE none
# 6 FALSE TRUE FALSE FALSE B
# 7 FALSE TRUE FALSE FALSE B
# 8 FALSE FALSE FALSE FALSE none
# 9 FALSE FALSE FALSE FALSE none
# 10 TRUE FALSE TRUE TRUE C
# 11 FALSE TRUE FALSE TRUE D
# 12 FALSE FALSE TRUE FALSE C
# 13 FALSE TRUE FALSE FALSE B
# 14 FALSE FALSE FALSE TRUE D
# 15 FALSE FALSE FALSE FALSE none
我想我现在明白你的订购概念了。但是,我认为你可以先放心地忽略它。例如,G
与B
的级别相同。但永远不会比较G
和B
;你只能有一个{B,E,H,G}
。所以,只要每个&#34;分支&#34;按照正确的顺序,它不重要。如果您为新分支提供了一些示例数据,我可以测试一下,但尝试这样的事情:
order<-c(D,C,F,I,B,E,H,G,A)
levs<-c(4,3,3,3,2,2,2,2,1)
names(levs)<-order
reordered.Data<-Data[order]
Data$max<-
c(order,'none')[apply(reordered.Data,1,function(x) min(which(c(x,TRUE))))]
Data$lev<-levs[Data$max]
答案 1 :(得分:2)
这是一个data.table方法:
require(data.table)
DT <- data.table(Data)
valord <- c('none','A','B','D','C')
DT[,val:={
vals <- c('none'=TRUE,unlist(.SD))[valord]
names(vals)[max(which(vals))]
},by=1:nrow(DT)]
结果是
A B C D val
1: FALSE FALSE FALSE TRUE D
2: FALSE FALSE FALSE FALSE none
3: FALSE TRUE FALSE FALSE B
4: TRUE FALSE FALSE FALSE A
5: FALSE FALSE FALSE FALSE none
6: FALSE TRUE FALSE FALSE B
7: FALSE TRUE FALSE FALSE B
8: FALSE FALSE FALSE FALSE none
9: FALSE FALSE FALSE FALSE none
10: TRUE FALSE TRUE TRUE C
11: FALSE TRUE FALSE TRUE D
12: FALSE FALSE TRUE FALSE C
13: FALSE TRUE FALSE FALSE B
14: FALSE FALSE FALSE TRUE D
15: FALSE FALSE FALSE FALSE none
如果你跑
class(DT) # [1] "data.table" "data.frame"
您会看到这是一个data.frame,就像您的“数据”一样,并且可以应用相同的功能。