Lesson 2 Practice Exercises
Lesson 2 Practice Exercises jed124Before trying to tackle Project 2, you may want to try some simple practice exercises, particularly if the concepts in this lesson are new to you. Remember to choose File > New in PyScripter to create a new script (or click the empty page icon). You can name the scripts something like Practice1, Practice2, etc.
Find the spaces in a list of names
Python String objects have an index method that enables you to find a substring within the larger string. For example, if I had a variable defined as name = "James Franklin" and followed that up with the expression name.index("Fr"), it would return the value 6 because the substring "Fr" begins at character 6 in the string held in name. (The first character in a string is at position 0.)
For this practice exercise, start by creating a list of names like the following:
beatles = ["John Lennon", "Paul McCartney", "Ringo Starr", "George Harrison"]
Then write code that will loop through all the items in the list, printing a message like the following:
"There is a space in blank's name at character (blank character)" where the first blank is filled in with the name currently being processed by the loop, and the second blank is filled in with the position of the first space in the name as returned by the index method. (You should obtain values of 4, 4, 5 and 6, respectively, for the items in the list above.)
This is a good example in which it is smart to write and test versions of the script that incrementally build toward the desired result rather than trying to write the final version in one fell swoop. For example, you might start by setting up a loop and simply printing each name. If you get that to work, give yourself a little pat on the back and then see if you can simply print the positions of the space. Once you get that working, then try plugging the name and space positions into the larger message.
- Practice 1 Solution
Solution:
beatles = ["John Lennon", "Paul McCartney", "Ringo Starr", "George Harrison"] for member in beatles: space = member.index(" ") print ("There is a space in " + member + "'s name at character " + str(space) + ".")Explanation:
Given the info on the index method in the instructions, the trick to this exercise is applying the method to each string in the list. To loop through all the items in a list, you use a for statement that ends with 'in <list variable>', which in this case was called beatles. Between the words 'for' and 'in', you insert a variable with a name of your choosing. That variable will store a different list item on each iteration through the loop.
In my solution, I called this loop variable member. The member variable takes on the value of "John Lennon" on the first pass through the loop, "Paul McCartney" on the second pass through the loop, etc. The position of the first space in the member variable is returned by the index method and stored in a variable called space.
The last step is to plug the group member's name and the space position into the output message. This is done using the string concatenation character (+). Note that the space variable must be converted to a string before it can be concatenated with the literal text surrounding it.
Convert the names to a "Last, First" format
Build on Exercise 1 by printing each name in the list in the following format:
Last, First
To do this, you'll need to find the position of the space just as before. To extract part of a string, you can specify the start character and the end character in brackets after the string's name, as in the following:
name = "James Franklin" print (name[6:14]) # prints Franklin
One quirky thing about this syntax is that you need to specify the end character as 1 beyond the one you really want. The final "n" in "Franklin" is really at position 13, but I needed to specify a value of 14.
One handy feature of the syntax is that you may omit the end character index if you want everything after the start character. Thus, name[6:] will return the same string as name[6:14] in this example. Likewise, the start character may be omitted to obtain everything from the beginning of the string to the specified end character.
- Practice 2 Solution
Solution:
beatles = ["John Lennon", "Paul McCartney", "Ringo Starr", "George Harrison"] for member in beatles: space = member.index(" ") last = member[space+1:] first = member[:space] print (last + ", " + first) # or doing away with the last and first variables # print (member[space+1:] + ", " + member[:space])Explanation:
Building on the first exercise, this script also uses a for loop to process each item in the beatles list. And the index method is again used to determine the position of spaces in the names. The script then obtains all characters after the space (member[space+1:]) and stores them in a variable called last. Likewise, it obtains all characters before the space (member[:space]) and stores them in a variable called first. Again, string concatenation is used to append the first name to the last name with a comma in between.
Convert scores to letter grades
Write a script that accepts a score from 1-100 as an input parameter, then reports the letter grade for that score. Assign letter grades as follows:
A: 90-100
B: 80-89
C: 70-79
D: 60-69
F: <60
- Practice 3 Solution
Solution:
import arcpy input = arcpy.GetParameterAsText(0) score = int(input) if score > 89: print ("A") elif score > 79: print ("B") elif score > 69: print ("C") elif score > 59: print ("D") else: print ("F")Explanation:
This exercise requires accepting an input parameter, so in my solution, I imported arcpy so that I could make use of the GetParameterAsText method. If you run this script directly in PyScripter, you can provide the input value for GetParameterAsText by going to Run > Configuration per file, checking the Command line options box, and entering a score in the Command line options text box. An alternative to using GetParameterAsText would be to use the Python function input() to read from the keyboard. Because the input parameter is being stored as a text string, I include an additional step that converts the value to an integer. While not critical in this instance, it's generally a good idea to avoid treating numeric values as strings when possible. For example, the number 10 treated as a string ("10") is considered less than 2 treated as a string ("2") because the first character in "10" is less than the first character in "2".
An 'if' block is used to print the appropriate grade for the inputted score. You may be wondering why all of the expressions handle only the lower end of the grade range and not the upper end. While it would certainly work to specify both ends of the range (e.g., 'elif score > 79 and score < 90:'), it's not really necessary if you evaluate the code as the interpreter would. If the user enters a value greater than 89, then the 'print "A"' statement will be executed and all of the other conditions associated with the if block will be ignored. Code execution would pick up with the first line after the if block. (In this case, there are no lines after the if block, so the script ends.)
What this means is that the 'elif score > 79' condition will only ever be reached if the score is not greater than 89. That makes it unnecessary to specify the upper end of the grade range. The same logic applies to the rest of the conditions in the block.
Create copies of a template shapefile
Imagine that you're again working with the Nebraska precipitation data from Lesson 1 and that you want to create copies of the Precip2008Readings shapefile for the next 4 years after 2008 (e.g., Precip2009Readings, Precip2010Readings, etc.). Essentially, you want to copy the attribute schema of the 2008 shapefile, but not the data points themselves. Those will be added later. The tool for automating this kind of operation is the Create Feature Class tool in the Data Management toolbox. Look up this tool in the Help system and examine its syntax and the example script. Note the optional template parameter, which allows you to specify a feature class whose attribute schema you want to copy. Also note that Esri uses some inconsistent casing with this tool, and you will have to call arcpy.CreateFeatureclass_management() using a lower-case "c" on "class." If you follow the examples in the Geoprocessing Tool Reference help, you will be fine.
To complete this exercise, you should invoke the Create Feature Class tool inside a loop that will cause the tool to be run once for each desired year. The range(...) function can be used to produce the list of years for your loop.
- Practice 4 Solution
Solution:
import arcpy try: arcpy.env.workspace = "C:\\Data\\" template = "Precip2008Readings.shp" for year in range(2009,2013): newfile = "Precip" + str(year) + "Readings.shp" arcpy.CreateFeatureclass_management(arcpy.env.workspace, newfile, "POINT", template, "DISABLED", "DISABLED", template) except: print (arcpy.GetMessages())Explanation:
This script begins by initializing the geoprocessing object, setting the workspace, and selecting the toolbox that holds the desired tool. A reference to the shapefile to be copied is then stored in a variable. Because the idea is to create a copy of that shapefile for each year from 2009-2012, a logical approach is to set up a for loop defined with bounds of 'in range(2009,2013)'. (Recall that the upper end of the range should be specified as 1 higher than the actual desired end point.)
Within the loop, the name of the new shapefile is put together by concatenating the number held in the loop variable with the parts before and after that are always the same. The CreateFeatureClass tool can then be called upon, supplying the desired workspace for the output feature class, its name, its geometry type, the feature class to use as the schema template, whether the feature class should be configured to store m and z values, and finally a spatial reference argument. Note from the documentation that the output feature class's spatial reference can be specified in a number of different ways and will be undefined if a spatial reference argument is not supplied. (It does not automatically take on the spatial reference of the schema template feature class.)Note also that the inconsistent casing CreateFeatureclass looks like a mistake on the part of Esri and is unfortunately required in this script.
Clip all feature classes in a geodatabase
The data for this practice exercise consists of two file geodatabases: one for the USA and one for just the state of Iowa. The USA dataset contains miscellaneous feature classes. The Iowa file geodatabase is empty except for an Iowa state boundary feature class.
Your task is to write a script that programmatically clips all the feature classes in the USA geodatabase to the Iowa state boundary. The clipped feature classes should be written to the Iowa geodatabase. Append "Iowa" to the beginning of all the clipped feature class names.
Your script should be flexible enough that it could handle any number of feature classes in the USA geodatabase. For example, if there were 15 feature classes in the USA geodatabase instead of three, your final code should not need to change in any way.
- Practice 5 Solution
Here's one way you could approach Lesson 2 Practice Exercise 5.
If you're new to programming, I encourage you to try figuring out a solution yourself before studying this solution. Then once you've given it your best shot, study the answer carefully. The video below (6:10) explains the script in more depth. Please note that the video uses the old Python 2 syntax with no parentheses (...) for the print statements. That is the only real difference to the Python 3 solution code you see below though.
#This script clips all feature classes in a file geodatabase import arcpy # Create path variables sourceWorkspace = "C:\\Data\\Lesson2PracticeExercise\\USA.gdb" targetWorkspace = "C:\\Data\\Lesson2PracticeExercise\\Iowa.gdb" clipFeature = "C:\\Data\\Lesson2PracticeExercise\\Iowa.gdb\\Iowa" # Get a list of all feature classes in the USA folder arcpy.env.workspace = sourceWorkspace featureClassList = arcpy.ListFeatureClasses() try: # Loop through all USA feature classes for featureClass in featureClassList: # Construct the output path outClipFeatureClass = targetWorkspace + "\\Iowa" + featureClass # Perform the clip and report what happened arcpy.Clip_analysis(featureClass, clipFeature, outClipFeatureClass) arcpy.AddMessage("Wrote clipped file " + outClipFeatureClass + ". ") print ("Wrote clipped file " + outClipFeatureClass + ". ") except: # Report if there was an error arcpy.AddError("Could not clip feature classes") print ("Could not clip feature classes") print (arcpy.GetMessages())Click for a transcript of "Explanation of Lesson 2 practice exercise" video. (6:10)PRESENTER: This video explains the solution to the Lesson 2 practice exercise in which we clip a bunch of feature classes from the USA geo database to the Iowa State boundary.
In line 3, we import the arcpy site package. This is required whenever we work with Esri tools or datasets.
In line 6, 7, & 8, we set up a series of string variables representing the locations on disk that we will work with during this script. Now, if you were going to go ahead and make a script tool out of this, you would eventually want to change these to arcpy.GetParameterAsText as described in the lesson text. However, while you're working on your script in PyScripter, you'll want to actually write out the full path so you can test and make sure that your script works. Once you're absolutely certain that your script works, then you can go ahead and insert arcpy.GetParameterAsText and then you can start making the script tool and test out the tool. So I recommend you keep those two parts separate, testing in PyScripter first and then making the tool.
In line 6 & 7, we set up the paths to some of the geodatabases will be working with. Line 6 is the USA geodatabase that contains the feature classes that we will clip. In line 7, is the Iowa geodatabase where we will put the output feature classes. Line 8 is the actual click feature, this is our cookie cutter feature that is the state boundary of Iowa.
Now, we've used the term workspace in the line six and seven variable names just to mean a folder or a geodatabase in which we're working. But there is an official workspace for arcpy which is the current folder or geodatabase in which arcpy is looking for items; and so, in line 11, we actually set the arcpy workspace to be the same path that we set up in line 6 which is USA, and when we start listing feature classes, that's going to be the workspace that we look in.
Line 12 is where we called the method to list feature classes and the question is, list feature classes where? And the answer is the workspace that we just set up in line 11, so it's going to default to look in arcpy's workspace for the feature classes it should list. And so, what we get out of this is a variable that we call featureClassList and it is a Python list of all the feature classes in the USA geodatabase. This is great now, because we know how to loop through a list, and we're going to do that here in just a minute.
In line 14, we begin a try block, so we'll try to run all this code from line 14 to 25. If for some reason there's a crash, the code will go down to line 27 and run the except block.
In line 17, we begin a for loop to try to go through each feature class in the list. And we create a new variable in line 17 called featureClass. So we say for featureClass in featureClassList. featureClass is just a name that we come up with for that variable. We could name it anything. In this case, it's pretty intuitive to call it featureClass.
Line 20 is setting up the output path. Now, one of our requirements was to append Iowa at the beginning of the output feature class name, and so we're doing a little bit of string manipulation using the plus sign to make a path. This particular string outClipFeatureClass is going to be created by concatenating the target workspace so— that's what you created up in line 7. So that will always be part of the output path. Then, we're going to explicitly add slash Iowa and then, on to that, we'll tack the name of the feature class that we're currently working with. This type of string concatenation to make a path is something that you will also need to do during your Lesson 2 project.
In line 23, we actually perform the clip by calling arcpy.Clip, and we need to put underscore analysis because we want to specify that we're using the Clip tool that's in the Analysis toolbox. Now, Clip, in this case, has three required parameters: the input feature class to be clipped so that's the current one that we're looping on right now—it was defined up in line 17. So that's the first parameter featureClass. The second parameter there is the clipFeature, and we defined that up in line 8. That's our cookie cutter feature what we'll be clipping all the rest of the feature classes to. And then, the third is the output path, and that's the path we just created back in line 20, and once we have all three of those things, we can run the Clip tool.
In lines 24 and 25, we report what happened, and we use a little bit of string concatenation there as well to make a custom message saying that exactly the path of the clipped file that we created. This will be useful in your project, as well. Now, this is an either/or scenario you would need to call either AddMessage or do a print statement in line 25. I just put both of them in here so you can see how they were both used. If you're going to go ahead and make a script tool out of this, that would go in your toolbox in ArcGIS, then you would use the approach in line 24, AddMessage. If you're just going to run this inside of PyScripter, then you would use line 25. If for some reason there were to be a crash in the above code, it would jump down to line 27 and run the except statement, and again we're using the approach of doing either an AddError, this is what you would use if you were making a toolbox, so that's line 30; or if you're just running in PyScripter, you can do line 31. And then, line 32 is another print statement you could do out of PyScripter. It actually gets the message from the Clip tool. The Esri geoprocessing tools report their own error messages, so if you were to have a problem that happened inside the Clip, you might be able to get it back this way by doing print arcpy.GetMessages.
Source: Sterling QuinnNotes
- The code above both prints messages and uses arcpy.AddMessage and arcpy.AddError to add geoprocessing messages. Normally, you would only put one or the other in your script. When you make a script tool, take out the print statements and add the arcpy.AddMessage and arcpy.AddError functions, so that your messages will appear in the ArcGIS tool results window.