1

澄清帖子底部的“地图”或“排序”

想象一下,我们有一个包含多个逻辑列的数据框和一个“映射”,对于这些逻辑列的特定组合,它会给出一个值。

计算与数据帧的每一行关联的值的最佳/最有效方法是什么。

我有以下三种可能的解决方案: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 行。本质上,我正在尝试基于笨拙的数据存储格式构建一个因素。因此,代码的清晰度是我的首要任务,速度/内存效率是我的第二要务。

提前致谢。

PS 修改问题标题的建议也欢迎。

澄清

真正的应用涉及一个包含 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 中实现这一点。

4

2 回答 2

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,就像您的“数据”一样,并且可以对其应用相同的功能。

于 2013-09-23T17:31:36.243 回答
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}。因此,只要每个“分支”的顺序正确,就没有关系。如果您为新分支提供了一些示例数据,我可以对此进行测试,但请尝试以下操作:

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]
于 2013-09-23T14:46:10.710 回答