2121
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.
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.
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.
2121
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.
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 + 232
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.
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:
| 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 |
What is the remainder from dividing 365 by 7? Please write some Python code that produces the answer.
365 % 71
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**264
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* *2Cell 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.
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 = 10We 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.
a10
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
a21
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
t168
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
tt will be change to be 21 (i.e., 7 * 3).t will not change and will still contain 168.h = 3
t168
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!
Variable names in Python must follow certain rules:
@, #, $, etc.).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.
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:
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_earnings15080.0
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
32
3
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 * 7The 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
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.
Python uses two different formats to store numerical data known as “integers” and “floating-point numbers”.
int): Whole numbers without a decimal point, such as 5, -3, or 1000.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
55
# Although we are dividing two integers, the result is a floating point number
# which we can tell becaues there is a decimal point
10/25.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
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)**211.999999999999998
As you can see, there is slight imprecision here so we get a result of 11.999999999999998 rather than a value of 12.
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).
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).
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 4242
float("3.14") # Converts the string "3.14" to the float 3.143.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.
Do the following two lines of code produce the same result?
10 + 20int("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.
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.'
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.'
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 |
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
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
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
TrueTrue
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 + True2
We will use the fact that Booleans can be treated the integers 1 and 0 later in some of our analyses.
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 > 3True
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:
| 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 |
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 == TrueFalse
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.
Python provides several built-in data structures that allow you to store and organize collections of data. The most common ones are:
We will introduce each of these data structures in the following sections.
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']
Suppose we have a list of numbers: my_numbers = [5, 10, 15, 5, 20]. Please use Python do the following:
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.
Use Python to count how many times the number 5 appears in the list.
Add the number 25 to the end of the list.
Change the first element of the list to 1.
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 changes2
2
[1, 10, 15, 5, 20, 25]
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
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
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: 30If 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)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.")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:
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'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)Length (len() function): Getting the number of items in a sequence.
print(len(my_list_sequence)) # Output: 5
print(len(my_string_sequence)) # Output: 55
5
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).
* 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
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.
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).
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.
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 |
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.
5 > = 2True**5"The cat" == "The Cat"5 > = 2 will produce a SyntaxError. The correct operator is >= (greater than or equal to), not > = with a space.
# 5 >= 2 # This would be TrueTrue**5 will evaluate to 1. True is treated as 1 in arithmetic operations, so 1**5 is 1.
print(True**5)1
"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
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 == 3print(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.
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 lettersTrue
False
False
This shows that string comparisons are case-sensitive as well.
num1 and num2, and assign them the values 15 and 4 respectively.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.
colors containing the strings “red”, “green”, “blue”, and “yellow”.# 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']
user_profile with the following key-value pairs:
"name": “Alex”"age": 28"hobbies": A list containing “reading”, “hiking”, and “coding”"city" with the value “Toronto” to the user_profile 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.
You are given a string: raw_data = " Product_ID:12345, ProductName:SuperWidget, Price:0050.75 ". Please do the following:
Remove the leading and trailing whitespace from raw_data.
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.
Convert the Price to a floating-point number.
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 placesCleaned 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.
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.↩︎
Or to be more precise, a method is a function attached to an object↩︎
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:
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.