2  Python basics

This chapter introduces the fundamental concepts and basic syntax of the Python programming language. All the code covered here is part of the standard Python language and does not require any additional packages or libraries. Thus the Python discussed here forms the foundation for not only analyzing data in Python, but also for writing any kind of Python code.

While the chapter covers several different key concepts and syntax of Python, we focus on a subset of features that are most central for data analysis, rather than covering the full range of Python’s capabilities. Becoming proficient in the basic Python covered in this chapter will be important as a basis for writing code in subsequent chapters, so make sure to practice and understand these concepts thoroughly.

Note

Two foundational Python tools — loops (for repeating an action many times) and writing your own functions — are intentionally not covered here. Because the array and table tools introduced in the next several chapters often let us operate on a whole dataset at once, we rarely need loops at first, so we postpone these topics until Chapter 8, once you’ve seen why they are useful. You can read the book straight through without them until that point.

By the end of this chapter, you should be comfortable with writing basic Python code, performing simple calculations, and understanding how Python represents and manipulates different types of data. These foundational skills will prepare you for more advanced topics in data analysis that are covered in the rest of the book.

2.1 Expressions

A Python expression is any piece of code that produces a value.. For example, the following is an expression that simply creates the number 21.

21
21

Similarly, an expression could be a series of mathematical operations that evaluate to a number. For example, if we want to add 5 plus 2 and then multiply the result by 6 we can write:

6 * (5 + 2) 
42

As mentioned above, the defining features of a python expression is that it produces a value. Expressions are one of the fundamental building blocks of data analysis and they will appear frequently throughout this book.

TipExercise

What would happen if we remove the parenthesis from the expression we ran above and instead run 6 * 5 + 2. See if you can predict what the result will be and then try it out in Python by running the code in a code cell and see if you get the result you predicted.

6 * 5 + 2
32

The result is 32, which makes sense because in the standard order of mathematical operations, multiplication occurs before addition so we multiply 6 * 5 and get 30, and then we add 2 to get 32.

2.1.1 Mathematical expressions

The expressions shown above were all “mathematical expressions” because they involve calculating numeric quantities. We can also write statements that will do operations on text and other types of data which we will describe more below. But first, let’s explore mathematical expressions a bit more. Below is a table of some of the mathematical operations that are part of Python:

Table 2.1: Python mathematical operators
Operation Symbol Example Result
Addition + 5 + 3 8
Subtraction - 10 - 4 6
Multiplication * 7 * 2 14
Division / 12 / 5 2.4
Exponentiation ** 3 ** 2 9
Remainder % 10 % 3 1
TipExercise

What is the remainder from dividing 365 by 7? Please write some Python code that produces the answer.

365 % 7
1

2.2 Syntax

Syntax is the set of rules that defines how Python code must be written. One can think of syntax as the grammar of the Python programming language. In order for Python to be able to run your code, it must use the correct syntax. If incorrect syntax is used, then one will get a “syntax error”, and the code will not run.

To illustrate this, let’s calculate the value of 8 squared (\(8^2\)) which hopefully you remember is equal to the value of 64. As shown Table 2.1, if we want to take a value x to the power y (i.e., to calculate \(x^y\)) we use the syntax x**y. So, if we wanted to calculate \(8^2\) we would write the following Python code:

8**2
64

Since we have written the correct syntax, the code runs and the result of 64 is calculated as expected.

However, if we accidentially put an extra space between the two * symbols, Python will not know how to interpret the expression and we will get a syntax error as shown below:

8* *2
  Cell In[6], line 1
    8* *2
       ^
SyntaxError: invalid syntax

When there is a syntax error, Python will print out SyntaxError and give you an indication where the syntax error has occurred using a ^ symbol.1 As we can see here, Python is trying to show that the syntax error has occurred due to the extra space between the * symbols.

The ability to be able to spot and fix syntax errors is a fundamental skill you will develop as become proficient in analyzing data in Python.

2.3 Assignment statements

An assignment statement is a line of code that is used to store a value in a named variable. We can then refer back to this variable name to retrieve the value we have stored.

To assign a value to a variable we use the = symbol. For example, the following code assigns the value 10 to the variable a:

a = 10

We can then refer back to the variable a later in our code to retrive the stored value. For example, if we just write a by itself on the last line of our Python code cell, it will print out the value stored in a.

a
10

As we can see, the value printed out is 10 which is the value we had previously stored in the name a.

If we were to assign the name a to another value, it will overwrite the previously stored value and a will store the new value.

a = 21
a
21

We can also do mathematical operations on values stored in variables, such as adding and multiplying variables together. For example, we can assign the variable h to store the value 24, and the variable d to store the value 7, and then we can multiply these together and store the result in the variable t.

h = 24
d = 7
t = h * d
t
168
TipExercise

