2017-01-12 9 views
4

は、私はこのようになりますデータフレームがあるとします。他の列の順列に基づいてデータフレームに新しい列を作成するにはどうすればよいですか?

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 

私はそれぞれの上部に変数に対して持ってTRUEFALSEのどの順列に基づいてaからfにカテゴリを割り当てて新しい列を作成します。

この簡略化した例では、結果は次のようになります。TRUEFALSEの各ユニークな順列が異なるカテゴリになっていること

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 

お知らせを、そしてaeので、同じ順列を持って、彼らはに終わります同じカテゴリ(A)。

これには簡単な方法があります。これは、上に多数の変数があり、潜在的にTRUEFALSEに限定されていない可能性がありますが、データフレームにカテゴリ/

答えて

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") 
+0

受理素晴らしく、エレガントな答え番目私は私自身のニーズに合わせて修正することができました。 –

+2

'(list(x = interaction(df))、match(x、x))]'をわずかに簡略化したものです。 – thelatemail

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)))]) 
+1

'as.numeric(as.factor(do.call(paste、df)))'もう少し一般的です。 –

+0

あなたの結果は、OP –

+1

によって要求された順番になっていません。[LETTERS [interaction(df、drop = TRUE)] '文字の順序が重要でない場合 – thelatemail

1

ここで "文字" Sに強制変換を避けるために便利になるかもしれないもう一つのアイデアがあります。 (「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) 

注文「データと、このベクトルを比較します。フレームの場合は、行が以前のものと異なる場合はいつも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を使用する順序付け「データの各グループ化された行の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" 

上記の便利な代替を添加した機能groupingに基づいています。以前のように、順序を返しますが、あまりにもいくつかの便利な属性、grouping「data.table」パッケージオフRの新しいバージョンへ:

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" 
関連する問題