Столбцы даты с NA в R - неожиданное поведение с mutate - PullRequest
3 голосов
/ 11 марта 2020

Я пытаюсь следовать этому процессу с помощью набора данных. Вот тестовый фрейм данных:

id <- c("Johnboy","Johnboy","Johnboy")
orderno <- c(2,2,1)
validorder <- c(0,1,1)
ordertype <- c(95,94,95)
orderdate <- as.Date(c("2019-06-17","2019-03-26","2018-08-23"))

df <- data.frame(id, orderno, validorder, ordertype, orderdate)

Затем я делаю следующее:

## compute order date for order types
df <- df %>%
  mutate(orderdate_dried = if_else(validorder == 1 &
                                  ordertype == 95,
                                  orderdate, as.Date(NA)),
         orderdate_fresh = if_else(validorder == 1 &
                                  ordertype == 94,
                                  orderdate, as.Date(NA)))

## take minimum order date by type by order number
df <- df %>%
  group_by(id, orderno) %>%
  mutate(orderdate_dried = min(orderdate_dried, na.rm = TRUE),
         orderdate_fresh = min(orderdate_fresh, na.rm = TRUE)) %>%
  ungroup()

## aggregate order date for each type over individual
df <- df %>%
  group_by(id) %>%
  mutate(max_orderdate_dried = max(orderdate_dried, na.rm=TRUE),
         max_orderdate_fresh = max(orderdate_fresh, na.rm=TRUE)) %>%
  ungroup()

Но все максимальные даты в конце этого процесса равны NA! Я не понимаю как? Кроме того, если я проверю исходные orderdate_dried для NA:

is.na(df$orderdate_dried)

, я получу NA для каждой строки! Как это происходит?!

1 Ответ

3 голосов
/ 11 марта 2020

Очень интересный вопрос и ответ кроется в самом вопросе. Для ясности вместо обновления одного и того же df каждый раз я буду использовать df1, df2 et c.

Давайте сначала начнем с данных.

id <- c("Johnboy","Johnboy","Johnboy")
orderno <- c(2,2,1)
validorder <- c(0,1,1)
ordertype <- c(95,94,95)
orderdate <- as.Date(c("2019-06-17","2019-03-26","2018-08-23"))

df <- data.frame(id, orderno, validorder, ordertype, orderdate)

library(dplyr)

Шаг 1 -

df1 <- df %>%
        mutate(orderdate_dried = if_else(validorder == 1 &
                                         ordertype == 95,
                                        orderdate, as.Date(NA)),
               orderdate_fresh = if_else(validorder == 1 &
                                         ordertype == 94,
                                         orderdate, as.Date(NA)))

df1
#       id orderno validorder ordertype  orderdate orderdate_dried orderdate_fresh
#1 Johnboy       2          0        95 2019-06-17            <NA>            <NA>
#2 Johnboy       2          1        94 2019-03-26            <NA>      2019-03-26
#3 Johnboy       1          1        95 2018-08-23      2018-08-23            <NA>

Здесь все как положено.

Шаг 2 -

df2 <- df1 %>%
        group_by(id, orderno) %>%
        mutate(orderdate_dried = min(orderdate_dried, na.rm = TRUE),
                orderdate_fresh = min(orderdate_fresh, na.rm = TRUE)) %>%
        ungroup()

df2
# A tibble: 3 x 7
#  id      orderno validorder ordertype orderdate  orderdate_dried orderdate_fresh
#  <fct>     <dbl>      <dbl>     <dbl> <date>     <date>          <date>         
#1 Johnboy       2          0        95 2019-06-17 NA              2019-03-26     
#2 Johnboy       2          1        94 2019-03-26 NA              2019-03-26     
#3 Johnboy       1          1        95 2018-08-23 2018-08-23      NA           

Здесь все также выглядит как и ожидалось, мы получаем NA, когда в группе нет другой даты.

Шаг 3 -

