Автоматичне розширення 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для мене.
Метт Веллер

3
@GregSnow я розглянув 2 - й абзац ?formula, а також ?model.matrix, але було неясно , (може бути просто під час моєї відсутності глибини знань в матричної алгебри і формулюванні моделі). Після копання більше, я зміг зрозуміти, що -1 просто вказує, що не включати стовпчик "перехоплення". Якщо ви не залишите -1, у виході ви побачите стовпчик перехоплення 1, а двійковий стовпець не залишиться. Ви можете бачити, які значення опущеного стовпця - це позначки 1 на основі рядків, де значення інших стовпців дорівнюють 0. Документація здається криптовалютною - є ще один хороший ресурс?
Райан Чейз

1
@RyanChase, є багато навчальних посібників в Інтернеті та книг про R / S (декілька з короткими описами на веб-сторінці r-project.org). Моє власне вивчення S та R було досить еклектичним (і тривалим), тому я не найкраще давати думку про те, як сучасні книги / навчальні посібники подобаються новачкам. Я, однак, шанувальник експериментів. Спробувати щось на новому сеансі R може бути дуже освіжаючим і не небезпечним (найгірше, що трапилося зі мною, - це збій R, і це рідко, які призводять до поліпшення R). Тоді Stackoverflow - це гарний ресурс для розуміння того, що сталося.
Грег Сніг

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)
Грег Сніг

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!
Сергій

Мені подобається, як називаються змінні; Мені не подобається, що вони повертаються як голодні для зберігання числа, коли слід (ІМХО) просто бути логіками.
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 (яйця = c ("foo", "bar", "foo"), ham = c (1,2,1)); dcast (d, шинка ~ яйця, довжина) робить foo = 2.
kohske

@Kohske, правда, але я припускав ham, що це унікальний ідентифікатор рядка Якщо hamце не унікальний ідентифікатор, то слід використовувати якийсь інший унікальний ідентифікатор (або створити манекен) та використовувати його замість ham. Перетворення категоричної мітки у бінарний індикатор має сенс лише для унікальних ідентифікаторів.
Прасад Чаласані

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

Мені була потрібна функція, щоб "вибухнути" фактори, які є трохи більш гнучкими, і я зробив її на основі функції acm.disjonctif з пакета ade4. Це дозволяє вибирати значення, що підірвалися, що в 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)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.