Mathew K Analytics

Lesson 4 · Python for Data Analysts

Python for Beginners: From Variables to Real Mini Projects

By the end, you'll have built three real mini projects: a deadline countdown tool, a spreadsheet automation script, and a program that talks to a live web…

What you'll learn

Datasets used in this lesson

Save these next to the notebook. In Google Colab, upload them with the 📁 icon on the left first.

📓 Full notebook

Download .ipynb

Python for Beginners: From Variables to Real Mini Projects#

  • By the end, you'll have built three real mini projects: a deadline countdown tool, a spreadsheet automation script, and a program that talks to a live web API.
  • No prior experience needed. Let's jump straight in.

Before You Start#

  • Install Python 3 from python.org (on Windows, check 'Add python.exe to PATH'), then install VS Code from code.visualstudio.com with its Python and Jupyter extensions.
  • Open the Command Palette (Control/Command plus Shift plus P), run 'Create: New Jupyter Notebook', and select your Python interpreter as the kernel.

Your First Cell: print()#

print(1)
print(200)
print('some sentence')
1
200
some sentence

Data Types: Strings and Numbers#

  • Text values are called strings, and can be written with either single or double quotes; there is no difference between the two.
  • Whole numbers, like 2, 20, 0, or -5, are integers.
  • Numbers with a decimal point, like 4.99, are floats, used whenever you need precision, like a price or a weight.
a_string = 'Hello there'
an_integer = 20
a_float = 4.99
print(type(a_string))
print(type(an_integer))
print(type(a_float))
<class 'str'>
<class 'int'>
<class 'float'>

Combining Text and Numbers#

  • Let's write a tiny calculation and then describe the result in a full sentence.
  • We'll calculate how many minutes there are in a given number of days.
days = 20
hours_per_day = 24
minutes_per_hour = 60
total_minutes = days * hours_per_day * minutes_per_hour
print(total_minutes)
28800
message = str(days) + ' days are ' + str(total_minutes) + ' minutes'
print(message)
20 days are 28800 minutes
message = f'{days} days are {total_minutes} minutes'
print(message)
20 days are 28800 minutes

Variables Avoid Repetition#

  • Notice that hours_per_day and minutes_per_hour never change, no matter how many days we convert.
  • Storing repeating values in well-named variables, instead of retyping them everywhere, is one of the most common things you'll do in any program.
days = 35
total_minutes = days * hours_per_day * minutes_per_hour
print(f'{days} days are {total_minutes} minutes')
days = 110
total_minutes = days * hours_per_day * minutes_per_hour
print(f'{days} days are {total_minutes} minutes')
35 days are 50400 minutes
110 days are 158400 minutes

Functions: Avoiding Repeated Logic#

  • Variables help us avoid repeating single values, but what if we want to avoid repeating an entire calculation?
  • A function is a named, reusable block of code. We define one with def, a name, parentheses, and a colon.
def days_to_minutes():
    total = days * hours_per_day * minutes_per_hour
    print(f'{days} days are {total} minutes')
days_to_minutes()
110 days are 158400 minutes

Function Parameters#

def days_to_minutes(num_days):
    total = num_days * hours_per_day * minutes_per_hour
    print(f'{num_days} days are {total} minutes')

days_to_minutes(20)
days_to_minutes(35)
20 days are 28800 minutes
35 days are 50400 minutes

Multiple Parameters#

def days_to_minutes(num_days, note):
    total = num_days * hours_per_day * minutes_per_hour
    print(f'{num_days} days are {total} minutes ({note})')

days_to_minutes(20, 'a short sprint')
days_to_minutes(110, 'nearly four months')
20 days are 28800 minutes (a short sprint)
110 days are 158400 minutes (nearly four months)

Returning Values From a Function#

  • So far our function prints its own result. Often you instead want a function to hand a value back to you, so you can use it elsewhere.
  • We do that with the return keyword.
def days_to_minutes(num_days):
    total = num_days * hours_per_day * minutes_per_hour
    return f'{num_days} days are {total} minutes'

result = days_to_minutes(20)
print(result)
20 days are 28800 minutes

