【问题标题】:Faster way to process 1.2 million JSON geolocation queries from large dataframe更快地处理来自大型数据帧的 120 万个 JSON 地理位置查询
【发布时间】:2016-04-10 07:19:21
【问题描述】:

我正在处理Gowalla location-based checkin dataset,它有大约 644 万签到。这些签到的唯一位置为 128 万个。但 Gowalla 只给出纬度和经度。所以我需要为每个纬度和经度找到城市、州和国家。从 StackOverflow 上的另一篇文章中,我能够在下面创建 R 查询,该查询查询开放的街道地图并找到相关的地理细节。

不幸的是,处理 125 行大约需要 1 分钟,这意味着 128 万行需要几天时间。有没有更快的方法来找到这些细节?也许有一些内置世界城市经纬度的软件包可以找到给定经纬度的城市名称,所以我不必进行在线查询。

场地表是一个包含 3 列的数据框:1: vid(venueId), 2 lat(latitude), 3: long(longitude)

for(i in 1:nrow(venueTable)) {
 #this is just an indicator to display current value of i on screen
 cat(paste(".",i,".")) 

 #Below code composes the url query 
 url <- paste("http://nominatim.openstreetmap.org/reverse.php? format=json&lat=",
              venueTableTest3$lat[i],"&lon=",venueTableTest3$long[i])
 url <- gsub(' ','',url)
 url <- paste(url)
 x <- fromJSON(url)
 venueTableTest3$display_name[i] <- x$display_name
 venueTableTest3$country[i] <- x$address$country
}

我在 R 中使用 jsonlite 包,这使得 x 这是 JSON 查询的结果,作为存储返回的各种结果的数据框。所以使用x$display_namex$address$city 我使用我的必填字段。

我的笔记本电脑是 Core i5 3230M,配备 8gb 内存和 120gb SSD,使用 Windows 8。

【问题讨论】:

  • 不确定如何在 R 中执行此操作,但我认为您的代码是同步的,这意味着一次只发送 1 个 HTTP 请求。如果您能够一次发送大约 10 个,您的速度可能会提高约 5 倍。
  • 您的问题有一个内置假设,即您需要为每个 1.28m 不同的位置进行 API 调用,因为您查看的 SO 解决方案确实如此。但离线查找更好。您可能想编辑您的问题以区分假设。

标签: r dataframe geolocation


【解决方案1】:

即使你坚持时间,你也会遇到问题。您正在查询的服务允许“每秒最多一个请求”,您已经违反了。在您达到 120 万个查询之前,他们可能会限制您的请求。他们的网站指出,用于更大用途的类似 API 每天只有大约 15,000 个免费请求。

您最好使用离线选项。快速搜索显示有许多免费可用的人口稠密地区数据集,以及它们的经度和纬度。这是我们将使用的一个:http://simplemaps.com/resources/world-cities-data

> library(dplyr)

> cities.data <- read.csv("world_cities.csv") %>% tbl_df
> print(cities.data)

Source: local data frame [7,322 x 9]

             city     city_ascii     lat     lng    pop     country   iso2   iso3 province
           (fctr)         (fctr)   (dbl)   (dbl)  (dbl)      (fctr) (fctr) (fctr)   (fctr)
1   Qal eh-ye Now      Qal eh-ye 34.9830 63.1333   2997 Afghanistan     AF    AFG  Badghis
2     Chaghcharan    Chaghcharan 34.5167 65.2500  15000 Afghanistan     AF    AFG     Ghor
3     Lashkar Gah    Lashkar Gah 31.5830 64.3600 201546 Afghanistan     AF    AFG  Hilmand
4          Zaranj         Zaranj 31.1120 61.8870  49851 Afghanistan     AF    AFG   Nimroz
5      Tarin Kowt     Tarin Kowt 32.6333 65.8667  10000 Afghanistan     AF    AFG  Uruzgan
6    Zareh Sharan   Zareh Sharan 32.8500 68.4167  13737 Afghanistan     AF    AFG  Paktika
7        Asadabad       Asadabad 34.8660 71.1500  48400 Afghanistan     AF    AFG    Kunar
8         Taloqan        Taloqan 36.7300 69.5400  64256 Afghanistan     AF    AFG   Takhar
9  Mahmud-E Eraqi Mahmud-E Eraqi 35.0167 69.3333   7407 Afghanistan     AF    AFG   Kapisa
10     Mehtar Lam     Mehtar Lam 34.6500 70.1667  17345 Afghanistan     AF    AFG  Laghman
..            ...            ...     ...     ...    ...         ...    ...    ...      ...

