我有一个包含八个变量的netCDF文件。 (抱歉,无法分享实际文件) 每个变量都有两个维度,时间和站点。时间大约是14步,站目前有38000个不同的ID。 因此,对于38000个不同的“位置”(实际上只是一个id),我们有8个变量和14个不同的时间。
$ncdump -h stationdata.nc
netcdf stationdata {
dimensions:
station = 38000 ;
name_strlen = 40 ;
time = UNLIMITED ; // (14 currently)
variables:
int time(time) ;
time:long_name = "time" ;
time:units = "seconds since 1970-01-01" ;
char station_name(station, name_strlen) ;
station_name:long_name = "station_name" ;
station_name:cf_role = "timeseries_id" ;
float var1(time, station) ;
var1:long_name = "Variable 1" ;
var1:units = "m3/s" ;
float var2(time, station) ;
var2:long_name = "Variable 2" ;
var2:units = "m3/s" ;
...
需要将此数据加载到PostGres数据库中,以便数据可以连接到与station_name匹配的某些几何图形,以便以后进行可视化。
目前我已经使用netCDF4模块在Python中完成了这项工作。工作,但它需要永远! 现在我像这样循环:
times = rootgrp.variables['time']
stations = rootgrp.variables['station_name']
for timeindex, time in enumerate(times):
stations = rootgrp.variables['station_name']
for stationindex, stationnamearr in enumerate(stations):
var1val = var1[timeindex][stationindex]
print "INSERT INTO ncdata (validtime, stationname, var1) \
VALUES ('%s','%s', %s);" % \
( time, stationnamearr, var1val )
我的机器运行需要几分钟才能运行,我觉得可以用更聪明的方式完成。
任何人都知道如何以更聪明的方式完成这项工作?最好是在Python中。
答案 0 :(得分:2)
不确定这是正确的做法,但我找到了解决这个问题的好方法,并认为我应该分享它。
在第一个版本中,脚本运行大约需要一个小时。重写代码后,它现在运行不到30秒!
最重要的是使用numpy数组并将NetCDF阅读器中的变量数组转换为行,然后将所有列堆叠到一个矩阵中。然后使用psycopg2 copy_from函数将该矩阵加载到db中。我从这个问题中得到了代码
Use binary COPY table FROM with psycopg2
我的部分代码:
dates = num2date(rootgrp.variables['time'][:],units=rootgrp.variables['time'].units)
var1=rootgrp.variables['var1']
var2=rootgrp.variables['var2']
cpy = cStringIO.StringIO()
for timeindex, time in enumerate(dates):
validtimes=np.empty(var1[timeindex].size, dtype="object")
validtimes.fill(time)
# Transponse and stack the arrays of parameters
# [a,a,a,a] [[a,b,c],
# [b,b,b,b] => [a,b,c],
# [c,c,c,c] [a,b,c],
# [a,b,c]]
a = np.hstack((
validtimes.reshape(validtimes.size,1),
stationnames.reshape(stationnames.size,1),
var1[timeindex].reshape(var1[timeindex].size,1),
var2[timeindex].reshape(var2[timeindex].size,1)
))
# Fill the cStringIO with text representation of the created array
for row in a:
cpy.write(row[0].strftime("%Y-%m-%d %H:%M")+'\t'+ row[1] +'\t' + '\t'.join([str(x) for x in row[2:]]) + '\n')
conn = psycopg2.connect("host=postgresserver dbname=nc user=user password=passwd")
curs = conn.cursor()
cpy.seek(0)
curs.copy_from(cpy, 'ncdata', columns=('validtime', 'stationname', 'var1', 'var2'))
conn.commit()
答案 1 :(得分:1)
您可以通过一些简单的改进来加快速度。所有这些都是独立的,你可以尝试所有这些或只是一对,看看它是否足够快。它们的难度大致呈递增顺序:
psycopg2
数据库驱动程序,它更快psycopg2
,那么您已经在执行此操作 - 它会自动打开您最后必须commit
的交易。multiprocessing
模块。由于GIL(全局解释器锁定)问题,线程无法正常工作。如果您不想使用一个大事务,可以设置synchronous_commit = off
并设置commit_delay
,以便在磁盘刷新实际完成之前连接可以返回。如果您在一次交易中完成所有工作,这对您没有多大帮助。
Psycopg2不直接支持多值INSERT
,但你可以写:
curs.execute("""
INSERT INTO blah(a,b) VALUES
(%s,%s),
(%s,%s),
(%s,%s),
(%s,%s),
(%s,%s);
""", parms);
并循环使用类似:
parms = []
rownum = 0
for x in input_data:
parms.extend([x.firstvalue, x.secondvalue])
rownum += 1
if rownum % 5 == 0:
curs.execute("""INSERT ...""", tuple(parms))
del(parms[:])
答案 2 :(得分:1)
组织循环以访问每次的所有变量。换句话说,一次读取和写入记录而不是一次读取一个变量。这可以极大地加速,特别是如果源netCDF数据集存储在具有大磁盘块的文件系统上,例如, 1MB或更大。为了解释为什么这更快,并讨论数量级导致的加速,请参阅this NCO speedup discussion,从条目7开始。