如何识别多列中的重叠

时间:2015-02-16 11:30:50

标签: r data.table

我有一个数据集(mydata),其中包含多个列,这些列可以适合存储在另一个数据集(mycomparison)中的范围。

我想将mycomparison加入mydata,其中mydata值在mycomparison的范围内。

MWE

library(data.table)

mydata<-data.table(
  id=1:5,
  val1=seq(10000, 50000, by=10000),
  val2=floor(rnorm(5,mean=400,sd=100)),
  val3=rnorm(5,mean=.7,sd=.1)
)

mycomparison<-data.table(
  Name=LETTERS[1:3],
  minval1=c(0,30000,10000),
  maxval1=c(50000,80000,30000),
  minval2=c(300,400,300),
  maxval2=c(800,800,800),
  minval3=c(0,.5,.2),
  maxval3=c(1,.9,.8),
  correspondingval=c(.1,.2,.3)
)

期望输出

> mydata.withmatches
   id  val1 val2      val3 Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
1:  1 10000  387 0.4844319    A       0   50000     300     800       0       1              0.1
2:  2 20000  425 0.7856313   NA      NA      NA      NA      NA      NA      NA               NA
3:  3 30000  324 0.8063969   NA      NA      NA      NA      NA      NA      NA               NA
4:  4 40000  263 0.5590113   NA      NA      NA      NA      NA      NA      NA               NA
5:  5 50000  187 0.8764396   NA      NA      NA      NA      NA      NA      NA               NA

当前解决方案

这感觉/非常笨重,涉及交叉加入数据(使用optiRum::CJ.dt),进行大的逻辑检查,然后重新组合数据。

library(optiRum)

workingdt<-CJ.dt(mydata,mycomparison)

matched<-workingdt[val1>=minval1 &
                     val1<=maxval1 &
                     val2>=minval2 &
                     val2<=maxval2 &
                     val3>=minval3 &
                     val3<=maxval3][which.min(correspondingval)]
notmatched<-mydata[id!= matched[,id]]

all<-list(matched,notmatched)

mydata.withmatches<- rbindlist(all, fill=TRUE, use.names=TRUE)

寻找更好的解决方案 - 更新

我知道foverlaps,但它会在一个时间间隔内工作,而不是像在这种情况下的许多范围。

我希望不那么笨重,更优雅的解决方案。

3 个答案:

答案 0 :(得分:0)

好问题!下面你可以找到我的快速修复,但它仍然有点太 cluncky 符合我的口味。

生成的mydata集:

   id  val1 val2      val3
1:  1 10000  377 0.7912443
2:  2 20000  378 0.7709792
3:  3 30000  484 0.7049517
4:  4 40000  513 0.5169590
5:  5 50000  474 0.7987448

一个简单的函数,用于过滤掉比较数据集的不匹配行(多行可以匹配)。

library(dplyr)

find_interval_func<-function(var.min.name, var.max.name, value, val.to.return){
  compset<-data.frame(mycomparison)

  for(i in 1:length(var.min.name)){

    compset<-
      compset %>%
      filter_(paste0(var.min.name[[i]], "<=", value[[i]]),
              paste0(var.max.name[[i]], ">", value[[i]]))    
  }

  paste(compset[,val.to.return], collapse="|")

}

结果:

> mydata %>%
+   group_by(1:n()) %>%
+   mutate(matchedValue = find_interval_func(c("minval1", "minval2", "minval3"),
+                                        c("maxval1", "maxval2", "maxval3"),
+                                        c(val1, val2, val3), 
+                                        "Name"))
Source: local data table [5 x 6]

  id  val1 val2      val3 1:n() matchedValue
1  1 10000  377 0.7912443     1          A|C
2  2 20000  378 0.7709792     2          A|C
3  3 30000  484 0.7049517     3          A|B
4  4 40000  513 0.5169590     4          A|B
5  5 50000  474 0.7987448     5            B

答案 1 :(得分:0)

我并不完全理解您的期望输出,因为多个ID与mycomparison data.table相匹配。使用您的数据(四舍五入到小数点后两位):

> mydata
   id  val1 val2 val3
1:  1 10000  387 0.48
2:  2 20000  425 0.79
3:  3 30000  324 0.81
4:  4 40000  263 0.56
5:  5 50000  187 0.88

