Группировка по последовательности, а затем поиск минимального значения в столбце - PullRequest
2 голосов
/ 24 июня 2019

У меня есть набор данных, который среди других столбцов имеет столбцы date, sequence and low, см. df ниже. Последовательность из 1-to-9 считается одним блоком или одним полным циклом в столбце sequence Набор данных имеет несколько таких полных блоков / циклов и частично завершенных, eg: 1-to-4

Вот что я пытаюсь решить:

  1. Удалить частично завершенные циклы и затем сгруппировать полные циклы (см. df1)
  2. Для каждого блока / цикла (то есть последовательности от 1 до 9) я хочу найти минимум блока вместе со днем, когда был достигнут минимум.
  3. Если есть два минимума с одинаковым значением, но в разные даты, то он должен выводить только самую последнюю дату (см. 3-й блок в выходных данных)

    library(lubridate)
    library(tidyverse)
    ### Sample data
    df <- data.frame(stringsAsFactors=FALSE,
    date = c("1/01/2019", "2/01/2019", "3/01/2019", "4/01/2019",
    "5/01/2019", "6/01/2019", "7/01/2019", "8/01/2019",
    "9/01/2019", "10/01/2019", "11/01/2019", "12/01/2019", "13/01/2019",
    "14/01/2019", "15/01/2019", "16/01/2019", "17/01/2019", "18/01/2019",
    "19/01/2019", "20/01/2019", "21/01/2019", "22/01/2019",
    "23/01/2019", "24/01/2019", "25/01/2019", "26/01/2019", "27/01/2019",
    "28/01/2019", "29/01/2019", "30/01/2019", "31/01/2019",
    "1/02/2019", "2/02/2019", "3/02/2019", "4/02/2019"),
    sequence = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8,
    9, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9),
    low = c(96, 81, 43, 18, 43, 65, 48, 90, 69, 50, 41, 73, 1, 1, 7, 49,
    16, 79, 2, 74, 8, 88, 56, 57, 66, 29, 79, 51, 52, 47, 42, 9,
    41, 9, 50)) %>% mutate(date = dmy(date))
    

    Данные сгруппированы по циклам / блокам

    df1 <- data.frame(stringsAsFactors=FALSE,
        date = c("1/01/2019", "2/01/2019", "3/01/2019", "4/01/2019",
                 "5/01/2019", "6/01/2019", "7/01/2019", "8/01/2019",
                 "9/01/2019", "14/01/2019", "15/01/2019", "16/01/2019", "17/01/2019",
                 "18/01/2019", "19/01/2019", "20/01/2019", "21/01/2019", "22/01/2019",
                 "27/01/2019", "28/01/2019", "29/01/2019", "30/01/2019",
                 "31/01/2019", "1/02/2019", "2/02/2019", "3/02/2019", "4/02/2019"),
    sequence = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3,
                 4, 5, 6, 7, 8, 9),
         low = c(96, 81, 43, 18, 43, 65, 48, 90, 69, 1, 7, 49, 16, 79, 2, 74,
                 8, 88, 79, 51, 52, 47, 42, 9, 41, 9, 50),
       group = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
                 3, 3, 3, 3, 3, 3)) %>% mutate(date = dmy(date))
    

Окончательный вывод Я после

  df_final <- data.frame(stringsAsFactors=FALSE,
         date = c("4/01/2019", "14/01/2019", "3/02/2019"),
          low = c(18, 1, 9)) %>% mutate(date = dmy(date))

Есть идеи?
Ps. Я столкнулся с некоторыми проблемами форматирования этого вопроса, отсюда и неопрятность.

Ответы [ 3 ]

2 голосов
/ 24 июня 2019

Другая возможность dplyr может быть такой:

df %>%
 group_by(group = cumsum(sequence == 1), rleid = with(rle(group), rep(seq_along(lengths), lengths))) %>%
 filter(all(c(1:9) %in% sequence)) %>%
 slice(which.min(rank(low, ties.method = "last"))) %>%
 ungroup() %>%
 select(-group, -rleid)

  date       sequence   low
  <date>        <dbl> <dbl>
1 2019-01-04        4    18
2 2019-01-14        1     1
3 2019-02-03        8     9

Здесь она, во-первых, создает совокупную сумму «sequence» == 1 и rleid() -подобную переменную на основе совокупной суммы изатем выполняет группировку по двум.Во-вторых, он удаляет случаи, когда последовательность не содержит все девять значений.Наконец, он возвращает минимальное значение для каждой группы, если связи возвращают последнее минимальное значение (вы можете изменить его с помощью аргумента ties.method).

2 голосов
/ 24 июня 2019

Мы создаем группирующую переменную, беря кумулятивную сумму, где последовательность равна 1, затем filter только группы с 9 элементами и slice строки, где минимальное значение минимального значения после arrange после даты.'в desc конечном порядке, чтобы позаботиться о случаях, когда есть связи для самого низкого значения

df %>% 
   group_by(group = cumsum(sequence == 1)) %>% 
   filter(n() == 9) %>% 
   select(date, low) %>%
   arrange(desc(date)) %>%
   slice(which.min(low)) %>%
   ungroup %>%
   select(-group)
# A tibble: 3 x 2
#  date         low
#  <date>     <dbl>
#1 2019-01-04    18
#2 2019-01-14     1
#3 2019-02-03     9

или аналогичного варианта с data.table

library(data.table)
setDT(df)[, .SD[.N == 9], .(group = cumsum(sequence == 1))
          ][order(-date), .SD[which.min(low)], group]
1 голос
/ 24 июня 2019

Это также возможно в базе R. Может быть немного карта sy, хотя.

w <- which(df$sequence == 1)
w <- w[sapply(w, function(x) df$sequence[x + 8] == 9 & sum(df$sequence[x:(x + 8)]) == 45)]
do.call(rbind, Map(function(x) x[which.min(x$low), ], 
                   Map(function(s) df[s, ], Map(seq, w, l=9))))
#          date sequence low
# 4  2019-01-04        4  18
# 14 2019-01-14        1   1
# 32 2019-02-01        6   9

Хитрость заключается в том, чтобы найти завершенные последовательности и сгруппировать их в списке,затем rbind which.min каждой группы.Проверка sum(.) == 45 должна учитывать, если на самом деле нет ложной последовательности.

Данные

df <- structure(list(date = structure(c(17897, 17898, 17899, 17900, 
17901, 17902, 17903, 17904, 17905, 17906, 17907, 17908, 17909, 
17910, 17911, 17912, 17913, 17914, 17915, 17916, 17917, 17918, 
17919, 17920, 17921, 17922, 17923, 17924, 17925, 17926, 17927, 
17928, 17929, 17930, 17931), class = "Date"), sequence = c(1, 
2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9), low = c(96, 81, 43, 18, 
43, 65, 48, 90, 69, 50, 41, 73, 1, 1, 7, 49, 16, 79, 2, 74, 8, 
88, 56, 57, 66, 29, 79, 51, 52, 47, 42, 9, 41, 9, 50)), row.names = c(NA, 
-35L), class = "data.frame")
...