Temukan interval yang tumpang tindih dalam kelompok dan pertahankan periode terbesar yang tidak tumpang tindih

Masalah Saya memiliki kerangka data yang dikelompokkan dengan interval yang tumpang tindih (tanggal sebagai ymd). Saya hanya ingin mempertahankan interval terbesar yang tidak tumpang tindih di setiap grup.

Contoh data

# Packages
library(tidyverse)
library(lubridate)

# Example data
df <- tibble(
  group = c(1, 1, 1, 2, 2, 3, 3, 3, 3),
  start = as_date(
    c("2019-01-10", "2019-02-01", "2019-10-05", "2018-07-01", "2019-01-01", "2019-10-01", "2019-10-01", "2019-11-30","2019-11-20")),
  end = as_date(
    c("2019-02-07", "2019-05-01", "2019-11-15", "2018-07-31", "2019-05-05", "2019-11-06", "2019-10-07", "2019-12-10","2019-12-31"))) %>%
  mutate(intval = interval(start, end),
         intval_length = intval / days(1))

df
#> # A tibble: 9 x 5
#>   group start      end        intval                         intval_length
#>   <dbl> <date>     <date>     <Interval>                             <dbl>
#> 1     1 2019-01-10 2019-02-07 2019-01-10 UTC--2019-02-07 UTC            28
#> 2     1 2019-02-01 2019-05-01 2019-02-01 UTC--2019-05-01 UTC            89
#> 3     1 2019-10-05 2019-11-15 2019-10-05 UTC--2019-11-15 UTC            41
#> 4     2 2018-07-01 2018-07-31 2018-07-01 UTC--2018-07-31 UTC            30
#> 5     2 2019-01-01 2019-05-05 2019-01-01 UTC--2019-05-05 UTC           124
#> 6     3 2019-10-01 2019-11-06 2019-10-01 UTC--2019-11-06 UTC            36
#> 7     3 2019-10-01 2019-10-07 2019-10-01 UTC--2019-10-07 UTC             6
#> 8     3 2019-11-30 2019-12-10 2019-11-30 UTC--2019-12-10 UTC            10
#> 9     3 2019-11-20 2019-12-31 2019-11-20 UTC--2019-12-31 UTC            41

# Goal
# Row: 1 and 2; 6 to 9 have overlaps; Keep rows with largest intervals (in days)
df1 <- df[-c(1, 7, 8),]

df1
#> # A tibble: 6 x 5
#>   group start      end        intval                         intval_length
#>   <dbl> <date>     <date>     <Interval>                             <dbl>
#> 1     1 2019-02-01 2019-05-01 2019-02-01 UTC--2019-05-01 UTC            89
#> 2     1 2019-10-05 2019-11-15 2019-10-05 UTC--2019-11-15 UTC            41
#> 3     2 2018-07-01 2018-07-31 2018-07-01 UTC--2018-07-31 UTC            30
#> 4     2 2019-01-01 2019-05-05 2019-01-01 UTC--2019-05-05 UTC           124
#> 5     3 2019-10-01 2019-11-06 2019-10-01 UTC--2019-11-06 UTC            36
#> 6     3 2019-11-20 2019-12-31 2019-11-20 UTC--2019-12-31 UTC            41

Pendekatan saat ini Saya menemukan pertanyaan terkait di thread lain (lihat: Temukan tanggal dalam interval periode berdasarkan grup). Namun, solusi masing-masing mengidentifikasi semua baris yang tumpang tindih berdasarkan kelompok. Dengan cara ini, saya tidak dapat mengidentifikasi interval terbesar yang tidak tumpang tindih.

df$overlap <- unlist(tapply(df$intval, #loop through intervals
                            df$group,  #grouped by id
                            function(x) rowSums(outer(x,x,int_overlaps)) > 1))

Sebagai contoh, perhatikan grup 3 dalam contoh data saya. Di sini baris 6/7 dan 8/9 tumpang tindih. Karena baris 6 dan 9 merupakan periode terbesar yang tidak tumpang tindih, saya ingin menghapus baris 7 dan 8.

Saya akan sangat menghargai jika seseorang dapat memberi saya solusi.


person Fabian.Fuchs.    schedule 30.11.2020    source sumber
comment
Ini terlihat tidak sepele. Jika data Anda tidak terlalu besar, Anda dapat melakukan brute force. Jika tidak, Anda memerlukan suatu algoritma. Ini sebenarnya bukan pertanyaan R.   -  person Michael Dewar    schedule 01.12.2020


Jawaban (1)


Setelah mencari masalah terkait di stackoverflow, saya menemukan bahwa pendekatan berikut (di sini: Collapse dan gabung interval waktu yang tumpang tindih) dan (di sini: Cara meratakan/menggabungkan periode waktu yang tumpang tindih) dapat disesuaikan dengan masalah saya.

# Solution adapted from:
# here https://stackoverflow.com/questions/53213418/collapse-and-merge-overlapping-time-intervals
# and here: https://stackoverflow.com/questions/28938147/how-to-flatten-merge-overlapping-time-periods/28938694#28938694 

# Note: df and df1 created in the initial reprex (above)

df2 <- df %>%
  group_by(group) %>%
  arrange(group, start) %>%
  mutate(indx = c(0, cumsum(as.numeric(lead(start))  >            # find overlaps
                              cummax(as.numeric(end)))[-n()])) %>%
  ungroup() %>%
  group_by(group, indx) %>%
  arrange(desc(intval_length)) %>%                                # retain largest interval
  filter(row_number() == 1) %>%
  ungroup() %>%
  select(-indx) %>%
  arrange(group, start)

# Desired output?
identical(df1, df2)
#> [1] TRUE
person Fabian.Fuchs.    schedule 01.12.2020