Rファクターをすべてのファクターレベルの1/0インジケーター変数のコレクションに自動的に拡張する


108

「拡張」したい因子を含むRデータフレームがあり、各因子レベルに対して、新しいデータフレームに1/0インジケーターを含む関連する列があります。たとえば、私が持っていると仮定します:

df.original <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c(1,2,3,4))

が欲しいです:

df.desired  <- data.frame(foo = c(1,1,0,0), bar=c(0,0,1,1), ham=c(1,2,3,4))

完全な数値データフレームが必要な特定の分析(主成分分析など)では、この機能が組み込まれているのではないかと思いました。これを行う関数の記述はそれほど難しくはないはずですが、列名に関連する課題。すでに何かが存在する場合は、むしろそれを使用します。

回答:


131

model.matrix関数を使用します。

model.matrix( ~ Species - 1, data=iris )

1
この方法がcast私よりもはるかに高速であったことを付け加えることができますか?
Matt Weller、

3
@GregSnowのと?formula同様にの2番目の段落を確認しましたが、不明確でした(おそらく?model.matrix、マトリックス代数とモデルの定式化に関する知識の深さが不足しているだけかもしれません)。さらに掘り下げた後、-1は単に「インターセプト」列を含めないように指定していることを収集できました。-1を省略すると、出力に1のインターセプト列が表示され、1つのバイナリ列が省略されます。他の列の値が0である行に基づいて、省略された列が1である値を確認できます。ドキュメントは不可解なようです-別の優れたリソースはありますか?
Ryan Chase

1
@ RyanChase、R / Sに関する多くのオンラインチュートリアルと本があります(r-project.org Webページに簡単な説明があるものもいくつかあります)。SとRについての私自身の学習はかなり折衷的(そして長い)であったので、私は現在の本/チュートリアルが初心者にどのようにアピールするかについて意見を述べるのは最善ではありません。しかし、私は実験のファンです。新しいRセッションで何かを試すことは非常に啓発的で危険ではありません(私に起こった最悪の事態はRをクラッシュさせ、まれにRの改善につながります)。Stackoverflowは、何が起こったかを理解するための優れたリソースです。
Greg Snow

7
そして、あなたはすべての因子の列を変換したい場合は、あなたが使用することができますmodel.matrix(~., data=iris)[,-1]
user890739

1
@colin、完全に自動化されていませんが、を使用naresidした後に欠落している値を戻すために使用できますna.exclude。簡単な例:tmp <- data.frame(x=factor(c('a','b','c',NA,'a'))); tmp2 <- na.exclude(tmp); tmp3 <- model.matrix( ~x-1, tmp2); tmp4 <- naresid(attr(tmp2,'na.action'), tmp3)
Greg Snow

17

データフレームが因子のみで構成されている場合(またはすべての因子である変数のサブセットで作業している場合)、パッケージのacm.disjonctif関数を使用することもできade4ます。

R> library(ade4)
R> df <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c("red","blue","green","red"))
R> acm.disjonctif(df)
  eggs.bar eggs.foo ham.blue ham.green ham.red
1        0        1        0         0       1
2        0        1        1         0       0
3        1        0        0         1       0
4        1        0        0         0       1

あなたが説明しているケースとまったく同じではありませんが、それも役に立ちます...


おかげで、model.matrixよりもメモリの使用量が少ないので、非常に役立ちました。
Serhiy

変数に名前を付ける方法が好きです。それら(IMHO)だけで論理的である必要があるときに、ストレージを大量に消費する数値として返されるのが嫌いです。
dsz

9

reshape2パッケージを使用する簡単な方法:

require(reshape2)

> dcast(df.original, ham ~ eggs, length)

Using ham as value column: use value_var to override.
  ham bar foo
1   1   0   1
2   2   0   1
3   3   1   0
4   4   1   0

これにより、必要な列名が正確に生成されることに注意してください。


良い。ただし、ハムの複製には注意してください。たとえば、d <-data.frame(eggs = c( "foo"、 "bar"、 "foo")、ham = c(1,2,1)); dcast(D、ハム〜卵、長さ)は、FOO = 2になり
kohske

@Kohske、本当ですが、私はham一意の行IDであると想定していました。hamが一意のIDでない場合は、他の一意のIDを使用して(またはダミーのIDを作成して)、の代わりに使用する必要がありhamます。カテゴリーラベルをバイナリインジケーターに変換することは、一意のIDに対してのみ意味があります。
Prasad Chalasani、2011

6

