2015-01-10 2 views
10

У меня есть кадр данных, который содержит историю событий, и я хочу проверить его целостность, проверив, соответствует ли последнее событие для каждого идентификационного номера текущему значению в системе для этого идентификационного номера. Данные кодируются как факторы. Следующий кадр игрушка данных минимальный пример:Обработка переменных факторов в 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"))) 

, который дает OUTPUT

> 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

+0

Добавить 'stringsAsFactors = FALSE' при создании вашего набора данных, и ваш код будет работать нормально, см.' 'Data.frame'. См. Также [здесь] (http://stackoverflow.com/questions/3418128/how-to-convert-a-factor-to-an-integer-numeric-without-a-loss-of-information) для общего понимания факторы или [здесь] (http://stackoverflow.com/questions/2851015/convert-data-frame-columns-from-factors-to-characters) –

+0

@David Кажется, что OP (довольно разумно) * хочет * здесь. –

+0

@ KonradRudolph, что также может быть правдой (я не читал фон, а скорее проблему, с которой они сталкиваются), хотя я не уверен, как вы определяете * достаточно разумно * (память мудрая?). –

ответ

0

Я предполагаю, что это лежит в природе factor объекта в R, который устанавливается целых кодов с «уровнями» атрибутом режим. Один из способов преодолеть проблему: Оберните переменных факторов в 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))) 
+0

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

+0

Что делать, если вы замените 'last (...)' на 'tail (..., 1)'? – lukeA

+0

Да. Использование 'tail (..., 1)', а не 'last (...)' похоже, сохраняет переменные как факторы. Это хороший способ обхода. Спасибо. Слишком плохо, хотя, поскольку я понимаю, что это именно то, что была предназначена для функции 'last()'. – tcquinn

3

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

UPDATE 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)]) 

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

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

Да, у моего реального набора данных есть метка времени для каждой записи, и я использую эту метку времени для сортировки в каждой группе с помощью 'arr()' перед тем, как применить функцию 'last()' (то есть, я хочу, чтобы последняя запись , не обязательно тот, который имеет наибольшее значение для оценки или что-то еще). Я оставил этот шаг из моего примера игрушек для простоты. Ваше последнее предложение является хорошим, предполагая, что 'last()' является виновником (в отличие от 'summaryize()' или 'group_by()'. Как вы думаете, это так? – tcquinn

+0

[Ответ на обновление] Ваше решение, использующее 'slice()' действительно работает в моем примере с игрушкой (спасибо!), но в моем реальном примере я хочу применить эту операцию только к некоторым столбцам (я хочу применить другие обобщенные функции к другим столбцам). Вот почему я использовал 'summaryize()' и 'last()' .Похоже, что это одна из двух функций, которая преобразует факторы в целые числа. Любые другие предложения? Я предположил, что могу использовать 'select()' для вывода столбцов, которые я хочу , затем примените 'slice()', а затем снова присоедините результат к результатам моих других сводных функций, используя 'merge()', но ... ugh. – tcquinn

+0

Посмотрите мое второе обновление – eipi10