2

I'm trying to work out how to populate a field depending on the lowest score across 6 other fields. I've read several other questions relating to if/else statements but I'm only just starting to learn Python so I'm struggling to put together any code that works.

I'm trying to populate a 'Main Failing' field, where for each object ID in the feature class the worst scored field out of 6 other fields will be noted.

The other fields are: 'Interconnectivity', 'PropCover', 'PatchSize', 'ShapeIndex', 'Naturalness' and 'EdgeNat' and they are all short integer, with high numbers being better than low numbers.

So for example if 'Interconnectivity' had the lowest score across the 5 fields for one objectID, I would like to work out how to populate the 'Main Failing' column for that objectID with text saying 'Interconnectivity'.

Edited: Here's an example of the code I've tried so far with Branco's help. It's running without errors but is either leaving the MainFailing field blank (if 'return resultList[0]') or is populating it with 0's (if return resultList[1]).

Codeblock:

myFunction( !InterconnectivityScore!, !PropCoverScore!, !PatchSizeScore!, !ShapeIndexScore!, !NatScore!, !EdgeNatScore!)

Pre-logic script code:

def myFunction ( interconnectivityScoreValue, propCoverScoreValue, patchSizeScoreValue, shapeIndexScoreValue, natScoreValue, edgeNatScoreValue):
resultList = ["",0]
if interconnectivityScoreValue <= resultList[1]:
    resultList[0] = "Interconnectivity"
    resultList[1] = InterconnectivityScore
if propCoverScoreValue <= resultList[1]:
    resultList[0] = "ProportionalCover"
    resultList[1] = PropCoverScore
if patchSizeScoreValue <= resultList[1]:
    resultList[0] = "PatchSize"
    resultList[1] = PatchSizeScore
if shapeIndexScoreValue <= resultList[1]:
    resultList[0] = "ShapeIndex"
    resultList[1] = ShapeIndexScore
if natScoreValue <= resultList[1]:
    resultList[0] = "Naturalness"
    resultList[1] = NatScore
if edgeNatScoreValue <= resultList[1]:
    resultList[0] = "EdgeNaturalness"
    resultList[1] = EdgeNatScore
return resultList[1]
2
  • Do you have duplicate min values or "None" values in your attribute table?
    – Aaron
    Feb 17, 2015 at 19:48
  • @Aaron - I don't have any 'none' values, but there may be cases where there may be a joint minimum value across the fields
    – Vanessa
    Feb 18, 2015 at 15:56

2 Answers 2

2

Try this (edited per @alpha-beta-soup comment):

def myFunction (inter, prop, patch, shp, nat, edge):
    myList = [inter, prop, patch, shp, nat, edge]
    myList.sort()
    myDict = {'Interconnectivity' : inter,
              'PropCover' : prop,
              'PatchSize' : patch,
              'ShapeIndex': shp,
              'Naturalness' : nat,
              'EdgeNat' : edge}

    myVal = 101   # using 101 as a max + 1 possible value; adjust if needed
    myReturn = ''
    for k, v in myDict.iteritems():
        if v < myVal:
            myVal = v
            myReturn = k

    return myReturn

Note that if you have two matching low values, there's nothing to select one over the other. That would require additional checking based on whatever your criteria is. Since a dictionary is unordered, you can't control it that way.

Here's another way without dictionaries, with an optional clause for ties. You may want different logic in case of ties.

def myFunction2 (inter, prop, patch, shp, nat, edge):
    # use tuples to ensure value order
    myScoreTuple = (prop, patch, shp, nat, edge)
    myTextTuple = ('PropCover',
                   'PatchSize',
                   'ShapeIndex',
                   'Naturalness',
                   'EdgeNat')

    # initialize values
    myLowValue = inter
    myReturn = 'Interconnectivity'

    # now check for any lower value
    for i in range(len(myScoreTuple)):
        if myScoreTuple[i] < myLowValue:
            myLowValue = myScoreTuple[i]
            myReturn = myTextTuple[i]

        # optional - add multiple text values in case of tie. Remove if unneeded
        elif myScoreTuple[i] == myLowValue:
            myReturn = ', '.join((myReturn, myTextTuple[i]))

    return myReturn
