Обработка переменных факторов в dplyr

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

df <-data.frame(ID=c(1,1,1,1,2,2,2,3,3),
                 current.grade=as.factor(c("Senior","Senior","Senior","Senior",
                                         "Junior","Junior","Junior",
                                         "Sophomore","Sophomore")),
                 grade.history=as.factor(c("Freshman","Sophomore","Junior","Senior",
                                   "Freshman","Sophomore","Junior",
                                   "Freshman","Sophomore")))

который дает выход

> df
  ID current.grade grade.history
1  1        Senior      Freshman
2  1        Senior     Sophomore
3  1        Senior        Junior
4  1        Senior        Senior
5  2        Junior      Freshman
6  2        Junior     Sophomore
7  2        Junior        Junior
8  3     Sophomore      Freshman
9  3     Sophomore     Sophomore
> str(df)
'data.frame':   9 obs. of  3 variables:
 $ ID           : num  1 1 1 1 2 2 2 3 3
 $ current.grade: Factor w/ 3 levels "Junior","Senior",..: 2 2 2 2 1 1 1 3 3
 $ grade.history: Factor w/ 4 levels "Freshman","Junior",..: 1 4 2 3 1 4 2 1 4

Я хочу использовать dplyr для извлечения последнего значения в grade.history и проверить его на current.grade:

df.summary <- df %>%
  group_by(ID) %>%
  summarize(current.grade.last=last(current.grade),
            grade.history.last=last(grade.history))

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

> df.summary
Source: local data frame [3 x 3]

  ID current.grade.last grade.history.last
1  1                  2                  3
2  2                  1                  2
3  3                  3                  4
> str(df.summary)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   3 obs. of  3 variables:
 $ ID                : num  1 2 3
 $ current.grade.last: int  2 1 3
 $ grade.history.last: int  3 2 4

Обратите внимание, что значения не выстраиваются в линию, потому что исходные факторы имели разные уровни. Какой правильный способ сделать это с помощью dplyr?

Я использую R версию 3.1.1 и dplyr версию 0.3.0.2

Ответ 1

Еще один способ приблизиться к этому - поставить свои уровни факторов в их естественном порядке, в этом случае Freshman, Sophomore, Junior, Senior, а затем выбрать максимальное значение для каждого идентификатора, используя функцию which.max для индексирования. Если вы сделаете это так, вам не придется беспокоиться о том, упорядочены ли ваши столбцы от самого низкого до высшего уровня для каждого идентификатора (как и при использовании функции last).

library(dplyr)

df <-data.frame(ID=c(1,1,1,1,2,2,2,3,3),
                current.grade=as.factor(c("Senior","Senior","Senior","Senior",
                                          "Junior","Junior","Junior",
                                          "Sophomore","Sophomore")),
                grade.history=as.factor(c("Freshman","Sophomore","Junior","Senior",
                                          "Freshman","Sophomore","Junior",
                                          "Freshman","Sophomore")))


# Ordered vector of grades
gradeLookup = c("Freshman", "Sophomore", "Junior", "Senior")

# Reset the values in the grade columns to the ordering in gradeLookup
df[,-1] = lapply(df[,-1], function(x) {
  factor(x, levels=gradeLookup)
})

# For each ID, select the values of current.grade and grade.history at the maximum
# value of grade.history
df %>% group_by(ID) %>%
  summarise(current.grade.last = current.grade[which.max(grade.history)],
            grade.history.last = grade.history[which.max(grade.history)])

  ID current.grade.last grade.history.last
1  1             Senior             Senior
2  2             Junior             Junior
3  3          Sophomore          Sophomore

ОБНОВЛЕНИЕ 2:. Поскольку вы хотите сортировать и фиксировать последнее значение (а не максимальное значение) по столбцу, а не целые строки, попробуйте следующее:

df %>% group_by(ID) %>%
  summarise(current.grade.last = current.grade[length(grade.history)],
            grade.history.last = grade.history[length(grade.history)])

END UPDATE 2

Имеются ли в ваших данных переменная времени, например год, срок или учебный год? Если это так, вы можете отказаться от current.grade и direclty выбрать значение grade.history в последний год посещаемости. Это даст вам каждый студент последнего уровня. Например (если ваша временная переменная называется year):

df %>% group_by(ID) %>%
  summarise(last.grade = grade.history[which.max(year)])

ОБНОВЛЕНИЕ 1: Я не уверен, что заставляет ваш код возвращать числовой код для каждого уровня, а не метку уровня. Это не просто проблема с функцией last (вы можете видеть это, если вы делаете last(df$grade.history)). Однако, если вы хотите отсортировать по метке времени, а затем вернуть последнюю строку, код ниже сохранит метки уровня. slice возвращает строки, указанные вами в каждом значении ID. В этом случае мы указываем последнюю строку, используя n(), которая возвращает общее количество строк для каждого значения ID.

df.summary <- df %>%
  group_by(ID) %>%
  slice(n())

Ответ 2

Я предполагаю, что он лежит в природе объекта factor в R, который представляет собой набор целых кодов с атрибутом "levels" для символа режима. Один из способов преодолеть вашу проблему: обмотать фактор-переменные в as.character:

  df.summary <- df %>%
  group_by(ID) %>%
  summarize(current.grade.last=last(as.character(current.grade)),
            grade.history.last=last(as.character(grade.history)))