在 R 中处理地理数据时提高性能

Posted

技术标签:

【中文标题】在 R 中处理地理数据时提高性能【英文标题】:Improving performance when working with geodata in R 【发布时间】:2014-09-20 21:31:20 【问题描述】:

我编写了以下脚本来生成用于绘制上述地图的原始数据。问题是,对于 550,000 个数据点,这需要大约 2 小时才能在相对强大的机器上运行。但是,我是 R 新手,我想知道是否有任何优化的功能可以利用?

基本思想是,给定一组地理空间数据,将数据集拆分为 200 行,并将每一行拆分为一堆正方形。然后,您计算一行中每个方格中的值的总和。我在下面采用的方法是取正方形的“左上”点,计算正方形边缘的纬度/经度,并排除所有不在这些范围内的点,然后将剩余的点相加。不使用 PostGIS 这样的解决方案有没有更好的方法?

all.data <- read.csv("FrederictonPropertyTaxDiffCleanedv3.csv", header=TRUE, 
stringsAsFactors=FALSE)
all.data$X <- as.numeric(all.data$X)
all.data$Y <- as.numeric(all.data$Y)

startEnd <- function(lats, lngs) 
  # Find the "upper left" (NW) and "bottom right" (SE) coordinates of a set of data.
  #
  # Args:
  #  lats: A list of latitude coordinates
  #  lngs: A list of longitude coordinates
  #
  # Returns: 
  #   A list of values corresponding to the northwest-most and southeast-most coordinates

  # Convert to real number and remove NA values
  lats <- na.omit(as.numeric(lats))
  lngs <- na.omit(as.numeric(lngs))

  topLat <- max(lats)
  topLng <- min(lngs)
  botLat <- min(lats)
  botLng <- max(lngs)

  return(c(topLat, topLng, botLat, botLng))


startEndVals <- startEnd(all.data$Y, all.data$X)
startLat <- startEndVals[1]
endLat <- startEndVals[3]
startLng <- startEndVals[2]
endLng <- startEndVals[4]

num_intervals = 200.0
interval <- (startEndVals[1] - startEndVals[3]) / num_intervals

# testLng <- -66.6462379307115
# testLat <- 45.9581234392

# Prepare the data to be sent in
data <- all.data[,c("Y", "X", "levy2014_ha")]

sumInsideSquare <- function(pointLat, pointLng, interval, data) 
  # Sum all the values that fall within a square on a map given a point,
  # an interval of the map, and data that contains lat, lng and the values
  # of interest

  colnames(data) <- c("lat", "lng", "value")

  # Data east of point
  data <- data[data$lng > pointLng,] 
  # Data west of point + interval
  data <- data[data$lng < pointLng + interval,] 
  # Data north of point + interval (down)
  data <- data[data$lat > pointLat - interval,]
  # Data south of point
  data <- data[data$lat < pointLat, ]

  # Clean remaining data
  data <- na.omit(data)
  return(sum(data$value))


# Debugging
# squareSumTemp <- sumInsideSquare(testLat, testLng, interval, data)

# Given a start longitude and an end longitude, calculate an array of values
# corresponding to the sums for that latitude

calcSumLat <- function(startLng, endLng, lat, interval, data) 
  row <- c()
  lng <- startLng
  while (lng < endLng) 
    row <- c(row, sumInsideSquare(lat, lng, interval, data))
    lng <- lng + interval
  
  return(row)


# Debugging
# rowTemp <- calcSumLat(startLng, endLng, testLat, interval, data)
# write.csv(rowTemp, file = "Temp.csv", row.names = FALSE)

# Get each line of data to plot
lat <- startLat
rowCount <- 1
all.sums <- list()
while (lat > endLat) 
  col <- calcSumLat(startLng, endLng, lat, interval, data)
  all.sums[[as.character(rowCount)]] <- col
  lat <- lat - interval
  rowCount <- rowCount + 1


# Convert to data frame
all.sums.frame <- data.frame(all.sums)

# Save to disk so I don't have to run it again
write.csv(all.sums.frame, file = "Levy2014Sums200.csv", row.names = FALSE)

【问题讨论】:

您是否尝试过使用 Rprof 分析您的代码以找出花费最多时间的地方? 直到现在才知道 RProf - 来看看吧! 使用 RProf 很明显瓶颈在 sumInsideSquares 函数中。我重新编写了该函数,以便所有数据帧分段都发生在一行上,从而将时间缩短了一半。但是,仍然需要很长时间才能运行,所以我需要进一步优化它。我可能会开始研究 R 中的多线程,因为这将是它的主要候选者。 【参考方案1】:

最终自己找到了解决方案。它的关键是将 foreach 包与 doParallel 包一起使用,这样它就可以利用我计算机上的所有内核。这里有一个很好的指南:http://www.r-bloggers.com/a-brief-foray-into-parallel-processing-with-r/

【讨论】:

以上是关于在 R 中处理地理数据时提高性能的主要内容,如果未能解决你的问题,请参考以下文章

如何最大限度地提高实时处理性能(Portaudio)

如何在处理西里尔文文本文件时提高 C++ 性能?

从数据库加载大数据并转换为 JSON 时如何提高性能

如何通过并行处理数据库结果来提高性能?

提高处理大熊猫数据帧的性能

在 LINQ 中使用组时如何提高性能