In the above code we calculated t = h * d. Which of the following do you think will happen to the value stored in t if we change the value of h to 3? I.e., if we run the following code, what do you think it will print out?

h = 3
t
  1. The value of t will be change to be 21 (i.e., 7 * 3).
  2. The value of t will not change and will still contain 168.
  3. Something else will happen (e.g., Python will give an error).
h = 3
t
168

As you can see, the value of t did not change. This illustrates an important point that once a value is calculated and stored in a variable it will not change if the variable that were used as part of the calculation are updated!

2.3.1 Variable names

Variable names in Python must follow certain rules:

  • Must start with a letter (a-z, A-Z) or an underscore (_), but not a number.
  • Can contain letters, numbers, and underscores.
  • Cannot contain spaces or special characters (like @, #, $, etc.).
  • Cannot be a reserved Python keyword that are part of the Python language (like for, if, class, etc.).

If these rules are not followed, Python will produce a syntax error

It’s also important to use meaningful variable names. For example, t is technically a valid variable name but it is not descriptive, while total_hours is much clearer. Using meaningful names makes your code easier to read and understand.

TipExercise

The minimum wage in the United States in 2025 is $7.25. If someone works 40 hours per week for all 52 weeks in a year, what would there yearly earnings be if they are being paid the minimum wage? Please calculate this quantity by creating meaningful (i.e., easy to understand) object names for:

  1. The minimum wage amount
  2. The number of hours worked in a week
  3. The number of weeks in a year

Then calculate the total yearly wage and store this result in another meaningful object name, and print out the value stored in this last object.

Hint: Using underscores _ in your object names is highly encouraged to make them more meaningful/readable.

min_wage = 7.25
hours_worked_in_a_week = 40
weeks_in_a_year = 52 
yearly_min_wage_earnings = min_wage * hours_worked_in_a_week * weeks_in_a_year
yearly_min_wage_earnings
15080.0

2.4 Comments

Another very useful feature in Python is the ability to add comments to your code. Comments are lines in your code that are ignored by Python when your code runs. They are used to explain what your code is doing, make notes to yourself, or leave instructions for others who may read your code in the future.

In Python, you create a comment by starting the line with the # symbol. Anything after the # on that line will be treated as a comment and not executed.

For example:

# The code below calculates the number of seconds in a day
seconds_in_a_day = 60 * 60 * 24

seconds_in_a_day
86400

We will use comments extensively throughout this book to explain what code is doing and to make our code easier to understand. Adding clear comments is a good habit that will help both you and others who read your code in the future, so we strongly encourage you to add comments liberally for all code you write.

2.5 Functions (call expressions)

A function is a reusable piece of code that performs a specific task. You can think of a function as a “machine” that takes some input, does something with it, and then gives you an output.

Python comes with many built-in functions that you can use right away, and you can also load in additional functions in packages that other people have written. You can also write own functions, which is a topic we will discuss later in this book.

To use a function, you “call” it by writing its name followed by parentheses. If the function needs information to do its job, you put that information (called “arguments”) inside the parentheses.

For example, the abs() function take in a number and returns the absolute value of the number.

abs(-10)
10

Some functions can take in multiple arguments. When multiple arguments are provided, they are separated by commas within the parentheses. For example, the min() function can take several numbers and will return the smallest one:

min(10, 2, 87, 5, 90)
2

Another useful function is the print() function for displaying multiple pieces of information in a single Jupyter notebook code cell. By default, Jupyter will only display the result of the last line in a code cell. If you want to display multiple values or add custom messages, you can use the print() function.

For example, the code below will print the number 2 because print(2) is called. The number 3 will also be displayed as output because it’s the last expression in the cell. If print(2) was not used, only 3 would be displayed. The print() function is useful when you want to display multiple values from within a single cell or when you want to output values that are not on the last line.

# We need to call print() explicitly here to print the value of 
# 2 since it is not on the last line of the code cell

print(2)  



# The value of 3 will be printed here without needing to call 
# the print() function because it is the last line in the cell

3
2
3
TipExercise

Try using the print() function to display both a message and a value in the same output. For example, print the message “The answer is:” followed by the result of 6 * 7.

print("The answer is:")
6 * 7
The answer is:
42
# We can also print multiple pieces of text on a single line by 
# passing multiple arguments to the print() function: 

print("The answer is:", 6 * 7)
The answer is: 42

2.6 Data types

Python is able to process many different types of data, referred to as “data types”. So far, we have only explored numeric data. Let’s continue exploring numerical data in a little more detail and then we will go on to examine other types of data.

2.6.1 Numbers

Python uses two different formats to store numerical data known as “integers” and “floating-point numbers”.

  • Integers (int): Whole numbers without a decimal point, such as 5, -3, or 1000.
  • Floating-point numbers (float): Numbers that have a decimal point, such as 3.14, -0.5, or 2.0.

We can tell if a number is a floating point number (i.e., a “float”) by seeing if there is a decimal point at the end of the number when we print out the number.

# This is an integer, which we can tell becaues there is no decimal point
5
5
# Although we are dividing two integers, the result is a floating point number
# which we can tell becaues there is a decimal point

10/2
5.0

We can also use the type() function to check if a number is an integer or a floating point number.

# This is a floating point number

type(5.0)
float

When analyzing the data, usually it does not matter if Python is storing a number as an integer or a floating point number since Python does the math sensibly and converts between integers and floating point numbers as needed. However, internally Python is representing these numbers in quite different ways.

More importantly, one should be aware that there are some limitations to the way Python stores both integers and floats. In particular, both of these types of numbers are represented using a finite amount of memory, so there is a largest number integer that can be represented and a limit to the precision of floating-point numbers. For most practical purposes, these limits are very large, but you may encounter issues with extremely large numbers or with floating-point arithmetic where results are not exactly as expected due to rounding errors.

For example, if we multiply integers that are very large, we can get a ValueError which indicates that Python is running into problems representing the result as an integer.

# There is a limited size to integers (although the size is pretty large)

1234567 ** 890 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit

Similarly, if we try to create a floating-point number with too many decimal points the number will be truncated, although no error is given, so one needs to be careful if very high precision is needed in a calculation.

# There is a limited precision to floating point numbers so the last digits are truncated

.12345678901234567890123456789 
0.12345678901234568

We can also convert numbers between integers and floating point numbers using the int() and float() functions. When converting from a floating point number to an integer using the int() function, one needs to be aware that the decimal part of the number will be removed (i.e., rounded down to the closest integer)

# Convert an integer to a floating point number.  We can see the conversion worked because the number is printed with a decimal point. 

float(5)
5.0
# Convert a floating point number to an integer. Note that the decimal part of the number is removed

int(3.14159)
3

Finally, one should be aware that Python sometimes prints out numbers using scientific notation. Scientific notation is a way of writing very large or very small numbers more compactly, using the letter e to indicate “times ten to the power of.” For example, 2.5e6 means (2.5 ^{6}), or 2,500,000. Similarly, 3e-09 means (3 ^{-9}), or 0.000000003. Python will automatically use this notation when displaying numbers that are extremely large or small.

# The output is in scientific notation
30 / 4000000000 
7.5e-09
TipExercise

Take the square root of 12 and then square the result; i.e., calculate \((\sqrt{12})^2\). Does Python return the correct result?

Hint: Note that you can calculate the square root of a number by taking a number to the 1/2 power; i.e., \(\sqrt{12} = 12^{0.5}\)

(12**.5)**2
11.999999999999998

As you can see, there is slight imprecision here so we get a result of 11.999999999999998 rather than a value of 12.

2.6.2 Character strings

A character string (or simply “string”) is a sequence of characters that are used to represent text, such as words, sentences, or any other sequence of characters. Strings in Python are enclosed in either single quotes ('...') or double quotes ("..."). However, your string must start and end with the same quote type; i.e., if the string starts with a single quote it also ends with a single quote, and the same for double quotes.

The following are valid strings in Python:

'This is a valid Python string with single quotes'

"This is another valid Python string using double quotes."
'This is another valid Python string using double quotes.'

While using single or double quotes gives the same result, there are cases where it is natural to use one over the other. For example, if your string contains an apostrophe (single quote), it’s easier to use double quotes:

"This string contains an apostrophe: it's easy to read."
"This string contains an apostrophe: it's easy to read."

And if your string contains double quotes, it is easier to single quotes when creating your string:

'She said, "Hello, world!"'
'She said, "Hello, world!"'

We can also perform operations on strings, such as concatenation (joining strings together). For example, to join two strings together, you can use the + operator:

"water" + "mellon"
'watermellon'

Note that the + operator we have used to concatenate strings is the same + operator we used to add numbers. This illustrates an important principle that an operator can behave differently depending on the type of data it is used with. In Python, this is called “operator overloading.” For numbers, + performs addition, while for strings, it performs concatenation (joining the strings together).

TipExercise

Above we have seen that the + operator can behave differently depending on whether it is operating on numbers or strings. We have also seen that * operator is used to multiple two numbers together. Do you think that the * operator will also work on strings? Please write down, or say outloud whether you think the * operator will work on strings, then see if your prediction is correct by running the following code:

'ha' * 5
'ha' * 5
'hahahahaha'

As you can see, the * operator works on strings by repeating the string the specified number of times. In this case, 'ha' * 5 produces 'hahahahaha' (which is very amusing).

2.6.2.1 String conversions

We can also convert strings into numbers and numbers into strings. To convert strings into numbers we can again use the int() and float() functions, but this time we are passing a string as the argument to these functions.

int("42")      # Converts the string "42" to the integer 42
42
float("3.14")  # Converts the string "3.14" to the float 3.14
3.14

We can see that the output from running these functions are numbers since the output is not in quotes.

We can convert a number into a string using the str() function.

str(2.5)       # Converts the float 2.5 to the string "2.5"
'2.5'

We can see that the output from running this function is a string since the output is in quotes.

TipExercise

Do the following two lines of code produce the same result?

  • 10 + 20
  • int("10" + "20")

Explain your reasoning then try it in Python to verify your answer is correct.

print(10 + 20)

print(int("10" + "20"))
30
1020

As we can see, the result of running these two pieces of code are different. The first line of code produce the value of 30 since we are simply adding the integers 10 and 20 together.

The second line of code first concatenates the strings "10" and "20" together to create the string "1020" and then converts it to the integer 1020, which is clearly different from the integer 30.

2.6.2.2 f-strings

An f-string (short for “formatted string literal”) is a way to embed the values of variables or expressions inside a string. To create an f-string, put the letter f before the opening quote, and then include curly braces {} around the variables or expressions you want to insert.

For example:

name = "Methuselah"
age = 969

f"My name is {name} and I am {age} years old."
'My name is Methuselah and I am 969 years old.'
TipExercise

Create three variables: name, age, and favorite_color, and assign them your own name, age and favorite color. Then, use an f-string to print a sentence like:
"My name is <name>, I am <age> years old, and my favorite color is <favorite_color>."

My solution (at the time of writing this book) is below.

name = "Ethan"
age = 45
favorite_color = "red"

f"My name is {name}, I am {age} years old, and my favorite color is {favorite_color}."
'My name is Ethan, I am 45 years old, and my favorite color is red.'

2.6.2.3 String methods

A method is a function that is attached to a piece of data 2. There are a number of “string methods” which allow you to perform specific operations on strings, such as changing their case, finding substrings, or replacing text.

You call a method by writing the string (or variable containing a string), followed by a dot (.), the method name, and parentheses. For example, the .upper() method returns a copy of the string with all letters converted to uppercase:

"hello".upper()
'HELLO'

Here is a table of some particularly useful string methods. Each method returns a new string that is modified as described below.

Method Example Description Result
.upper() "hello".upper() Converts all characters to uppercase 'HELLO'
.lower() "HELLO".lower() Converts all characters to lowercase 'hello'
.strip() " hello ".strip() Removes leading and trailing whitespace 'hello'
.replace(a, b) "ha".replace("a", "o") Replace all occurrences of a with b 'ho'
.count(x) "banana".count("a") Counts the number of occurrences of x 3
.zfill(n) "42".zfill(5) Pads the string with zeros to reach length n '00042'
.find(x) "hello".find("e") Returns the index of the first occurrence of x 1
TipExercise

Suppose we have the string my_sentence = "The quick brown fox jumps over the lazy dog". Please use string methods to do the following: 1. Count how many times the letter e appears in this sentence. 2. Find the index of the first occurance of the letter z.

my_sentence = "The quick brown fox jumps over the lazy dog"

print(my_sentence.count("e"))

my_sentence.find("z")
3
37
TipExercise

Suppose again we have the string my_sentence = "The quick brown fox jumps over the lazy dog". Please use the .replace() method to replace the word “dog” with the word “canine”. Does the string in the my_sentence variable change? If not, how could you update the string in the my_sentence variable to so that it contains the string “The quick brown fox jumps over the lazy canine”?

my_sentence = "The quick brown fox jumps over the lazy dog"

print(my_sentence.replace("dog", "canine"))

# notice that the variable my_sentence still has the original string
print(my_sentence)

# to update the string in the variable my_sentence we can do the following
my_sentence = my_sentence.replace("dog", "canine")

print(my_sentence)
The quick brown fox jumps over the lazy canine
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy canine

2.6.3 Booleans

A Boolean is a data type that can have only two possible values: True or False. Booleans are used to represent truth values and are very useful for making decisions in your code.

You can create Boolean values directly by writing True or False (note the capital letters):

# Create the Boolean True
True
True

The Boolean True is also the same as the integer 1, the Boolean False is the same as the integer 0. This means we can do arithmetic on Booleans such as:

True + False + True
2

We will use the fact that Booleans can be treated the integers 1 and 0 later in some of our analyses.

2.7 Comparisons

Comparison operators are used to compare values and produce Boolean results. For example, we can assess whether one number is greater than another number:

5 > 3
True

If we want to compare whether two values are the same, we use two equal signs ==. For example, we can see that indeed strings that are created using single quotes are the same as strings created using double quotes by running the following code.

"Octothorpe" == 'Octothorpe'
True

Here are some common comparison operators:

Python comparison operators
Operator Description Example Result
== Equal to 5 == 5 True
!= Not equal to 5 != 3 True
> Greater than 7 > 2 True
< Less than 3 < 1 False
>= Greater than or equal to 4 >= 4 True
<= Less than or equal to 2 <= 5 True
TipExercise

Is the string "99" equal to the integer 99 in Python? Also is 1 equal to True? Use the equal to operator (==) to do these comparisons and see what result you get.

print("99" == 99)

1 == True
False
True

As we can see, the string "99" is not equal to the integer 99 which once again showing strings and integers are not the same thing.

Conversely, the integer 1 is equal to the Boolean True again showing that these are identical.

2.8 Data structures

Python provides several built-in data structures that allow you to store and organize collections of data. The most common ones are:

  • Lists: Ordered, mutable collections of items.
  • Tuples: Ordered, immutable collections of items.
  • Dictionaries: Unordered collections of key-value pairs.

We will introduce each of these data structures in the following sections.

2.8.1 Lists

A list is an ordered collection of items that can be changed. Lists can contain any type of data, including numbers, strings, or even other lists. Lists are created by placing items inside square brackets [], separated by commas.

For example:

my_list = [1, 2, 3, "a", "b", "c", True]

my_list
[1, 2, 3, 'a', 'b', 'c', True]

We can access individual items in a list by their position (called the “index”) using square brackets. In Python, indexing starts at 0, so the first item is at index 0, the second at index 1, and so on.

For example, to get the first item in my_list, we would use:

my_list[0]
1

This returns 1 since the first element in the list (i.e., the element at position 0) is the integer 1.

Likewise, we can get the 6th element (remembering that indexing starts at 0) using:

my_list[5]
'c'

We can also change the value of an item in a list by assigning a new value to a specific index. For example, my_list[0] = 100 will change the first item in the list to 100.

my_list[0] = 100

my_list
[100, 2, 3, 'a', 'b', 'c', True]

The fact that we can change items of a list is what makes lists “mutable.” This means you can update, add, or remove elements after the list has been created.

List Methods

Lists also have a number of useful methods which we will use throughout this book including:

Method Example Description Result / Effect
.append(x) my_list.append(7) Adds item x to the end of the list [... , 7]
.index(x) my_list.index('b') Returns index of first occurrence of x Index of 'b'
.count(x) my_list.count(3) Counts occurrences of item x Number of times 3 appears

For example, we can use the append() method to adds new items to the end of a list (which again illustrates that lists are mutable):

my_list.append("zzz")

my_list
[100, 2, 3, 'a', 'b', 'c', True, 'zzz']
TipExercise

Suppose we have a list of numbers: my_numbers = [5, 10, 15, 5, 20]. Please use Python do the following:

  1. From just looking at the list, write down what you think the index of the number 15 is in the list. Then check your answer by running the code to find the index of 15.

  2. Use Python to count how many times the number 5 appears in the list.

  3. Add the number 25 to the end of the list.

  4. Change the first element of the list to 1.

  5. Print the updated list to see the changes.

# The list
my_numbers = [5, 10, 15, 5, 20]

# Find the index of 15
index_of_15 = my_numbers.index(15)  # Find the index of 15
print(index_of_15)

# Count how many times 5 appears
count_of_5 = my_numbers.count(5)  # Count how many times 5 appears
print(count_of_5)


# Modify the list and print it out
my_numbers.append(25)  # Add 25 to the end of the list
my_numbers[0] = 1      # Change the first element to 1
print(my_numbers)  # Print the updated list to see the changes
2
2
[1, 10, 15, 5, 20, 25]

2.8.2 Tuples

A tuple is similar to a list in that it is an ordered collection of items, but unlike lists, tuples are immutable—meaning their contents cannot be changed after creation. Tuples are created by placing items inside parentheses (), separated by commas.

my_tuple = (8, 9, "y", "z", False)

my_tuple
(8, 9, 'y', 'z', False)

Similar to lists, we can access individual items in a tuple using square bracket indexing. For example, my_tuple[2] will return the third element of the tuple (remember, indexing starts at 0):

my_tuple[2]
'y'

However, unlike lists, tuples are “immutable” meaning we can not cannot change the values stored in the tuple after they are created. In particular, if you try to assign a new value to an element of a tuple, Python will produce an error:

my_tuple[0] = 100
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[55], line 1
----> 1 my_tuple[0] = 100

TypeError: 'tuple' object does not support item assignment
TipExercise

Create a tuple named a_tuple with the numbers 10, 7, 32, 5, 81, and 12. Then, retrieve the third element of the tuple and print it out.

a_tuple = (10, 7, 32, 5, 81, 12)

a_tuple[2]
32

2.8.3 Dictionaries

A dictionary is data structure is useful for storing data in a way that allows you to quickly look up values based on a specific key; i.e., -you can think of a dictionary as a “lookup table,” where you use a key to quickly find the value associated with it.

Key Value
name Alice
age 30
city New York

We can do this in Python using:

my_dict = {"name": "Alice", "age": 30, "city": "New York"}

my_dict
{'name': 'Alice', 'age': 30, 'city': 'New York'}

Note: Keys in a dictionary must be unique and are typically strings or numbers. Values can be of any data type, including numbers, strings, lists, or even other dictionaries.

Accessing Values:

You can access the value associated with a key by using square brackets [] with the key inside:

print(my_dict["name"])  # Output: Alice
print(my_dict["age"])   # Output: 30

If you try to access a key that doesn’t exist, Python will raise a KeyError.

Adding or Modifying Key-Value Pairs:

Dictionaries are mutable, meaning you can change them after they are created. You can add a new key-value pair to a dictionary or modify an existing one by assigning a value to a key:


# Adding a new key-value pair
my_dict["occupation"] = "Engineer"
print(my_dict)

# Modifying an existing value
my_dict["city"] = "San Francisco"
print(my_dict)
TipExercise

Create a dictionary to store the number of legs for different animals (e.g., ‘dog’: 4, ‘spider’: 8, ‘ant’: 6). Then, add a new animal, ‘cat’, with 4 legs to the dictionary. Finally, print the number of legs for ‘spider’.

# Create the animal_legs dictionary
animal_legs = {
    "dog": 4,
    "spider": 8,
    "ant": 6
}
print(f"Original dictionary: {animal_legs}")

# Add 'cat' with 4 legs
animal_legs["cat"] = 4
print(f"Dictionary after adding 'cat': {animal_legs}")

# Print the number of legs for 'spider'
print(f"A spider has {animal_legs['spider']} legs.")

2.8.4 Sequences

In Python, a sequence refers to an ordered collection of items. Several of the data structures we have alreayd seen, including lists, tuples, and strings, are all sequences. When data is stored in a sequence, we can operate on the data in a consistent manner.

Let’s explore some operations that can be performed on any sequence. We’ll use a list as an example, but these apply to strings and tuples as well.

my_list_sequence = [10, 20, 30, 40, 50]
my_string_sequence = "Hello"

Common Sequence Operations:

  1. Indexing: Accessing an item by its position. Indexing starts from 0 for the first item.

    print(my_list_sequence[0])  # Output: 10
    print(my_string_sequence[1]) # Output: 'e'
  2. Slicing: Extracting a part of the sequence. Slicing my_sequence[start:end] extracts items from start up to (but not including) end.

    print(my_list_sequence[1:3])  # Output: [20, 30] (items at index 1 and 2)
    print(my_string_sequence[0:2]) # Output: 'He' (characters at index 0 and 1)
  3. Length (len() function): Getting the number of items in a sequence.

    print(len(my_list_sequence))  # Output: 5
    print(len(my_string_sequence)) # Output: 5
    5
    5
  4. Concatenation (+ operator): Combining two sequences of the same type.

list1 = [1, 2]
list2 = [3, 4]
combined_list = list1 + list2
print(combined_list)  # Output: [1, 2, 3, 4]

string1 = "Py"
string2 = "thon"
combined_string = string1 + string2
print(combined_string) # Output: 'Python'
[1, 2, 3, 4]
Python

Note: You cannot concatenate sequences of different types directly (e.g., a list and a string).

  1. Repetition (* operator): Repeating a sequence a certain number of times.
repeated_list = [0, 1] * 3
print(repeated_list)  # Output: [0, 1, 0, 1, 0, 1]

repeated_string = "Ja" * 7    # Laughing in spanish
print(repeated_string) # Output: 'JaJaJaJaJaJa'
[0, 1, 0, 1, 0, 1]
JaJaJaJaJaJaJa
  1. Sorting values: The sorted() function sorts values in sequence in ascending order and return a new list that has the sorted values. Note that this does not change the original sequence, but rather returns a new sorted list.
unsorted_list = [3, 1, 4, 2]
sorted_list = sorted(unsorted_list)
print(sorted_list)  # Output: [1, 2, 3, 4]

unsorted_string = "banana"
sorted_characters = sorted(unsorted_string)
print(sorted_characters)  
[1, 2, 3, 4]
['a', 'a', 'a', 'b', 'n', 'n']

These operations provide powerful ways to manipulate and work with ordered data in Python.

TipExercise

Given a list numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: 1. Extract the sub-list [3, 4, 5] using slicing and store it in a variable called sub_list. 2. Create a new list called doubled_sub_list by concatenating sub_list with itself. 3. Print sub_list and doubled_sub_list.

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 1. Extract the sub-list [3, 4, 5]
sub_list = numbers[3:6]
print(f"Sub-list: {sub_list}")

# 2. Create a new list by concatenating sub_list with itself
doubled_sub_list = sub_list + sub_list
print(f"Doubled sub-list: {doubled_sub_list}")

Note: dictionaries are not sequences because their items are not stored in a specific order and they are accessed by keys rather than by position (index).

2.9 Summary

In this chapter, we introduced the fundamental concepts and basic syntax of Python, focusing on the core features most relevant for data analysis. You learned about expressions, assignment statements, variable naming, comments, functions, and the main data types: numbers, strings, and Booleans. We also covered comparison operators and introduced basic data structures such as lists, tuples, and dictionaries. Mastering these basics will provide a strong foundation for more advanced topics in Python and data science. We strongly encourage you to practice everything we covered in this chapter by completing the exercises that are in the text and the exercises below, and make sure that you understand all of the solutions that are given. We will use the material covered in this chapter throughout the rest of the book, so we highly recommend that you become comfortable with this material before preceeding to the rest of the book.

2.10 Code summary

The functions and methods introduced in this chapter.

Types and conversion

Code What it does
type(x) Reports what kind of value x is
int(x), float(x), str(x) Converts a value to a whole number, a decimal number, or text

Numbers

Code What it does
abs(x) The distance of a number from zero, ignoring its sign
min(a, b, c) The smallest of the values given
print(x) Displays a value, which a script needs in order to show anything

Text

Code What it does
f"text {value}" Builds a string with the value of a variable inserted into it
s.upper(), s.lower() A copy of the string in upper or lower case
s.strip() A copy with blank space removed from both ends
s.replace(old, new) A copy with every occurrence of one piece of text swapped for another
s.split(sep) Breaks a string into a list, cutting it wherever sep appears
s.find(sub) The position where sub first appears, or -1 if it never does
s.count(sub) How many times sub appears

Lists, tuples and dictionaries

Code What it does
[1, 2, 3] Creates a list
(1, 2, 3) Creates a tuple, which works like a list but cannot be changed
{"a": 1} Creates a dictionary, which looks values up by key rather than by position
len(x) How many items a list, tuple, dictionary or string holds
x[0], x[-1] The first and last item, counting from zero
x[1:4] A slice, from position 1 up to but not including 4
my_list.append(v) Adds a value to the end of a list, changing the list itself
my_list.count(v) How many times a value appears
my_list.index(v) The position of the first occurrence of a value
sorted(x) A new list with the items in order, leaving the original alone

2.11 Exercises

2.11.1 Warm-up exercises

TipExercise

Try to predict which of the following lines of code produce errors, and try to explain why you believe they will produce errors. Then run these lines in Python to see if your predictions were correct.

  1. 5 > = 2
  2. True**5
  3. "The cat" == "The Cat"
  1. 5 > = 2 will produce a SyntaxError. The correct operator is >= (greater than or equal to), not > = with a space.

    # 5 >= 2  # This would be True
  2. True**5 will evaluate to 1. True is treated as 1 in arithmetic operations, so 1**5 is 1.

    print(True**5)
    1
  3. "The cat" == "The Cat" will evaluate to False because strings are case-sensitive. The ‘c’ in “cat” is lowercase, while ‘C’ in “Cat” is uppercase.

    print("The cat" == "The Cat")
    False
TipExercise

Try to predict what the following line of code will evaluate to. Then run it in Python to see if your prediction was correct. Explain the result.

True + True + True == 3
print(True + True + True == 3)
True

The code evaluates to True. In Python, True is equivalent to the integer 1 and False is equivalent to 0. So, True + True + True is the same as 1 + 1 + 1, which is 3. Then, 3 == 3 is True.

TipExercise

If you run the code "apple" > "banana", it will return False. Why is this the case? Experiment with different strings and see if you can figure out how string comparison works.

String comparison in Python is done lexicographically (alphabetical order). This means Python compares strings character by character, which corresponds to their alphabetical order.

In "apple" > "banana": - Python first compares ‘a’ (from “apple”) with ‘b’ (from “banana”). - Since ‘a’ comes before ‘b’ alphabetically, “apple” is considered “less than” “banana”. - Therefore, "apple" > "banana" is False.

You can test this with other strings:

print("zebra" > "apple") #  True, because lowercase 'z' comes after lowercase 'a'
print("car" > "cat")  # False, because 'r' comes before 't' in alphabetic order
print("Apple" > "apple") # False because lowercase letters come before upper case letters
True
False
False

This shows that string comparisons are case-sensitive as well.

TipExercise
  1. Create two variables, num1 and num2, and assign them the values 15 and 4 respectively.
  2. Calculate their sum, difference, product, and quotient (division). Store each result in a separate variable.
  3. Try to predict whether each of the variables is a float or an int. Then check your answers using the int() and float() functions.
# 1. Create variables
num1 = 15
num2 = 4

# 2. Perform calculations
sum_result = num1 + num2
difference_result = num1 - num2
product_result = num1 * num2
quotient_result = num1 / num2

# 3. Print results
print(f"Sum: {sum_result} is an {type(sum_result)}")
print(f"Difference: {difference_result} is an {type(difference_result)}")
print(f"Product: {product_result} is an {type(product_result)}")
print(f"Quotient: {quotient_result} is an {type(quotient_result)}")
Sum: 19 is an <class 'int'>
Difference: 11 is an <class 'int'>
Product: 60 is an <class 'int'>
Quotient: 3.75 is an <class 'float'>

The quotient will be a float (3.75) because division / always results in a float. All the other results are integers.

TipExercise
  1. Create a list named colors containing the strings “red”, “green”, “blue”, and “yellow”.
  2. Print the second element of the list.
  3. Change the third element of the list to “purple”.
  4. Print the entire updated list.
# 1. Create a list
colors = ["red", "green", "blue", "yellow"]

# 2. Print the second element (index 1)
# Remember that list indexing starts from 0
print(f"The second color is: {colors[1]}")

# 3. Change the third element (index 2) to "purple"
colors[2] = "purple"

# 4. Print the updated list
print(f"Updated list of colors: {colors}")
The second color is: green
Updated list of colors: ['red', 'green', 'purple', 'yellow']

2.11.2 Intermediate exercises

TipExercise
  1. Create a dictionary named user_profile with the following key-value pairs:
    • "name": “Alex”
    • "age": 28
    • "hobbies": A list containing “reading”, “hiking”, and “coding”
  2. Add a new key "city" with the value “Toronto” to the user_profile dictionary.
  3. Access the user’s name and their second hobby.
  4. Print a message using an f-string: “Alex’s second hobby is hiking.” using the values from the dictionary.
# 1. Create the dictionary
user_profile = {
    "name": "Alex",
    "age": 28,
    "hobbies": ["reading", "hiking", "coding"]
}

# 2. Add city
user_profile["city"] = "Toronto"
print(f"Updated profile: {user_profile}")

# 3. Access name and second hobby
user_name = user_profile["name"]
# Hobbies is a list, so we access its elements by index
second_hobby = user_profile["hobbies"][1]

# 4. Print the message
print(f"{user_name}'s second hobby is {second_hobby}.")
Updated profile: {'name': 'Alex', 'age': 28, 'hobbies': ['reading', 'hiking', 'coding'], 'city': 'Toronto'}
Alex's second hobby is hiking.
TipExercise

You are given a string: raw_data = " Product_ID:12345, ProductName:SuperWidget, Price:0050.75 ". Please do the following:

  1. Remove the leading and trailing whitespace from raw_data.

  2. Extract the Product_ID (including the number), ProductName (including the name), and Price (including the number) into separate variables. You might need string slicing and/or the .find() method.

  3. Convert the Price to a floating-point number.

  4. Print the extracted information in a formatted way, like:

    Product ID: 12345
    Product Name: SuperWidget
    Price: $50.75

    (Hint: You might need to use .replace() or other string methods to clean up the extracted parts before printing.)

raw_data = "  Product_ID:12345, ProductName:SuperWidget, Price:0050.75  "

# 1. Remove whitespace
cleaned_data = raw_data.strip()
print(f"Cleaned data: '{cleaned_data}'")

# 2. Extract parts
# Find positions of commas and colons to help with slicing
id_end = cleaned_data.find(",")
product_id_full = cleaned_data[0:id_end] # "Product_ID:12345"

name_start = id_end + 2 # Skip ", "
name_end = cleaned_data.find(",", name_start)
product_name_full = cleaned_data[name_start:name_end] # "ProductName:SuperWidget"

price_start = name_end + 2 # Skip ", "
price_full = cleaned_data[price_start:] # "Price:0050.75"

# Further extract actual values
product_id = product_id_full.split(":")[1]
product_name = product_name_full.split(":")[1]
price_str = price_full.split(":")[1]

# 3. Convert price to float
price_float = float(price_str)

# 4. Print formatted
print(f"Product ID: {product_id}")
print(f"Product Name: {product_name}")
print(f"Price: ${price_float:.2f}") # Format to 2 decimal places
Cleaned data: 'Product_ID:12345, ProductName:SuperWidget, Price:0050.75'
Product ID: 12345
Product Name: SuperWidget
Price: $50.75

This solution uses split(':') for simplicity after initial slicing. More robust parsing could use regular expressions, but that’s beyond this chapter. The .2f in the f-string formats the float to two decimal places.

2.11.3 Advanced exercises


  1. The reason this is a syntax error is because Python interprets a single * symbol as a multiplication symbol. Thus it is trying to multiply 8 by another multiplication symbol *, which gives an error since one can only multiply two numbers together.↩︎

  2. Or to be more precise, a method is a function attached to an object↩︎