Variable Scope: Local vs. Global#

  • Variables created outside any function, like hours_per_day and minutes_per_hour, are global: every function in your code can read them.
  • Variables created inside a function, like num_days or total above, are local: they only exist while that function is running, and only that function can see them.
def show_scope():
    print(minutes_per_hour)
    print(total)

show_scope()
60
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[12], line 5
      2     print(minutes_per_hour)
      3     print(total)
----> 5 show_scope()

Cell In[12], line 3, in show_scope()
      1 def show_scope():
      2     print(minutes_per_hour)
----> 3     print(total)

NameError: name 'total' is not defined

Getting Input From the User#

  • Every program so far has used hard-coded values. Real programs usually need to ask the person using them for information.
  • The built-in input function pauses your program, shows an optional prompt message, and waits for the user to type something and press Enter.
user_input = input('Enter a number of days: ')
print(user_input)
print(type(user_input))
20
<class 'str'>

Type Conversion (Casting)#

  • Because input always returns a string, using it directly in a math calculation causes problems, since Python can't multiply text.
  • We fix this with casting: converting a value from one data type to another, using functions like int(), float(), and str().
user_input = input('Enter a number of days: ')
total = user_input * hours_per_day * minutes_per_hour
print(total)
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
user_input = input('Enter a number of days: ')
num_days = int(user_input)
total = num_days * hours_per_day * minutes_per_hour
print(f'{num_days} days are {total} minutes')
10 days are 14400 minutes

Conditionals: if, elif, else#

  • A negative number of days doesn't make sense for our converter, so let's validate the user's input before doing any math.
  • if, elif, and else let a program branch: run one block of code when a condition is true, and a different block otherwise.
num_days = -10
if num_days > 0:
    total = num_days * hours_per_day * minutes_per_hour
    print(f'{num_days} days are {total} minutes')
elif num_days == 0:
    print('You entered zero, please enter a valid positive number.')
else:
    print('You entered a negative value, no conversion for you.')
You entered a negative value, no conversion for you.

Comparison Operators and Booleans#

  • Greater than, less than, and equals are called comparison operators, because they compare two values.
  • Every comparison produces a value of its own data type: a boolean, which is either True or False, always capitalized in Python.
is_positive = num_days > 0
print(is_positive)
print(type(is_positive))
False
<class 'bool'>

Handling Errors Gracefully With try/except#

  • if statements are great for validating specific rules, like 'is this positive'. But what if the user types text instead of a number at all?
  • try/except lets us attempt some code, and catch a specific type of error if it happens, instead of letting the whole program crash.
try:
    user_input = input('Enter a number of days: ')
    num_days = int(user_input)
    print(f'You entered {num_days} days.')
except ValueError as e:
    print('That is not a valid number:', e)
That is not a valid number: invalid literal for int() with base 10: 'twenty'

while Loops: Repeating Until a Condition Changes#

  • Right now our program asks for input exactly once, then ends. What if we want to keep converting values until the user decides to stop?
  • A while loop repeats its body for as long as its condition stays True.
user_input = ''
while user_input != 'exit':
    user_input = input("Enter a number of days, or 'exit' to stop: ")
    if user_input == 'exit':
        pass
    elif user_input.isdigit():
        print(f'{user_input} days are {int(user_input) * hours_per_day * minutes_per_hour} minutes')
    else:
        print('Please enter a whole number, or exit to stop.')
print('Goodbye!')
10 days are 14400 minutes
Please enter a whole number, or exit to stop.
Please enter a whole number, or exit to stop.
Goodbye!

Lists: An Ordered Collection of Values#

  • What if we wanted to convert several different day counts at once, without typing them in one at a time?
  • A list is a data type that holds multiple values, in order, written with square brackets.
days_list = [10, 45, 30]
print(days_list)
print(days_list[0])
print(days_list[2])
[10, 45, 30]
10
30
days_list.append(90)
print(days_list)
[10, 45, 30, 90]

for Loops: Doing Something for Every Element#

  • Most of the time, when you have a list, you want to do the same thing to every element in it.
  • A for loop runs its body once for each element in a list, automatically, with no manual indexing required.
