合并 logical/binary 数据框的相同列名

Merge same column names of a logical/binary dataframe

我有一个逻辑数据框,如:

> test
  apple apple apple  kiwi kiwi banana banana banana apple orange
1 FALSE  TRUE FALSE FALSE TRUE  FALSE   TRUE   TRUE  TRUE  FALSE
2  TRUE  TRUE  TRUE  TRUE TRUE   TRUE  FALSE   TRUE  TRUE  FALSE
3 FALSE FALSE FALSE  TRUE TRUE  FALSE  FALSE  FALSE FALSE  FALSE

我的目标是合并具有相同列名的列。也就是说输出应该是一个有 4 列(苹果、奇异果、香蕉、橙子)的数据框。

我试过了:

testmerge <- df[, !duplicated(colnames(df))]

但输出不是我要找的。对于给定相同列名的每一行,只要至少有 1 个 TRUE,输出就应该为 True。对于给定相同列名的每一行,如果有 0 个 TRUE,则输出应该为 False。

例如第一行第一列应该是 TRUE 而不是 FALSE。

不需要的 testmerge 输出:

  apple  kiwi banana orange
1 FALSE FALSE  FALSE  FALSE
2  TRUE  TRUE   TRUE  FALSE
3 FALSE  TRUE  FALSE  FALSE

期望的输出:

  apple  kiwi banana orange
1 TRUE   TRUE TRUE   FALSE
2 TRUE   TRUE TRUE   FALSE
3 FALSE  TRUE FALSE  FALSE

复制数据帧:

   test <- structure(list(apple = c(FALSE, TRUE, FALSE), apple = c(TRUE, TRUE, 
FALSE), apple = c(FALSE, TRUE, FALSE), kiwi = c(FALSE, TRUE, TRUE
), kiwi = c(TRUE, TRUE, TRUE), banana = c(FALSE, TRUE, FALSE), banana = c(TRUE, 
FALSE, FALSE), banana = c(TRUE, TRUE, FALSE), apple = c(TRUE, TRUE, 
FALSE), orange = c(FALSE, FALSE, FALSE)), .Names = c("apple", "apple", 
"apple", "kiwi", "kiwi", "banana", "banana", "banana", "apple", "orange"), row.names = c(NA, 
-3L), class = "data.frame")

也许有更有效的方法来实现这一点,但这里是一个尝试

我建议使用 make.unique 将列名转换为唯一的列名,然后转换为长格式,通过行 ID 和列名(再次成为唯一的)检查您的条件,然后再转换回来宽格式,比如

library(data.table)
setnames(setDT(test), make.unique(names(test))) # Make column names unique
res <- melt(test[, id := .I], id = "id" # Add a row index and melt by it
           )[, sum(value) > 0, # Check condition >>
               by = .(id, Names = sub("\..*", "", variable))] # by row id and unique names
dcast(res, id ~ Names, value.var = "V1") # Convert back to wide format
#    id apple banana kiwi orange
# 1:  1  TRUE   TRUE TRUE  FALSE
# 2:  2  TRUE   TRUE TRUE  FALSE
# 3:  3 FALSE  FALSE TRUE  FALSE

使用 sapply 和 rowSums:

as.data.frame(
  sapply(unique(colnames(test)),
         function(i){
           rowSums(test[, grepl(i, colnames(test)), drop = FALSE]) > 0})
  )

#output
#   apple kiwi banana orange
# 1  TRUE TRUE   TRUE  FALSE
# 2  TRUE TRUE   TRUE  FALSE
# 3 FALSE TRUE  FALSE  FALSE

我们正在根据水果名称对 datafame 进行子集化,然后计算 rowSums。 TRUE 为 1,FALSE 为 0,因此大于零的 rowSums 将至少有一个 TRUE 值。我有 drop = FALSE,因此在 orange 只有一列的情况下,子集将保留为数据框。

注: 如果数据很长,那么@akrun 的 Reduce() 解决方案效果更好,但如果数据很宽,那么 rowSums() 效率更高。

另一个选择是 split 数据集的列序列由它的 names 变成 list,通过 list 循环,基于子集在数字索引上,使用Reduce检查每行是否有TRUE。

sapply(split(seq_along(test), names(test)), function(i) Reduce(`|`, test[i]))
#     apple banana kiwi orange
#[1,]  TRUE   TRUE TRUE  FALSE
#[2,]  TRUE   TRUE TRUE  FALSE
#[3,] FALSE  FALSE TRUE  FALSE