4
  • 1
    Given all the parameters are integer values, it seems to me that there'd be an error with keys not being unique. Feb 18, 2015 at 20:26
  • Hmm, yes, hadn't thought of that. If the scores are guaranteed to be exclusive (i.e., a hierarchical ranking), it will work. Maybe I can flip the keys and values and search in a for loop for the smallest value. Let me think about it.
    – recurvata
    Feb 18, 2015 at 20:39
  • Thanks very much for your help. I've had a go both ways but am still struggling to make it work - my limited Python knowledge probably isn't helping! Error says 'a field name was not found or there were unbalanced quotation marks'
    – Vanessa
    Feb 19, 2015 at 15:19
  • It worked on my end in PyScripter. Did you copy and paste the code, or enter it manually? As the error states, check for missing or extra quotation marks, and that you're passing in all 6 (not 5) fields.
    – recurvata
    Feb 19, 2015 at 15:26
0

You can do this using the field calculator and python. Make sure you have the Parser set to Python and the code block being shown.

Here is an idea of what the field calculator should look like when finished..

The code I set up just uses a series of IF statements. I decided this was easier since I was comparing their scores, which I am assuming are numeric scores? In either case, I am not doing any checking for 2 scores having the same value. I am sure you can figure out that part, or figure out how to handle strings (if that is how the info is being stored). I wasn't going to worry about if, elif, etc etc... This would be run from your Main Failing field calculator. This is the main Pre-Logic Script Code: part. This outlines the function you're going to be using to process the passed in information in your *MainFailing * (Field12 for me) part. These are your function parameters.

def myFunction(interconnectivityValue, propCoverValue, patchSizeValue, shapeIndexValue, naturalnessValue, edgeNatValue):
  resultList = ["", 0]
  if interconnectivityValue <= resultList[1]:
    resultList[0] = "interconnectivity"
    resultList[1] = interconnectivityValue
  if propCoverValue <= resultList[1]:
    resultList[0] = "propCover"
    resultList[1] = propCoverValue
  if patchSizeValue <= resultList[1]:
    resultList[0] = "patchSize"
    resultList[1] = patchSizeValue 
  if shapeIndexValue <= resultList[1]:
    resultList[0] = "shapeIndex"
    resultList[1] = shapeIndexValue
  if naturalnessValue <= resultList[1]:
    resultList[0] = "naturalness"
    resultList[1] = naturalnessValue
  if edgeNatValue <= resultList[1]:
    resultList[0] = "edgeNat"
    resultList[1] = edgeNatValue
  return resultList[0]

The Main Failing part lets you specify how you want to call whatever function you declared in your Pre-Logic Script Code: section. To pass the fields for each row, notice that the field names are surrounded by !'s. This is how python knows to grab the value from the attribute table.

Using python (or VB Script) in the field calculator is a nice step towards using a more python centered approach. I would personally prefer to do this using the python interactive window and an arcpy UpdateCursor. That is another approach you can look at once you figure out the Field Calculator portion.

8
  • Nice, but I think you mean: return resultList[0]
    – phloem
    Feb 18, 2015 at 0:39
  • Thanks very much for your help. I've managed to get it running without errors but it doesn't seem to be working yet. It either leaves the MainFailing field blank (if I have 'return resultList[0]' or just populates it with 0s if I have 'return resultList[1]'. I'll post a copy of what I've done in a new answer...
    – Vanessa
    Feb 18, 2015 at 10:47
  • @phloem.. thanks for the catch.. I edited my answer.
    – Branco
    Feb 18, 2015 at 13:16
  • @Vanessa ... What kind of values are you using for the other fields? Are they strings, integers, floating point, etc? I set it up for comparing numbers. If you have string fields (check the field type), then it will not behave correctly.
    – Branco
    Feb 18, 2015 at 13:17
  • They're all short integer except for the MainFailing column which I set to text
    – Vanessa
    Feb 18, 2015 at 14:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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