Python Scripting with the Field Calculator
This post details how you can use some Python scripting to do if/else statements in the Field Calculator.
If the title of this post and the intro haven’t put you to sleep already, read on! Perhaps they’ve even piqued your interest! (There are support groups available for this condition.)
I was recently contacted by a keen GISer (who shall remain nameless) with a problem they were having. They were wanting to do some work around the Land Cover Database (LCDB) – a vector polygon layer, with up to 33 classes of land cover across the whole country. To cover the whole country it uses a set of 479,353 polygons! Previous versions were released for 1996, 2001, 2008 and this latest version has 2012 data, thus providing views of New Zealand’s landcover across those years, like snapshots in a photo album. The lastest version (4.1) includes data from the three previous versions so, in theory, one could track changes in land cover from 1996 to 2012. Here’s a shot of a portion of the table, focusing on the class names:
So, this person was interested in looking at landcover changes at particular study sites from one year to the next, with an eye towards quantifying changes based on % of total area in the study areas. In the table above, most of these polygons haven’t changed across the years, but several have. Some have gone from High Producing Exotic Grassland to Exotic Forest – and they wanted to quantify changes like this. But how to do so?
There’s a Feature Compare tool (sort of like Compare in Word) that lets you look at differences between two layers, but we were looking for changes within the table of a single layer; a different proposition. My first thought was to give yourself a new attribute that would signal if there had been changes from one year to another (a slightly simplified version of what they were after), which got me thinking about “if/then” conditional statements. And this takes us down the road of some simple scripting – abandon hope, ye who enter here…
Python is a useful scripting language and is the preferred one for ArcGIS. We’ve covered some scripting previously, and here I was interested in using some very simple Python to get this done within the table. After creating a new integer field in the table to store an integer (let’s call it Comparison), the basic idea was to:
- compare the landcover class names between two different years, and;
- if the values for a polygon are the same (no change) put a 0 in the Comparison field;
- if the values for a polygon are different (a change) put a 1 in the Comparison field;
- Do this for all the polygon records in the table.
In scripting terms, we can do with an “if” statement, which reads something like this:
if (some condition is true):
if (some other condition is true):
do the other thing
Often, we might write a stand alone script, in a separate file that does this, but I wanted to try using the Field Calculator instead. Using a smaller copy of the LCDB with only 160 records, called FieldCalculationTest, I added a new integer field called “Comparison”. The Field Calculator can be opened directly from the table by right-clicking on the name of the field you want to put the result in, as shown below:
Amongst other things, I’ve got all my Fields in the upper lefthand box, some functions to choose from at right, and a window at the bottom where I can set up my calculations. We can use this to put some Python script in by setting the Parser button to Python and ticking the “Show Codeblock” box:
I can now type in my script elements into the “Pre-Logic Script Code:” box and then call it from the “Comparison =” box. In the first, I need to set up a generic sort of code and in the second I can call it using specific fields from the table. To keep it generic I’ll use two simple names (variables actually) that will stand in for my true field names: class1 and class2. This means I could then run it using any two of my fields, so long are they’re of the same type. the code’s pretty simple: compare two field values in a record; if there’s the same, put a 0 in the field, if they’re not, put a 1. When writing code, it’s often better practice to start with the negative case first. So a simple version would be:
if class1 != class2:
The “if class1 != class2:” translates to: if class1 is not equal to class2, await an action. In this case the action is place a 1 in that record’s Comparison field. For any other conditions, place a 0.
So how does this work in practice? We’ve first got to wrap the code in a function definition, give it a name, and specify any inputs that the function may take. I can do this with the following text:
def compare(class1, class2)
The function’s name is “compare” and it takes two input variables, “class1” and “class 2”. Those are generic names and you’ll see how they get used below.
Next, I can use the “Comparison =” box to specifically run this function for every record in the table (or only those that are selected if there are any). The calculator knows to run it on the Comparison field because that’s where I started it from. In the “Comparison =” box I can invoke (great word there) the function and tell it to use specific fields as the input variables. To demonstrate this I’ll use the “Name_2001” and “Name_2008” fields. The image below pulls all this together and should do the trick.
When I click OK, here’s a sample of the output I get:
(I’ve simplified the table to only show the relevant fields by going to the layer Properties > Fields and only turning on the fields I want to display.) So any of the records with a 1 should have different values between Name_2001 and Name_2008.
So this is a pretty simple example, but does demonstrate how you can do simple scripting tasks from within the Field Calculator. In this one we’ve just compared two fields but there’s no reason we couldn’t consider more comparisons than that. With scripting we can make things as complex as we like, so long as things don’t become too complex and unwieldy. Given the generic nature of this function, I can save it and rerun it on other fields and can easily use different input fields by making small changes. Since the layer contains area measurements, a next step would be to summarise the areas of those polygons that changed, divide by the total area and there’s your measure of change. In all honesty, that’s something that might be easier to do in Excel, and frankly, the whole thing may have been easier to do in Excel from the start, but I do like a challenge. Especially one like this.
What I haven’t included above is all the trial and error work we went through to get this to work. I’m not a proficient enough user of Python to simply get this right the first time so it’s always trial and error for me. ArcGIS attribute tables in and off themselves aren’t that powerful and not nearly as powerful as an Excel spreadsheet, but hopefully this helps you see that some Python scripting does allow you to wring some additional information out of your tables.