for num_days in days_list:
    total = num_days * hours_per_day * minutes_per_hour
    print(f'{num_days} days are {total} minutes')
10 days are 14400 minutes
45 days are 64800 minutes
30 days are 43200 minutes
90 days are 129600 minutes

Sets: Only Unique Values#

  • Suppose someone accidentally enters the same day count twice; a list happily keeps both copies.
  • A set is like a list, but it automatically removes duplicates, and is written with curly braces.
days_with_duplicates = [10, 45, 30, 10]
unique_days = set(days_with_duplicates)
print(days_with_duplicates)
print(unique_days)
print(type(unique_days))
[10, 45, 30, 10]
{10, 45, 30}
<class 'set'>

Built-in Functions vs. Methods#

  • print, input, int, and set are all built-in functions: Python provides them ready to use, called by name with parentheses.
  • Some functions instead belong to a specific data type, and are called directly on a value with a dot, like 'hello'.upper(); these are called methods.
greeting = 'hello there'
print(greeting.upper())
print(greeting.title())
print('42'.isdigit())
HELLO THERE
Hello There
True

Dictionaries: Key-Value Pairs#

  • Lists and sets are great for a bunch of similar values, but sometimes each value needs its own label.
  • A dictionary stores key-value pairs, written with curly braces, where each key maps to a specific value.
conversion = {'days': 20, 'unit': 'hours'}
print(conversion)
print(conversion['days'])
print(conversion['unit'])
{'days': 20, 'unit': 'hours'}
20
hours
def convert(num_days, unit):
    if unit == 'hours':
        return f'{num_days} days are {num_days * 24} hours'
    elif unit == 'minutes':
        return f'{num_days} days are {num_days * 24 * 60} minutes'
    else:
        return f'Unsupported unit: {unit}'

print(convert(conversion['days'], conversion['unit']))
print(convert(5, 'minutes'))
print(convert(5, 'weeks'))
20 days are 480 hours
5 days are 7200 minutes
Unsupported unit: weeks

Comments#

  • A comment starts with a #, and is completely ignored when your code runs.
  • Use comments to leave notes for yourself or teammates, explaining why code does something, not just what it does.
# Convert days into the requested unit, defaulting gracefully for unknown units
print(convert(3, 'hours'))
3 days are 72 hours

Mini Project 1: Countdown to a Goal#

  • Let's build something genuinely useful: a program that takes a goal and a deadline date, and tells you how many days remain.
  • This needs a new built-in module, datetime, for working with dates.
from datetime import datetime

user_input = input('Enter your goal and deadline as goal:DD.MM.YYYY: ')
goal, deadline_text = user_input.split(':')
print(goal)
print(deadline_text)
Learn Python
31.12.2026
deadline_date = datetime.strptime(deadline_text, '%d.%m.%Y')
print(deadline_date)
print(type(deadline_date))
2026-12-31 00:00:00
<class 'datetime.datetime'>
today = datetime.today()
time_remaining = deadline_date - today
print(today)
print(time_remaining.days)
2026-08-15 05:00:30.351421
137
message = f'Time remaining for your goal "{goal}" is {time_remaining.days} days.'
print(message)
Time remaining for your goal "Learn Python" is 137 days.

Modules: Organizing Code Across Files#

  • Every Python file is a module: a container of functions, variables, and classes that other files can reuse.
  • We already used one: 'from datetime import datetime' imports the datetime class out of Python's built-in datetime module.
  • In a larger project you'd split your own code across multiple .py files too, for example a helpers.py with shared functions, and import from it the same way.

Built-in Modules#

  • Python ships with dozens of built-in modules for common tasks: math for mathematics, os for talking to your operating system, random for randomness, and many more.
  • You don't install these; they're simply there once Python itself is installed.
import math
import random

print(math.sqrt(64))
print(math.pi)
print(random.randint(1, 10))
8.0
3.141592653589793
10

