-1

I'm struggling to remember any Python code, I use it so infrequently. I want to write a simple if / elif statement but using any value from a list like a Boolean IN statement. So instead of:

def Type(fld):
    if fld == "Neighborhood":
        value = 1
    elif fld == "Apartment":
        value = 2
    elif fld == "Condo":
        value = 3
    return value‍‍‍‍‍‍‍‍

I want something like:

def Type(fld):
    if fld == ["Neighborhood", "Block", "Locality"]:
        value = 1
    elif fld == ["Apartment", "Unit"]:
        value = 2
    elif fld == ["Condo", "Penthouse"]:
        value = 3
    return value‍‍‍‍‍‍‍‍

But I can't get it to run. How should I structure this?

4

Use in:

def btype(fld):
    if fld in ["Neighborhood", "Block", "Locality"]:
        value = 1
    elif fld in ["Apartment", "Unit"]:
        value = 2
    elif fld in ["Condo", "Penthouse"]:
        value = 3
    else:
        value = 999 # or whatever you want, for example None 
    return value‍‍‍‍‍‍‍‍

call with:

btype(!your_actual_fieldname_here!)
4
  • This use pattern calls for a dictionary.
    – Vince
    May 20 at 11:13
  • It is another possibility. But since the if's and lists are already written I didnt bother writing a dictionary
    – BERA
    May 20 at 11:16
  • I should have mentioned this will be used in ArcMap Field Calculator, so this is the structure I'm more familiar with.
    – Steve H
    May 21 at 10:11
  • Ok! Both answers should work in field calculator
    – BERA
    May 21 at 11:58
3

The Pythonic solution is to use a dictionary.

def fldAsType(fld):
    fldLookup = {
        "Neighborhood" : 1,
        "Block" : 1,
        "Locality" : 1,
        "Apartment" : 2,
        "Unit" : 2,
        "Condo" : 3, 
        "Penthouse" : 3,
    }
    try:
        return fldLookup[fld]
    except:
        return -999

Note that "Type" is a dangerous name, because of the built-in type operator.

Conforming to the PEP 8 style guide will make your code integrate more easily with code found on the web. The relevant piece is that classes start with upper-case and methods with lower-case.


So I just got a chance to benchmark this in Python 2.7, and found that the "Pythonic" "EAFP" principle ("It's easier to ask for forgiveness than permission") didn't actually apply -- structuring this function as

def fldAsType(fld):
    fldLookup = {
        "Neighborhood" : 1,
        "Block" : 1,
        "Locality" : 1,
        "Apartment" : 2,
        "Unit" : 2,
        "Condo" : 3, 
        "Penthouse" : 3,
    }
    return fldLookup[fld] if fld in fldLookup else -999

was three times faster than using the exception handling, but was still four times slower than the if/elif/else cascade. Which seemed wrong. So I moved the lookup initialization outside the function definition:

fldLookup = {
    "Neighborhood" : 1,
    "Block"        : 1,
    "Locality"     : 1,
    "Apartment"    : 2,
    "Unit"         : 2,
    "Condo"        : 3,
    "Penthouse"    : 3,
}
def fldAsType(fld):
    return fldLookup[fld] if fld in fldLookup else -999

And then the dictionary was 10% faster than if/elif/else.

Then I applied EAFP:

fldLookup = {
    "Neighborhood" : 1,
    "Block"        : 1,
    "Locality"     : 1,
    "Apartment"    : 2,
    "Unit"         : 2,
    "Condo"        : 3,
    "Penthouse"    : 3,
}
def fldAsType(fld):
    try:
        return fldLookup[fld]
    except:
        return -999

and the dictionary was now 35% faster without any failed lookups, but 16% slower if there were 2% None values in the lookup population.

So, the lessons here are:

  • Never place dictionary initialization inside a method
  • Only use EAFP when you're reasonably certain you won't need forgiveness

Benchmark script:

import random
import datetime

def fldAsType0(fld):
    if fld in ["Neighborhood", "Block", "Locality"]:
        value = 1
    elif fld in ["Apartment", "Unit"]:
        value = 2
    elif fld in ["Condo", "Penthouse"]:
        value = 3
    else:
        value = 999 # or whatever you want, for example None
    return value

def fldAsType1(fld):
    fldLookup = {
        "Neighborhood" : 1,
        "Block"        : 1,
        "Locality"     : 1,
        "Apartment"    : 2,
        "Unit"         : 2,
        "Condo"        : 3,
        "Penthouse"    : 3,
    }
    try:
        return fldLookup1[fld]
    except:
        return -999

def fldAsType2(fld):
    fldLookup2 = {
        "Neighborhood" : 1,
        "Block"        : 1,
        "Locality"     : 1,
        "Apartment"    : 2,
        "Unit"         : 2,
        "Condo"        : 3,
        "Penthouse"    : 3,
    }
    return fldLookup2[fld] if fld in fldLookup2 else -999

fldLookup3 = {
    "Neighborhood" : 1,
    "Block"        : 1,
    "Locality"     : 1,
    "Apartment"    : 2,
    "Unit"         : 2,
    "Condo"        : 3,
    "Penthouse"    : 3,
}
def fldAsType3(fld):
    return fldLookup3[fld] if fld in fldLookup3 else -999

def fldAsType4(fld):
    try:
        return fldLookup3[fld]
    except:
        return -999

# choices not in Python 2.7
def choices(population, weights=None, cum_weights=None, k=1):
    result = []
    newpop = []
    for p in population:
        for w in weights:
            newpop.append(p)
    for i in range(k):
        result.append(random.choice(newpop))

    return result

# Generate 100000 values (even distribution)

random.seed(12345)
sampleSize      = 1000000
uniformDistro   = []
sortedDistro    = []

options1 = ["Neighborhood", "Block", "Locality", "Apartment", "Unit", "Condo", "Penthouse"]
options2 = sorted(options1)
options3 = random.sample(options1, k=len(options1))
options3.append(None)
options4 = random.sample(options1, k=len(options1))

for i in range(sampleSize):
    uniformDistro.append(random.choice(options1))
    sortedDistro.append(random.choice(options2))

missingDistro  = choices(options3,[7,7,7,7,7,7,7,1],k=sampleSize)
weightedDistro = choices(options4,[21,13,8,5,3,2,1],k=sampleSize)

start = datetime.datetime.utcnow()
for v in uniformDistro:
    t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in sortedDistro:
    t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in missingDistro:
    t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in weightedDistro:
    t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))


start = datetime.datetime.utcnow()
for v in uniformDistro:
    t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in sortedDistro:
    t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in missingDistro:
    t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in weightedDistro:
    t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))


start = datetime.datetime.utcnow()
for v in uniformDistro:
    t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in sortedDistro:
    t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in missingDistro:
    t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in weightedDistro:
    t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))


start = datetime.datetime.utcnow()
for v in uniformDistro:
    t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in sortedDistro:
    t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in missingDistro:
    t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in weightedDistro:
    t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))


start = datetime.datetime.utcnow()
for v in uniformDistro:
    t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in sortedDistro:
    t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in missingDistro:
    t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))

start = datetime.datetime.utcnow()
for v in weightedDistro:
    t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))

#EOF

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