Создание нестандартной фиктивной переменной

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

Вот пример данных:

#generate data
teacher.id <- c(1:5, 1:5)
class.taught <- c("ELA", "Math", "Science", "ELA", "Math", "Science", "Math", "ELA", "ELA", "Math")

# combine into data frame
dat <- data.frame(teacher.id, class.taught)

Как вы можете видеть, учителя с идентификаторами 1 и 3 ведут 2 разных класса.

Традиционный подход к созданию фиктивных переменных дает:

# example of what I have done so far 
dat$teach.ELA <- ifelse(dat$class.taught == "ELA", 1, 0 )
dat$teach.MATH <- ifelse(dat$class.taught == "Math", 1, 0 )
dat$teach.SCIENCE <- ifelse(dat$class.taught == "Science", 1, 0 )
dat

Однако вот как мне бы хотелось, чтобы новые фиктивные переменные выглядели:

desired.ELA <- c(1,0,1,1,0,1,0,1,1,0)
desired.MATH <- c(0,1,0,0,1,0,1,0,0,1)
desired.SCIENCE <- c(1,0,1,0,0,1,0,1,0,0)
dat.2 <- data.frame(dat, desired.ELA, desired.MATH, desired.SCIENCE)
dat.2

Я догадываюсь, что мне нужно перебрать идентификаторы, чтобы создать их, но после этого я действительно не вижу возможности достичь того, чего я хочу.


person bfoste01    schedule 25.05.2016    source источник
comment
Не data.frame(cbind(x,y)), поскольку cbind предоставит вам матрицу символов   -  person Frank    schedule 25.05.2016
comment
Есть какое-то правило желаемого. набор переменных?   -  person lmo    schedule 25.05.2016
comment
Я подозреваю, что это похоже на for (crs in levels(dat$class)) dat[crs] <- ave(dat$class, dat$teacher, FUN = function(x) crs %in% x), но, к сожалению, ave не сотрудничает. Вы можете использовать for (col in c("teach.ELA","teach.MATH","teach.SCIENCE")) dat[col] <- ave(dat[col], dat$teacher, FUN = max) после обычного кода, но это довольно долго.   -  person Frank    schedule 25.05.2016
comment
@Frank, по-твоему, в порядке, при условии, что вы приведете к character dat$class, а результат к логическому, а затем к числовому. Например as.logical(ave(as.character(dat$class.taught),dat$teacher.id,FUN=function(x) "ELA" %in% x)).   -  person nicola    schedule 25.05.2016
comment
@nicola Ладно, похоже, проблем больше, чем того стоит, а.   -  person Frank    schedule 25.05.2016


Ответы (4)


Вот базовый метод R. Идея состоит в том, что вы создаете манекены для каждого учителя, а затем объединяете их с исходными данными:

# get dummies for each teacher
temp <- as.data.frame(with(dat, table(teacher.id, class.taught) > 0))
temp$teacher.id <- as.integer(row.names(temp))

# merge onto dataset
merge(dat, temp, by="teacher.id")

Вы можете преобразовать логику в целое число, если это вас действительно беспокоит, но R сделает всю эту работу за вас.

person lmo    schedule 25.05.2016
comment
Извините, я сначала неправильно прочитал. Это хороший способ, наверное, лучший в базе R. - person Frank; 25.05.2016

Вы также можете сделать это с помощью %in%:

dums <- function(dt, x){
  ix <- dt[, 2] %in% x
  dt[, 1] %in% unique(dt[ix, 1])
}

dums(dat, 'ELA')
dums(dat, 'Math')
dums(dat, 'Science')

Это дает вам ИСТИНА / ЛОЖЬ, а не векторы 0/1, но as.integer при необходимости преобразует их в 0/1.

person David_B    schedule 25.05.2016

Просто для удовольствия, используя dplyr:

library(dplyr)
dat %>% left_join(
    dat %>%
      group_by(teacher.id) %>%
      summarize(desired.ELA = ifelse(sum(teach.ELA), 1, 0),
        desired.MATH = ifelse(sum(teach.MATH), 1, 0),
        desired.SCIENCE = ifelse(sum(teach.SCIENCE), 1, 0))
  )

Вывод:

   teacher.id class.taught teach.ELA teach.MATH teach.SCIENCE desired.ELA desired.MATH desired.SCIENCE
1           1          ELA         1          0             0           1            0               1
2           2         Math         0          1             0           0            1               0
3           3      Science         0          0             1           1            0               1
4           4          ELA         1          0             0           1            0               0
5           5         Math         0          1             0           0            1               0
6           1      Science         0          0             1           1            0               1
7           2         Math         0          1             0           0            1               0
8           3          ELA         1          0             0           1            0               1
9           4          ELA         1          0             0           1            0               0
10          5         Math         0          1             0           0            1               0
person Edward R. Mazurek    schedule 25.05.2016

Я бы использовал dplyr и tidyr.

library(dplyr)
library(tidyr)

dummies <- 
dat %>%
   group_by(teacher.id, class.taught) %>%
   summarise(is_taught = as.numeric(n() > 0)) %>%
   spread(class.taught, is_taught, fill = 0)

> dummies
Source: local data frame [5 x 4]

  teacher.id   ELA  Math Science
       (int) (dbl) (dbl)   (dbl)
1          1     1     0       1
2          2     0     1       0
3          3     1     0       1
4          4     1     0       0
5          5     0     1       0

Затем вы можете добавить их в исходные данные с помощью соединения.

> inner_join(dat, dummies)
Joining by: "teacher.id"
   teacher.id class.taught ELA Math Science
1           1          ELA   1    0       1
2           2         Math   0    1       0
3           3      Science   1    0       1
4           4          ELA   1    0       0
5           5         Math   0    1       0
6           1      Science   1    0       1
7           2         Math   0    1       0
8           3          ELA   1    0       1
9           4          ELA   1    0       0
10          5         Math   0    1       0
person Mhairi McNeill    schedule 25.05.2016
comment
Я знал, что с dplyr есть способ! - person bfoste01; 25.05.2016