Mengidentifikasi interval waktu yang tumpang tindih dengan dua kriteria lain dalam R?

10

Saya harus memeriksa pengamatan burung yang dilakukan lebih lama untuk entri duplikat / tumpang tindih.

Pengamat dari berbagai titik (A, B, C) melakukan pengamatan dan menandainya di peta kertas. Garis-garis di mana dibawa ke fitur garis dengan data tambahan untuk spesies, titik pengamatan dan interval waktu mereka terlihat.

Biasanya, para pengamat berkomunikasi satu sama lain melalui telepon sambil mengamati, tetapi kadang-kadang mereka lupa, jadi saya mendapatkan garis duplikat itu.

Saya sudah mengurangi data menjadi garis-garis yang menyentuh lingkaran, jadi saya tidak perlu membuat analisis spasial, tetapi hanya membandingkan interval waktu untuk setiap spesies dan dapat yakin bahwa itu adalah individu yang sama yang ditemukan oleh perbandingan .

Saya sekarang mencari cara di R untuk mengidentifikasi entri yang:

  • dibuat pada hari yang sama dengan interval yang tumpang tindih
  • dan di mana itu adalah spesies yang sama
  • dan yang dibuat dari titik pengamatan yang berbeda (A atau B atau C atau ...))

masukkan deskripsi gambar di sini

Dalam contoh ini, saya secara manual menemukan entri yang mungkin digandakan dari individu yang sama. Titik pengamatan berbeda (A <-> B), spesiesnya sama (Sst) dan interval waktu mulai dan akhir tumpang tindih.

masukkan deskripsi gambar di sini

Saya sekarang akan membuat bidang baru "duplikat" di data.frame saya, memberikan kedua baris id umum untuk dapat mengekspornya dan kemudian memutuskan apa yang harus dilakukan.

Saya mencari-cari banyak solusi yang sudah tersedia, tetapi tidak menemukan kenyataan bahwa saya harus mengatur ulang proses untuk spesies (lebih disukai tanpa loop) dan harus membandingkan baris untuk 2 + x titik pengamatan.

Beberapa data untuk dimainkan:

testdata <- structure(list(bird_id = c("20150712_0810_1410_A_1", "20150712_0810_1410_A_2", 
"20150712_0810_1410_A_4", "20150712_0810_1410_A_7", "20150727_1115_1430_C_1", 
"20150727_1120_1430_B_1", "20150727_1120_1430_B_2", "20150727_1120_1430_B_3", 
"20150727_1120_1430_B_4", "20150727_1120_1430_B_5", "20150727_1130_1430_A_2", 
"20150727_1130_1430_A_4", "20150727_1130_1430_A_5", "20150812_0900_1225_B_3", 
"20150812_0900_1225_B_6", "20150812_0900_1225_B_7", "20150812_0907_1208_A_2", 
"20150812_0907_1208_A_3", "20150812_0907_1208_A_5", "20150812_0907_1208_A_6"
), obsPoint = c("A", "A", "A", "A", "C", "B", "B", "B", "B", 
"B", "A", "A", "A", "B", "B", "B", "A", "A", "A", "A"), species = structure(c(11L, 
11L, 11L, 11L, 10L, 11L, 10L, 11L, 11L, 11L, 11L, 10L, 11L, 11L, 
11L, 11L, 11L, 11L, 11L, 11L), .Label = c("Bf", "Fia", "Grr", 
"Kch", "Ko", "Lm", "Rm", "Row", "Sea", "Sst", "Wsb"), class = "factor"), 
    from = structure(c(1436687150, 1436689710, 1436691420, 1436694850, 
    1437992160, 1437991500, 1437995580, 1437992360, 1437995960, 
    1437998360, 1437992100, 1437994000, 1437995340, 1439366410, 
    1439369600, 1439374980, 1439367240, 1439367540, 1439369760, 
    1439370720), class = c("POSIXct", "POSIXt"), tzone = ""), 
    to = structure(c(1436687690, 1436690230, 1436691690, 1436694970, 
    1437992320, 1437992200, 1437995600, 1437992400, 1437996070, 
    1437998750, 1437992230, 1437994220, 1437996780, 1439366570, 
    1439370070, 1439375070, 1439367410, 1439367820, 1439369930, 
    1439370830), class = c("POSIXct", "POSIXt"), tzone = "")), .Names = c("bird_id", 
"obsPoint", "species", "from", "to"), row.names = c("20150712_0810_1410_A_1", 
"20150712_0810_1410_A_2", "20150712_0810_1410_A_4", "20150712_0810_1410_A_7", 
"20150727_1115_1430_C_1", "20150727_1120_1430_B_1", "20150727_1120_1430_B_2", 
"20150727_1120_1430_B_3", "20150727_1120_1430_B_4", "20150727_1120_1430_B_5", 
"20150727_1130_1430_A_2", "20150727_1130_1430_A_4", "20150727_1130_1430_A_5", 
"20150812_0900_1225_B_3", "20150812_0900_1225_B_6", "20150812_0900_1225_B_7", 
"20150812_0907_1208_A_2", "20150812_0907_1208_A_3", "20150812_0907_1208_A_5", 
"20150812_0907_1208_A_6"), class = "data.frame")

