您的位置:首页 > 数据库

利用geopandas包对PostGIS数据库插入地理空间数据及性能对比

2018-01-29 22:27 537 查看
最近在使用Python中的geopandas包操作地理空间数据,由于数据量过大采用PostgreSQL中的PostGIS进行管理操作,最后查到了一些资料代码插入数据库,国内网上没有相关内容,这里分享一下,自己以后也需要对大批量数据操作,这里对比下不同数据量下的插入性能。

环境:

笔记本windows7 64位(内存8G) Python 3.5.2(Anaconda3)

PostgreSQL 10 PostGIS 2.4.2

geopandas 0.3.0 sqlalchemy 1.2.2 GeoAlchemy2 0.4.2

基本流程是生成n个随机的空间散点,然后对比下不同n取值下的插入效率,所有的代码如下:

import pandas as pd
import geopandas as gpd
import time,datetime
import numpy as np
import shapely
from geoalchemy2 import Geometry, WKTElement
from sqlalchemy import *

start = time.clock()
print(str(datetime.datetime.now()) + " : " + " start")
print('==================================================')

engine = create_engine('postgresql://postgres:password@localhost:5432/dbname', use_batch_mode=True)
df = pd.DataFrame()
size = 300000
df['id'] = range(0,size)
df['value1'] = np.random.randint(0, 100, size=size)
df['lon'] = np.random.randint(103000000, 105000000, size=size)/1000000
df['lat'] = np.random.randint(30000000, 35000000, size=size) / 1000000
geom = [shapely.geometry.Point(xy) for xy in zip(df.lon, df.lat)]
del df['lon']
del df['lat']
crs = {'init': 'epsg:4326'}
geodataframe = gpd.GeoDataFrame(df, crs=crs, geometry=geom)

time1 = time.clock()
print("生成: %f s" % (time1 - start))

geodataframe['geom'] = geodataframe['geometry'].apply(lambda x: WKTElement(x.wkt, 4326))
geodataframe.drop('geometry', 1, inplace=True)

time2 = time.clock()
print("修改geometry: %f s" % (time2 - time1))

geodataframe.to_sql('test1', engine, if_exists='replace',index=None,
dtype={'geom': Geometry('POINT', 4326)})

time3 = time.clock()
print("插入数据库: %f s" % (time3 - time2))

print('==================================================')
end = time.clock()
print(str(datetime.datetime.now()) + " : " + " end")
print("read: %f s" % (end - start))


整个代码大体分为三部分:

首先生成包含包括经纬度、id和value1等列的pandas,再加入WGS84投影转为geodataframe

第二步是将geodataframe中的geometry转换格式,由于geopandas中使用的是shapley的point格式,而PostGIS中使用的是geoalchemy2中的point(或者说插入数据需要使用geoalchemy2格式)

第三步是将geodataframe插入PostGIS中,由于geopandas是基于pandas的,可以直接使用to_sql()方法

n的取值分别取了10000,50000,100000,300000,600000,每个数据n做了五次插入操作取平均时间,用时如下:

点数第一步用时(秒)第二步用时(秒)第三步用时(秒)总时(秒)
100000.10268740.6804060.72500861.508102
500000.4826653.41943263.18439627.0864938
1000000.95531787.12014786.099202414.174668
3000002.899288220.312824220.028016443.2401288
6000005.813097841.994867634.265833885.6515864
我们可以用图表查看趋势



显然第一步花费的时间最少,第二步与第三步的时间基本持平。第三步的时间与to_sql()效率相关,而插入PostGIS采用了sqlalchemy 的create_engine,该包在1.2版本上加入了use_batch_mode方法,设置use_batch_mode=True能加速插入速度。

第二步的时间是我一直想削减的时间,它随着数据量的增大而超越了第三步的时间,实际上是因为它对每一个空间点都执行了WKTElement(x.wkt, 4326)操作,就是转换了geom类型,从代码上看很简单,但是从运行时间上看是一个阻碍,国外有另一种插入代码可以跳过这个步骤,但是这种插入只能插入一次,如果想下次为该表追加内容就会报错,具体可以看Write GeoDataFrame into SQL Database,如果有哪位知道削减这一步时间同时能保持表的追加,还请不吝赐教。

2018-3-10编辑更新

关于快速导入空间数据可以使用copy功能,参考我的博客“PostGIS快速导入大量点空间数据及最近邻要素跨表查询
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息