2015-02-23 56 views
10

目的:使用R,获得纬度和经度的数据地址的矢量通过open.mapquestapi地理编码一批地址与开放mapquestapi

出发点:由于geocodeggmap包被限制到一天2500个查询,我需要找到一个不同的方式(我的data.frame由9M条目组成)。数据科学工具包不是一种选择,因为我的大部分地址都位于英国/美国以外。我使用open.mapquestapi在http://rpubs.com/jvoorheis/Micro_Group_Rpres上发现了这个优秀片段。

geocode_attempt <- function(address) { 
    URL2 = paste("http://open.mapquestapi.com/geocoding/v1/address?key=", "Fmjtd%7Cluub2huanl%2C20%3Do5-9uzwdz", 
     "&location=", address, "&outFormat='json'", "boundingBox=24,-85,50,-125", 
     sep = "") 
    # print(URL2) 
    URL2 <- gsub(" ", "+", URL2) 
    x = getURL(URL2) 
    x1 <- fromJSON(x) 
    if (length(x1$results[[1]]$locations) == 0) { 
     return(NA) 
    } else { 
     return(c(x1$results[[1]]$locations[[1]]$displayLatLng$lat, x1$results[[1]]$locations[[1]]$displayLatLng$lng)) 
    } 
} 
geocode_attempt("1241 Kincaid St, Eugene,OR") 

我们需要这些库:

library(RCurl) 
library(rjson) 
library(dplyr) 

让我们创建一个实物模型data.frame 5不会忽略。

id <- c(seq(1:5)) 
street <- c("Alexanderplatz 10", "Friedrichstr 102", "Hauptstr 42", "Bruesseler Platz 2", "Aachener Str 324") 
postcode <- c("10178","10117", "31737", "50672", "50931") 
city <- c(rep("Berlin", 2), "Rinteln", rep("Koeln",2)) 
country <- c(rep("DE", 5)) 

