【问题标题】:join two data frames based on user defined function根据用户定义的函数连接两个数据框
【发布时间】:2015-04-15 14:22:06
【问题描述】:

我正在尝试(内部)基于我拥有的相似性函数连接两个数据框。 例如:

data1<-data.frame(a=c(1,2,3),lat=c(38.862976,37.878146,36.825658), lon=c(-99.336782,-99.326054,-98.475976))
data2<-data.frame(b=c(10,20),lat=c(38.863412,37.877333), lon=c(-99.336701,-99.325151))

并给定一个相似函数:

are.close(lat1,long1,lat2,long2)

类似

data3<-join(a=data1,b=data2,by=c(lat,lon),FUN=are.close(a.lat,a.lon,b.lat,b.lon))

我希望收到的输出是:

  a b  lat        lon
1 1 10 38.862976 -99.336782
2 2 20 37.878146 -99.326054

lat/lon 属于其中一个表的位置(不管哪个表,比如说第一个)。

我检查的所有连接/合并方法都不允许您定义连接是如何发生的。它只允许您指定 col1=col2 之类的内容。

有没有一种方法可以提高计算效率(不是通过在两组上运行两个循环)?

【问题讨论】:

    标签: r join data.table plyr dplyr


    【解决方案1】:

    我建议使用outer 来识别符合标准的 (a,b) 对:

    neighbormat <- outer(
      1:nrow(data1),
      1:nrow(data2),
      function(i1,i2){  
        are.close(
          data1$lat[i1],
          data1$lon[i1],
          data2$lat[i2],
          data2$lon[i2]
        )
      }
    )
    dimnames(neighbormat) <- list(data1$a,data2$b)
    

    仅当 ab 是唯一的时才使用名称才有意义,但我会假设它们是唯一的,因为 OP 以这种方式使用它们。对于@konvas 的are.close 函数,这给出了

         10    20
    1  TRUE  TRUE
    2  TRUE  TRUE
    3 FALSE FALSE
    

    要获得符合条件的 (a,b) 对,请使用

    ns <- which(neighbormat,arr.ind=TRUE,use.names=TRUE)
    dimnames(ns) <- list(NULL,c("a","b"))
    
         a b
    [1,] 1 1
    [2,] 2 1
    [3,] 1 2
    [4,] 2 2
    

    将这些合并回原始数据很简单。 (不过,像 OP 那样采用任意 (lat,lon) 似乎是一个非常糟糕的主意。)

    【讨论】:

      【解决方案2】:

      这是一种使用dplyr 的方法。我假设are.close() 被矢量化并返回TRUE/FALSE,例如这将与are.close &lt;- function(a, b, c, d) (a-c)^2 + (b-d)^2 &lt; 1 之类的函数一起使用

      library(dplyr)
      expand.grid(a = data1$a, b = data2$b) %>%
          left_join(data1, by = "a") %>%
          left_join(data2, by = "b") %>%
          mutate(close = are.close(lat.x, lon.x, lat.y, lon.y)) %>%
          filter(close)
      

      【讨论】:

        【解决方案3】:

        我不知道有什么功能可以做到这一点(但当然可能有......),所以我会尝试自己编写一些代码。根据数据,这可能很困难。但是假设夫妻真的很清楚(例如,点 1 的纬度可能最接近 b 10,而经度可能更接近 b 20 等),这可能是一些工作的开始:

        data1<-data.frame(a=c(1,2,3),lat=c(38.862976,37.878146,36.825658), lon=c(-99.336782,-99.326054,-98.475976))
        data2<-data.frame(b=c(10,20),lat=c(38.863412,37.877333), lon=c(-99.336701,-99.325151))
        
        # calculate which is the closest value
        names(data1)=c("a","lat_original","lon_original")
        closest=function(x,to=to) to[which.min(abs(to - x))]
        data1$lat=sapply(data1$lat_original,function(x) closest(x,to=data2$lat))
        data1$lon=sapply(data1$lon_original,function(x) closest(x,to=data2$lon))
        
        # if dataframes are not equally big: remove biggest assigned "closest values" (or doubles?)
        if(nrow(data1)!=nrow(data2)) {
          data1$diff=abs(data1$lat-data1$lat_original)+abs(data1$lon-data1$lon_original)
          maxN <- function(x, N=N){
          x=x[!is.na(x)]
          len=length(x)
          if(N>len){
            warning('N greater than length(x).  Setting N=length(x)')
            N=length(x)
          }
          sort(x,partial=len-N+1)[as.numeric(len-N+1):len]
        }
        data1=data1[!data1$diff %in% maxN(data1$diff,N=nrow(data1)-nrow(data2)),]}
        
        # perhaps check if doubles (two different points of data 1 assigned to the same point in data2)
        which(duplicated(paste(data1$lat,data1$lon))==T)
        
        #merge based on those closest values
        merge(data1,data2,by=c("lat","lon"))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-08
          • 2018-12-25
          相关资源
          最近更新 更多