【发布时间】:2013-09-28 10:40:04
【问题描述】:
在帖子底部澄清“地图”或“排序”
假设我们有一个包含多个逻辑列的数据框,以及一个“映射”,对于这些逻辑列的特定组合,它会给出一个值。
计算与数据框每一行关联的值的最佳/最有效方法是什么。
我有以下三种可能的解决方案:ifelse()、merge() 和 table()。我将不胜感激任何 cmets 或替代解决方案。
[抱歉,一篇比较长的帖子]
考虑以下示例数据框:
# 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 行。本质上,我正在尝试基于笨拙的数据存储格式构建一个因素。因此,代码的清晰性是我的第一要务,速度/内存效率是我的第二要务。
提前致谢。
附:也欢迎提出修改问题标题的建议。
澄清
真正的应用涉及一个包含 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 中实现这一点。
【问题讨论】:
-
仅供参考,“地图”已经是 R 函数。
-
@Frank,好地方。所以“映射”可能不是我想要的词。
-
不,我猜是;我只是说你不想给已经在使用的 R 对象命名。
-
@Frank...您当然是对的,这是不好的做法。一开始我误解了你的意思。我觉得有趣的是,在使用 R 5 年多之后,我仍然在
base中找到我从未听说过的功能。也破坏了我总是用大写字母开始用户对象以避免这种情况的计划。 -
是的,我很惊讶地发现,R 中的任何内容也都以大写字母开头。如果您查看
?Map,您会看到更多。也许他们想使用我听说过的 Map-Reduce 方法中的确切名称。