Packages, pip, and PyPI#

  • Built-in modules cover common needs, but for anything more specialized, like reading spreadsheets or talking to a web API, you'll reach for an external package.
  • Packages live in a public repository called PyPI, the Python Package Index, and are installed with pip, Python's built-in package manager.
  • From a terminal, 'pip install package_name' downloads and installs a package; 'pip uninstall package_name' removes it.

Mini Project 2: Automating a Spreadsheet#

  • A very common real-world use of Python is automating repetitive spreadsheet work.
  • We'll use openpyxl, an external package for reading and writing Excel files, installed with: pip install openpyxl
  • Our spreadsheet, inventory.xlsx, lists products with a product number, an inventory count, a price, and a supplier, one row per product.
import openpyxl

workbook = openpyxl.load_workbook('inventory.xlsx')
sheet = workbook['Sheet1']
print(sheet.max_row)
print(sheet.max_column)
41
4
for row in range(2, sheet.max_row + 1):
    product_number = sheet.cell(row, 1).value
    inventory = sheet.cell(row, 2).value
    price = sheet.cell(row, 3).value
    supplier = sheet.cell(row, 4).value
    print(product_number, inventory, price, supplier)
101 41 12.24 Beta Supply Co
102 48 26.56 Gamma Distribution
103 15 14.71 Alpha Traders
104 7 61.7 Beta Supply Co
105 58 50.21 Gamma Distribution
106 38 39.41 Alpha Traders
107 2 10.7 Beta Supply Co
108 15 46.71 Gamma Distribution
109 2 51.6 Alpha Traders
110 46 59.36 Beta Supply Co
111 35 39.2 Gamma Distribution
112 29 54.05 Alpha Traders
113 52 78.56 Beta Supply Co
114 49 73 Gamma Distribution
115 45 39.47 Alpha Traders
116 18 16.1 Beta Supply Co
117 49 31.95 Gamma Distribution
118 6 35.74 Alpha Traders
119 23 76.65 Beta Supply Co
120 39 25.64 Gamma Distribution
121 3 66.34 Alpha Traders
122 35 13.42 Beta Supply Co
123 60 35.62 Gamma Distribution
124 36 28.15 Alpha Traders
125 41 56.61 Beta Supply Co
126 56 34.14 Gamma Distribution
127 13 64.14 Alpha Traders
128 3 60.35 Beta Supply Co
129 50 27.82 Gamma Distribution
130 6 77.33 Alpha Traders
131 56 11.34 Beta Supply Co
132 18 42.17 Gamma Distribution
133 54 34.42 Alpha Traders
134 24 33.58 Beta Supply Co
135 43 25.86 Gamma Distribution
136 60 62.3 Alpha Traders
137 5 55.79 Beta Supply Co
138 11 49.23 Gamma Distribution
139 16 16.8 Alpha Traders
140 25 26.12 Beta Supply Co
products_per_supplier = {}
for row in range(2, sheet.max_row + 1):
    supplier = sheet.cell(row, 4).value
    if supplier in products_per_supplier:
        products_per_supplier[supplier] = products_per_supplier[supplier] + 1
    else:
        products_per_supplier[supplier] = 1
print(products_per_supplier)
{'Beta Supply Co': 14, 'Gamma Distribution': 13, 'Alpha Traders': 13}
low_stock = {}
for row in range(2, sheet.max_row + 1):
    product_number = sheet.cell(row, 1).value
    inventory = sheet.cell(row, 2).value
    if inventory < 10:
        low_stock[product_number] = inventory
print(low_stock)
{104: 7, 107: 2, 109: 2, 118: 6, 121: 3, 128: 3, 130: 6, 137: 5}
for row in range(2, sheet.max_row + 1):
    inventory = sheet.cell(row, 2).value
    price = sheet.cell(row, 3).value
    sheet.cell(row, 5).value = inventory * price
workbook.save('inventory_with_totals.xlsx')
print('Saved inventory_with_totals.xlsx')
Saved inventory_with_totals.xlsx

Classes and Objects#

  • Imagine an app with many users, where each user has an email, a name, and a job title, and can change their own password.
  • A class is a blueprint describing what data and behavior something has. An object is one specific instance built from that blueprint.
  • We define a class with the class keyword and a capitalized name.
