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?


Корисно вказати, чи всі 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 змінився %.%на %>%відповідні зміни.

Оновлення 2 dplyr тепер має 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))
Олексій

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функцію в тому ж пакеті.
Джейк Фішер,

Схоже, recodeз dplyr 0.5 це зробить. Я ще не досліджував це. Дивіться blog.rstudio.org/2016/06/27/dplyr-0-5-0
Jake Fisher

Це було повільно для моїх даних з 1e6 рядків.
Fato39

3
@ Fato39 Так, mosaic::derivedFactorсімейство функцій дуже повільне. Якщо ви зрозуміли, чому, будь ласка, дайте відповідь на моє запитання щодо цього: stackoverflow.com/questions/33787691/… . Я радий бачити з вашого іншого коментаря, що dplyr::case_whenвін швидший - мені доведеться перейти на це.
Джейк Фішер,

Я пробую наступну команду, бібліотека (мозаїка) VENEZ.FINAL2 <- mutate (VENEZ, SEX = derivaFactor ("M" = (CATEGORY == "BULL" & CATEGORY! = "SIRE"), "F" = ( CATEGORY == "COW" & CATEGORY! = "HEIFER"), .method = "first", .default = "NA")), але це не працює, просто вирішіть умову VENEZ.FINAL2 <- mutate (VENEZ, SEX = derivaFactor ("M" = (CATEGORY == "BULL Чи можете ви мені допомогти? Велике спасибі!
Йоганна Рамірес,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.