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