Как взять макс / секунду макс с уровнями, используя dplyr? - PullRequest
1 голос
/ 30 апреля 2019

У меня есть данные, где я хотел бы получить следующую информацию.Пример основан на ID == 1.

  1. Первая дата каждого идентификатора и соответствующий ему результат (для ID == 1 его DATE == 1/3/2018).Если есть уровни, возьмите последнюю строку (RESULT == 110).
  2. Вторая дата каждого идентификатора и соответствующий ему результат (для ID == 1 его DATE == 2/3/2018).Если есть уровни, возьмите последнюю строку (RESULT == 117).
  3. Самый низкий результат и соответствующую ему дату.Если наименьшее значение имеет 2 даты, выведите первую дату.Второй датой будет дата второго наименьшего результата.
  4. Второй наименьший результат и соответствующая ему дата.Если самое низкое значение отличается от самого низкого результата.

Данные:

df <- read.table(text = "
                ID DATE RESULT
                1 1/3/2018 110
                1 1/3/2018 120
                1 2/3/2018 115
                1 2/3/2018 117
                1 3/3/2018 100
                1 4/3/2018 100
                2 1/11/2018 110
                2 1/11/2018 120
                2 1/11/2018 108
                2 2/11/2018 115
                2 3/11/2018 80
                2 4/11/2018 70", header = TRUE, stringsAsFactors = FALSE) 

Желаемый результат:

df_out <- read.table(text = "
                ID FIRST_DATE FIRST_RESULT SECOND_DATE SECOND_RESULT LOWEST_DATE LOWEST_RESULT SECOND_LOWEST_DATE SECOND_LOWEST_RESULT
                1 2018-03-01 120 2018-03-02 117 2018-03-03 100 2018-03-04 100
                2 2018-11-01 108 2018-11-02 115 2018-11-04 70 2018-11-03 80", header = TRUE, stringsAsFactors = FALSE)

Выполненные шаги:

  1. Разделите задачу на 2, одну, которая включает ранжирование по дате, а другую, включающую ранжирование по результату.
  2. Использованиеdplyr::dense_rank для первой части и dplyr::row_number для второй части.
  3. Возьмите dplyr::full_join, чтобы получить окончательный кадр данных.

Мой код работает отлично, но я чувствую, что онслишком долгоПоэтому я хотел бы спросить, может ли кто-нибудь написать это более кратко.

library(dplyr)
library(tidyr)

# First Portion
df_DATE <- df %>% 
  group_by(ID) %>% 
  mutate(DATE = dmy(DATE),
         RANK_DATE = dense_rank(DATE)) %>% 
  group_by(ID, RANK_DATE) %>% 
  filter(RANK_DATE %in% 1:2,
         row_number() == n()) %>% 
  ungroup() %>% 
  mutate(DATE = as.character(DATE)) %>% 
  gather(VARIABLE, VALUE, -c(ID, RANK_DATE)) %>% 
  unite(VARIABLE, VARIABLE, RANK_DATE) %>% 
  spread(VARIABLE, VALUE) %>% 
  select(ID, 
         FIRST_DATE = DATE_1, FIRST_RESULT = RESULT_1, 
         SECOND_DATE = DATE_2, SECOND_RESULT = RESULT_2)

# Second Portion
df_RESULT <- df %>%
  group_by(ID) %>% 
  mutate(DATE = dmy(DATE),
         RANK_RESULT = row_number(RESULT)) %>% 
  filter(RANK_RESULT %in% 1:2) %>% 
  mutate(DATE = as.character(DATE)) %>% 
  gather(VARIABLE, VALUE, -c(ID, RANK_RESULT)) %>% 
  unite(VARIABLE, VARIABLE, RANK_RESULT) %>% 
  spread(VARIABLE, VALUE) %>% 
  select(ID, 
         LOWEST_DATE = DATE_1, LOWEST_VALUE = RESULT_1, 
         SECOND_LOWEST_DATE = DATE_2, SECOND_LOWEST_VALUE = RESULT_2)

# Combine the 2 portions
df_out <- full_join(df_DATE, df_RESULT)

1 Ответ

2 голосов
/ 30 апреля 2019

Если я ничего не пропустил, мы можем сделать это в одной цепочке без изменения формы или соединения

library(dplyr)

df %>%
  mutate(DATE = as.Date(DATE, "%d/%m/%Y")) %>%
  #arrange(ID, DATE) %>% #if need to be sure that data is arranged by `ID` and `DATE`
  group_by(ID) %>%
  summarise(FIRST_DATE = first(DATE), 
            FIRST_RESULT = RESULT[max(which(DATE == FIRST_DATE))], 
            SECOND_DATE = unique(DATE)[2], 
            SECOND_RESULT = RESULT[max(which(DATE == SECOND_DATE))], 
            LOWEST_DATE = DATE[which.min(RESULT)], 
            LOWEST_RESULT = RESULT[which.min(RESULT)], 
            SECOND_LOWEST_DATE = DATE[order(RESULT)[2]], 
            SECOND_LOWEST_RESULT = RESULT[order(RESULT)[2]])

# A tibble: 2 x 9
#     ID FIRST_DATE FIRST_RESULT SECOND_DATE SECOND_RESULT LOWEST_DATE LOWEST_RESULT SECOND_LOWEST_D… SECOND_LOWEST_R…
#  <int> <date>            <int> <date>              <int> <date>              <int> <date>                      <int>
#1     1 2018-03-01          120 2018-03-02            117 2018-03-03            100 2018-03-04                    100
#2     2 2018-11-01          108 2018-11-02            115 2018-11-04             70 2018-11-03                     80

Объяснить:

FIRST_DATE - выбирает значение first из DATE

FIRST_RESULT = RESULT[max(which(DATE == FIRST_DATE)) - получает индексы, где DATE равен FIRST_DATE, и выбирает индекс max для получения последней строки в случае связей.

SECOND_DATE = unique(DATE)[2] получает второе отличительное DATE

SECOND_RESULT = RESULT[max(which(DATE == SECOND_DATE))] - получает индексы, где DATE равен SECOND_DATE, и выбирает индекс max для получения последней строки в случае связей.

LOWEST_DATE = DATE[which.min(RESULT)] - Получите самый низкий DATE. which.min возвращает индекс первого минимума DATE

LOWEST_RESULT - Получить соответствующий RESULT из LOWEST_DATE

SECOND_LOWEST_DATE = DATE[order(RESULT)[2]] - используя order, мы размещаем RESULT в порядке возрастания и выбираем вторую запись. Это будет работать как для связей, так и для другой записи в RESULT.

SECOND_LOWEST_RESULT - получить соответствующую запись RESULT.

...