x = 1
y = "GIS"
x1 = 2
y1 = "Remote Sensing"
_x = 3
_y = "Web GIS"
print(x)1
print(y)GIS
print(x1)2
print(y1)Remote Sensing
print(_x)3
print(_y)Web GIS
This first chapter of Open Python for Geospatial Professionals introduces the Python programming language for data science and geospatial data science. We assume that you have no prior experience with coding in general or Python specifically. If you already have a solid understanding of Python, feel free to skip this chapter or just skim it. With that said, we feel strongly that a foundational understanding of the language goes a long way.
For a more detailed discussion of general Python, please consult W3Schools, which is a great resource for coders, scientists, and web developers.
We think of variables as links to information or data. For example, a variable could reference a file path on your local machine, such as a path to a video file, or a set of numbers. Once you create a variable, you can call it to reference the associated data or object for use in processes and analyses. There are a few rules for variable names. Variable names:
x1 is valid while 1x is not).x, x1, _x, and x1_ are all valid while x$ is not).x1 and X1 are two separate variables).The print() function is used to print the data referenced by the variable.
x = 1
y = "GIS"
x1 = 2
y1 = "Remote Sensing"
_x = 3
_y = "Web GIS"
print(x)1
print(y)GIS
print(x1)2
print(y1)Remote Sensing
print(_x)3
print(_y)Web GIS
You can also assign data to multiple variables in a single line of code, as demonstrated below. Note that we are reusing variable names. In Python, variable names are dynamic, so you can overwrite them. This can, however, be problematic if you overwrite a variable accidentally. So, use unique names if you do not want to overwrite prior variables.
print(x)1
x, x1, _x = 1, 2, 3
print(x)1
print(x1)2
print(_x)3
Assignment operators are used to assign values or data to variables. The most commonly used operator is =. However, there are some other options that allow variables to be manipulated mathematically with the resulting value saved back to the original data object that the variable references. These additional assignment operators can be useful, but we will use the = operator most of the time.
For example, += will add a value to the current value and assign the result back to the original variable. In the first example below, x references the value 2. The += assignment operator is then used to add 3 to x and save the result back to x. Work through the provided examples and make sure you understand the use of each operator.
x = 2
print(x)2
x += 3
print(x)5
x = 2
print(x)2
x -= 3
print(x)-1
x = 2
print(x)2
x *= 3
print(x)6
x = 2
print(x)2
x /= 3
print(x)0.6666666666666666
x = 2
print(x)2
x **= 3
print(x)8
We now want to step through a simple experiment that explains some important behavior of the Python language. In the code below, we define a variable a that holds a list of three values. We will discuss lists later in this chapter. We then create a new variable b and set it equal to a. Next, we edit a by appending a new value to the list (we will discuss how this is done later, so don’t worry if you don’t understand how this works yet). When we print a and b, you can see that both variables contain the same set of numbers even though we added 8 to a after setting b equal to a. In other words, the change that we made to a was also applied to b.
In Python, certain types of objects, such as lists, are mutable. This means that it is possible to change the data stored in memory and referenced by the variable. When a mutable object is altered, all variables that point to it will reflect this change. What this means practically is that setting b equal to a results in a and b pointing to the same object or data in memory. So, changes to a or b will be reflected in both variables since the data being referenced by both have been updated.
If you are coming to Python from R, this behavior may be surprising. In R, setting a variable equal to another variable results in a copy that is not linked and can be altered without changing the original variable.
To test whether two variables reference the same object in memory, you can use the is keyword. If True is returned, then they reference the same object. You can also print the object ID, which represents the memory address for the object, using the id() function. Using both methods below, you can see that a and b reference the same object.
a = [5, 6, 7]
b = a
a.append(8)
print(a)[5, 6, 7, 8]
print(b)[5, 6, 7, 8]
print(a is b)True
print(id(a))2800823708864
print(id(b))2800823708864
What if you wanted to make a copy of a variable referencing mutable data that does not reference the same object? For example, you may want to be able to make changes to a that do not impact b. This can be accomplished using the copy() or deepcopy() functions from the copy module.
In the experiment below, we define b as a deep copy of a. Now, changes made to a do not impact b. This is because they do not reference the same object in memory since deepcopy() makes a copy of the object or data to a new location in memory. This is confirmed using is and id().
import copy
a = [5, 6, 7]
b = copy.deepcopy(a)
a.append(8)
print(a)[5, 6, 7, 8]
print(b)[5, 6, 7]
print(a is b)False
print(id(a))2800823786048
print(id(b))2800823777216
A variety of data types are available in Python to store and work with a variety of input. Below are explanations of the data types that you will use most often. There are additional types that we will not discuss.
When creating a variable, it is not generally necessary to explicitly define the data type. However, this can be accomplished using constructor functions if so desired. Constructor functions can also be used to change the data type of a variable, a process known as casting. Available constructor functions include str(), int(), float(), complex(), list(), tuple(), dict(), set(), and bool().
To determine the data type, you can use the type() function. In the example below, we convert an integer to a float and then a float to a string.
True or False
#Create a variable and check the data type
x = 1
print(type(x))<class 'int'>
#Change the data type
x = float(x)
print(type(x))<class 'float'>
x= str(x)
print(type(x))<class 'str'>
Regardless of the type (integer, float, or complex), numbers are defined without using quotes. If a number is placed in quotes, it will be treated as a string, as demonstrated below. This is important since the behavior of the data is altered. In the example, x represents 1 as a number while y represents "1" as a string (note the quotes). Adding x to itself will yield 2 (1 + 1). Adding y to itself will yield "11"; in other words, the two strings are combined or concatenated.
#Create variables
x = 1
y = "1"
print(x + x)2
print(y + y)11
Numbers support mathematical operations, as demonstrated below. If you are not familiar with these concepts, modulus will return the remainder after division while floor division will round down to the nearest whole number after division.
If a whole number is printed with a period or decimal value (1. or 1.0 as opposed to 1), this indicates that the output is in the float data type as opposed to the integer type. For example, division always returns a float, even when the result is a whole number.
x = 4
y = 3
print(x + y) #Addition7
print(x - y) #Subtraction1
print(x * y) #Multiplication12
print(x / y) #Division1.3333333333333333
print(x % y) #Modulus1
print(x ** y) #Exponentiation64
print(x // y) #Floor Division1
Strings are defined using single or double quotes. If quotes are included as part of the text or string, then you can use the other type to define the data as text. Again, numbers placed in quotes will be treated as strings.
x = "GIS"
y = "That's great" #Must use double quotes since a single quote is used in the string
z = "2" #Number treated as a stringPortions of a string can be sliced out using indexes. Note that Python starts indexing at 0 as opposed to 1. So, the first character is at index 0 as opposed to index 1. Negative indexes can be used to index relative to the end of the string. In this case, the last character has an index of -1.
Indexes combined with square brackets can be used to slice strings. Note that the last index specified in the selection or range will not be included and that spaces are counted in the indexing.
x = "Geography 350: GIScience"
print(x[0:9])Geography
print(x[10:13])350
print(x[15:25])GIScience
print(x[-14:-11])350
Strings can be combined or concatenated using the addition sign. If you want to include a number in the string output, you can cast it to a string using str(). In the example below, note the use of blank spaces so that the strings do not run together.
The len() function can be used to return the length of the string, which will count blank spaces along with characters.
x = "Geography"
y = 350
z = ":"
w = "GIScience"
strng1 = x + " " + str(y) + z + " " + w
print(strng1)Geography 350: GIScience
print(len(strng1))24
A method is a function that belongs to or is associated with an object. In other words, it allows you to work with or manipulate the object and its associated properties in some way. Data types have default methods that can be applied to them.
Methods applicable to strings are demonstrated below. Specifically, methods are being used to change the case and to split the string at each space to save each component to a list.
x = "Geography 657: Remote Sensing Principles"
print(x.upper())GEOGRAPHY 657: REMOTE SENSING PRINCIPLES
print(x.lower())geography 657: remote sensing principles
lst1 = x.split(" ")
print(lst1)['Geography', '657:', 'Remote', 'Sensing', 'Principles']
When generating strings, issues arise when you use characters that have special uses or meaning in Python. These issues can be alleviated by including an escape character or backslash, as demonstrated below.
s1 = "Issue with \"quotes\" in the string."
s2 = "C:\\data\\project_1" #Issue with file paths.
s3 = "Add a new line \nto text string"
print(s1)Issue with "quotes" in the string.
print(s2)C:\data\project_1
print(s3)Add a new line
to text string
Booleans can only be True or False and are often returned when an expression is logically evaluated. A variety of comparison operators are available. Note the use of double equals; a single equals cannot be used since it is already used for variable assignment (it is an assignment operator) and would thus be ambiguous.
==
!=
>
>=
<
<=
Logical statements or multiple expressions can be combined using logical operators.
and
or
not
x = 3
y = 7
z = 2
print(x == 7)False
print(x > y)False
print(x < y)True
print(x < y and x > z)True
print(x < y and x < z)False
print(x < y or x < z)True
You can also assign Booleans to a variable. Note that you do not use quotes, as that would cause the text to be treated as a string instead of a Boolean.
x = "True"
y = True
print(type(x))<class 'str'>
print(type(y))<class 'bool'>
Lists allow you to store multiple numbers, strings, or Booleans in a single variable. Square brackets are used to denote lists.
Items in a list are ordered, indexed, and allow for duplicate members. Indexing starts at 0. If counting from the end, you start at -1 and subtract as you move left. A colon can be used to denote a range of indexes. An empty argument before the colon indicates to select all elements up to the index following the colon while an empty argument after the colon indicates to select the element at the index specified before the colon and all elements up to the end of the list. The element at the last index is not included in the selection.
Python lists can contain elements of different data types.
lst1 = [6, 7, 8, 9, 11, 2, 0]
lst2 = ["A", "B", "C", "D", "E"]
lst3 = [True, False, True, True, True, False]
print(lst1[0])6
print(lst1[0:3])[6, 7, 8]
print(lst2[-4:-1])['B', 'C', 'D']
print(lst2[:3])['A', 'B', 'C']
print(lst2[3:])['D', 'E']
lst4 = [1, 2, "A", "B", True]
print(type(lst4[0]))<class 'int'>
print(type(lst4[2]))<class 'str'>
print(type(lst4[4]))<class 'bool'>
When the len() function is applied to a list, it will return the number of items or elements in the list as opposed to the number of characters. When applied to a string item in a list, this function will return the length of the string.
lst1 = ["A", "B", "C", "D", "E"]
print(len(lst1))5
print(len(lst1[0]))1
The code below shows some example methods for lists.
lst1 = ["A", "B", "C", "D", "E"]
lst1.append("F") #Add item to list
print(lst1)['A', 'B', 'C', 'D', 'E', 'F']
lst1.remove("F") #Remove item from a list
print(lst1)['A', 'B', 'C', 'D', 'E']
lst1.insert(2, "ADD") #Add item to list at defined position
print(lst1)['A', 'B', 'ADD', 'C', 'D', 'E']
lst1.pop(2) #Remove item at specified index or the last item if no index is provided
print(lst1)['A', 'B', 'C', 'D', 'E']
As explained above, in order to copy a list and not just reference the original data object, you must use the copy() method or the deepcopy() function. Simply setting a new variable equal to the original list will cause it to reference the original data object, so changes made to the old list will also appear in the new list. This is demonstrated in the example below.
lst1 = ["A", "B", "C", "D", "E"]
lst2 = lst1
lst3 = lst1.copy()
print(lst2)['A', 'B', 'C', 'D', 'E']
print(lst3)['A', 'B', 'C', 'D', 'E']
lst1.append("F")
print(lst2)['A', 'B', 'C', 'D', 'E', 'F']
print(lst3)['A', 'B', 'C', 'D', 'E']
Lists can be concatenated together, or a list can be appended to another list, using the methods demonstrated below.
lst1 = ["A", "B", "C"]
lst2 = ["D", "E", "F"]
lst3 = lst1 + lst2
print(lst1)['A', 'B', 'C']
print(lst2)['D', 'E', 'F']
print(lst3)['A', 'B', 'C', 'D', 'E', 'F']
lst1.extend(lst2)
print(lst1)['A', 'B', 'C', 'D', 'E', 'F']
Lastly, lists can contain other lists, tuples, or dictionaries, which will be discussed below. In the example, lst2 contains four elements, the last of which is a list with three elements.
lst1 = ["A", "B", "C"]
lst2 = ["D", "E", "F", lst1]
print(lst2)['D', 'E', 'F', ['A', 'B', 'C']]
Tuples are similar to lists in that they are ordered and allow duplicate elements. However, they cannot be altered by adding items, removing items, or changing the order of items. To differentiate them from lists, parentheses are used as opposed to square brackets. We generally think of tuples as lists that are protected from alteration, so we tend to use them when we want to make sure we don’t accidentally make changes.
If you need to change a tuple, it can be converted to a list, manipulated, then converted back to a tuple.
t1 = (1, 3, 4, 7)
print(type(t1))<class 'tuple'>
A set is similar to a tuple or list. However, elements are unordered, not indexed, and no duplicate elements are allowed. Sets are defined using curly brackets. Since no indexing is included, elements cannot be selected using associated indexes.
We find that we rarely use sets.
Dictionaries are ordered (as of Python 3.7), changeable, indexed by keys, and do not allow for duplicate keys. In contrast to lists, tuples, and sets, each value is also assigned a key. Elements can be selected using the associated key.
You can also use the key to define a value to change.
Similar to lists, you must use the copy() method or the deepcopy() function to obtain a copy of the dictionary that will not reference the original data or memory object.
cls = {"prefix" : "Geography", "Number" : 661, "Name": "Web GIS"}
print(cls){'prefix': 'Geography', 'Number': 661, 'Name': 'Web GIS'}
print(cls["Name"])Web GIS
cls["Number"] = 461
print(cls){'prefix': 'Geography', 'Number': 461, 'Name': 'Web GIS'}
Multiple dictionaries can be combined into a nested dictionary, as demonstrated below.
The keys can then be used to extract a sub-dictionary or an individual element from a sub-dictionary.
cls1 = {"prefix" : "Geography", "Number" : 150, "Name": "Digital Earth"}
cls2 = {"prefix" : "Geography", "Number" : 350, "Name": "GIScience"}
cls3 = {"prefix" : "Geography", "Number" : 455, "Name": "Introduction to Remote Sensing"}
cls4 = {"prefix" : "Geography", "Number" : 661, "Name": "Web GIS"}
clsT = {
"class1" : cls1,
"class2" : cls2,
"class3" : cls3,
"class4" : cls4
}
print(clsT){'class1': {'prefix': 'Geography', 'Number': 150, 'Name': 'Digital Earth'}, 'class2': {'prefix': 'Geography', 'Number': 350, 'Name': 'GIScience'}, 'class3': {'prefix': 'Geography', 'Number': 455, 'Name': 'Introduction to Remote Sensing'}, 'class4': {'prefix': 'Geography', 'Number': 661, 'Name': 'Web GIS'}}
print(clsT["class1"]){'prefix': 'Geography', 'Number': 150, 'Name': 'Digital Earth'}
print(clsT["class1"]["Name"])Digital Earth
Arrays are similar to lists; however, they must be declared. They are sometimes used in place of lists as they can be very compact and make it easy to apply mathematical operations. In this book, we will primarily work with NumPy arrays, which are discussed in more detail in Chapter 3.
Classes are used to define specific types of objects in Python and are often described as templates. Once a class is defined, it can be copied and manipulated to create a subclass, which will inherit properties and methods from the parent class but can be altered for specific uses. We will not explore this topic in detail in this chapter but will return to it in Chapter 2. You will also see example uses of classes in later chapters.
One use of classes is to define specialized data models and their associated methods and properties. For example, classes have been defined to work with digital map data.
Before moving on, we want to note which data types are mutable and which are immutable. Again, data or objects that are mutable can be altered after they are defined (such as adding a new element to a list).
Mutable types include lists, sets, and dictionaries.
Immutable types include Booleans, integers, floats, complex numbers, strings, and tuples.
Our goal here was to provide a basic introduction to Python. Again, W3Schools is a great resource for coders, scientists, and web developers if you want to explore additional examples and topics.
You likely do not feel comfortable with general Python yet. However, you will get practice while working through the remaining chapters. We think you will find that a good grasp of the basics can go a long way.
In Chapter 2, we discuss more components of the Python language including functions, control flow, loops, modules, and reading data from disk.
a is a list and you set b = a, why does changing a also change b? How can you create a copy of a that is independent of the original?int(), float(), and str()? What is casting?%) and floor division (//) operators.= and == in Python.
1.4 Comments
Comments are used to make your code more readable and are not interpreted by the computer. Instead, they are skipped and meant for humans. Different languages use different syntax to denote comments. Python uses the hashtag or pound sign (
#). You can add comments as new lines or following code on the same line. Python does not have specific syntax for multi-line comments. However, this can be accomplished by adding hashtags before each separate line or by using a multi-line string that is not assigned to a variable. Examples are shown below.It is generally a good idea to comment your code for later use and for use by others.