Saya memiliki set data panjang dengan kolom yang mewakili waktu mulai dan berhenti, dan saya ingin menjatuhkan baris jika tumpang tindih dengan yang lain dan memiliki prioritas yang lebih tinggi (mis. 1 adalah prioritas tertinggi). Contoh data saya adalah
library(tidyverse)
library(lubridate)
times_df <- tibble(start = as_datetime(c("2019-10-05 14:05:25",
"2019-10-05 17:30:20",
"2019-10-05 17:37:00",
"2019-10-06 04:43:55",
"2019-10-06 04:53:45")),
stop = as_datetime(c("2019-10-05 14:19:20",
"2019-10-05 17:45:15",
"2019-10-05 17:50:45",
"2019-10-06 04:59:00",
"2019-10-06 05:07:10")), priority = c(5,3,4,3,4))
Cara saya menemukan serangan masalah ke belakang dengan menemukan tumpang tindih dengan nilai prioritas lebih tinggi dan kemudian menggunakan anti_join
untuk menghapusnya dari kerangka data asli. Kode ini tidak berfungsi jika ada tiga periode yang tumpang tindih pada titik waktu yang sama dan saya yakin ada cara yang lebih efisien dan fungsional untuk melakukan ini.
dropOverlaps <- function(df) {
drops <- df %>%
filter(stop > lead(start) | lag(stop) > start) %>%
mutate(group = ({seq(1, nrow(.)/2)} %>%
rep(each=2))) %>%
group_by(group) %>%
filter(priority == max(priority))
anti_join(df, drops)
}
dropOverlaps(times_df)
#> Joining, by = c("start", "stop", "priority")
#> # A tibble: 3 x 3
#> start stop priority
#> <dttm> <dttm> <dbl>
#> 1 2019-10-05 14:05:25 2019-10-05 14:19:20 5
#> 2 2019-10-05 17:30:20 2019-10-05 17:45:15 3
#> 3 2019-10-06 04:43:55 2019-10-06 04:59:00 3
Adakah yang bisa membantu saya mendapatkan output yang sama tetapi dengan fungsi yang lebih bersih? Bonus jika dapat menangani input dengan tiga periode waktu atau lebih yang semuanya tumpang tindih.
combn
, meskipun itu bisa menjadi mahal jika Anda memiliki banyak baris.times_df %>% mutate(interval = interval(start, stop)) %>% {combn(nrow(.), 2, function(x) if (int_overlaps(.$interval[x[1]], .$interval[x[2]])) x[which.min(.$priority[x])], simplify = FALSE)} %>% unlist() %>% {slice(times_df, -.)}
plyranges
acak mana yang mengadaptasi IRanges / GRanges (digunakan untuk menemukan tumpang tindih antar genom) untuk tidyverse. Saya pikir Anda bisa mengubah waktu Anda menjadi rentang "genomik" dengan mengubah hari Anda + jam menjadi integer jam ("choromosome") dan menit Anda + detik menjadi integer detik ("nukleotida"). Jika Anda melihat output daripair_overlaps
(dan menggunakan kolom ID untuk menghapus tumpang-tindih sendiri), Anda dapat mempertahankan prioritas Anda dan melakukan filter yang bagus dari hasil + inner_join dengan tabel asli Anda. Ini macet tetapi harus mengoptimalkan kemudahan pengkodean + efisiensi.Jawaban:
Berikut ini adalah
data.table
solusi menggunakanfoverlaps
untuk mendeteksi catatan yang tumpang tindih (seperti yang telah disebutkan oleh @GenesRus). Catatan yang tumpang tindih ditugaskan ke grup untuk memfilter catatan dengan maks. prioritas dalam grup. Saya menambahkan dua rekaman lagi ke data contoh Anda, untuk menunjukkan bahwa prosedur ini juga berfungsi untuk tiga atau lebih rekaman yang tumpang tindih:Sunting: Saya memodifikasi dan menerjemahkan solusi @ pgcudahy
data.table
yang memberikan kode lebih cepat:Untuk perincian lebih lanjut, lihat
?foverlaps
- Ada beberapa fitur yang lebih berguna yang diterapkan untuk mengontrol apa yang dianggap tumpang tindih sepertimaxgap
,minoverlap
atautype
(setiap, di dalam, mulai, berakhir dan sama).Perbarui - patokan baru
Kode benchmark:
sumber
Saya memiliki fungsi pembantu yang mengelompokkan data yang tumpang tindih / waktu menggunakan paket igraph (ini dapat mencakup buffer tumpang tindih, yaitu terminal berada dalam 1 menit ...)
Saya menggunakannya untuk mengelompokkan data Anda berdasarkan interval di lubridate, kemudian melakukan beberapa pertengkaran data untuk mendapatkan hanya entri prioritas utama dari waktu yang tumpang tindih.
Saya tidak yakin seberapa baik skala itu.
Pemberian yang mana:
sumber
Saya pergi ke lubang kelinci melihat pohon interval (dan implementasi R seperti IRanges / plyranges) tapi saya pikir masalah ini tidak memerlukan struktur data yang terlibat karena waktu mulai dapat dengan mudah disortir. Saya juga memperluas set tes seperti @ismirsehregal untuk mencakup lebih banyak hubungan interval potensial seperti interval yang dimulai sebelum dan berakhir setelah tetangganya, atau ketika tiga interval tumpang tindih tetapi yang pertama dan terakhir tidak saling tumpang tindih, atau dua interval yang dimulai dan berhenti pada waktu yang persis sama.
Saya kemudian membuat dua melewati setiap interval untuk melihat apakah itu tumpang tindih dengan pendahulunya atau penggantinya
stop >= lead(start, default=FALSE)
danstart <= lag(stop, default=FALSE))
Selama setiap lintasan, ada pemeriksaan kedua untuk melihat apakah prioritas interval memiliki nilai numerik yang lebih tinggi daripada pendahulu atau penggantinya
priority > lead(priority, default=(max(priority) + 1))
. Selama setiap pass, jika kedua kondisi ini benar, bendera "hapus" disetel ke true di kolom baru menggunakanmutate
. Setiap baris dengan tanda hapus kemudian disaring.Ini menghindari memeriksa semua kemungkinan kombinasi interval seperti jawaban @ Paul (perbandingan 2n versus n!) Serta mengakomodasi ketidaktahuan saya akan teori grafik :)
Demikian pula jawaban @ ismirsehregal memiliki sihir data.table yang di luar pemahaman saya.
Solusi @ MKa tampaknya tidak bekerja dengan> 2 periode yang tumpang tindih
Pengujian memberikan solusi
Dari kode ini
sumber
tibble
struktur dan sepertinyapull()
menyebabkan masalah. Sebabdataframe()
, itu harus bekerja apa adanya. Baru saja memperbarui jawabannya.data.table
yang membuat segalanya lebih cepat (silakan periksa tolok ukur baru saya).Juga menggunakan
igraph
untuk mengidentifikasi grup yang tumpang tindih, Anda dapat mencoba:sumber