df <- data.frame(id, street, postcode, city, country 

对于添加纬度lat和经度lon变量,我们可以用一个for -loop工作data.frame。我将介绍这些代码,只是为了说明该功能在原理上起作用。

for(i in 1:5){ 
    df$lat[i] <- geocode_attempt(paste(df$street[i], df$postcode[i], df$city[i], df$country[i], sep=","))[1] 
    df$lon[i] <- geocode_attempt(paste(df$street[i], df$postcode[i], df$city[i], df$country[i], sep=","))[2] 
} 

从性能的角度来看,这段代码非常糟糕。即使对于这个小的数据框架,我的电脑也花了大约9秒,很可能是因为web服务查询,但没关系。所以我可以在我的9M行上运行这些代码,但时间会很长。

我的尝试是利用dplyr包中的mutate函数。 这里是我的尝试:在只有2.3秒

df %>% 
    mutate(lat = geocode_attempt(paste(street, postcode, city, country, sep=","))[1], 
     lon = geocode_attempt(paste(street, postcode, city, country, sep=","))[2]) 

system.time停止。不错。但现在的问题是:

id    street postcode city country  lat  lon 
1 1 Alexanderplatz 10 10178 Berlin  DE 52.52194 13.41348 
2 2 Friedrichstr 102 10117 Berlin  DE 52.52194 13.41348 
3 3  Hauptstr 42 31737 Rinteln  DE 52.52194 13.41348 
4 4 Bruesseler Platz 2 50672 Koeln  DE 52.52194 13.41348 
5 5 Aachener Str 324 50931 Koeln  DE 52.52194 13.41348 

latlon正是所有条目的相同。在我的理解中,mutate函数正在逐行工作。但在这里,纬度和经度是从第一行算起的。因此,第一行是正确的。有没有人有一个想法,为什么?我提供的代码是完整的。没有额外的加载。有任何想法吗?如果你有一个高性能的替代方法,而不是优化我的代码,我会很感激。

+0

@NicE提供的查询如何最终为您的9M行工作?是你能够进行地理编码所有实例的时间相对较少,还是你击中MapQuest的限制? – bshelt141 2017-05-12 11:57:28

回答

4

真的很容易看mutate()并得出结论,发生的事情与您在for循环中说明的相似 - 但您实际看到的仅有一个R函数,该函数作用于整列数据帧。

如果其他人有这种误解,dplyr教程没有解决矢量化/非矢量化函数与(更危险)R规则意味着应用标量函数将不会必然会引发错误。还有一些关于here的更多讨论。

一种选择是重写你的geocode_attempt,这样它可以将作为的一个地址向量。

如果你要保持你的功能是,但要dplyr表现得更像是从-ply family你有两种可能的方法的东西:

第一种方法是使用您在数据具有分组变量:

df %>% 
    group_by(id) %>% 
    mutate(
    lat = geocode_attempt(paste(street, postcode, city, country, sep=","))[1], 
    lon = geocode_attempt(paste(street, postcode, city, country, sep=","))[2]) 

第二个是用rowwise()函数描述的this的答案。

df %>% 
    rowwise() %>% 
    mutate(
    lat = geocode_attempt(paste(street, postcode, city, country, sep=","))[1], 
    lon = geocode_attempt(paste(street, postcode, city, country, sep=","))[2]) 

我的机器上的group_by解决方案明显更快。不知道为什么!

不幸的是,你从dplyr看到速度以上的储蓄可能有些虚幻 - 最有可能的地理编码函数的结果得到调用一次(与每行一次的循环)。有可能有所斩获,但你需要再次运行timmings。

10

您可能需要向量化你的geocode_attempt功能做纵列:

vecGeoCode<-Vectorize(geocode_attempt,vectorize.args = c('address')) 

然后调用:

df %>% 
     mutate(lat = vecGeoCode(paste(street, postcode, city, country, sep=","))[1,], 
       lon =vecGeoCode(paste(street, postcode, city, country, sep=","))[2,]) 

要速度的东西,你可能想看看的批处理模式API可以一次性获得100拉特币和多头头寸。

要使用该API的批量请求您可以使用此功能:

geocodeBatch_attempt <- function(address) { 
    #URL for batch requests 
    URL=paste("http://open.mapquestapi.com/geocoding/v1/batch?key=", "Fmjtd%7Cluub2huanl%2C20%3Do5-9uzwdz", 
      "&location=", paste(address,collapse="&location="),sep = "") 

    URL <- gsub(" ", "+", URL) 
    data<-getURL(URL) 
    data <- fromJSON(data) 

    p<-sapply(data$results,function(x){ 
    if(length(x$locations)==0){ 
     c(NA,NA) 
    } else{ 
     c(x$locations[[1]]$displayLatLng$lat, x$locations[[1]]$displayLatLng$lng) 
    }}) 
    return(t(p)) 
} 

为了测试它:

#make a bigger df from the data (repeat the 5 lines 25 times) 
biggerDf<-df[rep(row.names(df), 25), ] 

#add a reqId column to split the data in batches of 100 requests 
biggerDf$reqId<-seq_along(biggerDf$id)%/%100 

#run the function, first grouping by reqId to send batches of 100 requests 
biggerDf %>% 
    group_by(reqId) %>% 
    mutate(lat = geocodeBatch_attempt(paste(street, postcode, city, country, sep=","))[,1], 
     lon =geocodeBatch_attempt(paste(street, postcode, city, country, sep=","))[,2]) 
+0

我该如何改变功能?我猜只是改变URL2将无法正常工作:) 矢量化功能的作品和稍微比'group_by'和'rowwise'选项 – 2015-02-24 09:45:34

+0

是的,我编辑我的回答补充一点,将处理一批请求geocode_attempt的修改版本速度更快。在125排上,速度是前者的两倍。 – NicE 2015-02-24 12:38:30

+0

就像一个魅力...因为它是一个API调用它不是很快,但工程。 任何人有一个想法,什么Mapquest服务的查询限制是什么? – 2015-02-25 09:40:00

0

有使用诺基亚HERE服务是一个geocoding package。它有一个批处理模式。您可以将其与测试API密钥一起使用,并且您可能未达到限制。值得一看...

+0

geocodeHERE的批量限制是多少?当我假设你是开发者时,我是否正确? – 2015-02-24 09:56:23

+0

限制是10K,但他们允许使用他们的默认键,似乎没有限制。是的,这只是API的简单包装。批处理功能虽然不错。 – cory 2015-02-24 15:20:54