df3 <- df2 %>%
        group_by(id) %>%
        mutate(max_orderdate_dried = max(orderdate_dried, na.rm=TRUE),
               max_orderdate_fresh = max(orderdate_fresh, na.rm=TRUE)) %>%
         ungroup()

df3
# A tibble: 3 x 9
#  id      orderno validorder ordertype orderdate  orderdate_dried orderdate_fresh max_orderdate_dried max_orderdate_fresh
 #  <fct>     <dbl>      <dbl>     <dbl> <date>     <date>          <date>          <date>              <date>             
#1 Johnboy       2          0        95 2019-06-17 NA              2019-03-26      NA                  NA                 
#2 Johnboy       2          1        94 2019-03-26 NA              2019-03-26      NA                  NA                 
#3 Johnboy       1          1        95 2018-08-23 2018-08-23      NA              NA                  NA    

Кажется, здесь все не так. Это в основном те же самые шаги, которые вы выполнили, и это тот же вывод, который вы получаете, поэтому мы не сделали ничего другого до этого момента.

Одна вещь, которую мы пропустили, хотя в шаге 2 мы получил предупреждающее сообщение.

Предупреждающие сообщения: 1: в min.default (c (NA_real_, NA_real_), na.rm = TRUE): нет не пропущенных аргументов для min; возврат Inf 2: в min.default (NA_real_, na.rm = TRUE): нет не пропущенных аргументов для min; возвращая Inf

Поскольку в группе не было значения, отличного от NA, он вернул Inf, хотя вывод df2 показывает NA (почему он показывает NA, когда значение Inf добавил пояснения к нему в конце ответа). Так что даже если вы протестируете is.na с ним, это не удастся.

is.na(df2$orderdate_dried)
#[1] FALSE FALSE FALSE

Следовательно, max с na.rm тоже дает сбой.

 max(df2$orderdate_dried, na.rm = TRUE)
#[1] NA

Следовательно, вы получаете все NA с на шаге 3.


Решение

Решение состоит в том, чтобы проверить с помощью is.finite

df3 <- df2 %>%
        group_by(id) %>%
         mutate(max_orderdate_dried = max(orderdate_dried[is.finite(orderdate_dried)], na.rm=TRUE),
                 max_orderdate_fresh = max(orderdate_fresh[is.finite(orderdate_fresh)], na.rm=TRUE)) %>%
         ungroup()


df3
# A tibble: 3 x 9
#  id      orderno validorder ordertype orderdate  orderdate_dried orderdate_fresh max_orderdate_dried max_orderdate_fresh
#  <fct>     <dbl>      <dbl>     <dbl> <date>     <date>          <date>          <date>              <date>             
#1 Johnboy       2          0        95 2019-06-17 NA              2019-03-26      2018-08-23          2019-03-26         
#2 Johnboy       2          1        94 2019-03-26 NA              2019-03-26      2018-08-23          2019-03-26         
#3 Johnboy       1          1        95 2018-08-23 2018-08-23      NA              2018-08-23          2019-03-26   

Почему он показывает значение как NA, когда значение равно Inf?

На шаге 2, что мы в основном делают это

min(NA, na.rm = TRUE)
#[1] Inf

Предупреждение: в мин. (NA, na.rm = TRUE): нет не пропущенных аргументов для min; возвращая Inf

Возвращает Inf с предупреждением, которое мы получаем.

Однако, поскольку мы знаем, что столбец может содержать значение только одного class.

class(Inf) #is
#[1] "numeric"

, но у нас есть данные класса "Date" в столбце df1 orderdate_dried

 class(df1$orderdate_dried)
#[1] "Date"

, поэтому Inf затем приводится к классу "Date", который возвращается.

as.Date(min(NA, na.rm = TRUE))
#[1] NA

Опять же, это возвращает NA, но это не реально NA, и is.na не срабатывает на этом

is.na(as.Date(min(NA, na.rm = TRUE)))
#[1] FALSE

, следовательно, шаг 3 не работает должным образом ,

Надеюсь, этот ответ ясен и не слишком запутан.

...