UpdateCursor, SearchCursor and Multiple For Loops

786
4
11-02-2020 06:48 PM
NataliaGutierrez1
New Contributor III

Hello,

Here's the code I wrote to do the following:

1. Assign a min and max value to multiple fields on multiple feature classes.

2. The Min and Max values have to correlate with the min and max value in a different field on the same feature class.

For example:

A. Select the min value from the MEDIAN_HOUSEHOLD_INCOME_2018 field and assign a specific value to all of the other fields in the feature class. All of the values are different for each field and each feature class.

B. Do the same but for the max value of the MEDIAN_HOUSEHOLD_INCOME_2018 field

 

I am having trouble with the loops when assigning the Construction costs to all of the different feature classes.

I am creating too many loops and I don't know what are really necessary and which ones I don't need.

 

I would appreciate any help with this,

I am learning and have tried to fix it for a day and haven't been able to identify how to do it,

I am aware that there is a major problem with the iteration of the different lists,

thank you!

import operator
import arcpy

# script to assign construction costs to census tracts where we have construction cost data from cummings
# https://gis.stackexchange.com/questions/264051/selecting-maximum-value-in-attribute-table-using-python-code

arcpy.env.workspace = r"D:\APRX_MXDS\FAR_Initialization_Project\10_2020_Linear_Regression_USA\construction_costs\test_gdb.gdb"
arcpy.env.overwriteOutput = True

#create a list of all the feature classes
feature_class_list = []
list_fc = arcpy.ListFeatureClasses()
for m in list_fc:
    feature_class_list.append(m)
print(feature_class_list)

field_list = ["MEDIAN_HOUSEHOLD_INCOME_2018", "CONST_PRICE_PER_SF_CONDO", "CONST_PRICE_PER_SF_MULTIFAM", "CONST_PRICE_PER_SF_OFFICE", "CONST_PRICE_PER_SF_RETAIL", "CONST_PRICE_PER_SF_HOTEL",
              "CONST_PRICE_PER_SF_INDUSTRIAL", "CONST_PRICE_PER_SF_PARKING"]

fc_list = ['Atlanta', 'Boston', 'Chicago', 'Dallas', 'Denver', 'Honolulu', 'Las_Vegas', 'Los_Angeles', 'Miami', 'Nashville', 'New_York', 'Orlando',
            'Philadelphia', 'Portland', 'Raleigh', 'Sacramento', 'San_Diego', 'San_Francisco', 'Seattle', 'Washington']


# list of all the max and min median incomes per city
max_list = []
min_list = []
for fc in fc_list:
    print("Starting with feature class: {}".format(fc))
    all_entries = [row for row in arcpy.da.SearchCursor(fc, ['MEDIAN_HOUSEHOLD_INCOME_2018'])]
    maximum = (max(all_entries)[0])
    minimum = (min(all_entries)[0])
    max_list.append(maximum)
    min_list.append(minimum)
print("Maximum Values List: {}".format(max_list))
print("Minimum Values List: {}".format(min_list))


max_construction_lists = [[394, 394, 674, 452, 708, 589, 139], [510, 510, 874, 586, 912, 763, 180], [534, 534, 914, 613, 906, 798, 188],
                         [383, 383, 657, 440, 685, 573, 135], [314, 314, 485, 461, 717, 600, 142], [537, 537, 690, 617, 960, 803, 189],
                         [469, 469, 803, 538, 838, 701, 165], [529, 529, 688, 607, 945, 790, 186], [379, 379, 648, 435, 676, 566, 133],
                         [390, 390, 667, 447, 662, 583, 186], [859, 859, 1026, 687, 1108, 895, 211], [372, 372, 637, 427, 664, 556, 131],
                        [512, 512, 876, 587, 914, 765, 180], [445, 445, 661, 511, 796, 666, 157], [373, 373, 641, 429, 673, 559, 132], [463, 463, 624, 536, 655, 695, 125],
                        [503, 503, 678, 583, 712, 755, 180], [584, 584, 1001, 671, 1044, 874, 168], [469, 469, 696, 538, 838, 701, 175], [418, 418, 716, 480, 747, 625, 147]]
