wanted: easyest way for field calculation with python

973
10
12-05-2018 07:08 AM
SilvanStöckli
New Contributor III

Hello,

I'd like to do some complicated field calculation.

I want to calculate the Field 6-value (see table below, representing a FeatureClass table) as follows:

If Field 1 = 617: Field 3-value

Else: Field 2-value

AND

"|"

AND

If Field 4 = ABC:

      If Field 5 is Null:

            "Sometext"

      Else:

            "Someothertext" + Field 5

Else:

I entered the results in Field 6 as an example

Field 1Field 2Field 3Field 4Field 5Field 6
611from July 1April to NovemberTextWith_äöüfrom July 1 |  Sometext
612from July 1April to NovemberABCTextWith_äöüfrom July 1 | Someothertext TextWith_äöü
612from July 15April to NovemberABCfrom July 15 | Someothertext
617from July 1April to NovemberABCApril to November | Someothertext

Up until now, I exported the table and calculated the value with some "when"-statements in Excel. But since I'd like to have the results in GIS i searched for an other solution.

I ended up using "arcpy.CalculateField_management" and something like this:

def compare(class1, class2, class3):

   if class1 in (612, 611):
       return class2
   elif class1 == 617:
       return class3
   else:
       return class1

# Expression below

compare(!some_field!, !another_field!, !another_field2!)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This didn't work quite well because

- the code will become huge (I have more fields and values to compare)

- there seem to be problems with converting/using characters which don't fit into ASCII

- i couldn't figure out how to ask properly if Field 5 is Null

- I could not properly translate the "AND" and the "OR" from Excel to Python (for example: If Field 4 is Null AND Field 5 is Null AND Field 1 is 612, Field 6 = ...)

-...

Is there an other, easyer solution how I can calculate Field 6 value?

Thanks a lot for your help!

10 Replies
JoshuaBixby
MVP Esteemed Contributor

Just to double check, the responses from arcpy.CalculateField_management - compare: how to return fieldvalues got you a working answer, you just have concerns about whether it is functional or optimal if scaled up?

Addressing some of your concerns:

-  "the code will become huge" not because of Python itself or the structure of the suggestions people provided in the referenced thread, the code will become huge because the logic is nested and you want to apply it to more and more fields.

- Python works fine with Unicode, in fact the default encoding for strings in Python 3.x is UTF-8.  What specific issues are you having?

- Regarding NULL, Python packages typically interpret/translate it as None, and this includes Esri tools.  var is None is the general syntax.

- AND and OR are implemented the same in Python as in all other programming languages.  In fact, Python has some really nice functions to simplify logical checks.  Your partial example above is implemented as:

fld4 is None and fld5 is None and fld6 == 612

In Python, just like in SQL and many other languages, "is" is reserved for singleton checking while the equality operator is used for checking values.  For example, the syntax is fld6 == 612 and not fld6 is 612.

JoshuaBixby
MVP Esteemed Contributor

Since the logic is incomplete, I can't say the following is exactly what you want, but it does reproduce Field 6 in your sample table:

def combine(fld1, fld2, fld3, fld4, fld5):
    part1 = (
        fld3 
        if fld1 == 617
        else fld2
    )
    part2 = (
        (
            u"Sometext"
            if fld5 is None
            else u"Someothertext" + fld5
        )
        if fld4 == u"ABC"
        else u"Sometext"
    )

    return u"{} | {}".format(part1, part2)



# Expression below

combine(!field1!, !field2!, !field3!, !field4!, !field5!)
SilvanStöckli
New Contributor III

Hey Joshua

Thank you a lot for all of your help!

After several hours of code typing, (at least in my opinion / compared to the excel-code) a kind of large code and several things not working (properly), I asked myself if there might be an easyer way to write the code. That's why I started thinking if I'm actually really using the easyest way to do it and therefore I opened this new thread.

I'm not sure if that makes a difference: I'm using ArcGIS 10.3 with python 2.7.8.

I imported my example-table as a table to a GDB and run your code. It did not work:

"Messages
Executing: CalculateField Tabelle Field_6 compare(!Field_1!, !Field_2!, !Field_3!, !Field_4!, !Field_5!) PYTHON_9.3 "def combine (fld1, fld2, fld3, fld4, fld5):\n    part1 = (\n        fld3 \n        if fld1 == 617\n        else fld2\n    )\n    part2 = (\n        (\n            u"Sometext"\n            if fld5 is None\n            else u"Someothertext" + fld5\n        )\n        if fld4 == u"ABC"\n        else u"Sometext"\n    )\n\n    return u"{} | {}".format(part1, part2)"
Start Time: Thu Dec 06 11:04:19 2018
ERROR 000539: Error running expression: compare(612.0, u"from July 1", u"April to November", u"ABC", u"TextWith_äöü")
Traceback (most recent call last):
  File "<expression>", line 1, in <module>
TypeError: compare() takes exactly 3 arguments (5 given)

