将具有逻辑值的列折叠为一个

时间:2014-04-04 15:48:10

标签: r dataframe

模拟数据

df <- data.frame(posterior=rnorm(5), A=sample(c(T,F), 5, replace=T), 
             B=sample(c(T,F), 5, replace=T), C=sample(c(T,F), 5, replace=T) )

由此我希望产生一个包含2列的数据框:第一列是相同的,第二列是具有TRUE的所有变量(A,B或C)的名称。 (这是在变量选择的背景下完成的。)

我当前的解决方案在每个逻辑行上使用apply,然后按colnames(df[2:4])[row]提取变量名列表。有更好的解决方案吗?

3 个答案:

答案 0 :(得分:2)

这会创建您正在寻找的列,我相信:

df$Letters = apply(df[,2:4], 1, function(x) paste(names(x)[x], collapse=", "))

您可以使用df[,c("posterior", "Letters")]

将其隔离为两列

答案 1 :(得分:2)

如果速度很重要,这里是一个矢量化解决方案,但它不是特别直观:

combs <- c("","A","B","AB","C","AC","BC","ABC") #all letter combinations in a binary order
transform(df, vars = combs[rowSums(t(t(df[2:4])*2^(0:2)))+1])
    posterior     A     B     C vars
1  1.28037818  TRUE  TRUE FALSE   AB
2 -0.07794835 FALSE  TRUE FALSE    B
3  0.86463032 FALSE FALSE  TRUE    C
4 -1.04609087 FALSE  TRUE FALSE    B
5 -1.13092499  TRUE  TRUE  TRUE  ABC

基本上,您将行视为二进制数的表示,其中数字表示变量的存在或不存在。然后,您只需将此数字映射到组合向量。

修改

要自动生成梳子,您可以使用intToBitswhich

getCombs <- function(vars) c("", sapply(seq(2^length(vars)-1),
             function(x) paste(vars[which(intToBits(x)==1)],collapse="")))

getCombs(LETTERS[1:3])
[1] ""    "A"   "B"   "AB"  "C"   "AC"  "BC"  "ABC"

这需要不到一秒钟(在我的机器上)最多16个变量,每个附加变量的时间大约是两倍。这是因为在添加另一个变量时,组合数量会翻倍。

答案 2 :(得分:0)

我一直在花太多时间思考这个问题(因为我有类似的事情,我自己在做)。

我真的很喜欢詹姆斯的答案......但正如他所提到的,随着列数开始增加,它肯定会减慢很多。我也喜欢Senor O的直接apply答案,这是一个很难被击败的答案。

这就是我想出来的。

logiNames <- function(inmatrix) {
  if (!is.matrix(inmatrix)) inmatrix <- as.matrix(inmatrix)
  if (is.null(colnames(inmatrix))) {
    stop("The whole point of this is to work with colnames")
  }
  dims <- dim(inmatrix)
  cn <- colnames(inmatrix)
  if (!is.logical(inmatrix)) inmatrix <- as.logical(inmatrix)
  M <- character(length(inmatrix))
  M[inmatrix] <- rep(cn, each = dims[1])[inmatrix]
  dim(M) <- dims
  do.call(paste0, data.frame(M))
}

以下是几个例子......

示例数据:

set.seed(123)
ncol = 10
nrow = 10
m <- matrix(sample(c(TRUE, FALSE), ncol * nrow, TRUE), ncol = ncol, 
            dimnames=list(NULL, sample(c(letters, LETTERS), ncol, FALSE)))
n <- matrix(sample(c(1, 0), ncol * nrow, TRUE), ncol = ncol,
            dimnames=list(NULL, sample(c(letters, LETTERS), ncol, FALSE)))

logical矩阵上:

m
#           F     q     y     U     x     P     Q     B     s     g
#  [1,]  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE
#  [2,] FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
#  [3,]  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE
#  [4,] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE
#  [5,] FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE
#  [6,]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
#  [7,] FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE
#  [8,] FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE
#  [9,] FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE FALSE  TRUE
# [10,]  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE

logiNames(m)
#  [1] "FxPsg"    "qxPQ"     "FxQsg"    "xPQB"     "qUxBsg"   "FUxPQBsg" "qxPB"
#  [8] "qUxg"     "qyUxBg"   "FyUPQBs"     

在零和一的矩阵上:

n
#       z m k H c G q s K N
#  [1,] 0 0 0 1 0 0 0 0 1 1
#  [2,] 1 1 0 1 1 1 1 1 1 0
#  [3,] 1 1 0 1 1 0 0 0 0 0
#  [4,] 0 1 0 1 1 1 0 1 0 0
#  [5,] 0 1 0 0 1 1 0 0 0 1
#  [6,] 1 0 0 1 1 0 0 1 1 0
#  [7,] 0 1 0 1 0 0 1 1 1 1
#  [8,] 0 1 0 1 1 1 0 0 0 1
#  [9,] 0 1 0 1 1 1 0 0 1 1
# [10,] 1 0 1 0 1 1 0 0 0 1
logiNames(n)
#  [1] "HKN"     "zmHcGqsK" "zmHc"    "mHcGs"   "mcGN"    "zHcsK"   "mHqsKN"
#  [8] "mHcGN"   "mHcGKN"   "zkcGN" 

当您处理包含大量 的数据时,这种情况的好处才会明显。例如,在上面的数据创建步骤中将“ncol”更改为20并将“nrow”更改为1e5,这给了我这些时间:

set.seed(123)
ncol = 20
nrow = 1e5
m <- matrix(sample(c(TRUE, FALSE), ncol * nrow, TRUE), ncol = ncol, 
            dimnames=list(NULL, sample(c(letters, LETTERS), ncol, FALSE)))

fun3 <- function() apply(m, 1, function(x) paste(colnames(m)[x], collapse=""))

system.time(fun3())
#    user  system elapsed 
#   2.427   0.000   2.441 
system.time(logiNames(m))
#    user  system elapsed 
#   0.860   0.001   0.868