合并 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
我有一个逻辑数据框,如:
> 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