Saya menemukan solusi parsial dengan fungsi data.table foverlaps yang disebutkan misalnya di sini https://stackoverflow.com/q/25815032

library(data.table)
#Subsetting the data for each observation point and converting them into data.tables
A <- setDT(testdata[testdata$obsPoint=="A",])
B <- setDT(testdata[testdata$obsPoint=="B",])
C <- setDT(testdata[testdata$obsPoint=="C",])

#Set a key for these subsets (whatever key exactly means. Don't care as long as it works ;) )
setkey(A,species,from,to)    
setkey(B,species,from,to)
setkey(C,species,from,to)

#Generate the match results for each obsPoint/species combination with an overlapping interval
matchesAB <- foverlaps(A,B,type="within",nomatch=0L) #nomatch=0L -> remove NA
matchesAC <- foverlaps(A,C,type="within",nomatch=0L) 
matchesBC <- foverlaps(B,C,type="within",nomatch=0L)

Tentu saja, ini entah bagaimana "berhasil", tetapi sebenarnya bukan apa yang ingin saya capai pada akhirnya.

Pertama, saya harus membuat kode titik pengamatan. Saya lebih suka mencari solusi dengan mengambil sejumlah poin sembarang.

Kedua, hasilnya tidak dalam format yang saya dapat benar-benar dapat melanjutkan bekerja dengan mudah. Baris yang cocok sebenarnya dimasukkan ke dalam baris yang sama, sedangkan tujuan saya adalah membuat baris diletakkan di bawahnya, dan di kolom baru, mereka akan memiliki pengidentifikasi umum.

Ketiga, saya harus memeriksa secara manual lagi, jika interval tumpang tindih dari ketiga titik (yang tidak terjadi pada data saya, tetapi umumnya bisa)

Pada akhirnya, saya hanya ingin menerima data baru. Bingkai dengan semua kandidat yang dapat diidentifikasi oleh id grup sehingga saya dapat bergabung kembali ke garis dan mengekspor hasilnya sebagai lapisan untuk pemeriksaan lebih lanjut.

Jadi, siapa lagi yang tahu bagaimana melakukan ini?

Bernd V.
sumber
Saya tidak yakin saya mengerti sepenuhnya, tetapi sepertinya tugas yang cukup mudah di PostgreSQL. Ada fungsi untuk rentang waktu. Seperti yang saya pahami, berbagi data antara PostgreSQL dan R. seharusnya mudah
Nicklas Avén
Saya harus mengakui bahwa saya tidak memiliki pengetahuan nol tentang Postgres, tetapi sebenarnya, ketika minum bir malam ini, saya juga memiliki gagasan bahwa beberapa barang sql mungkin tersedia untuk ini. Untuk sisa operasi saya yang harus saya lakukan dengan dataset, R adalah alat meskipun, tetapi saya tahu bahwa fungsi sql juga dapat dilakukan dalam R melalui beberapa paket. Investigasi ....
Bernd V.
Seberapa besar dataset - ratusan, ribuan, jutaan baris? Untuk fungsi SQL apakah Anda menemukan sqldf ?
Simbamangu
Sementara itu, saya menemukan solusi yang berfungsi. Malu pada saya, saya tidak memposting sejauh ini. Harus membuatnya lebih umum agar bermanfaat bagi orang lain, dan kemudian saya akan mempostingnya segera.
Bernd V.
Akan memberi +1 jika semuanya vektor dan tidak menggunakan forloop!
Simbamangu

