如果相同,则折叠连续的周期

collapse consecutive periods if the same

我试着用R代码表达我的用例如下:

haves <- data.frame(
          id = c(1,1,1,1)
        , start = c(as.Date('2022-01-01'), as.Date('2022-02-01'), as.Date('2022-02-01'), as.Date('2022-02-16'))
        , end = c(as.Date('2022-01-31'), as.Date('2022-02-28'), as.Date('2022-02-15'), as.Date('2022-02-28'))
        , category = c("A", "B", "A", "A")
    )

haves

wants <- data.frame(
          id = c(1,1,1)
        , start = c(as.Date('2022-01-01'), as.Date('2022-02-01'), as.Date('2022-02-01'))
        , end = c(as.Date('2022-01-31'), as.Date('2022-02-28'), as.Date('2022-02-28'))
        , category = c("A", "B", "A")
    )

wants

基本上,如果时间段是连续的并且包含相同的类别但也取决于 id,我想折叠时间段(请参阅需求中的最后 2 列)。你认为这可能吗?按 id 和类别分组,然后在开始和结束时使用 min 和 max 是行不通的。

这应该可行,但如果您有任何问题,请告诉我。

首先为 id 和类别中的连续行块创建一个 ID,然后在其中找到连续的块。

haves %>%
  as_tibble() %>%
  mutate(
      g_id = cumsum( paste0(id, '|', category) !=  lag(paste0(id, '|', category), n = 1L, default = '&%' ))
  ) %>%
  group_by(id, category, g_id) %>%
  mutate(
    lag1 = replace_na(lag(end, n = 1L)+1 == start, T)
    ) %>%
  ungroup() %>%
  mutate(
    g_id2 = cumsum( paste0(g_id, '|', lag1) !=  lag(paste0(g_id, '|', lag1), n = 1L, default = '&%' ))
  ) %>%
  group_by(id, category, g_id2) %>%
  summarise(
    start = min(start),
    end = max(end)
  ) %>%
  arrange(g_id2)

使用 rle,这应该有效:

r <- rle(haves$category)$l
haves %>% 
  mutate(cons = rep(seq(r), r)) %>% 
  group_by(id, category, cons) %>% 
  summarise(start = min(start),
            end = max(end))

# A tibble: 3 x 5
# Groups:   id, category [2]
     id category  cons start      end       
  <dbl> <chr>    <int> <date>     <date>    
1     1 A            1 2022-01-01 2022-01-31
2     1 A            3 2022-02-01 2022-02-28
3     1 B            2 2022-02-01 2022-02-28

cons = data.table::rleid(category).

一个data.table选项

library(data.table)

unique(
  setDT(haves)[
    ,
    `:=`(start = min(start), end = max(end)), rleid(category)
  ]
)

给予

   id      start        end category
1:  1 2022-01-01 2022-01-31        A
2:  1 2022-02-01 2022-02-28        B
3:  1 2022-02-01 2022-02-28        A