Estou procurando algumas sugestões sobre como tornar meu código python mais eficiente. Normalmente, a eficiência não importa para mim, mas agora estou trabalhando com um arquivo de texto de locais nos EUA com mais de 1,5 milhão de pontos. Com a configuração fornecida, leva cerca de 5 segundos para executar as operações em um ponto; Eu preciso entender esse número.
Estou usando três pacotes GIS python diferentes para fazer algumas operações diferentes nos pontos e gerar um novo arquivo de texto delimitado.
- Eu uso o OGR para ler um shapefile de limite do condado e obter acesso à geometria do limite.
- Shapely verifica se há um ponto em qualquer um desses municípios.
- Se estiver dentro de um, eu uso a Biblioteca Shapefile do Python para extrair informações de atributo do limite .dbf.
- Depois, escrevo algumas informações de ambas as fontes em um arquivo de texto.
Suspeito que a ineficiência esteja em ter um loop de 2 a 3 camadas ... não sei bem o que fazer sobre isso. Estou particularmente procurando ajuda com alguém experiente no uso de qualquer um desses 3 pacotes, pois é a primeira vez que utilizo um deles.
import os, csv
from shapely.geometry import Point
from shapely.geometry import Polygon
from shapely.wkb import loads
from osgeo import ogr
import shapefile
pointFile = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\NationalFile_20110404.txt"
shapeFolder = "C:\NSF_Stuff\NLTK_Scripts\Gazetteer_New"
#historicBounds = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\US_Counties_1860s_NAD"
historicBounds = "US_Counties_1860s_NAD"
writeFile = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\NewNational_Gazet.txt"
#opens the point file, reads it as a delimited file, skips the first line
openPoints = open(pointFile, "r")
reader = csv.reader(openPoints, delimiter="|")
reader.next()
#opens the write file
openWriteFile = open(writeFile, "w")
#uses Python Shapefile Library to read attributes from .dbf
sf = shapefile.Reader("C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\US_Counties_1860s_NAD.dbf")
records = sf.records()
print "Starting loop..."
#This will loop through the points in pointFile
for row in reader:
print row
shpIndex = 0
pointX = row[10]
pointY = row[9]
thePoint = Point(float(pointX), float(pointY))
#This section uses OGR to read the geometry of the shapefile
openShape = ogr.Open((str(historicBounds) + ".shp"))
layers = openShape.GetLayerByName(historicBounds)
#This section loops through the geometries, determines if the point is in a polygon
for element in layers:
geom = loads(element.GetGeometryRef().ExportToWkb())
if geom.geom_type == "Polygon":
if thePoint.within(geom) == True:
print "!!!!!!!!!!!!! Found a Point Within Historic !!!!!!!!!!!!"
print str(row[1]) + ", " + str(row[2]) + ", " + str(row[5]) + " County, " + str(row[3])
print records[shpIndex]
openWriteFile.write((str(row[0]) + "|" + str(row[1]) + "|" + str(row[2]) + "|" + str(row[5]) + "|" + str(row[3]) + "|" + str(row[9]) + "|" + str(row[10]) + "|" + str(records[shpIndex][3]) + "|" + str(records[shpIndex][9]) + "|\n"))
if geom.geom_type == "MultiPolygon":
for pol in geom:
if thePoint.within(pol) == True:
print "!!!!!!!!!!!!!!!!! Found a Point Within MultiPolygon !!!!!!!!!!!!!!"
print str(row[1]) + ", " + str(row[2]) + ", " + str(row[5]) + " County, " + str(row[3])
print records[shpIndex]
openWriteFile.write((str(row[0]) + "|" + str(row[1]) + "|" + str(row[2]) + "|" + str(row[5]) + "|" + str(row[3]) + "|" + str(row[9]) + "|" + str(row[10]) + "|" + str(records[shpIndex][3]) + "|" + str(records[shpIndex][9]) + "|\n"))
shpIndex = shpIndex + 1
print "finished checking point"
openShape = None
layers = None
pointFile.close()
writeFile.close()
print "Done"
Respostas:
O primeiro passo seria mover o shapefile aberto para fora do loop de linhas, você está abrindo e fechando o shapefile 1,5 milhão de vezes.
Para ser sincero, eu colocaria tudo no PostGIS e o faria usando SQL em tabelas indexadas.
fonte
Uma rápida olhada no seu código traz algumas otimizações à mente:
Verifique cada ponto contra a caixa delimitadora / envelope dos polígonos primeiro, para eliminar discrepâncias óbvias. Você poderia dar um passo adiante e contar o número de bboxes em que um ponto se encontra, se for exatamente um, não precisará ser testado com a geometria mais complexa (bem, na verdade será se estiver em mais mais de um, ele precisará ser testado mais adiante. Você pode fazer duas passagens para eliminar os casos simples dos casos complexos).
Em vez de percorrer cada ponto e testá-lo contra polígonos, faça um loop entre os polígonos e teste cada ponto. O carregamento / conversão da geometria é lento, então você deseja fazer o mínimo possível. Além disso, crie uma lista de pontos a partir do CSV inicialmente, novamente para evitar ter que fazer isso várias vezes por ponto e depois descartar os resultados no final dessa iteração.
Indexe espacialmente seus pontos, o que envolve a conversão para um arquivo shapefile, SpatialLite ou algo como um banco de dados PostGIS / PostgreSQL . Isso tem a vantagem de que ferramentas como o OGR poderão fazer a maior parte do trabalho para você.
Não escreva a saída até o final: print () é uma função cara na melhor das hipóteses. Em vez disso, armazene os dados como uma lista e grave-os no final, usando as funções padrão de decapagem do Python ou funções de despejo de lista.
fonte