min_construction_lists = [[303, 303, 245, 253, 333, 152, 50], [392, 392, 318, 327, 532, 196, 65], [411, 411, 332, 343, 483, 206, 68],
                          [295, 295, 239, 246, 336, 148, 49], [133, 133, 135, 258, 364, 155, 51], [413, 413, 335, 345, 480, 207, 8],
                          [361, 361, 292, 301, 354, 180, 60], [294, 294, 327, 165, 425, 203, 49], [230, 230, 235, 242, 351, 146, 48],
                          [300, 300, 243, 250, 353, 150, 49], [461, 461, 373, 384, 625, 231, 76], [190, 190, 231, 238, 345, 143, 47],
                          [393, 393, 319, 328, 534, 197, 65], [343, 343, 277, 286, 358, 171, 65], [288, 288, 233, 240, 317, 144, 4],
                          [251, 251, 289, 150, 320, 178, 61], [273, 273, 325, 163, 348, 194, 66], [449, 449, 364, 375, 555, 225, 79],
                          [361, 361, 292, 301, 376, 180, 68], [322, 322, 260, 268, 436, 161, 53]]


# Assign Max construction cost values to all cities
for fc in fc_list: # iterate through each feature class
    with arcpy.da.UpdateCursor(fc, field_list) as cursor:
        for row in cursor: # iterate through each row
            for max_income in max_list: # iterate through each max MEDIAN_HOUSEHOLD_INCOME_2018 value
                print(max_income)
                for lists in max_construction_lists: # iterate through each city's list of max construction values
                    print(lists)
                    if row[0] == max_income: # if MEDIAN_HOUSEHOLD_INCOME_2018 equals to max income
                        row[1] = lists[0]# assign the first element of each list of the max_construction_Lists. For example: For the first feature class iteration I would expect row[0] = 394
                        print("This should be the condo max value for each city: {}".format(lists[0]))
                        row[2] = lists[1]
                        row[3] = lists[2]
                        row[4] = lists[3]
                        row[5] = lists[4]
                        row[6] = lists[5]
                        row[7] = lists[6]
                        cursor.updateRow(row)
print("Done updating max construction costs in all cities")



# Assign Min construction cost values to all cities
for fc in fc_list: # iterate through each feature class
    with arcpy.da.UpdateCursor(fc, field_list) as cursor:
        for row in cursor: # iterate through each row
            for min_income in min_list: # iterate through each max MEDIAN_HOUSEHOLD_INCOME_2018 value
                for lists in min_construction_lists: # iterate through each city's list of min construction values
                    if row[0] == min_income: # if MEDIAN_HOUSEHOLD_INCOME_2018 equals to min income
                        row[1] = lists[0]
                        row[2] = lists[1]
                        row[3] = lists[2]
                        row[4] = lists[3]
                        row[5] = lists[4]
                        row[6] = lists[5]
                        row[7] = lists[6]
                        cursor.updateRow(row)
print("Done updating min construction costs in all cities")
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
4 Replies
DanPatterson
MVP Esteemed Contributor

Since you report no errors... Can we assume that it works but doesn't do what you want? or are you getting error messages?

Beyond that...

You seem to be cycling through stuff twice... can't you consolidate and dump one cycle through all the fcs?

if row[0] == max_income:  # line 61-ish
    read from a certain list
    blah
elif row[0] == min_income:  # line 81 -ish
    read from some other list
    blah
else:
    don't know
do the update thing

But having said all that, it would be nice to know if the assigning of the min and max actually worked and perhaps a teeny sample output might help.


... sort of retired...
NataliaGutierrez1
New Contributor III

Hi Dan, thank you for your response,

You are right on that, I did not get any errors but I did not obtain the results I need.

After running the code the min and max values from the lists were assigned to each field in each feature class but the values were the wrong ones.

The values assigned to all of the feature classes are the ones from the last lists only:

Expected behavior: Each list corresponds to one feature class. Each element on the list corresponds to one field.

Actual Behavior: The elements on the lists marked in red were assigned to all of the feature classes.

