用R?中的另外两个条件来识别重叠的时间间隔。


10

对于重复/重叠的条目,我必须检查较长时间的鸟类观察。

来自不同地点(A,B,C)的观察者进行了观察,并将其标记在纸质地图上。这些线与其他物种有关的数据,观察点和观察到的时间间隔加入其中。

通常,观察者在观察的同时通过电话彼此交流,但有时他们会忘记,所以我得到了重复的线路。

我已经将数据简化为可以触及圆的线,因此不必进行空间分析,而只需比较每种物种的时间间隔,就可以确定比较得出的是同一个人。

我现在正在R中寻找一种方法来识别那些条目:

  • 在同一天以重叠的间隔进行
  • 哪里是同一个物种
  • 由不同的观察点(A或B或C或...)制成

在此处输入图片说明

在此示例中,我手动找到了同一个人的可能重复的条目。观察点不同(A <-> B),种类相同(Sst),开始时间和结束时间的间隔重叠。

在此处输入图片说明

现在,我将在data.frame中创建一个新字段“ duplicate”,为两行提供一个通用ID,以便能够导出它们并稍后决定要做什么。

我在很多地方搜索了已经存在的解决方案,但是没有发现有关我必须对物种的过程进行子集化(最好没有循环)并且必须比较2 + x个观察点的行这一事实。

一些数据可玩:

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")

我发现了提到的data.table函数foverlaps的部分解决方案,例如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)

当然,这种方式“可行”,但实际上并不是我最终想要实现的目标。

首先,我必须对观察点进行硬编码。我宁愿找到一个取任意点数的解决方案。

其次,结果的格式不是我可以轻松恢复的格式。匹配的行实际上放在INTO的同一行中,而我的目标是将这些行放在下面,并在一个新列中将有一个公共标识符。

第三,我必须再次手动检查间隔是否在所有三个点上都重叠(我的数据不是这种情况,但通常可以)

最后,我只想接收一个新的data.frame,其中所有候选者都可以通过组ID进行识别,然后我可以加入这些行,然后将结果导出为一层以供进一步检查。

那么,还有其他人怎么做呢?


我不确定我是否完全理解,但这似乎是一个在PostgreSQL中相当简单的任务。有用于时间范围的功能。正如我的理解是应该很容易PostgreSQL和R之间共享数据
尼克拉斯AVEN

我必须承认,我对Postgres的了解不为零,但是实际上,当今天晚上喝啤酒时,我还想到了一些SQL方面的知识可能适用于此。对于我的其余操作,我必须处理数据集,尽管R是THE工具,但是我知道sql函数也可以通过某些程序包在R中执行。正在调查....
Bernd V.

数据集有多大?几百,几千,几百万行?对于SQL函数,您找到sqldf吗?
Simbamangu '16

同时,我找到了可行的解决方案。真可惜,我到目前为止还没有发布。必须使其更通用才能为他人使用,然后我将其尽快发布。
Bernd V.

如果所有向量化并且不使用for循环,则将对其+1 !
Simbamangu '16

Answers:


1

正如一些评论者所暗示的那样,SQL是表达相当复杂的约束集的好选择。该sqldf包可以很容易地使用SQL的力量中的R,而无需建立一个关系数据库自己。

这是使用SQL的解决方案。在运行之前,我不得不将数据的间隔列重命名为startTime和,endTime因为该名称from在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)

为了帮助理解,SQL响应dupes_wide最终如下所示:

                         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

自连接 FROM testdata x JOIN testdata y从单个数据集中查找成对的行是自连接。我们需要比较每一行。该ON表达式列出了保持配对的约束。

重叠间隔:我很确定在此SQL中使用的重叠定义()与foverlaps为您所做的不同。您使用了“内部”类型,它要求较早obsPoint的观察结果完全在较晚的观察结果之内obsPoint(但它会错过相反的情况,例如,如果C的观察结果完全在B的观察范围内)。幸运的是,如果需要对重叠进行不同的定义编码,则在SQL中这很容易。

不同的点:你的约束是可以表达来自不同观察点的重复(x.obsPoint <> y.obsPoint)。如果键入该内容,SQL将返回每个重复的对两次,只是每行的鸟切换顺序相同。相反,我使用a <仅保留行的唯一一半。(这不是唯一的方法)

唯一重复ID:与您先前的解决方案一样,SQL本身在同一行中列出重复项。 hex(randomblob(16))是SQLite中的一种hacky(但仍推荐)方法,用于为每对生成唯一的ID。

输出格式:您不喜欢同一行中的重复项,因此melt将它们拆分开,然后merge将重复项ID分配回您的初始数据帧。

局限性:我的解决方案无法处理同一鸟被捕获超过两条轨道的情况。这比较棘手,定义也不明确。例如,如果他们的时间范围看起来像

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

然后Bird1是重复BIRD2,这是一个重复的Bird3,但Bird1Bird3重复?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.