2017-01-16 23 views
7

私はカテゴリ変数を持つデータフレームを持っていますの文字列を可変長でリストしています(これは重要ではないのでこの質問はthisまたはthis)、例えば:R:リストのカテゴリ変数*に基づいてダミー変数を作成する*

df <- data.frame(x = 1:5) 
df$y <- list("A", c("A", "B"), "C", c("B", "D", "C"), "E") 
df 
x  y 
1 1  A 
2 2 A, B 
3 3  C 
4 4 B, D, C 
5 5  E 

そして所望の形態がdf$y、すなわち内のどこにでも見られた各一意の文字列のためのダミー変数である:

data.frame(x = 1:5, A = c(1,1,0,0,0), B = c(0,1,0,1,0), C = c(0,0,1,1,0), D = c(0,0,0,1,0), E = c(0,0,0,0,1)) 
x A B C D E 
1 1 1 0 0 0 0 
2 2 1 1 0 0 0 
3 3 0 0 1 0 0 
4 4 0 1 1 1 0 
5 5 0 0 0 0 1 

この単純なアプローチは動作します:

> uniqueStrings <- unique(unlist(df$y)) 
> n <- ncol(df) 
> for (i in 1:length(uniqueStrings)) { 
+ df[, n + i] <- sapply(df$y, function(x) ifelse(uniqueStrings[i] %in% x, 1, 0)) 
+ colnames(df)[n + i] <- uniqueStrings[i] 
+ } 

、それは非常に醜い怠惰とビッグデータフレームと遅いですが。

提案がありますか? tidyverseから何か気に入っていますか?


更新:以下の3つのアプローチがあります。私はリアルデータセットの私の(Windows 7、32GBのRAM)ラップトップ上のsystem.timeを使用してそれらをテストしました。1M行からなり、各行は1〜4文字の長さのリスト(約350個の一意の文字列値)ディスク上に。したがって、予想される結果は、1M x 350のデータフレームです。tidyverse(@Sotos)とbase(@ joel.wilson)のアプローチは、Rを再起動しなければならないほど長くかかりました。しかし、qdapTools(@akrun)のアプローチは素晴らしい:

> system.time(res1 <- mtabulate(varsLists)) 
    user system elapsed 
    47.05 10.27 116.82 

これが私が受け入れる方法です。

+0

または 'data.frame(X = DFの$ Xを、T(sapply(DFの$ yを、関数(L){テーブル(因子(L、レベル=文字[1:5] ))}))) ' – alistaire

+0

@ letterista多分' LETTERS [1:5] 'の代わりに' levels = unique(unlist(df $ y)) 'でしょうか? – Sotos

+0

@Sotos私はそれを持っていましたが、これは計算が少ないと考えました。最高のルートは、別の変数としてそれを格納することですが、それは2行目を必要とするでしょう... – alistaire

答えて

6

私たちはあなたのコメントで述べた例にmtabulate

library(qdapTools) 
cbind(df[1], mtabulate(df$y)) 
# x A B C D E 
#1 1 1 0 0 0 0 
#2 2 1 1 0 0 0 
#3 3 0 0 1 0 0 
#4 4 0 1 1 1 0 
#5 5 0 0 0 0 1 
+0

これは印象的で超高速です(私のPC上で〜350個の一意の値を〜1M行に数秒間) 。まったく新しいパッケージを必要としない答えがありますか?ありがとう。 –

+0

@GioraSimchoni誰かがパッケージなしでそれに答えたように見える – akrun

+2

@GioraSimchoni;基本的な代替案は 'table(rep(df $ x、lengths(df $ y))、unlist(df $ y))'でしょうか? –

6

にもう一つのアイデア、さらに

library(dplyr) 
library(tidyr) 

df %>% 
unnest(y) %>% 
mutate(new = 1) %>% 
spread(y, new, fill = 0) 

# x A B C D E 
#1 1 1 0 0 0 0 
#2 2 1 1 0 0 0 
#3 3 0 0 1 0 0 
#4 4 0 1 1 1 0 
#5 5 0 0 0 0 1 

を使用することができ、それはspreadよりも柔軟性があるとして、我々はreshape2からdcastを使用することができますが、

df2 <- df %>% 
     unnest(y) %>% 
     group_by(x) %>% 
     filter(!duplicated(y)) %>% 
     ungroup() 

reshape2::dcast(df2, x ~ y, value.var = 'y', length) 

# x A B C D E 
#1 1 1 0 0 0 0 
#2 2 1 1 0 0 0 
#3 3 0 0 1 0 0 
#4 4 0 1 1 1 0 
#5 5 0 0 0 0 1 

#or with df$x <- c(1, 1, 2, 2, 3) 

# x A B C D E 
#1 1 1 1 0 0 0 
#2 2 0 1 1 1 0 
#3 3 0 0 0 0 1 

#or with df$x <- rep(1,5) 

# x A B C D E 
#1 1 1 1 1 1 1 
+0

ありがとうございました。df $ x = rep(1、5)を実行するとどうなりますか。 "エラー:行(1,2)、(3,5)、(4,7)の識別子が重複しています" –

+0

このような場合、どのような結果になるでしょうか? '%d'%n> '%n'>%n ''%>%n ''%n ''% – Sotos

+0

元のx列を保持する同じ結果です。これは、元の 'df'に、" Error:行(1,2)の識別子が重複しています "となります。 –

1

このインフォLVES外部パッケージない、

# thanks to Sotos for suggesting to use `unique(unlist(df$y))` instead of `LETTERS[1!:5]` 
sapply(unique(unlist(df$y)), function(j) as.numeric(grepl(j, df$y))) 
#  A B C D E 
#[1,] 1 0 0 0 0 
#[2,] 1 1 0 0 0 
#[3,] 0 0 1 0 0 
#[4,] 0 1 1 1 0 
#[5,] 0 0 0 0 1 
+2

'LETTERS'部分が悪いです。 – Sotos

+0

'df $ x = rep(1,5)'や 'df $ x = c(1,1,2,2,3)では動作しません。(unlist(df $ y) ) '。 'df $ x'が何であるかは関係ありません。 –

+1

@ joel.wilsonはうまくいきますが、他の "魅力的な"ソリューションと比較していくつかのベンチマークを行います。 –

関連する問題