Please see results for two feature classes below:

The blue box shows the min MEDIAN_HOUSEHOLD_INCOME_2018 and the values from the list above assigned to each field.

The green box shows the max MEDIAN_HOUSEHOLD_INCOME_2018 and the values from the list above assigned to each field.

The same values are assigned to all of the feature classes.

Feature class _1 :

 Feature class_2:

I am not sure how to iterate through so many things at the same time. I need to iterate through:

1. Multiple feature classes

2. Rows within each feature class

3. Fields within each feature class

4. Lists (to select the values I need to assign in the fields)

 - List of Max and Min MEDIAN_HOUSEHOLD_INCOME_2018

 - List of Max and Min construction_lists

I really appreciate your help in this matter,

Thank you!

0 Kudos
JoeBorgione
MVP Esteemed Contributor

I'm famous for looping within a loop and just as famous for them failing.  You might want to focus on getting it to work with just one feature class at a time and see if that gets you what you want and then move up to multiple feature classes.  In other words, throttle back your approach to just one loop and get it to work.

That should just about do it....
0 Kudos
RandyBurton
MVP Regular Contributor

I'm not sure I completely understand what you are trying to accomplish.  If the variables fc_list, max_construction_lists, and min_construction_lists are related, then I suggest you combine them into a dictionary.  That way, you can use the feature name to access the min and max lists.

It looks like you are trying to determine the max and min values of household income in a variety of features.  But I don't see where you are keeping track of what feature has what max and min values.  Again, I would use a dictionary for this purpose with the key coming from the fc_list.

My thoughts...

const_lists = {
    'Atlanta': {'max': [394, 394, 674, 452, 708, 589, 139], 'min': [303, 303, 245, 253, 333, 152, 50]},
    'Boston': {'max': [510, 510, 874, 586, 912, 763, 180], 'min': [392, 392, 318, 327, 532, 196, 65]} }


field_list = ["MEDIAN_HOUSEHOLD_INCOME_2018", "CONST_PRICE_PER_SF_CONDO", "CONST_PRICE_PER_SF_MULTIFAM",
              "CONST_PRICE_PER_SF_OFFICE", "CONST_PRICE_PER_SF_RETAIL", "CONST_PRICE_PER_SF_HOTEL",
              "CONST_PRICE_PER_SF_INDUSTRIAL", "CONST_PRICE_PER_SF_PARKING"]

fc_list = ['Atlanta', 'Boston']

mm_dict = {} # max, min dictionary

for fc in fc_list:
    print("Processing feature class: {}".format(fc))
    with arcpy.da.SearchCursor(fc, 'MEDIAN_HOUSEHOLD_INCOME_2018') as cursor:
        max = max(cursor)[0]
        cursor.reset()
        min = min(cursor)[0]
        # print(fc, max, min)
        mm_dict[fc] = {'max': max, 'min' : min }
        del cursor
        del max
        del min

# print(mm_dict)

for fc in fc_list: # iterate through each feature class

    # limit rows selected/updated to min and max household income
    where = "MEDIAN_HOUSEHOLD_INCOME_2018 in ({}, {})".format(mm_dict[fc]['min'], mm_dict[fc]['max'])
    # print where
    
    with arcpy.da.UpdateCursor(fc, field_list, where) as cursor:
        for row in cursor: # iterate through each row
            if row[0] == mm_dict[fc]['min']:
                lists = const_lists[fc]['min']
            else:
                lists = const_lists[fc]['max']
            # print(fc, row[0], lists)
            row[1] = lists[0]
            row[2] = lists[1]
            row[3] = lists[2]
            row[4] = lists[3]
            row[5] = lists[4]
            row[6] = lists[5]
            row[7] = lists[6]
            cursor.updateRow(row)

print("Done updating max/min construction costs in all cities"‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I'm not sure what version you are using (Pro or Desktop) so there may be some code tweaks need - again, if I understand your problem correctly.  And you may wish to add some additional error checking.

Do you only want to update the rows with the minimum and maximum income?   Anyway, hope this helps.