【问题标题】:Is it possible to optimize this lookup function? (Faster IF Statement)是否可以优化此查找功能? (更快的 IF 语句)
【发布时间】:2020-07-30 14:51:34
【问题描述】:

是否可以优化这段代码?

在我的数据上运行一次大约需要 2 秒,因为我必须重复运行它,这会增加整个程序的时间。

此代码设置 2(f1,f2) 地理围栏并检查 node_coords 中的点是否在这些围栏之一内。因此,它会生成一个逻辑向量索引,可用于过滤 node_coords 并仅将那些位于这两个地理围栏之一中的点留在原处。

提前非常感谢您! BR 安德烈亚斯

library("vctrs")



node_coords<-structure(list(lon = c(11.34175, 12.2063556, 12.2066937, 12.2068632, 
12.2070187, 12.2078502), lat = c( 48.27649, 47.8399432, 47.8397677, 
47.8396466, 47.8396952, 47.8395169)), row.names = c(172422L,
260117L, 147288L, 1337832L, 1850176L, 260151L), class = "data.frame")


check_if_point_is_within_geofence <- function(top, left, bottom, right, latitude, longitude){
  # Check latitude bounds first.
  if(top >= latitude && latitude >= bottom){
    # If your bounding box doesn't wrap 
    #              the date line the value
    #               must be between the bounds.
    #               If your bounding box does wrap the 
    #               date line it only needs to be  
    #               higher than the left bound or 
    #               lower than the right bound. 
    if(left <= right && left <= longitude && longitude <= right){
      return(TRUE)
    } else if(left > right && (left <= longitude || longitude <= right)) {
      return(TRUE) 
    }
  }
  return(FALSE)
}

geofence <- function(lon,lat){
  f1 <- base::data.frame("left" = 11.34175, "bottom" = 47.98702 ,"right" = 11.77417 ,"top" = 48.27649)
  f2 <- base::data.frame("left" = 12.10723, "bottom" = 47.84540, "right" = 12.15024, "top" = 47.87435 )
  
  fences <- rbind.data.frame(f1,f2)
  f_list <- apply(fences,1,function(x)  check_if_point_is_within_geofence(top = x[4],left = x[1],bottom = x[2],right = x[3],latitude = lat,longitude = lon ) )
  
  if (vec_in(TRUE,f_list))
    return(TRUE)
  return(FALSE)
}

index <- apply(cbind(node_coords$lon,node_coords$lat),1,function(x)  geofence(x[1],x[2]) )

【问题讨论】:

  • 如果您能用几句话描述您的代码,将会有所帮助。 check_if_point_is_within_geofence 是描述性的命名和彻底的评论,所以很清楚。除此之外,几乎没有什么信息。你能写一两句关于geofence()、你的目标、index 是什么等吗?
  • 您好 Gregor,感谢您的回复。我补充了几句澄清。
  • 谢谢 - 更清楚了。您能否提供更好的样本数据?您提供的数据似乎返回 FALSE FALSE FALSE FALSE FALSE FALSE 作为所需的结果。最好对应该产生 TRUE 和 FALSE 结果混合的数据进行测试,以确保一切正常。
  • 抱歉没问题,我更改了问题中的数据集;现在应该说 TRUE FALSE FALSE FALSE FALSE FALSE

标签: r dplyr tidyr


【解决方案1】:

这将是您的代码的优化版本:

vec_geofence <- function(top, left, bottom, right, lat, lon) {

  # The mask vector represents whether a coordinate is seen in any of the
  #   fences defined by the top, left, bottom and right vectors. In the beginning
  #   all the coordinates haven't been tested, so the respective value in the
  #   mask vector is initialized as False.
  mask <- rep(F, length(lon))
  
  # For each fence...
  for(i in seq_along(top)) {

    # ... check for all the coordinates if they are inside of the fence
    if( left[i] > right[i] )
      new_mask <- top[i] >= lat & lat >= bottom[i] & (left[i] <= lon | lon <= right[i])
    else
      new_mask <- top[i] >= lat & lat >= bottom[i] & (left[i] <= lon & lon <= right[i])
    
    # For all the coordinates that hadn't yet been seen in a fence, and that
    #   are inside the current fence, update the respective mask value to True
    mask[!mask][new_mask] <- T

    # The coordinates that will pass through to the next fence check are the ones
    #   that still haven't been seen inside a fence
    lat <- lat[!new_mask]
    lon <- lon[!new_mask]
  }
  
  mask
}

vec_geofence(fences$top, fences$left, fences$bottom, fences$right, node_coords$lat, node_coords$lon)
#> [1]  TRUE FALSE FALSE FALSE FALSE FALSE

我主要改变了 4 件事:

  1. 将栅栏数据框移到地理围栏函数之外,因此每次运行该函数时都不会创建它
  2. 将check_if_point_is_within_geofence 中的 if 语句转换为逻辑公式
  3. 将这两个函数合并为一个,这样您就可以避免函数调用延迟
  4. 将单值逻辑公式转换为向量逻辑公式

此函数需要 5 秒来计算包含 10 000 行的 node_coords 数据框和包含 10 000 行的 fences 数据框的地理围栏。

node_coords_10k = do.call(rbind.data.frame, rep(list(node_coords), 10000/6))
fences_10k = do.call(rbind.data.frame, rep(list(fences), 10000/2))

system.time(vec_geofence(
    fences_10k$top, fences_10k$left, fences_10k$bottom, fences_10k$right, 
    node_coords_10k$lat, node_coords_10k$lon
))
#> user  system elapsed 
#> 4.78    0.03    4.85 

【讨论】:

  • 好的,这太疯狂了 :) 非常感谢。好快啊!
  • MkWTF 您能否详细说明 for 序列和掩码语句?我的意思是这太好了,非常感谢!我只是想了解它:)
  • @Andreas 刚刚在上面的代码中添加了 cmets 来解释是怎么回事,这是你需要的吗?
  • 我不知道您是否会读到这篇文章,因为已经过了一段时间:它仍然是一个很好的解决方案,我想知道是否可以进行反向检查 ieoutput 哪个地理围栏有一个点?
猜你喜欢
  • 1970-01-01
  • 2015-02-12
  • 2014-12-07
  • 2016-08-08
  • 1970-01-01
  • 2021-11-20
  • 1970-01-01
  • 2013-09-19
  • 2022-11-24
相关资源
最近更新 更多