dplyr мутировать с условными значениями

87

В большом фрейме данных («myfile») с четырьмя столбцами мне нужно добавить пятый столбец со значениями, условно основанными на первых четырех столбцах.

Предпочитайте ответы с dplyrи mutate, главным образом из-за его скорости в больших наборах данных.

Мой фрейм данных выглядит так:

  V1 V2 V3 V4
1  1  2  3  5
2  2  4  4  1
3  1  4  1  1
4  4  5  1  3
5  5  5  5  4
...

Значения пятого столбца (V5) основаны на некоторых условных правилах:

if (V1==1 & V2!=4) {
  V5 <- 1
} else if (V2==4 & V3!=1) {
  V5 <- 2
} else {
  V5 <- 0
}

Теперь я хочу использовать эту mutateфункцию для использования этих правил во всех строках (чтобы избежать медленных циклов). Примерно так (и да, я знаю, что так не работает!):

myfile <- mutate(myfile, if (V1==1 & V2!=4){V5 = 1}
    else if (V2==4 & V3!=1){V5 = 2}
    else {V5 = 0})

Должен быть результат:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Как это сделать dplyr?

rdatскульптор
источник
Полезно указать, являются ли все V1..4 целыми числами (не факторными, логическими, строковыми или плавающими)? и вы заботитесь о правильном обращении NA, ( NaN, +Inf, -Inf)?
smci
Если скорость кажется проблемой для предпочтения dplyr, я лучше воспользуюсь ею data.table.
Валентин

Ответы:

105

Попробуй это:

myfile %>% mutate(V5 = (V1 == 1 & V2 != 4) + 2 * (V2 == 4 & V3 != 1))

давая:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

или это:

myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, ifelse(V2 == 4 & V3 != 1, 2, 0)))

давая:

  V1 V2 V3 V4 V5
1  1  2  3  5  1
2  2  4  4  1  2
3  1  4  1  1  0
4  4  5  1  3  0
5  5  5  5  4  0

Заметка

Предложите вам лучше назвать фрейм данных. myfile создает впечатление, что он содержит имя файла.

Выше использовали этот ввод:

myfile <- 
structure(list(V1 = c(1L, 2L, 1L, 4L, 5L), V2 = c(2L, 4L, 4L, 
5L, 5L), V3 = c(3L, 4L, 1L, 1L, 5L), V4 = c(5L, 1L, 1L, 3L, 4L
)), .Names = c("V1", "V2", "V3", "V4"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

Обновление 1 Поскольку первоначально опубликованный dplyr был изменен %.%на %>%соответствующий ответ.

Теперь у dplyr есть обновление 2 , case_whenкоторое предлагает другое решение:

myfile %>% 
       mutate(V5 = case_when(V1 == 1 & V2 != 4 ~ 1, 
                             V2 == 4 & V3 != 1 ~ 2,
                             TRUE ~ 0))
Г. Гротендик
источник
Я попробовал ваше второе решение. Я получил эту ошибку: Ошибка в mutate_impl (.data, named_dots (...), environment ()): REAL () может применяться только к «числовому», а не «логическому». Вы знаете, что происходит не так?
rdatasculptor
5
Я обнаружил способ, который позволяет вам не вкладывать ifelseутверждения:myfile %>% mutate(V5 = ifelse(V1 == 1 & V2 != 4, 1, 0), V5 = ifelse(V2 == 4 & V3 != 1, 2, V5))
Alex
31

С помощью dplyr 0.7.2вы можете использовать очень полезную case_whenфункцию:

x=read.table(
 text="V1 V2 V3 V4
 1  1  2  3  5
 2  2  4  4  1
 3  1  4  1  1
 4  4  5  1  3
 5  5  5  5  4")
x$V5 = case_when(x$V1==1 & x$V2!=4 ~ 1,
                 x$V2==4 & x$V3!=1 ~ 2,
                 TRUE ~ 0)

Выражаясь с помощью dplyr::mutate, это дает:

x = x %>% mutate(
     V5 = case_when(
         V1==1 & V2!=4 ~ 1,
         V2==4 & V3!=1 ~ 2,
         TRUE ~ 0
     )
)

Обратите внимание, что NAне обрабатываются специально, так как могут ввести в заблуждение. Функция вернется NAтолько в том случае, если ни одно условие не выполнено. Если вы поместите строку с TRUE ~ ..., как я сделал в моем примере, возвращаемого значения никогда не будет NA.

Таким образом, вы должны сказать выразительнее case_whenпоставить , NAгде она принадлежит, добавив такое заявление is.na(x$V1) | is.na(x$V3) ~ NA_integer_. Подсказка: dplyr::coalesce()функция иногда может быть здесь действительно полезной!

Кроме того, обратите внимание , что в NAодиночку, как правило , не работают, вы должны поставить специальные NAзначения: NA_integer_, NA_character_или NA_real_.

Дэн Чалтиэль
источник
1
Это было значительно быстрее, чем производныйFactor.
Fato39
12

Похоже, derivedFactorиз mosaicупаковки было предназначено для этого. В этом примере это будет выглядеть примерно так:

library(mosaic)
myfile <- mutate(myfile, V5 = derivedFactor(
    "1" = (V1==1 & V2!=4),
    "2" = (V2==4 & V3!=1),
    .method = "first",
    .default = 0
    ))

(Если вы хотите, чтобы результат был числовым, а не множителем, derivedFactorзаключите в скобку as.numeric.)

Обратите внимание, что .defaultопция в сочетании с .method = "first"устанавливает условие «else» - этот подход описан в файле справки для derivedFactor.

Джейк Фишер
источник
Вы также можете предотвратить влияние результата, используя .asFactor = Fопцию или (аналогичную) derivedVariableфункцию в том же пакете.
Джейк Фишер
Похоже, что recodedplyr 0.5 это сделает. Однако я еще не исследовал это. См. Blog.rstudio.org/2016/06/27/dplyr-0-5-0
Джейк Фишер,
Это было медленным для моих данных с 1e6 строками.
Fato39
3
@ Fato39 Да, mosaic::derivedFactorсемейство функций очень медленное. Если вы выясните, почему, ответьте на мой ТАК вопрос об этом: stackoverflow.com/questions/33787691/… . Я рад видеть из вашего другого комментария, что dplyr::case_whenэто быстрее - мне придется переключиться на это.
Джейк Фишер,
Я пытаюсь выполнить следующую команду, библиотека (мозаика) VENEZ.FINAL2 <- mutate (VENEZ, SEX = производныйFactor ("M" = (CATEGORY == "BULL" & CATEGORY! = "SIRE"), "F" = ( CATEGORY == "COW" & CATEGORY! = "HEIFER"), .method = "first", .default = "NA")) но это не работает, просто решите условие VENEZ.FINAL2 <- mutate (VENEZ, СЕКС = производный фактор ("М" = (КАТЕГОРИЯ == "БЫК) Не могли бы вы мне помочь? Большое спасибо!
Джоанна Рамирес,