[英]How to create a new column in a dataframe based on grouped permutations of another column
[英]How can I create a new column in a dataframe based on permutations of other columns?
假设我有一个如下所示的数据框:
var1 var2 var3 var4
a TRUE FALSE TRUE FALSE
b TRUE TRUE TRUE FALSE
c FALSE TRUE FALSE TRUE
d TRUE FALSE FALSE FALSE
e TRUE FALSE TRUE FALSE
f FALSE TRUE FALSE TRUE
我想创建一个新的列,其指定a
以f
基于什么排列到类别TRUE
和FALSE
每个具有沿顶部的变量。
在这个简化的例子中,结果如下:
var1 var2 var3 var4 category
a TRUE FALSE TRUE FALSE A
b TRUE TRUE TRUE FALSE B
c FALSE TRUE FALSE TRUE C
d TRUE FALSE FALSE FALSE D
e TRUE FALSE TRUE FALSE A
f FALSE TRUE FALSE TRUE C
请注意, TRUE
和FALSE
每个唯一排列变为不同的类别,并且由于a
和e
具有相同的排列,因此它们最终位于相同的类别( A
)中。
有没有一种简单的方法可以做到这一点,如果顶部有大量变量,可能会有效,并且可能不限于TRUE
和FALSE
但如果数据框中填充了类别/数字?
你可以做点什么
## paste the rows together, creating a character vector
x <- do.call(paste, df)
## match it against itself and apply to 'LETTERS', and assign as new column
df$category <- LETTERS[match(x, x)]
df
# var1 var2 var3 var4 category
# a TRUE FALSE TRUE FALSE A
# b TRUE TRUE TRUE FALSE B
# c FALSE TRUE FALSE TRUE C
# d TRUE FALSE FALSE FALSE D
# e TRUE FALSE TRUE FALSE A
# f FALSE TRUE FALSE TRUE C
如果我们使用命名列表作为环境,上面的代码可以写成一行代码。 这样可以避免对全局环境进行任何新的分配。
df$category <- LETTERS[with(list(x = do.call(paste, df)), match(x, x))]
数据:
df <- structure(list(var1 = c(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE),
var2 = c(FALSE, TRUE, TRUE, FALSE, FALSE, TRUE), var3 = c(TRUE,
TRUE, FALSE, FALSE, TRUE, FALSE), var4 = c(FALSE, FALSE,
TRUE, FALSE, FALSE, TRUE)), .Names = c("var1", "var2", "var3",
"var4"), row.names = c("a", "b", "c", "d", "e", "f"), class = "data.frame")
#Example DATA
mydata = structure(list(V1 = c(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE),
V2 = c(FALSE, TRUE, TRUE, FALSE, FALSE, TRUE), V3 = c(TRUE,
TRUE, FALSE, FALSE, TRUE, FALSE), V4 = c(FALSE, FALSE, TRUE,
FALSE, FALSE, TRUE)), .Names = c("V1", "V2", "V3", "V4"),
class = "data.frame", row.names = c(NA,-6L))
#RUN THE ONE LINER (Incorporating David Arenburg's advice in comment)
mydata$category = toupper(letters[as.numeric(as.factor(do.call(paste, mydata)))])
这是另一个可能派上用场以避免胁迫“角色”的想法。 (“data.frame” df
来自RichScriven的答案。)
订单数据:
o = do.call(order, df)
对于每个有序列 ,找出下一个元素是否与之前的元素不同:
starts_new_elt = lapply(df, function(x) {
xo = x[o]
c(TRUE, xo[-1] != xo[-length(x)])
})
这将返回一个TRUE
/ FALSE
向量,其中,对于每个位置,指定是否与其前一个元素相同或不相同。 有了这个,我们可以得到一个类似的向量,在有序的 “data.frame”中,一行是否与之前的行相同或不同:
starts_new_row = Reduce("|", starts_new_elt)
该向量与有序“data.frame”比较,我们注意到,有一个TRUE
每当行到以前的和不同的FALSE
否则。
starts_new_row
#[1] TRUE FALSE TRUE TRUE FALSE TRUE
df[o, ]
# var1 var2 var3 var4
#c FALSE TRUE FALSE TRUE
#f FALSE TRUE FALSE TRUE
#d TRUE FALSE FALSE FALSE
#a TRUE FALSE TRUE FALSE
#e TRUE FALSE TRUE FALSE
#b TRUE TRUE TRUE FALSE
最后,在此向量上使用cumsum
返回有序 “data.frame”中每个分组行的id,可以按order(o)
重新排序:
gr = cumsum(starts_new_row)[order(o)]
gr
#[1] 3 4 1 2 3 1
对于确切的输出,我们可以使用:
LETTERS[match(gr, unique(gr))]
#[1] "A" "B" "C" "D" "A" "C"
上面的一个方便的替代方案是基于从“data.table”包中添加到R的较新版本的功能grouping
。 grouping
返回顺序,如前所述,但也有一些方便的属性:
o2 = do.call(grouping, df)
ends = attr(o2, "ends")
gr2 = rep(seq_along(ends), c(ends[1], diff(ends)))[order(o2)]
gr2
#[1] 3 4 1 2 3 1
LETTERS[match(gr2, unique(gr2))]
#[1] "A" "B" "C" "D" "A" "C"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.