I’m new to python and would love some suggestions on improving the efficiency of my script.
Background: My script is intended to create a report of the dominant and limiting soil types for agricultural fields. The dominant soil is simply the soil type with the greatest area in a field. The limiting soil is the soil type with greatest area, but whose name appears on the limiting soil list. So, in some fields, the limiting soil may be the same as the dominant soil, while other fields may have no limiting soils at all.
The script needs to work for all the parcels owned by an individual landowner. Each landowner may have multiple properties (called tracts—each tract has a unique ID number ), and each tract has multiple fields (numbered 1, 2, 3,… etc). The parcel data for each landowner is stored in a single feature class. It is important to be able to sort the final output by tract and field
Problem: The current script is slow! I was able to figure out how to accomplish my intended goal, but the script runs very slowly. I think it is because of my reliance on cursors and multiple if then loops. Is there a more efficient or elegant way to do this? It takes 15 to 20 min to work through some of our mid-sized landowners and I haven’t dared to try it on our largest ones.
I am running Arc 10.0 and Pythonwin 2.6.5, so I cannot use DA cursors. The main area of trouble is in the portion of code that builds the dictionaries (following "# loop through by tract"). I timed the script on a mid-sized (for this application) dataset that had 682 rows (9 tracts) and it took 00:14:16 to complete, 00:13:02 of which was in this portion of code.
import arcpy
import collections
# set environments
arcpy.env.overwriteOutput = True
workspace = r"H:\pathway..."
arcpy.env.workspace = workspace
# input data
soil = #feature class of soil polygons
parcels = #feature class of land units
outputTableMaster = #emptyDbaseTable
#intersect soil polygons and parcel layer
inFeatures = [soil, parcels]
soilTable = #output featureclass from intersect
arcpy.Intersect_analysis(inFeatures, soilTable)
# create list of limiting soils
limitingSoils = ['Au', 'Bg', 'Br', 'Ca', 'Cb', 'Ce', 'Co', 'Ru']
#identify whether or not soil types found on property are limiting
arcpy.AddField_management(soilTable, "Limiting", "TEXT", "", "", 1)
urows = arcpy.UpdateCursor(soilTable)
for row in urows:
soilType = row.getValue("MUSYM")
soilShort = str(soilType)[0:2]
if soilShort in limitingSoils:
##soil is limiting
row.setValue("Limiting", "Y")
else:
##soil is not limiting
row.setValue("Limiting", "N")
urows.updateRow(row)
del urows
del row
# create list of tracts
tractList = []
srows = arcpy.SearchCursor(soilTable)
for row in srows:
tract = row.getValue("TRACTNBR")
if tract not in tractList:
tractList.append(tract)
del srows
del row
# loop through by tract and field to build dictionary
for tract in tractList:
dictionary = collections.defaultdict(list)
fieldList = []
srows = arcpy.SearchCursor(soilTable)
for row in srows:
field = row.getValue("FieldNBR")
if field not in fieldList:
fieldList.append(field)
del srows
del row
for field in fieldList:
areaList = []
srows = arcpy.SearchCursor(soilTable)
for row in srows:
if field == row.getValue("FieldNBR"):
area = row.getValue("Shape_Area")
soil = row.getValue("SoilName")
limiting = row.getValue("Limiting")
if area not in areaList:
dictionary[field].append([area, soil, limiting])
del srows
del row
#use dictionary to generate report of dominant and limiting soils for each field
for data, dataList in dictionary.iteritems():
field = str(data)
dominantSoil = max(dataList)[1]
domSoilLimitStatus = max(dataList)[2]
domSoilArea = str(max(dataList)[0])
if domSoilLimitStatus == "Y":
limitingSoilName = dominantSoil
limitingSoilArea = domSoilArea
elif domSoilLimitStatus == "N":
limitedList = [x for x in dataList if x[2] == "Y"]
if not limitedList:
limitingSoilName = "None"
limitingSoilArea = "n/a"
if limitedList:
limitingSoilName = max(limitedList)[1]
limitingSoilArea = str(max(limitedList)[0])
#update the outputTable for each field
irows = arcpy.InsertCursor(outputTable)
row = irows.newRow()
row.setValue("Tract", str(tract))
row.setValue("Field", field)
row.setValue("DominantSoil", dominantSoil)
row.setValue("DomSoilArea", domSoilArea)
row.setValue("LimitingSoil", limitingSoilName)
row.setValue("LimitingSoilArea", limitingSoilArea)
irows.insertRow(row)
del irows
del row
da
version of the cursor (arcpy.da.UpdateCursor
) as it is built more for speed.