如果没有任何实际的数据示例,很难演示(提供帮助!),但我们可以编造一些玩具数据。

# make up toy data
> candidate.longlat <- data.frame(vid = 1:3, 
                                lat = c(12.53, -16.31, 42.87), 
                                long = c(-70.03, -48.95, 74.59))

使用geosphere 中的distm 函数,我们可以一次计算所有数据与所有城市位置之间的距离。对您而言,这将生成一个包含约 8,400,000,000 个数字的 matrix,因此可能需要一段时间(可以探索并行化),并且可能会占用大量内存。

> install.packages("geosphere")
> library(geosphere)

# compute distance matrix using geosphere
> distance.matrix <- distm(x = candidate.longlat[,c("long", "lat")], 
                         y = cities.data[,c("lng", "lat")])

然后很容易找到离您的每个数据点最近的城市,并将cbind它发送到您的data.frame

# work out which index in the matrix is closest to the data
> closest.index <- apply(distance.matrix, 1, which.min)

# rbind city and country of match with original query
> candidate.longlat <- cbind(candidate.longlat, cities.data[closest.index, c("city", "country")])
> print(candidate.longlat)

  vid    lat   long       city    country
1   1  12.53 -70.03 Oranjestad      Aruba
2   2 -16.31 -48.95   Anapolis     Brazil
3   3  42.87  74.59    Bishkek Kyrgyzstan

【讨论】:

  • 这是一些解释!非常感谢这么详细的回复。
  • 是的!非常感谢。
  • 在没有指定距离函数时distm默认使用哪个距离函数?
  • Haversine。您可以在函数中指定,请参阅文档。
  • 我尝试在 5k 行的块中执行你的代码,直到我从 R 中收到内存超出错误。所以我一次达到了 20k 行。令人惊讶的是,20k 行的矩阵生成只需要 2 分钟,矩阵大小为 1.2gb,这样 120 万行的矩阵生成大约只需要 2 小时。这节省了大量时间。
【解决方案2】:

这是使用 R 固有空间处理能力的另一种方式:

library(sp)
library(rgeos)
library(rgdal)

# world places shapefile
URL1 <- "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_populated_places.zip"
fil1 <- basename(URL1)
if (!file.exists(fil1)) download.file(URL1, fil1)
unzip(fil1)

places <- readOGR("ne_10m_populated_places.shp", "ne_10m_populated_places",
                  stringsAsFactors=FALSE)

# some data from the other answer since you didn't provide any
URL2 <- "http://simplemaps.com/resources/files/world/world_cities.csv"
fil2 <- basename(URL2)
if (!file.exists(fil2)) download.file(URL2, fil2)

# we need the points from said dat
dat <- read.csv(fil2, stringsAsFactors=FALSE)
pts <- SpatialPoints(dat[,c("lng", "lat")], CRS(proj4string(places)))

# this is not necessary
# I just don't like the warning about longlat not being a real projection
robin <- "+proj=robin +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
pts <- spTransform(pts, robin)
places <- spTransform(places, robin)

# compute the distance (makes a pretty big matrix so you should do this
# in chunks unless you have a ton of memory or do it row-by-row
far <- gDistance(pts, places, byid=TRUE)

# find the closest one
closest <- apply(far, 1, which.min)

# map to the fields (you may want to map to other fields)
locs <- places@data[closest, c("NAME", "ADM1NAME", "ISO_A2")]

locs[sample(nrow(locs), 10),]

##              NAME        ADM1NAME ISO_A2
## 3274     Szczecin West Pomeranian     PL
## 1039     Balakhna      Nizhegorod     RU
## 1012       Chitre         Herrera     PA
## 3382     L'Aquila         Abruzzo     IT
## 1982       Dothan         Alabama     US
## 5159 Bayankhongor     Bayanhongor     MN
## 620        Deming      New Mexico     US
## 1907   Fort Smith        Arkansas     US
## 481      Dedougou        Mou Houn     BF
## 7169       Prague          Prague     CZ

大约 7500 分钟(在我的系统上)大约需要一分钟,因此您看到的是几个小时而不是一天或更长时间。您可以并行执行此操作,并且可以在一小时内完成。

为了获得更好的位置分辨率,您可以使用国家或 Admin 1 多边形的非常轻量级的 shapefile,然后使用第二个过程来计算这些地理位置与更好分辨率点的距离。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-18
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    相关资源
    最近更新 更多