R dplyr::mutate с использованием ifelse и %in% не может создать фактор (в mutate_impl: связывание символа и вектора фактора, преобразование в вектор символов)

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

Очевидно, я мог бы просто оставить все как есть и добавить строку, используя df$col <- factor(df$col), но я хотел бы понять, что не так с моим кодом, и исправить его, чтобы он работал непосредственно внутри mutate.

Вот MWE, который воспроизводит ошибку на обоих компьютерах, к которым у меня есть доступ:

df <- data.frame(
  Subject = c(1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8),
  StimLabel = factor(c("NoLabelFeedback","NoLabelFeedback",
                       "NoLabelFeedback","NoLabelFeedback",
                       "Saldie","Gatoo",
                       "Gatoo","Saldie",
                       "NoLabelFeedback","NoLabelFeedback",
                       "NoLabelFeedback","NoLabelFeedback",
                       "Saldie","Gatoo",
                       "Gatoo","Saldie"))
)

df <- df %>% group_by(Subject) %>%
  mutate(Condition = factor(ifelse("NoLabelFeedback" %in% StimLabel,"NoLabel","Label")))

Изменить: моя проблема здесь указана в заголовке: я получаю предупреждение о принуждении. Вывод, созданный R, просто прекрасен, за исключением того факта, что Condition является столбцом character, а не factor.

Что я пытаюсь сделать, так это то, что если какое-либо из значений в StimLabel для субъекта равно "NoLabelFeedback", то установите значение Condition на "NoLabel" для субъекта. На практике я использую %in%, так как для каждого субъекта либо все значения StimLabel, либо ни одно из значений StimLabel будет "NoLabelFeedback", и мне показалось, что таким образом R будет проводить меньше тестов в половине случаев, поскольку он перестанет проверять кадр данных после первого теста. Если у кого-то есть идеи о том, как сделать это лучше, я за это, но на самом деле это не суть этого вопроса.


person Arthur Spoon    schedule 22.11.2017    source источник
comment
@akrun Я почти уверен, что дело не в этом, так как до того, как я пытался использовать group_by и mutate для всего набора данных, я делал то же самое при импорте данных каждого субъекта, и это работало нормально. Дело здесь в том, чтобы сказать, для каждого предмета, является ли одно из значений для StimLabel "NoLabelFeedback". В противном случае я бы просто использовал == вместо %in%...   -  person Arthur Spoon    schedule 22.11.2017
comment
@akrun, как бы вы использовали if/else в этом случае? Можете ли вы разработать, поскольку я не уверен, как это будет работать, и полностью сделал бы это, если бы это был лучший или более быстрый способ сделать это.   -  person Arthur Spoon    schedule 22.11.2017
comment
В таком случае df %>% group_by(Subject) %>% mutate(Condition = factor(if("NoLabelFeedback" %in% StimLabel) "NoLabel" else "Label", levels = c("NoLabel", "Label")))   -  person akrun    schedule 22.11.2017


Ответы (2)


Похоже, проблема в том, что вы объявляете факторную переменную при использовании функции ifelse. Таким образом, R не будет знать уровни факторов. Работает следующий код:

library(dplyr)

df <- data.frame(
  Subject = c(1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8),
  StimLabel = factor(c("NoLabelFeedback","NoLabelFeedback",
                       "NoLabelFeedback","NoLabelFeedback",
                       "Saldie","Gatoo",
                       "Gatoo","Saldie",
                       "NoLabelFeedback","NoLabelFeedback",
                       "NoLabelFeedback","NoLabelFeedback",
                       "Saldie","Gatoo",
                       "Gatoo","Saldie"))
)

df2 <- df %>% group_by(Subject) %>%
    mutate(Condition = factor(ifelse("NoLabelFeedback" %in% StimLabel,
                                     "NoLabel","Label"),
                              levels = c("NoLabel","Label")))
person Tyler    schedule 22.11.2017
comment
Это действительно решило проблему! Мне кажется странным, что R не может угадывать уровни фактора на ходу, но теперь, когда я это знаю, я просто правильно объявлю свои уровни. - person Arthur Spoon; 22.11.2017
comment
@ArthurSpoon Вы применяете factor к каждой группе. Проблема в том, что каждая группа имеет разные уровни, и поэтому их нельзя последовательно объединить, если только они не будут принудительно character. Вы должны принуждать к фактору после группировки, что понятнее и эффективнее. - person nicola; 22.11.2017

Существует небольшое улучшение эффективности, если мы используем if/else

df %>%
   group_by(Subject) %>%
   mutate(Condition = factor(if("NoLabelFeedback" %in% StimLabel) "NoLabel" else "Label",
                                  levels = c("NoLabel", "Label")))

Но, если мы пойдем с присваиванием data.table, это будет быстрее

Ориентиры

set.seed(24)
df <- data.frame(Subject = rep(1:1e5, each = 30),
               StimLabel = sample(c("NoLabelFeedback","Saldie","Gatoo"),
                      1e5*30, replace = TRUE))


system.time({
  r1 <- df %>%
             group_by(Subject) %>%
             mutate(Condition = factor(if("NoLabelFeedback" %in% StimLabel) "NoLabel"
                        else "Label", levels = c("NoLabel", "Label")))
   })
 # user  system elapsed 
 #  8.55    0.00    8.58 




system.time({
   r2 <- df %>% group_by(Subject) %>%
    mutate(Condition = factor(ifelse("NoLabelFeedback" %in% StimLabel,
                                     "NoLabel","Label"),
                              levels = c("NoLabel","Label")))
}) 
#user  system elapsed 
#   9.46    0.00    9.62 

используя data.table

library(data.table)
system.time({

     setDT(df)[, Condition := factor(if("NoLabelFeedback" %in% StimLabel) "NoLabel"
      else "Label", levels = c("NoLabel", "Label")), Subject]

})
# user  system elapsed 
#   1.48    0.02    1.50 

identical(df$Condition, r1$Condition)
#[1] TRUE
person akrun    schedule 22.11.2017