2015-02-23 51 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