Автоматическое расширение R-фактора в набор 1/0 переменных индикатора для каждого уровня фактора - PullRequest
98 голосов
/ 19 февраля 2011

У меня есть фрейм данных 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))

Потому что для определенных анализов, для которых вам нужно иметь полностью числовой фрейм данных (например, анализ главных компонентов),Я думал, что эта функция может быть встроена. Написание функции для этого не должно быть слишком сложным, но я могу предвидеть некоторые проблемы, связанные с именами столбцов, и если что-то уже существует, я бы лучше использовал это.

Ответы [ 8 ]

119 голосов
/ 19 февраля 2011

Используйте функцию model.matrix:

model.matrix( ~ Species - 1, data=iris )
16 голосов
/ 19 февраля 2011

Если ваш фрейм данных состоит только из факторов (или вы работаете с поднабором переменных, которые являются всеми факторами), вы также можете использовать функцию 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

Не совсем тот случай, который вы описываете, но он также может быть полезен ...

8 голосов
/ 19 февраля 2011

Быстрый способ использования пакета 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

Обратите внимание, что при этом создаются именно те имена столбцов, которые вам нужны.

6 голосов
/ 19 февраля 2013

Поздняя запись 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
6 голосов
/ 19 февраля 2011

вероятно, фиктивная переменная похожа на то, что вы хотите. Тогда модель.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
4 голосов
/ 30 октября 2011

Только что наткнулся на этот старый поток и подумал, что я бы добавил функцию, которая использует 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 голосов
/ 21 мая 2016

Вот более понятный способ сделать это. Я использую 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 голосов
/ 22 июня 2015

Мне нужна была функция для «разнесения» факторов, которая была бы более гибкой, и я сделал ее на основе функции acm.disjonctif из пакета ade4. Это позволяет вам выбрать разнесенные значения, которые равны 0 и 1 в файле acm.disjonctif. Это только взрывает факторы, которые имеют «несколько» уровней. Числовые столбцы сохранены.

# 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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...