根据用户定义的函数连接两个数据帧

时间:2015-04-15 14:22:06

标签: r join data.table plyr dplyr

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

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。

这样的东西

有没有办法计算效率(不是通过在两组上运行两个循环)?

3 个答案:

答案 0 :(得分:3)

我建议使用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

将这些合并回原始数据非常简单。 (尽管如此,采取任意(纬度,经度)可能是一个非常糟糕的主意。)

答案 1 :(得分:2)

以下是使用dplyr的方法。我假设are.close()已经过矢量化并返回TRUE/FALSE,例如,这将适用于are.close <- function(a, b, c, d) (a-c)^2 + (b-d)^2 < 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)

答案 2 :(得分:1)

我不知道这样做的功能(但当然可能是......),所以我会尝试自己写一些代码。根据数据,这可能很难。但假设情侣真的很清楚(例如,第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"))