pythongisarcgisarcmap

Locating a substring in Field Calculator using ArcGIS Pro


I have what should be a fairly straightforward operation failing in ArcGIS Pro 2.4, and cannot for the life of me work out why.

If the field "assettype" contains a portion of the search string, then set the value of assettype_groupup to the value I return.

Eg, if "assetttype" contains the string "Building |Residential |Large ", and I test whether it contains the term "Residential", and that evaluates to true, then return the string "Residential".

Currently the code does not seem to be returning any result / has no effect, and appears to run too quickly (2-3 seconds for 3,000,000 lines).

If I try a ternary statement, which means using a single term at a time, it seems to work just fine. I'd rather not take this approach as the if/elif possibilities could wind up being extensive in nature and I'd like to run the routine only once.

Can you see any obvious issues with the setup below

#Target Field Name 
assettype_groupup

#Expression
func(!assettype!)

# Code block
def func(input):
    if 'Residential' in input:
        return 'Residential'
    elif 'Industrial/Utilities' in input:
        return 'Industrial/Utilities'
    elif 'Transport/Infrastructure' in input:
        return 'Transport/Infrastructure'
    elif 'Conservation/National Park' in input:
        return 'Conservation/National Park'
    elif 'Recreational/Open Space' in input:
        return 'Recreational/Open Space'
    elif 'Mixed Use' in input:
        return 'Mixed Use'
    elif 'Community Use' in input:
        return 'Community Use'
    elif 'Rural/Primary Production' in input:
        return 'Rural/Primary Production'
    elif 'Special Use' in input:
        return 'Special Use'
    elif 'Unknown' in input:
        return 'Unknown'
    else:
        ''

Solution

  • I am not familiar with ArcGIS Pro, but do know Python fairly well.

    In Python you should avoid naming variables or arguments the same as any built-ins, and input() is the name of a built-in function. Also, the line after the final else:, should be return ''. Since that's not the case, your function will effectively return None when there are no matches, not the empty string '' (assuming that's what you intended).

    With that in mind, the following would be a better way to write the function that would avoid both of the issues mentioned above:

    TARGETS = (
        'Residential',
        'Industrial/Utilities',
        'Transport/Infrastructure',
        'Conservation/National Park',
        'Recreational/Open Space',
        'Mixed Use',
        'Community Use',
        'Rural/Primary Production',
        'Special Use',
        'Unknown',
    )
    
    
    # Code block
    def func(info):
        for target in TARGETS:
            if target in info:
                return target
        else:  # Nothing matched.
            return ''
    

    A more advanced, slightly shorter and likely much faster, way to do would be by using Python's re regular expression module which is part of its standard library:

    import re
    
    regex = re.compile('|'.join(map(re.escape, TARGETS)))
    
    # Code block
    def func(info):
        mo = regex.search(info)
        return mo.group(0) if mo else ''
    

    In Python 3.8.0+ it could be shortened a little bit more by taking advantage of PEP 572's new syntax for assignment expressions using := (aka the "walrus" operator) which was added beginning in that version:

    import re
    
    regex = re.compile('|'.join(map(re.escape, TARGETS)))
    
    def func(info):
        return mo.group(0) if (mo := regex.search(info)) else ''  # Py 3.8.0+