class User:
    def __init__(self, email, name, job_title):
        self.email = email
        self.name = name
        self.job_title = job_title

    def get_info(self):
        return f'{self.name} works as a {self.job_title}. Contact: {self.email}'

    def change_job_title(self, new_title):
        self.job_title = new_title
user_one = User('alex@example.com', 'Alex', 'Data Analyst')
print(user_one.get_info())
user_one.change_job_title('Senior Data Analyst')
print(user_one.get_info())
Alex works as a Data Analyst. Contact: alex@example.com
Alex works as a Senior Data Analyst. Contact: alex@example.com
user_two = User('sam@example.com', 'Sam', 'Product Manager')
print(user_two.get_info())
Sam works as a Product Manager. Contact: sam@example.com

Connecting Classes#

class Post:
    def __init__(self, message, author):
        self.message = message
        self.author = author

    def get_post_info(self):
        return f'"{self.message}" - posted by {self.author.name}'

new_post = Post('Just finished my first Python project!', user_two)
print(new_post.get_post_info())
"Just finished my first Python project!" - posted by Sam

Mini Project 3: Talking to a Web API#

  • Two applications on the internet usually communicate over HTTP: your program sends a request to a URL, and gets a response back.
  • The external requests package makes this easy in Python: pip install requests
  • To keep this notebook fully self-contained and runnable offline, we'll spin up a tiny local demo server that responds exactly like a real API would, then query it with requests, exactly as you would query any real API on the internet.
import json
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

demo_projects = [
    {'name': 'weather-dashboard', 'web_url': 'https://example.com/demo/weather-dashboard'},
    {'name': 'expense-tracker', 'web_url': 'https://example.com/demo/expense-tracker'},
    {'name': 'recipe-finder', 'web_url': 'https://example.com/demo/recipe-finder'},
]

class DemoHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(demo_projects).encode())
    def log_message(self, *args):
        pass

demo_server = ThreadingHTTPServer(('127.0.0.1', 0), DemoHandler)
demo_port = demo_server.server_address[1]
threading.Thread(target=demo_server.serve_forever, daemon=True).start()
print(f'Demo API running at http://127.0.0.1:{demo_port}')
Demo API running at http://127.0.0.1:58209
import requests

response = requests.get(f'http://127.0.0.1:{demo_port}/api/v4/projects')
print(response.status_code)
print(response.text)
200
[{"name": "weather-dashboard", "web_url": "https://example.com/demo/weather-dashboard"}, {"name": "expense-tracker", "web_url": "https://example.com/demo/expense-tracker"}, {"name": "recipe-finder", "web_url": "https://example.com/demo/recipe-finder"}]
projects = response.json()
print(type(projects))
for project in projects:
    print(f"{project['name']}: {project['web_url']}")
<class 'list'>
weather-dashboard: https://example.com/demo/weather-dashboard
expense-tracker: https://example.com/demo/expense-tracker
recipe-finder: https://example.com/demo/recipe-finder

Wrap-Up: What You Learned#

  • Core Python building blocks: variables, data types, string formatting with f-strings, functions, parameters, return values, and variable scope.
  • Getting and validating user input with input, type conversion, if/elif/else, comparison operators, booleans, and try/except.
  • Repetition and collections: while loops, for loops, lists, sets, and dictionaries.
  • Organizing and reusing code with modules, built-in modules, and external packages installed through pip.
  • Mini Project 1: a deadline countdown tool built with the datetime module.
  • Mini Project 2: automated spreadsheet processing and reporting with openpyxl.
  • Object-oriented programming: classes, objects, methods, and connecting classes together.
  • Mini Project 3: talking to a web API with requests and parsing JSON responses.
  • Practice prompt: pick one mini project and extend it, for example adding a new unit to the countdown tool, a new report to the spreadsheet script, or a new field to the User class.
  • You went from print('Hello') to shipping three real projects in one sitting. That's rare. Subscribe so the next build shows up in your feed, no searching required.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.