おそらくダミー変数はあなたが望むものに似ています。次に、model.matrixが役立ちます。

> with(df.original, data.frame(model.matrix(~eggs+0), ham))
  eggsbar eggsfoo ham
1       0       1   1
2       0       1   2
3       1       0   3
4       1       0   4

6

パッケージclass.indからの遅いエントリーnnet

library(nnet)
 with(df.original, data.frame(class.ind(eggs), ham))
  bar foo ham
1   0   1   1
2   0   1   2
3   1   0   3
4   1   0   4

4

この古いスレッドに出くわしたので、ade4を使用して因子や数値データで構成されるデータフレームを取得し、因子を含むデータフレームをダミーコードとして返す関数を追加したいと思いました。

dummy <- function(df) {  

    NUM <- function(dataframe)dataframe[,sapply(dataframe,is.numeric)]
    FAC <- function(dataframe)dataframe[,sapply(dataframe,is.factor)]

    require(ade4)
    if (is.null(ncol(NUM(df)))) {
        DF <- data.frame(NUM(df), acm.disjonctif(FAC(df)))
        names(DF)[1] <- colnames(df)[which(sapply(df, is.numeric))]
    } else {
        DF <- data.frame(NUM(df), acm.disjonctif(FAC(df)))
    }
    return(DF)
} 

試してみよう。

df <-data.frame(eggs = c("foo", "foo", "bar", "bar"), 
            ham = c("red","blue","green","red"), x=rnorm(4))     
dummy(df)

df2 <-data.frame(eggs = c("foo", "foo", "bar", "bar"), 
            ham = c("red","blue","green","red"))  
dummy(df2)

3

これを行うためのより明確な方法を次に示します。model.matrixを使用してダミーのブール変数を作成し、それを元のデータフレームにマージします。

df.original <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c(1,2,3,4))
df.original
#   eggs ham
# 1  foo   1
# 2  foo   2
# 3  bar   3
# 4  bar   4

# Create the dummy boolean variables using the model.matrix() function.
> mm <- model.matrix(~eggs-1, df.original)
> mm
#   eggsbar eggsfoo
# 1       0       1
# 2       0       1
# 3       1       0
# 4       1       0
# attr(,"assign")
# [1] 1 1
# attr(,"contrasts")
# attr(,"contrasts")$eggs
# [1] "contr.treatment"

# Remove the "eggs" prefix from the column names as the OP desired.
colnames(mm) <- gsub("eggs","",colnames(mm))
mm
#   bar foo
# 1   0   1
# 2   0   1
# 3   1   0
# 4   1   0
# attr(,"assign")
# [1] 1 1
# attr(,"contrasts")
# attr(,"contrasts")$eggs
# [1] "contr.treatment"

# Combine the matrix back with the original dataframe.
result <- cbind(df.original, mm)
result
#   eggs ham bar foo
# 1  foo   1   0   1
# 2  foo   2   0   1
# 3  bar   3   1   0
# 4  bar   4   1   0

# At this point, you can select out the columns that you want.

0

もう少し柔軟な要素を「分解」するための関数が必要であり、ade4パッケージのacm.disjonctif関数に基づいて関数を作成しました。これにより、acm.disjonctifの0と1の分解値を選択できます。それは「少数」のレベルを持つ要因を爆発させるだけです。数値列は保持されます。

# Function to explode factors that are considered to be categorical,
# i.e., they do not have too many levels.
# - data: The data.frame in which categorical variables will be exploded.
# - values: The exploded values for the value being unequal and equal to a level.
# - max_factor_level_fraction: Maximum number of levels as a fraction of column length. Set to 1 to explode all factors.
# Inspired by the acm.disjonctif function in the ade4 package.
explode_factors <- function(data, values = c(-0.8, 0.8), max_factor_level_fraction = 0.2) {
  exploders <- colnames(data)[sapply(data, function(col){
      is.factor(col) && nlevels(col) <= max_factor_level_fraction * length(col)
    })]
  if (length(exploders) > 0) {
    exploded <- lapply(exploders, function(exp){
        col <- data[, exp]
        n <- length(col)
        dummies <- matrix(values[1], n, length(levels(col)))
        dummies[(1:n) + n * (unclass(col) - 1)] <- values[2]
        colnames(dummies) <- paste(exp, levels(col), sep = '_')
        dummies
      })
    # Only keep numeric data.
    data <- data[sapply(data, is.numeric)]
    # Add exploded values.
    data <- cbind(data, exploded)
  }
  return(data)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.