如何根据其他列的排列在数据框中创建新列?

时间:2017-01-12 20:59:25

标签: r dataframe

假设我有一个如下所示的数据框:

    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

我想创建一个新列,根据每个af对顶部变量的排列,将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

请注意,TRUEFALSE的每个唯一排列都属于不同的类别,并且由于ae具有相同的排列,因此它们最终属于同一类别(A)。

是否有一种简单的方法可以执行此操作,如果顶部有大量变量,可能会有效,并且可能不限于TRUEFALSE,但如果数据框已填满与类别/数字?

3 个答案:

答案 0 :(得分:7)

您可以执行类似

的操作
## 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")

答案 1 :(得分:2)

#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)))])

答案 2 :(得分:1)

这是另一个可以派上用手来避免胁迫角色的想法。 (&#34; data.frame&#34; 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的向量,其中,对于每个位置,指定是否与其前一个元素相同。有了这个,我们可以得到一个类似的向量, ordered &#34; data.frame&#34;中的行是否与之前的行相同或不同:

starts_new_row = Reduce("|", starts_new_elt)

将此向量与有序&#34; data.frame&#34;进行比较我们注意到,只要行与之前的行不同,就会有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会返回 ordered &#34; data.frame&#34;中每个分组行的ID,可以通过{重新排序{1}}:

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" ,该函数被添加到&#34; data.table&#34;的更新版本的R中。包。 grouping像以前一样返回排序,但也有一些方便的属性:

grouping