I have some code that classifies features based on a combination of landuse and "points" assigned across three variables. In running this code, there are 4 classification values to assign, with the last being a catch-all for some miscellaneous cases. I am iterating through a dictionary that holds values for land use, and upper and lower point value limits that affect the classification. I tried using a sequence of if/elif/elif/else statements and if I run it this way, everything gets assigned the catch-all value but if I drop the else statement at the end the code classifies everything correctly except for the miscellaneous features which remain null. Any thoughts as to what is happening? I've found an alternative solution to this but am curious why this approach fails.
field_list = ['LandUse','APD_Points','AFR_Points','CED_Points','TotalPoints','Program']
prop_dict = {'undev':('Undeveloped',10, 20), 'res':('Residential',15,30),'mfam':('Multifamily',80,160),'NR':('Non Residential',40,80),'ED':('Education Facility'),'EX':('Exclude')}
# iterate through dictionary and update attributes with classifcation values based on point ranges and landuse category
for in_arg in prop_dict.values():
ptype = in_arg[0]
llimit = in_arg[1]
ulimit = in_arg[2]
with arcpy.da.UpdateCursor(fc,field_list) as cursor:
for row in cursor:
if (row[0] == ptype and row[2]+row[3] > 0 and (row[1]>=llimit)):
row[5] = 'PGM1 Qualifying'
elif (row[0] == ptype and row[1]>= ulimit):
row[5] = 'PGM2 Qualifying'
elif (row[0] == ptype and row[1] < ulimit):
row[5] = 'Non-Qualifying'
else:
row[5] = 'Public'
cursor.updateRow(row)
Solved! Go to Solution.
If the first three checks of your if/elif statements are not satisfied, your else statement will set Program = 'Public' for any ptype, not just the ptype you're currently trying to check - I don't think that's what you want, right? You'd need to enclose the whole if statement structure in an outer "if row[0] == ptype:" (or replace the final else with "elif row[0] == ptype:").
I think the problem is that you're doing two different iterations together, possibly causing everything to get overwritten with whatever comes last. Instead of iterating the cursor rows inside of iterating the dictionary, just look up the values in the dictionary for each row.
field_list = [
'LandUse',
'APD_Points',
'AFR_Points',
'CED_Points',
'TotalPoints',
'Program'
]
prop_dict = {
'Undeveloped': {'llimit': 10, 'ulimit': 20},
'Residential': {'llimit': 15, 'ulimit': 30},
'Multifamily': {'llimit': 80, 'ulimit': 160},
'Non Residential': {'llimit': 40, 'ulimit': 80},
# 'Education Facility': {'llimit': ?, 'ulimit': ?},
# 'Exclude': {'llimit': ?, 'ulimit': ?},
}
with arcpy.da.UpdateCursor(fc, field_list) as cursor:
for row in cursor:
limits = prop_dict.get(row[0])
if limits:
llimit, ulimit = limits.values()
if row[1] >= llimit and row[2] + row[3] > 0:
row[5] = 'PGM1 Qualifying'
elif row[1] >= ulimit:
row[5] = 'PGM2 Qualifying'
elif row[1] < ulimit:
row[5] = 'Non-Qualifying'
else:
row[5] = 'Public'
else:
print(f"'{row[0]}' is not defined in prop_dict")
row[5] = 'Public'
cursor.updateRow(row)
If the first three checks of your if/elif statements are not satisfied, your else statement will set Program = 'Public' for any ptype, not just the ptype you're currently trying to check - I don't think that's what you want, right? You'd need to enclose the whole if statement structure in an outer "if row[0] == ptype:" (or replace the final else with "elif row[0] == ptype:").
Thanks Chris - I think this is the problem I was encountering. I resolved this in my working script by testing in a final elif to see if the 'ptype' variable is either 'Education Facility' or 'Exclude'. Another solution suggested a more elegant approach that I may try and implement.
clarification needed on a few issues on the first "if"
......
for row in cursor:
if row[0] == ptype:
if row[1] >= llimit):
if row[2] + row[3] > 0:
row[5] = 'PGM1 Qualifying'
else:
row[5] = 'PGM2 Qualifying'
elif row[1] < ulimit):
row[5] = 'Non-Qualifying'
else:
row[5] = 'Public'
else:
???? row[5] = 'Public or something else
Hey Dan - the first if is finding all cases where there is at least one 'point' in either the two variables (AFR_Points and CED_Points) and I do this by summing them together, and then where the lower limit is exceeded. Does that make sense?
I think the problem is that you're doing two different iterations together, possibly causing everything to get overwritten with whatever comes last. Instead of iterating the cursor rows inside of iterating the dictionary, just look up the values in the dictionary for each row.
field_list = [
'LandUse',
'APD_Points',
'AFR_Points',
'CED_Points',
'TotalPoints',
'Program'
]
prop_dict = {
'Undeveloped': {'llimit': 10, 'ulimit': 20},
'Residential': {'llimit': 15, 'ulimit': 30},
'Multifamily': {'llimit': 80, 'ulimit': 160},
'Non Residential': {'llimit': 40, 'ulimit': 80},
# 'Education Facility': {'llimit': ?, 'ulimit': ?},
# 'Exclude': {'llimit': ?, 'ulimit': ?},
}
with arcpy.da.UpdateCursor(fc, field_list) as cursor:
for row in cursor:
limits = prop_dict.get(row[0])
if limits:
llimit, ulimit = limits.values()
if row[1] >= llimit and row[2] + row[3] > 0:
row[5] = 'PGM1 Qualifying'
elif row[1] >= ulimit:
row[5] = 'PGM2 Qualifying'
elif row[1] < ulimit:
row[5] = 'Non-Qualifying'
else:
row[5] = 'Public'
else:
print(f"'{row[0]}' is not defined in prop_dict")
row[5] = 'Public'
cursor.updateRow(row)
Thanks - this is more elegant, I'll give it a try and see how it works out. I was concerned a bit about doing two sets of iterations, it works fine without that else statement though, I think the issue there was identified by one of the other commentors, and the solution I'd already found fits that issue.