Написание функции, классифицирующей числовой вектор с помощью mutate() и ifelse()

Простите, если это вопрос новичка. Я относительно новичок в R. Я практикую написание функций. В настоящее время я пытаюсь написать функцию, которая принимает любой числовой вектор, представляющий время, и классифицирует каждое наблюдение по следующим категориям: «Утро», «День», «Вечер» и «Ночь». Все векторы, представляющие время, относятся к военному времени.

time_cat <- function(df, column) {
 select(df, column) %>%
 mutate(time_category = ifelse(column %in% 500:1159, "Morning", 
                           ifelse(column %in% 1200:1659, "Afternoon", 
                                  ifelse(column %in% 1700:2059, "Evening", "Night"))))
}

Для практики я использую набор данных Flights из пакета nycflights13. Однако функция, по-видимому, неправильно относит все наблюдения к категории «Ночь».

time_cat(flights, "dep_time")
 # A tibble: 336,776 x 2
   dep_time time_category
      <int>         <chr>
 1      517         Night
 2      533         Night
 3      542         Night
 4      544         Night
 5      554         Night
 6      554         Night
 7      555         Night
 8      557         Night
 9      557         Night
10      558         Night
# ... with 336,766 more rows

Очевидно, что все эти наблюдения следует отнести к категории «Утро».

Может кто-нибудь объяснить, что не так с этим кодом?

Я буду очень признателен.

Спасибо.


person jp334    schedule 13.02.2018    source источник


Ответы (2)


Проблема в том, что при вызове time_cat он пытается сопоставить строку "dep_time" с диапазоном чисел, а не сопоставить столбец с этим именем во фрейме данных с диапазоном чисел.

Например, в приведенном ниже коде mutate сравнивает "a" == "b" в первом вызове с f и "b" == "b" во втором. Ни в том, ни в другом случае не используются столбцы a или b.

dd <- data.frame(a = c("a", "b", "c"), b = c("A", "B", "C"))
f <- function(x) dd %>% mutate(new_column = x == "b")
f("a")
##   a b new_column
## 1 a A      FALSE
## 2 b B      FALSE
## 3 c C      FALSE
f("b")
##   a b new_column
## 1 a A       TRUE
## 2 b B       TRUE
## 3 c C       TRUE

Чтобы обойти это, используйте tidyeval средства пакета rlang:

time_cat <- function(df, column) {
 column <- sym(column)
 select(df, !!column) %>%
 mutate(time_category = ifelse((!!column) %in% 500:1159, "Morning", 
                           ifelse((!!column) %in% 1200:1659, "Afternoon", 
                                  ifelse((!!column) %in% 1700:2059, "Evening", "Night"))))
}

time_cat(flights, "dep_time")

Также вам может быть удобнее использовать case_when. Также обычно начинают с входа на левой стороне трубы.

time_cat <- function(df, column) {
 column <- sym(column)
 df %>%
    select(!!column) %>%
    mutate(time_category = case_when((!!column) %in% 500:1159 ~ "Morning", 
                                     (!!column) %in% 1200:1659 ~ "Afternoon", 
                                     (!!column) %in% 1700:2059 ~ "Evening", 
                                     TRUE ~ "Night"))
}

time_cat(flights, "dep_time")
person G. Grothendieck    schedule 13.02.2018
comment
Большое спасибо за Вашу помощь. - person jp334; 13.02.2018
comment
Скорее похоже на работу для cut. - person Roland; 13.02.2018

Возможно, некоторые из вас не согласятся со мной, но я бы не стал использовать функцию и решил бы ее с помощью библиотеки data.table следующим образом:

library(data.table)
flights <- as.data.table(flights)

flights[dep_time < 1200, time_cat := "Morning"]
flights[dep_time > 1159 & dep_time < 1700, time_cat := "Afternoon"]
flights[dep_time > 1659 & dep_time < 2100, time_cat := "Evening"]
flights[dep_time > 2059, time_cat := "Night"]
person Bernd Schmidl    schedule 13.02.2018