Variables are the fundamental building blocks of any programming language, and Python is no exception. Think of variables as labeled containers that store data values, allowing you to reference and manipulate information throughout your program. Technically, variables create an index for a value in memory, acting as a pointer to the location of that value. When we declare a variable, we are saving the value of the variable in memory. The variable name is just a label for that memory location. In data analytics, we use variables constantly. They enable you to store datasets, calculated results, and intermediate values that form the foundation of your analysis.
This chapter will guide you through the essential concepts of working with data in Python, starting with the basics of variables and data types, then progressing to more advanced topics like type conversion, data manipulation methods, and working with specialized modules. By the end of this chapter, you’ll have a solid understanding of how to store, manipulate, and transform data—skills that are essential for any data analytics workflow.
We’ll begin by exploring how to document your code effectively with comments, then dive into Python’s data type system, which determines how your computer interprets and processes different kinds of information. You’ll learn about both simple data types (like numbers and text) and complex data structures (like lists and dictionaries) that are particularly useful for organizing and analyzing data. Finally, we’ll cover practical techniques for validating, converting, and manipulating data using Python’s built-in functions and specialized modules.
Comments
Comments are an integral part of any programming language. They are used to explain the code and make it more readable. Comments are ignored by the interpreter and are not executed. In Python, comments are created using the # symbol.
# This is a comment
Well-written code is always easy to read, so comments help in that process. They explain what is taking place in the code. While commenting should always be encouraged, it is also important to not overdo it. Choose descriptive variable names and make the code self-explanatory. Use comments to avoid ambiguity and maintain clarity, but do not make the code too verbose.
Understanding Variables & Data Types
Variables in Python are fundamental building blocks that allow programmers to store, retrieve, and manipulate data. When you assign a value to a variable, Python automatically determines its data type based on the value provided. We can assign a variable by giving it a name and setting it equal to (=) a value. For instance, assigning x = 3 creates a variable x with the integer data type (more on integers in a bit), while y = 3.0 creates a variable y with the float data type. These data types are crucial because they dictate how the computer encodes and processes the data internally.
At the lowest level, computers represent all data as binary—sequences of 1s and 0s. The encoding of these binary sequences depends on the data type. Integers are stored as fixed-width binary numbers, which are straightforward representations of whole numbers. If we take the integer 32, for instance, it is represented in binary form as 101001. Floats, on the other hand, use a more complex encoding based on the IEEE 754 standard, which represents numbers in scientific notation. Strings are encoded using character encoding standards like ASCII or UTF-8, where each character is mapped to a unique binary value.
These differences in encoding are essential for the computer to interpret and manipulate data correctly. For instance, when performing arithmetic operations like 3 + 3, the computer knows to treat 3 as an integer and perform addition. However, if the same 3 is part of a string like "diff3rence", the computer interprets it as text and processes it accordingly. Without data types, the computer would have no way to distinguish between these contexts. Data types, and understanding them, is fundamental to programming because every variable must havae a data type at the moment of instantiation. Some programming languages force the programmer to declare the data type manually (such as Java). Python, luckily, makes this process much easier.
Python has a dynamic typing system that simplifies working with variables by automatically assigning data types based on the value. Each type has its own notating to make it clear to the programmer what type it is. For instance, as already indicated above, all strings exist in double or single quotation marks. Lists are represented in squar brackets ([]). We will go into the specifics more below. Python reads these indicators and interprets the type at run-time. This flexibility allows programmers to focus on logic rather than worrying about low-level details like binary encoding. However, understanding how data types work behind the scenes (such as how integers, floats, and strings are encoded) can help developers write more efficient and accurate programs. In fact, most bugs end up being data type errors. For this reason, Python has created a complete hinting system that helps to catch errors before that arise. We will introduce this system in the next chapter, but know that it is completely optional and does not impact how the code runs. Instead, the IDE uses it to predict errors before run-time.
The Basic Data Types in Python:
The basic data type in Python are the simplest forms of data that you can work with. They are the building blocks for more complex data structures and are essential for performing basic operations in your programs. There are four basic data types.
Integers are whole numbers, positive or negative, without decimals. Examples include 42, -17, 0, and 1000. They are primarily used for counting, indexing, and mathematical operations. Unlike some other programming languages, Python has no size limit for integers, allowing you to work with arbitrarily large numbers without worrying about overflow errors. Integers will never include a decimal, and they are represented without any quotation marks.
Floats are real numbers, positive or negative, that contain one or more decimals. Examples include 3.14, -2.5, 0.0, and 1.23e-4 (scientific notation). They will always be represented with a decimal and are represented without any quotation marks: hence 0.0.
Strings are sequences of characters enclosed in single or double quotes. Examples include "Hello", 'Python', "123", and "" (empty string). They are used for text data, names, addresses, descriptions, and any other textual information. Strings can contain letters, numbers, symbols, and special characters, making them incredibly versatile for data representation. An important characteristic of strings is that they are immutable, meaning they cannot be changed after creation—any string operation creates a new string object.
Booleans represent True or False values, and it’s important to note the capitalization in Python. The only two boolean values are True and False. They are used for logical operations, conditions, and decision-making in your programs. Booleans often result from comparison operations (for example, 5 > 3 returns True), and they are essential for controlling program flow with if statements and loops (more on this in next chapter), allowing your programs to make decisions based on conditions.
numeric constant: 6
string constant: something and another thing
Float: 6.0
Integer: 6
String: 3.03.0
Note
Notice: print()
The print() function is used to display output in Python. It takes one or more arguments and prints them to the console. In the examples above, we use print() to show the results of various operations involving different data types.
Notice the difference in the output when we use different data types. A float and an integer are both numerics, so they will be added together mathematically. But because we coerce the 3 into a float the outcome of the addition produces 6.0. Similarly, when we coerce the 3.0 into an integer, the outcome of the addition produces 6.
A string, however, is not a numeric data type, so we cannot use math operations with it. Instead, the + operator becomes a concatenation operator, which joins the two strings together.
Complex Data Types in Python
Lists are collections of items that can hold multiple values of any type. They are ordered, meaning items have a defined order and maintain that order throughout the life of the list. Lists are changeable (mutable), so you can add, remove, or modify items after creation, making them extremely flexible for dynamic data storage. They allow duplicate members, meaning the same value can appear multiple times within the same list. Examples include [1, 2, 3], ["apple", "banana", "cherry"], and [1, "hello", 3.14, True], demonstrating their ability to store mixed data types. Lists are ideal for storing related data that might need to be modified during program execution, making them perfect for collections that grow or shrink as your program runs.
# A list is written with square brackets.# We can create a list in two ways:# the inferred waylist1 = ["apple", "banana", "cherry"]# and the explicit way, using the list() constructor to make a List.list2 =list(("apple", "banana", "cherry")) print('list1: ', list1)print('list2: ', list2)
Tuples are collections of items that can hold multiple values, similar to lists but with key differences. They are ordered, maintaining a defined sequence of items, but are unchangeable (immutable), meaning you cannot add, remove, or modify items after creation. Like lists, tuples allow duplicate members, so the same value can appear multiple times. Examples include (1, 2, 3), ("apple", "banana", "cherry"), and (1, "hello", 3.14, True). Tuples are particularly useful for storing related data that should remain constant throughout your program’s execution. They are perfect for coordinates, database records, or any fixed collection of values. Additionally, tuples are more memory efficient than lists for read-only data, making them a good choice when you need to store data that won’t change.
# A tuple is written with round brackets.# We can create a tuple in two ways:# the inferred waytuple1 = ("apple", "banana", "cherry")# and the explicit way, using the tuple() constructor to make a tuple.tuple2 =tuple(("apple", "banana", "cherry"))print('tuple1: ', tuple1)print('tuple2: ', tuple2)
Sets are collections of unique items with no duplicates allowed. They are unordered, meaning items have no defined order and the order may vary each time you access the set. Sets are not indexed, so you cannot access items by position (there’s no set[0] operation). The defining characteristic of sets is that they automatically remove duplicate values, ensuring each item appears only once. Examples include {1, 2, 3}, {"apple", "banana", "cherry"}, and notably, {1, 2, 2, 3} automatically becomes {1, 2, 3}. Sets are particularly useful for mathematical set operations like union, intersection, and difference. They are perfect for removing duplicates from data or testing membership, and they offer very fast performance for checking if an item exists in the collection.
# A set is written with curly brackets.# We can create a set in two ways:# the inferred wayset1 = {"apple", "banana", "cherry"}# and the explicit way, using the set() constructor to make a set.set2 =set(("apple", "banana", "cherry"))print('set1: ', set1)print('set2: ', set2)
Dictionaries are collections of items stored as key:value pairs, providing a way to associate related pieces of information. While they are technically unordered, Python 3.7+ preserves insertion order as an implementation detail. Dictionaries are changeable (mutable), allowing you to add, remove, or modify items after creation. They are indexed by keys rather than positions, meaning you access values using their associated key rather than a numerical index. Each key must be unique within the dictionary, but values can be duplicated across different keys. Examples include {"name": "John", "age": 36}, {1: "apple", 2: "banana"}, and {"data": [1, 2, 3]}. Dictionaries are perfect for storing related data with meaningful labels, making them ideal for representing structured data like records, configurations, or mappings. Keys must be immutable types (strings, numbers, tuples), while values can be any type, providing tremendous flexibility in data organization.
# A dictionary is written with curly brackets, and they have keys and values.# We can create a dictionary in two ways:# the inferred waydict1 = {"brand": "Ford", "model": "Mustang", "year": 1964}# and the explicit way, using the dict() constructor to make a dictionary.dict2 =dict(brand="Ford", model="Mustang", year=1964)print('dict1: ', dict1)print('dict2: ', dict2)
Note that a dictionary and a set are both written with curly brackets, but they serve different purposes. A dictionary stores key:value pairs, while a set stores unique values without any specific order. You can tell the difference by looking for the colon : in a dictionary, which separates keys from values. In contrast, a set contains only values without any associated keys.
x =5y ="10"z = x +int(y)print("x: ", x)print("y: ", y)print("z: ", z)
x: 5
y: 10
z: 15
Indexed data types and Iterable data types
Some data types in Python are indexed, meaning that you can access individual items by their position in the collection. In other words, they are ordered, and their order does not change unless you explicitly modify it. You can access items in these collections using square brackets ([]) with the index of the item you want to retrieve. For example, my_list[0] retrieves the first item in a list.
Strings are indexed because they are a sequence of characters, and you can access individual characters by their position. For example, "hello"[0] retrieves the first character, which is 'h'. Dictionaries are indexed slightly different. They are indexed by keys, allowing you to access values using their associated keys. For example, my_dict["key"] retrieves the value associated with "key".
Index notation is zero-based, meaning the first item in a collection is at index 0, the second item is at index 1, and so on. This is important to remember when working with indexed data types, as it can lead to off-by-one errors if you mistakenly use 1 instead of 0 for the first item. In addition, you can use negative indexing to access items from the end of the collection. For example, my_list[-1] retrieves the last item in the list, my_list[-2] retrieves the second-to-last item, and so on.
We can also use slicing to access a range of items in a collection. Slicing allows you to retrieve a subset of items by specifying a start index, an end index, and an optional step value. For example, my_list[1:4] retrieves items from index 1 to index 3. Note that the end index is exclusive, meaning the item at the end index is not included in the result. You can also omit the start or end index to retrieve items from the beginning or to the end of the collection, respectively. For example, my_list[:3] retrieves items from the beginning to index 2, and my_list[2:] retrieves items from index 2 to the end of the list.
my_list = [10, 20, 30, 40, 50]print(my_list[1:4]) # Output: [20, 30, 40]print(my_list[2:]) # Everything from index 2 to the end: [30, 40, 50]print(my_list[:3]) # Everything from the beginning to index 2:
[20, 30, 40]
[30, 40, 50]
[10, 20, 30]
Finally, we can use the third argument in the slice notation to specify a step value, which determines how many items to skip between each item in the result. For example, my_list[0:5:2] retrieves every second item from index 0 to index 4, resulting in [10, 30, 50]. If you omit the step value, it defaults to 1, meaning every item is included in the result.
You cannot use slicing on non-indexed data types like sets or dictionaries. For example, trying to slice a set like my_set[0:2] will result in an error because sets are unordered and do not support indexing. Similarly, dictionaries do not support slicing directly, but you can convert their keys or items to a list and then slice that list.
Some data types in Python are iterable, which means that the items within the collection can be accessed one at a time. This includes data types like lists, tuples, and strings. You can iterate over these collections using loops, such as for loops, to access each item in turn. We will get to loops in chapter 3. For now, it is important to understand which data types are iterable and which are not.
Non-iterable data types include: - Integers - Floats - Booleans
Note that being iterable is different from being indexed. Sets and dictionaries are iterable (you can loop through them), but sets are not indexed (you cannot access items by position like set[0]).
Important note about variables
Variables in Python are case-sensitive, meaning that myVariable, MyVariable, and MYVARIABLE are all considered different variables. This is crucial to remember when naming your variables, as it can lead to confusion or errors if you accidentally use the wrong case. Typically, variables in Python are declared using snake case, which employs all lower-case letters with underscores to separate different words. So in Python, we would use my_variable. The exception to this rule is in the case of Classes and Objects, which use Pascal Case, which capitalizes the first letter of words and combines them together with no spaces: MyVariable. It is strongly advised to follow these rules for casing.
Variables are always stored as their evaluated result. In Python, this means that expressions are computed first, and the resulting value is stored in the variable. For example:
Arithmetic: x = 5 + 5 evaluates 5 + 5 and stores x as 10.
Comparison: y = 5 > 4 evaluates 5 > 4 and stores y as True.
String concatenation: z = "Something plus " + "another thing" evaluates the concatenation of two strings and stores z as "Something plus another thing".
(Note: Concatenation refers to joining two strings together, which is a common operation in Python.)
Variables can be reassigned at any time, meaning you can change the value stored in a variable without any restrictions. For example, if you initially set x = 5, you can later change it to x = 10 or even x = "Hello". This flexibility allows you to adapt your program’s behavior dynamically as needed.
You cannot name a variable beginning with a number, and you cannot use special characters like !, @, #, $, %, ^, &, *, (, ), -, +, =, {, }, [, ], ;, ', ", <, >, /, or \ in variable names. Variable names must start with a letter (a-z, A-Z) or an underscore (_), followed by letters, numbers, or underscores. This rule helps maintain clarity and prevents conflicts with Python’s syntax.
Comments
Comments are an integral part of any programming language. They are used to explain the code and make it more readable. Comments are ignored by the interpreter and are not executed. In Python, comments are created using the
#symbol.Well-written code is always easy to read, so comments help in that process. They explain what is taking place in the code. While commenting should always be encouraged, it is also important to not overdo it. Choose descriptive variable names and make the code self-explanatory. Use comments to avoid ambiguity and maintain clarity, but do not make the code too verbose.