> mycomparison
   Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
1:    A       0   50000     300     800     0.0     1.0              0.1
2:    B   30000   80000     400     800     0.5     0.9              0.2
3:    C   10000   30000     300     800     0.2     0.8              0.3

这给出了:

> workingdt
    id  val1 val2 val3 Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
 1:  1 10000  387 0.48    A       0   50000     300     800     0.0     1.0              0.1
 2:  2 20000  425 0.79    A       0   50000     300     800     0.0     1.0              0.1
 3:  3 30000  324 0.81    A       0   50000     300     800     0.0     1.0              0.1
 4:  4 40000  263 0.56    A       0   50000     300     800     0.0     1.0              0.1
 5:  5 50000  187 0.88    A       0   50000     300     800     0.0     1.0              0.1
 6:  1 10000  387 0.48    B   30000   80000     400     800     0.5     0.9              0.2
 7:  2 20000  425 0.79    B   30000   80000     400     800     0.5     0.9              0.2
 8:  3 30000  324 0.81    B   30000   80000     400     800     0.5     0.9              0.2
 9:  4 40000  263 0.56    B   30000   80000     400     800     0.5     0.9              0.2
10:  5 50000  187 0.88    B   30000   80000     400     800     0.5     0.9              0.2
11:  1 10000  387 0.48    C   10000   30000     300     800     0.2     0.8              0.3
12:  2 20000  425 0.79    C   10000   30000     300     800     0.2     0.8              0.3
13:  3 30000  324 0.81    C   10000   30000     300     800     0.2     0.8              0.3
14:  4 40000  263 0.56    C   10000   30000     300     800     0.2     0.8              0.3
15:  5 50000  187 0.88    C   10000   30000     300     800     0.2     0.8              0.3

取消which.min()

> workingdt[val1>=minval1 & val1<= maxval1 & val2>=minval2 &
            val2<=maxval2 & val3>=minval3 & val3<=maxval3]
   id  val1 val2 val3 Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
1:  1 10000  387 0.48    A       0   50000     300     800     0.0     1.0              0.1
2:  2 20000  425 0.79    A       0   50000     300     800     0.0     1.0              0.1
3:  3 30000  324 0.81    A       0   50000     300     800     0.0     1.0              0.1
4:  1 10000  387 0.48    C   10000   30000     300     800     0.2     0.8              0.3
5:  2 20000  425 0.79    C   10000   30000     300     800     0.2     0.8              0.3

如果您使用data.table分组依据功能,则可以为每个min(correspondingval)选择id(我暂时不会使用不匹配的数据):

> workingdt[val1>=minval1 & val1<= maxval1 & val2>=minval2 & 
            val2<=maxval2 & val3>=minval3 & val3<=maxval3]
                   [,.SD[which.min(correspondingval)], by=id]
   id  val1 val2 val3 Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
1:  1 10000  387 0.48    A       0   50000     300     800       0       1              0.1
2:  2 20000  425 0.79    A       0   50000     300     800       0       1              0.1
3:  3 30000  324 0.81    A       0   50000     300     800       0       1              0.1

或者,max(correspondingval)如果您愿意:

> workingdt[val1>=minval1 & val1<= maxval1 & val2>=minval2 &
            val2<=maxval2 & val3>=minval3 & val3<=maxval3]
                   [,.SD[which.max(correspondingval)], by=id]
   id  val1 val2 val3 Name minval1 maxval1 minval2 maxval2 minval3 maxval3 correspondingval
1:  1 10000  387 0.48    C   10000   30000     300     800     0.2     0.8              0.3
2:  2 20000  425 0.79    C   10000   30000     300     800     0.2     0.8              0.3
3:  3 30000  324 0.81    A       0   50000     300     800     0.0     1.0              0.1

如果你想要的只是 - 如期望的输出所示 - 是第一行的correspondingval最小,其他一切都是NA,那么有更简单的方法做这个。如果你想知道每个id匹配一个范围 - 正如我在输出中所示 - 那么更清晰,更优雅的解决方案是不同的。

让我知道。

答案 2 :(得分:0)

有一种简单的方法,它使用来自tidyr的交叉函数,以及来自dplyr的函数之间非常有用的。当然,只要至少有一个表格相对较小,这就可以安全地工作,否则交叉可能是一个记忆猪。

reset