Failed to execute (CalculateField).
Failed at Thu Dec 06 11:04:19 2018 (Elapsed Time: 0.02 seconds)"

I can't figure out why it thinks that not all the arguments would be used...?

Anyway, I tried to simplify my code based on your model.

This works perfectly:

def compare(Code, SchnittZP, WeideZP, NHG_YN):
    part1 = (
        "-"
        if Code in (921, 922, 923, 924)

        else "ERROR"
    )
    return part1‍‍‍‍‍‍‍‍

then I tried to enter an other argument (line 6+7) - resulting in an error "ERROR 000989: Python syntax error: Parsing error SyntaxError: invalid syntax (line 7) Failed to execute (CalculateField).  ":

def compare(Code, SchnittZP, WeideZP, NHG_YN):
    part1 = (
        "-"
        if Code in (921, 922, 923, 924)

        "Text 634"
        elif Code == 634

        else "Wrong code"
    )
    return part1p‍‍‍‍‍‍‍‍‍‍‍

So I thought, maybe I need to adjust the whole code like this:

def compare(Code, SchnittZP, WeideZP, NHG_YN):
    part1 = (
        if Code in (921, 922, 923, 924):
            "-"
     
        elif Code == 634:
            "Text 634"

        else:
            "Wrong code"
    )
    return part1‍‍‍‍‍‍‍‍‍‍‍‍

because I've never seen the order you put the result bevore the condition. (What's the difference? Why do you prefer result bevore condition? How can you enter en elif-statement?)

Anyway it gives back following error: "Runtime error  Traceback (most recent call last):   File "<string>", line 56, in <module>   File "c:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\management.py", line 3453, in CalculateField     raise e ExecuteError: Failed to execute. Parameters are not valid. ERROR 000989: Python syntax error: Parsing error SyntaxError: invalid syntax (line 4) Failed to execute (CalculateField). "

I tried to handle this by changing row 4 to

value = "-"

and in the end "return value" but it didn't change anything.

Thanks for the other useful hints about "my concerns"!

I will try to implement some of your hints and explain my problems with unicode later :)

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Looking at your error message:

"Messages
Executing: CalculateField Tabelle Field_6 compare(!Field_1!, !Field_2!, !Field_3!, !Field_4!, !Field_5!) PYTHON_9.3 "def combine (fld1, fld2, fld3, fld4, fld5):\n    part1 = (\n        fld3 \n        if fld1 == 617\n        else fld2\n    )\n    part2 = (\n        (\n            u"Sometext"\n            if fld5 is None\n            else u"Someothertext" + fld5\n        )\n        if fld4 == u"ABC"\n        else u"Sometext"\n    )\n\n    return u"{} | {}".format(part1, part2)"
Start Time: Thu Dec 06 11:04:19 2018
ERROR 000539: Error running expression: compare(612.0, u"from July 1", u"April to November", u"ABC", u"TextWith_äöü")
Traceback (most recent call last):
  File "<expression>", line 1, in <module>
TypeError: compare() takes exactly 3 arguments (5 given)

Failed to execute (CalculateField).
Failed at Thu Dec 06 11:04:19 2018 (Elapsed Time: 0.02 seconds)"

My function is called "combine" and takes 5 inputs, you called "compare."  You need to update your expression field to call the correct function.

BlakeTerhune
MVP Regular Contributor

Your psuedo code, example data, and test code all have differences and some of it appears incomplete. It's impossible to accurately determine what exactly your logic needs to be so here are some tips:

  1. Ensure you're using the Python interpreter (PYTHON_9.3) in Calculate Field.
  2. Use None for null values. Something like if Field5 is None:
  3. Use the str.format() method to ensure numbers are properly converted to strings when combining values.
  4. Try adding # -*- coding: latin-1 -*- or # -*- coding: utf-8 -*- at the beginning of your script (or the code block if running in ArcToolbox) to handle the special characters.
  5. Check if Field1 has an integer or text datatype. If text, you'll need to put quotes around the numbers.
  6. You should use == for comparing values.
SilvanStöckli
New Contributor III

Thank your for all those tips!

Concerning 3. str.format: is there a way to put together two strings and when one of them is "NULL/empty", ignore it and only take the "not empty" string?

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Yes, but it also might be easier to use a generator expression with str.join:

>>> string_list = ["Hello", None, ",", None, "Goodbye"]
>>> "".join(s for s in string_list if s)
'Hello,Goodbye'
>>> 
DanPatterson_Retired
MVP Esteemed Contributor

Excel To Table—Conversion toolbox | ArcGIS Desktop 

might be a better option if most of you work is done there and python isn't in your comfort realm just yet

SilvanStöckli
New Contributor III

Is there a way to convert the code from excel to arcgis? Because if not I will have to export the feature class-table to excel, apply the codes and join the new table to the feature class every time I change some data. That's what I try to avoid.

0 Kudos