Jawaban:

1

Seperti yang disinggung oleh beberapa komentator, SQL adalah pilihan yang baik untuk mengekspresikan set kendala yang agak rumit. The sqldf paket memudahkan untuk menggunakan kekuatan SQL di R tanpa perlu untuk membuat sebuah database relasional sendiri.

Inilah solusi menggunakan SQL. Sebelum menjalankan, saya harus mengganti nama kolom interval data Anda ke startTimedan endTimekarena namanya fromdicadangkan dalam SQL.

library(reshape2)
library(sqldf)

dupes_wide <- sqldf("SELECT hex(randomblob(16)) dupe_id, x.bird_id x_bird_id, y.bird_id y_bird_id
                     FROM testdata x JOIN testdata y
                          ON (x.startTime <= y.endTime)
                         AND (x.endTime >= y.startTime)
                         AND (x.species = y.species)
                         AND (x.obsPoint < y.obsPoint)")
dupes_long <- melt(dupes_wide, id.vars='dupe_id', value.name='bird_id')
merge(testdata, dupes_long[, c('dupe_id', 'bird_id')], by='bird_id', all.x=TRUE)

Untuk membantu pemahaman, respons SQL dupes_wideakhirnya tampak seperti ini:

                         dupe_id x_bird_id y_bird_id
253FCC7A58FD8401960FC5D95153356C 20150727_1130_1430_A_2 20150727_1120_1430_B_1
9C1C1A13306ECC2DF78004D421F70CE6 20150727_1130_1430_A_5 20150727_1120_1430_B_4
1E8316DBF631BBF6D2CCBD15A85E6EF3 20150812_0907_1208_A_5 20150812_0900_1225_B_6

Bergabung sendiri FROM testdata x JOIN testdata y : Menemukan pasangan baris dari satu dataset adalah bergabung sendiri. Kita perlu membandingkan setiap baris dengan yang lainnya. The ONekspresi daftar kendala untuk menjaga pasangan.

Interval yang tumpang tindih : Saya cukup yakin definisi tumpang tindih yang saya gunakan dalam SQL ( sumber ) ini berbeda dari apa foverlapsyang dilakukan untuk Anda. Anda menggunakan tipe "dalam", yang mengharuskan pengamatan di awal obsPointsepenuhnya di dalam pengamatan di kemudian hari obsPoint(tetapi ia melewatkan yang sebaliknya, misalnya jika pengamatan C sepenuhnya berada dalam B ). Untungnya mudah dalam SQL jika Anda perlu menyandikan definisi tumpang tindih yang berbeda.

Poin berbeda : Batasan Anda bahwa duplikat dibuat dari titik observasi berbeda akan benar-benar diungkapkan (x.obsPoint <> y.obsPoint). Jika saya mengetiknya, SQL akan mengembalikan setiap pasangan duplikat dua kali, hanya dengan urutan pengalihan burung di setiap baris. Alih-alih, saya menggunakan a <untuk menjaga hanya setengah baris yang unik. (Ini bukan satu-satunya cara untuk melakukan ini)

ID duplikat unik : Seperti halnya solusi Anda sebelumnya, SQL sendiri mencantumkan duplikat di baris yang sama. hex(randomblob(16))adalah cara hacky ( belum disarankan ) dalam SQLite untuk menghasilkan ID unik untuk setiap pasangan.

Format output : Anda tidak menyukai duplikat di baris yang sama, jadi meltpisahkan, dan mergeberikan duplikat ID kembali ke kerangka data awal Anda.

Keterbatasan : Solusi saya tidak menangani kasing di mana burung yang sama ditangkap di lebih dari dua trek . Ini lebih rumit dan agak tidak jelas. Misalnya, jika rentang waktunya terlihat seperti

    | - Bird1 - |
             | - Bird2 - |
                      | - Bird3 - |

maka Bird1 adalah duplikat Bird2 , yang merupakan duplikat Bird3 , tetapi apakah Bird1 dan Bird3 duplikat?

Jeff G
sumber