multiprocessing モジュール が含まれる Python 2.6 がArcGIS 製品に入ってきたのは、ArcGIS 10.0 の登場がはじめでした(ArcGIS 10.0 国内リリース:2010年10月22日)。それ以降、米国 Esri の配信している Blog でも Python Multiprocessing – Approaches and Considerations(2011年8月)、Multiprocessing with ArcGIS – Approaches and Considerations (Part 1):2012年9月としてマルチプロセス処理について記事で取り上げられたのをはじめ、近年でも世界中から多くの開発者が集まるEsri Developer Summit で Parallel Python: Multiprocessing with ArcPy:2017年3月 の動画、Vector and Raster Multiprocessing with ArcPy:2018年3月 の動画 としてテクニカル セッションとして取り上げられています。
ArcGIS Pro 動作環境 に見られるような高スペックのハードウェア(64-bit OSが標準、CPU 4コアを推奨、メモリも最適 16GB以上)が普及してきた一方で、分析や処理に使われるデータ量も飛躍的に増加しており、効率的な分析や処理が必要な状況は今も続いています。
そのような中で、今回は、ArcGIS プラットフォームの拡張手段として定着している ArcPy とPython でのマルチプロセス処理をとりあげ、実装の要点となる箇所とコーディングパターンをサンプルを交えつつ紹介したいと思います。
さまざまな処理での実際のコーディングは、関連情報に示したものを確認いただきつつ、実際にコードを書いて試してみることをお勧めしますが、本記事を参照して並列処理実装のポイントを掴んでいただき、現在のワークフローに上手く取り入れる一助となればと思います。
関連情報に示した各種資料では、ArcPy とPython を使用したマルチプロセス処理のベストプラクティスとして、次のようなことが言われています。
*複数のプロセスからの書込み先として、ワーカー関数でのエンタープライズ ジオデータベースを書込み先として使用するのも、一つの方法です。
今回のサンプルは、47都道県別のフォルダ下に複数のシェープファイルが入っており、それらを47都道府県別のファイル ジオデータベースを作成後、それぞれフィーチャクラスに変換する実装サンプルコードです。 実装する際のポイントは、上記のベストプラクティスで書いているように、ファイル ジオデータベースへの書込み時にロックされるので、「1都道府県フォルダのシェープファイルは1都道府県ファイル ジオデータベースでのフィーチャクラスへの変換となるようにコードを書くこと」です。
実際にコーディングパターンとして見ていきましょう。
コーディングパターンとしては、まずはワーカー関数を通常の ArcPyでの処理と同様に書きます。このときに注意するべきなのは、ベストプラクティスで書いたように、このワーカー関数での処理がプロセスでの処理に相当するので、複数プロセス(複数のワーカー関数)から書込み先としてアクセスする必要がある場合はエンタープライズ ジオデータベースを利用するか、同じファイル ジオデータベースを使用しないようにすることです。
def batch_convert(inws, outws):
'''
1プロセスで実行する処理(ワーカー関数):
ファイル ジオデータベースへの書込みは複数プロセスでできないため、
・1都道府県のフォルダ下の複数シェープ ファイルを
・1都道府県のファイル ジオデータベース下の複数フィーチャ クラスに変換
するように記載
'''
#print("変換: {0} ⇒ {1}\n".format(inws,outws))
if not arcpy.Exists(outws):
outfolder=os.path.dirname(outws)
foldername=os.path.basename(outws)
arcpy.CreateFileGDB_management(outfolder,foldername,"CURRENT")
arcpy.env.workspace = inws
fcs = arcpy.ListFeatureClasses()
for fc in fcs:
l=len(fc)
infc=os.path.splitext(fc)[0]
# 0埋めした4桁の名前にフィーチャクラス名を変更
# 例) P101_CITY.shp → P0101_CITY
newfc = fc[:1]+fc[1:l-4].zfill(4)
# 既存フィーチャクラスが存在する場合はAppend
# 存在しない場合はFeatureClassToFeatureClass
if arcpy.Exists(os.path.join(outws,newfc)):
outfc=os.path.join(outws,newfc)
arcpy.Append_management(fc,outfc)
else:
arcpy.FeatureClassToFeatureClass_conversion(fc,outws,newfc)
del fcs
return " 変換済:{0}".format(outws)
マルチプロセスからワーカー関数を指定する際、ワーカー関数の引数が単独の場合は不要ですが、サンプルのように複数の引数を渡す必要がある場合は、ラッパー関数を用意すると便利です。
def multi_run_batch_convert(args):
'''
batch_convert の wrapper:
複数の引数を実行処理に渡すためのラッパー
'''
return batch_convert(*args)
ワーカー関数へ渡す2つの引数(inws, outws)を params にリスト化していれます。
その後、
pool = multiprocessing.Pool(cpu_cnt) で確保した pool に
results = pool.map(multi_run_batch_convert , params) を指定します。
第1引数:処理するワーカー関数名(今回の場合はラッパー関数) multi_run_batch_convert
第2引数:リストかしたパラメータ params
def exec_batch_convert(infolder,outfolder):
'''
マルチプロセスでの処理(マスター関数):
'''
try:
start=datetime.datetime.now()
print "-- Strat: Multiprocess_ShapefileToFeatureClassWithRename --:",start
cpu_cnt=multiprocessing.cpu_count()
arcpy.env.workspace = infolder
inwss = arcpy.ListWorkspaces("*","Folder")
# 各プロセスへ渡すパラメータをリスト化
params=[]
for inws in inwss:
param1=inws # 都道府県フォルダ(シェープファイルが入っている)
gdbname="{0}.gdb".format(os.path.basename(inws))
param2=os.path.join(outfolder,gdbname) # 都道府県ファイルジオデータベース
params.append((param1,param2))
if len(inwss) < cpu_cnt: # 処理フォルダ数がCPUコアより少ない場合無駄なプロセスを起動不要
cpu_cnt=len(inwss)
pool = multiprocessing.Pool(cpu_cnt) #cpu数分のpythonプロセスを作成
results=pool.map(multi_run_batch_convert,params) #割り当てプロセスで順次実行される
pool.close()
pool.join()
# 各プロセスでの処理結果を出力
for r in results:
print(r)
# 都道府県別のファイル ジオデータベース内のフィーチャ クラスを統合して
# 全国版を作成する場合はここに記載します
# ~省略~
fin=datetime.datetime.now()
print "-- Finish: Multiprocess_ShapefileToFeatureClassWithRename --:",fin
print " Elapsed time:", fin-start
except:
print traceback.format_exc(sys.exc_info()[2])
上記までのコーディングパターンに従って実装したPythonスクリプト(Multiprocess_Sample.py)の infolder, outfolder をご自分の環境のものに変更してPython の実行環境から実行すれば、マルチプロセスでデータ変換が行われます。
#coding:cp932
import arcpy,os
import sys
import multiprocessing
import datetime
import traceback
reload(sys)
sys.setdefaultencoding('cp932')
def multi_run_batch_convert(args):
'''
batch_convert の wrapper:
複数の引数を実行処理に渡すためのラッパー
'''
return batch_convert(*args)
def batch_convert(inws, outws):
'''
1プロセスで実行する処理(ワーカー関数):
ファイル ジオデータベースへの書込みは複数プロセスでできないため、
・1都道府県のフォルダ下の複数シェープ ファイルを
・1都道府県のファイル ジオデータベース下の複数フィーチャ クラスに変換
するように記載
'''
#print("変換: {0} ⇒ {1}\n".format(inws,outws))
if not arcpy.Exists(outws):
outfolder=os.path.dirname(outws)
foldername=os.path.basename(outws)
arcpy.CreateFileGDB_management(outfolder,foldername,"CURRENT")
arcpy.env.workspace = inws
fcs = arcpy.ListFeatureClasses()
for fc in fcs:
l=len(fc)
infc=os.path.splitext(fc)[0]
# 0埋めした4桁の名前にフィーチャクラス名を変更
# 例) P101_CITY.shp → P0101_CITY
newfc = fc[:1]+fc[1:l-4].zfill(4)
# 既存フィーチャクラスが存在する場合はAppend
# 存在しない場合はFeatureClassToFeatureClass
if arcpy.Exists(os.path.join(outws,newfc)):
outfc=os.path.join(outws,newfc)
arcpy.Append_management(fc,outfc)
else:
arcpy.FeatureClassToFeatureClass_conversion(fc,outws,newfc)
del fcs
return " 変換済:{0}".format(outws)
def exec_batch_convert(infolder,outfolder):
'''
マルチプロセスでの処理(マスター関数):
'''
try:
start=datetime.datetime.now()
print "-- Strat: Multiprocess_ShapefileToFeatureClassWithRename --:",start
cpu_cnt=multiprocessing.cpu_count()
arcpy.env.workspace = infolder
inwss = arcpy.ListWorkspaces("*","Folder")
# 各プロセスへ渡すパラメータをリスト化
params=[]
for inws in inwss:
param1=inws # 都道府県フォルダ(シェープファイルが入っている)
gdbname="{0}.gdb".format(os.path.basename(inws))
param2=os.path.join(outfolder,gdbname) # 都道府県ファイルジオデータベース
params.append((param1,param2))
if len(inwss) < cpu_cnt: # 処理フォルダ数がCPUコアより少ない場合無駄なプロセスを起動不要
cpu_cnt=len(inwss)
pool = multiprocessing.Pool(cpu_cnt) #cpu数分のpythonプロセスを作成
results=pool.map(multi_run_batch_convert,params) #割り当てプロセスで順次実行される
pool.close()
pool.join()
# 各プロセスでの処理結果を出力
for r in results:
print(r)
# 都道府県別のファイル ジオデータベース内のフィーチャ クラスを統合して
# 全国版を作成する場合はここに記載します
# ~省略~
fin=datetime.datetime.now()
print "-- Finish: Multiprocess_ShapefileToFeatureClassWithRename --:",fin
print " Elapsed time:", fin-start
except:
print traceback.format_exc(sys.exc_info()[2])
def setup_batch_convert():
'''
コマンドプロンプトからの実行パラメータを設定:
都道府県別のシェープ ファイルが入った元フォルダ :infolder
例)
|-Shapefiles
|- 01_北海道
|-P101_CITY.shp
|-L102_RIVER.shp
・・・・・
|- 02_青森県
|- 03_岩手県
・・・・・
都道府県別のファイル ジオデータベースの作成先フォルダ :outfolder
例)
|-Filegdbs
|- 01_北海道.gdb
|-P0101_CITY
|-L0102_RIVER
・・・・・
|- 02_青森県.gdb
|- 03_岩手県.gdb
・・・・・
'''
infolder=r"E:\DATA\Shapefiles" #都道府県別のシェープファイルが入った元フォルダ
outfolder=r"E:\DATA\Filegdbs" #都道府県別のファイル ジオデータベースの作成先フォルダ
exec_batch_convert(infolder,outfolder)
if __name__ == '__main__':
setup_batch_convert()
なお、ベストプラクティスで書いたように、64-bitの Python環境から実行してください。Pythonスクリプトの実行例としてArcGIS Desktop、ArcGIS Proでの例を載せておきます。
>C:\Python27\ArcGISx6410.6\python.exe Multiprocess_Sample.py
>"c:\Program Files\ArcGIS\Pro\bin\Python\scripts\propy.bat" Multiprocess_Sample.py