Update values in attribute table

3636
15
11-15-2017 12:12 PM
ammaratalib
New Contributor II

I have three columns  (other than OID FID) in my shapefiles for river. And I want to update "from node" and "to node column"

Before update

from node    to node    gridcode   
 2            3          8
 3            2          9
 7            2          5

After update, I want "node column" to be same as "grid code" column, so I used field calculator in ArcGIS (from node = gridcode). My "from node" column has been changed to gridcode but not sure how to update "to node" column (that depends on values in “from node” column)

Below is the final result I want:

from node    to node    gridcode 
8            9          8 
9            8          9 
5            8          5
Half of the code is working  
import arcpy, os, sys    
 from arcpy import env    
 fc=r"C:/Users/wis/Desktop/python/fe/fea_c.shp"    
# instantiate empty dictionary    
#'FROM_NODE','GRID_CODE','TO_NODE' name of columns in my attribute table    
 node_values = {}   
 # build dictionary of values   
 with arcpy.da.SearchCursor(fc, ['FROM_NODE','GRID_CODE']) as cursor:
     for row in cursor:
           old_node = row[0]
           new_node= row[1]            
           node_values[FROM_NODE] = new_node      
with arcpy.da.UpdateCursor(fc, ['FROM_NODE','TO_NODE','GRID_CODE']) as cursor:        
    for row in cursor:            # set fromnode to gridcode value           
         row[0] = row[2]            # set tonode to new equivalent node           
         to_node = row[1]# I get an error here            
         new_to_node = node_values[TO_NODE]            
         row[1] = new_to_node             
         cursor.updateRow(row)

The error is because row[1] or "to node" does not have all numbers that are in "from node" or "grid code" Because some values will repeat in t"no node" because one river can be connected to more than one rivers. However no repetition in "gridcode" and "from node" Any suggestions

15 Replies
DanPatterson_Retired
MVP Emeritus

perhaps you could post or show a full example of the data you are working with since only portions of your example make sense

0 Kudos
ammaratalib
New Contributor II

Dan Patterson‌ i attached shapefile. You can familiar with watershed delineation so i guess i can give you some background why am i am doing this. I used swat model to delineate my watershed but because of low elevation, I am getting unwanted streams too (i tried burning original streams, arc hydro and different threshold value).Still I need to make some changes that involve removing some sub basins/streams , merging others. Because swat does not except any subbasin that does not have  any stream draining into it. So if i remove unwated streams, i need to merge some subbasins. When i remove some streams it  means that gridcode and from node will not be in consecutive order. Since swat model only accept watershed and streams that have consecutive gridcode for streams. So If I change gridcode, then it will also change from node and to node connection for streams. Long story 🙂 so i am trying to use arcpy to update my from node and to node when i change gridcode.  

gridcode should be same as from node and then no node will also change accordingly 

0 Kudos
TedKowal
Occasional Contributor III

What about using a dictionary to convert you node data to the grid number?

# dictionary mapping {From_Node : GridCode]
conv = {2:8,3:9,7:5}
....
New_From_Node=conv[From_Node]
New_To_Node=conv[[To_Node]
....‍‍‍‍‍‍

You can even programically build your dictionary

ammaratalib
New Contributor II

yes that's what i did in the above code. 

node_values = {}  is dictionary but code gets an error when I try to change no node. It works fine for grid code and from node.
0 Kudos
TedKowal
Occasional Contributor III

Sorry I got lost in the Nodes when reading your question!

Is there a value for the to_node you are trying to match?  With the sample data you gave  new_to_node = node_values[1] would fail or error because there is no lookup for it? 

Do you have a default grid code for the missing node values....  if you do you could do a node_values.get(to_node,mydefaultvalue)

There is a lot of good stuff here when you have a key with no value.... Initializing a dictionary in python with a key value and no corresponding values - Stack Overflow 

0 Kudos
ammaratalib
New Contributor II

"to_node" depends on "from node".I just want to update "to_node".  if i change "from node " lets say from node =3 has been changed to 2 then all 3's in "to node" should also changed to 2. Yes the reason I am getting error is because some values in "to_node" are occurring twice, because tributaries can  drain into more than one river/streams. Repitition is ok. As long as values are being updated.But no repetition in "grid code" and "from node" 

0 Kudos
MitchHolley1
MVP Regular Contributor

I think you're building your dictionary incorrectly.  Right now, it's setting the key "FROM_NODE" to the variable new_node which is row[1]. 

Should it be something like this?

node_values = {} 
with arcpy.da.SearchCursor(fc, ['FROM_NODE','GRID_CODE']) as cursor:
    for row in cursor:
          old_node = row[0]
          new_node= row[1]            
          node_values[old_node] = new_node  

If not, print your dictionary so we can see the contents. 

0 Kudos
ammaratalib
New Contributor II

>>> node_values  My dictionary. for first part (search cursor)

{1: 1, 3: 2, 4: 5, 5: 3, 6: 4, 7: 6, 8: 8, 9: 7, 10: 12, 11: 9, 12: 10, 13: 11, 14: 13, 15: 14, 16: 19, 17: 24, 18: 15, 19: 16, 20: 17, 21: 18, 22: 20, 23: 21, 25: 22, 26: 23, 27: 26, 28: 29, 29: 25, 30: 27, 31: 33, 32: 28, 33: 32, 34: 30, 35: 31, 37: 35, 38: 34, 39: 37, 40: 36, 42: 38, 43: 39, 44: 40}

yes the code for search cursor part is correct. I matched it with attribute table(in attached shaped file).because i am updating my "from code" based on "grid code". Next step is to update "to node" but i am getting error there (update cursor part), because to_node does not have all values that "from node" has and that's how it should be. code should not care about matching it should just update "to node". 

0 Kudos
XanderBakker
Esri Esteemed Contributor

I just ran the code below:

def main():
    import arcpy

    fc = r'C:\GeoNet\UpdateNodes\shp\fea_c.shp'

    node_values = {r[0]: r[1] for r in arcpy.da.SearchCursor(fc, ['FROM_NODE','GRID_CODE'])}

    with arcpy.da.UpdateCursor(fc, ['FROM_NODE','TO_NODE','GRID_CODE']) as cursor:
        for row in cursor:
            row[0] = row[2]             # set tonode to new equivalent node
            to_node = row[1]            # I get an error here
            if to_node in node_values:
                new_to_node = node_values[to_node]
                row[1] = new_to_node
            else:
                print to_node, " not in dictionary"
            cursor.updateRow(row)


if __name__ == '__main__':
    main()

and it updated, but there are as you mentioned some case that do not have value in the dictionary:

2 not in dictionary
24 not in dictionary
36 not in dictionary
41 not in dictionary