6

I have a classified raster with a class value that is a very long concatenated string name (Class_Name below). I'm trying to use arcpy to generate integer labels ranging from 1-24 (which are the third concatenated sub-group in these long strings). Here's an example of a previous (manually manipulated) similar raster attribute table. enter image description here

So far I've been manually using search criteria in ERDAS to find classes belonging to the same group such as:

$"Class_Names" contains "-14.01-" or $"Class_Names" contains "-14.02-" or $"Class_Names" contains "-14.03-"

I've tried using the following code (as a trial for the first two classes) with LIKE in a for loop with UpdateCursor, but I get a syntax error for my LIKE statement.

in_table = 'l12wm_agsup02.img'
field_names = ['Class_Name', 'crop']
with arcpy.da.UpdateCursor(in_table, field_names) as cursor:
    for row in cursor:
        if row[0] 'LIKE \'1.%\'':
            row[1] = 1
        elif row[0] 'LIKE \'2.%\'':
            row[1] = 2
        cursor.updateRow(row)

I've looked at Using LIKE clause in update cursor gives syntax error? and https://community.esri.com/thread/87848 but can't figure out how to use LIKE in the if else statements instead as a where clause in UpdateCursor.

I'm using ArcMap 10.5, Python 2.7

4
  • You've missed out an important bit of information, what is the actual error message?
    – Hornbydd
    Commented Apr 5, 2018 at 18:25
  • It highlights the last single quote in if row[0] 'LIKE \'1.%\ ' ':
    – ENIAC-6
    Commented Apr 5, 2018 at 18:27
  • Sounds like a syntax error message rather than a logic problem?
    – Hornbydd
    Commented Apr 5, 2018 at 18:41
  • regular expression can be used for mimmicking LIKE (stackoverflow.com/questions/26148712/…) but in your case @Aaron 's solution is the most straightforward (!Class_Name!.split("-")[2].split(".")[0] can also be applied in the field calculator)
    – radouxju
    Commented Apr 6, 2018 at 7:49

4 Answers 4

4

I would isolate the class you are after rather than searching for it. Then, simply assign row[1] that isolated value. For example:

in_table = 'l12wm_agsup02.img'
field_names = ['Class_Name', 'crop']
with arcpy.da.UpdateCursor(in_table, field_names) as cursor:
    for row in cursor:
        row[1] = row[0].split("-")[2].split(".")[0]
        cursor.updateRow(row)
1
  • Thank you Aaron, that worked like a charm! I forgot to mention that for some rows, 'Class_Name' was empty and one row would always be 'Unclassified'. I received an error something along the lines of "Index out of range" and so I added an if/elif/else to leave these as they were.
    – ENIAC-6
    Commented Apr 6, 2018 at 22:09
2

This does not have to be done in a python script, it can be done simpler by using the calculate field tool as shown below:

Tool

If you want to run this as python the code would be:

arcpy.CalculateField_management("myRaster","crop","cropnumber( !class! )","PYTHON_9.3","""def cropnumber(s):/n  if s.find("-1.") != -1:/n    return 1/n  elif s.find("-2.") != -1 :/n    return 2/n  else:/n    return -999""")
1

You can use the in operator, for example:

with arcpy.da.UpdateCursor(in_table, field_names) as cursor:
    for row in cursor:
        if '1.' in row[0]:
            row[1] = 1
...

And instead of writing 24 if statements you can use a dictionary:

d = {'1.':1, '2.':2, '3.':3} #add all values here

with arcpy.da.UpdateCursor(in_table, field_names) as cursor:
    for row in cursor:
        for key in d:
            if key in row[0]:
                row[1]=d[row[0]]
                cursor.updateRow(row)

But this wont work since '2.' is in both '12.' and '22.' so you can do:

row[1] = row[0].split('-')[2].split('.')[0]
2
  • Thanks for this code. I ran your first block of code and replaced the second to last line with your second block of code. For some reason, it didn't generate a value for half of the records and I can't find a pattern. Those that worked: 2.0, 13.01, 13.06, 11.0, 11.5, and 11.0. Those that didn't: 4.05, 5.03, 14.03, 5.01, 6.0, 6.04, 6.02, 7.0, 6.05.
    – ENIAC-6
    Commented Apr 5, 2018 at 19:48
  • You need to add those to the dictionary. Use @Aarons answer instead
    – Bera
    Commented Apr 5, 2018 at 19:51
1

You can parse your text based on dash (-) to split the text and select the 3rd number to convert it to integer. Try the following python code in your field calculator:

def select(f):
    if f == ' ':
        return 0
    else:
        txt = f.split("-")[2]
        digit = int(round(float(txt),0))
        return digit

The above code assumes all the column of Class_Name is filled with information with the same structure. If the field is empty of text, it will return zero (0). However, I used round() function to round the class to the nearest whole number in case you have something like 4.98, it will return 5 instead of 4. But, if you want to truncate the digit numbers, you can simply change the line:

digit = int(round(float(txt),0))

to:

digit = int(float(txt))

enter image description here

Here is sample output:

enter image description here

Not the answer you're looking for? Browse other questions tagged or ask your own question.