Introduction to Python¶

~2 hours · Google Colab · nothing to install

For graduate students with no prior programming experience.

By the end you can:

  • write and run basic Python
  • use functions and libraries
  • load and summarize data with pandas
  • plot with matplotlib

You need: a Google account and a browser. Open this notebook, then File → Save a copy in Drive.

Why Python

  • free and used in every discipline
  • readable syntax
  • one ecosystem: data, plots, machine learning, automation
  • notebooks are easy to share and rerun

1. Getting Python¶

  1. Google Colab — nothing to install; what we use today
  2. Local install — for offline work later

Option 1 · Google Colab¶

Runs in the browser, saves to Drive, ships with NumPy, pandas, and matplotlib.

  1. colab.research.google.com → sign in
  2. File → New notebook
  3. Type print("Hello, world!") and press Shift+Enter

Working in Colab¶

  • Shift+Enter — run a cell
  • Runtime → Run all — rerun from the top
  • File → Save a copy in Drive — keep your own editable copy

Option 2 · Local install (optional)¶

Anaconda — recommended for research anaconda.com/download → install → Navigator → Jupyter Notebook → New → Python 3

Python.org — minimal python.org/downloads → install. On Windows, check Add Python to PATH.

The notebook interface¶

Cells — code cells run Python; markdown cells hold notes.

Shortcuts — Shift+Enter run and move · Cmd/Ctrl+Enter run and stay · A insert above · B insert below

In [ ]:
print("Hello, world!")

Markdown cheat sheet¶

# Heading 1 ## Heading 2 ### Heading 3

Bold, italic, inline code.

Lists¶

  • bullet
    • sub-bullet
  1. numbered
  2. second

Links and images¶

[Python.org](https://www.python.org)

Python logo

Tables¶

Name Score Grade
Alice 90 A
Bob 85 B

Equations¶

Inline $E = mc^2$, block $$E = mc^2$$

Variables and data types¶

A variable is a name that stores a value.

type holds example
int whole numbers 3, -10
float decimals 3.14
str text "hello"
bool logic True, False
In [46]:
# Create one of each
age = 27             # int
temperature = 98.6   # float
greeting = "Hello"    # str
is_student = True     # bool

# Inspect values and types
print(age, type(age))
print(temperature, type(temperature))
print(greeting, type(greeting))
print(is_student, type(is_student))
27 <class 'int'>
98.6 <class 'float'>
Hello <class 'str'>
True <class 'bool'>

Basic operations¶

  • numbers — + - * / // % **
  • strings — + joins, * repeats
  • booleans — produced by comparisons
In [51]:
## Code — Arithmetic with int/float

a = 10        # int
b = 3         # int
c = 2.5       # float

d = a + b
print("a + b =", d)
print("a / b =", a / b)      # float division
print("a // b =", a // b)    # integer (floor) division
print("a ** b =", a ** b)    # exponent
print("b * c =", b * c)      # int * float → float
a + b = 13
a / b = 3.3333333333333335
a // b = 3
a ** b = 1000
b * c = 7.5

Strings¶

Single or double quotes: "Alice", 'Python is fun!'

  • + concatenates, * repeats
  • methods: .lower(), .upper(), .replace()
In [52]:
### Code — Basic string examples

name = "Ada"
greeting = "Hello, " + name + "!"
print(greeting)

# String methods
message = "Data Science"
print(message.lower())
print(message.upper())
print(message.replace("Science", "Analysis"))
Hello, Ada!
data science
DATA SCIENCE
Data Analysis
In [57]:
## Code — Strings (str)

first = "Ada"
last = "Lovelace"
full = first + " " + last     # concatenation: joining strings together
print("Full name:", full)

print("Repeat:", "ha" * 3)    # repetition

# f-string: readable string formatting
score = 95.2
print(first, "scored", score, "on the quiz")
print(f"{first} scored {score} on the quiz.")
Full name: Ada Lovelace
Repeat: hahaha
Ada scored 95.2 on the quiz
Ada scored 95.2 on the quiz.

f-strings¶

Put f before the quote; anything inside {} is evaluated.

f"{name} scored {score:.1f}%"

Easier to read than gluing pieces with +.

In [58]:
## Code — Booleans (bool) from comparisons

x = 7
y = 10
print("x < y:", x < y)
print("x == y:", x == y)
print("x != y:", x != y)

name = "Ada"
print("Name starts with A:", name.startswith("A"))
x < y: True
x == y: False
x != y: True
Name starts with A: True

Type conversion¶

int(), float(), str() convert between types.

In [62]:
# Number to string
n = 42
print(type(n))
print("As string:", str(n), type(str(n)))

# String to number (must be numeric!)
num_str = "3.14"
print("As float:", float(num_str), type(float(num_str)))

# Boolean from comparison
passed = 87 >= 60
print("Passed?", passed, type(passed))
<class 'int'>
As string: 42 <class 'str'>
As float: 3.14 <class 'float'>
Passed? True <class 'bool'>

Three gotchas¶

  • = assigns, == compares
  • 0.1 + 0.2 gives 0.30000000000000004 — use round(x, 2) for display
  • strings always need quotes
In [63]:
val = 0.1 + 0.2
print("Raw:", val)
print("Rounded to 2 decimals:", round(val, 2))
Raw: 0.30000000000000004
Rounded to 2 decimals: 0.3

Why 0.1 + 0.2 isn't 0.3¶

Floats are stored in binary (IEEE-754). Neither 0.1 nor 0.2 has an exact binary form — same problem as writing 1/3 in decimal — so the sum carries a tiny rounding error.

Exercises 1¶

  1. Create four variables: age (int), temperature (float), first name (str), likes coffee (bool).
  2. With base = 12 and height = 7.5, print the triangle area using an f-string.
  3. True or false? "Data".lower() == "data" · len("abc") == 3 · int("10") > 5
In [ ]:

Lists, tuples, dictionaries¶

type mutable ordered syntax example
list yes yes [ ] ["apple", "banana"]
tuple no yes ( ) (10, 20)
dict yes by key { } {"name": "Ada"}

List — ordered and changeable. Square brackets.

In [65]:
fruits = ["apple", "banana", "cherry"]
print("All fruits:", fruits)
print("First fruit:", fruits[1])   # first item
fruits.append("date")              # add new item
print("After adding:", fruits)
All fruits: ['apple', 'banana', 'cherry']
First fruit: banana
After adding: ['apple', 'banana', 'cherry', 'date']

Tuple — same idea, but fixed once created. Parentheses. Use for things that shouldn't change, like a coordinate pair.

In [68]:
coords = (10, 20)
print("Coordinates:", coords)
# coords[0] = 15  # ❌ This would cause an error (immutable)
Coordinates: (10, 20)

Dictionary — key–value pairs. Label data by name ("age") instead of position (1).

In [69]:
student = {"name": "Ada", "age": 23, "major": "Math"}
print(student)
print("Student name:", student["name"])
student["age"] = 24
print("Updated age:", student["age"])
{'name': 'Ada', 'age': 23, 'major': 'Math'}
Student name: Ada
Updated age: 24
In [ ]:
# One dictionary, each key stores multiple values (as a list)
students = {
    "name": ["Alice", "Bob"],
    "age": [24, 27],
    "major": ["Economics", "Mathematics"]
}

print(students)

Indexing and slicing¶

Zero-based: x[0] is first, x[-1] is last.

Slices are x[start:end] — the end is excluded.

In [70]:
nums = [10, 20, 30, 40, 50]
print("First three:", nums[:3])
print("Middle:", nums[1:4])
print("Every other number:", nums[::2])
print("Reversed:", nums[::-1])
First three: [10, 20, 30]
Middle: [20, 30, 40]
Every other number: [10, 30, 50]
Reversed: [50, 40, 30, 20, 10]

Exercises 2¶

  1. course = "Python Workshop" — print len(course).
  2. Make a list of three cities, add a fourth, print the first and last.
  3. Build a dict person with name, age, hobby — print only the hobby.

Functions and modules¶

Functions¶

def name(parameters):
    return value

Parameters carry information in, return sends a value back out. Write once, reuse everywhere.

In [71]:
### First function + parameters + return
def greet(name):
    """Return a friendly greeting for the given name."""
    return f"Hello, {name}!"

msg = greet("Ada")
print(msg)
Hello, Ada!

Reading def greet(name):

  • def starts the definition, greet is the name, name is a parameter
  • the """docstring""" on the next line documents it — read it later with help(greet)

Modules¶

A module is a file of ready-made code you import.

  • math — square roots, trigonometry, π
  • datetime — dates, formatting, differences
import math
math.sqrt(16)
In [72]:
import statistics as stats

vals = [2, 4, 4, 4, 5, 5, 7, 9]
print("Mean:", stats.mean(vals))
print("Median:", stats.median(vals))
print("Stdev:", round(stats.stdev(vals), 3))
Mean: 5
Median: 4.5
Stdev: 2.138

Import selectively to pull in only what you need:

from math import sqrt

Shorter to type and explicit about what you use. Explore any module with help(math) or help(math.sqrt).

In [74]:
from datetime import date, timedelta
today = date.today()
print(today)
tomorrow = today + timedelta(days=1) # creates a one-day time difference, then adding it to today shifts the date forward by one day
print("Tomorrow:", tomorrow)
2025-11-03
Tomorrow: 2025-11-04

Visualization¶

  • plot with matplotlib
  • build a line plot and a bar chart
  • add titles, axis labels, legends

Matplotlib¶

The base plotting library — seaborn and others build on it.

import matplotlib.pyplot as plt
In [77]:
### Simple line plot

import matplotlib.pyplot as plt

# Example data
years = ["2020", "2021", "2022", "2023", "2024"]
sales = [250, 300, 400, 350, 500]

# Create a line plot
plt.plot(years, sales, marker='o', label='Annual Sales')

# Add title, labels, legend
plt.title("Company Sales Over Time")
plt.xlabel("Year")
plt.ylabel("Sales (in $K)")
plt.legend()
plt.grid(True)
plt.show()
No description has been provided for this image

Bar chart¶

Best for comparing categories.

In [76]:
categories = ["Apples", "Bananas", "Cherries", "Dates"]
values = [50, 75, 30, 90]

plt.bar(categories, values, color="skyblue")
plt.title("Fruit Sales")
plt.xlabel("Fruit Type")
plt.ylabel("Units Sold")
plt.show()
No description has been provided for this image

Mini project · The Phillips curve¶

Real data, start to finish:

  • pull two series from FRED
  • clean and merge them with pandas
  • summary statistics and a correlation
  • plot inflation against unemployment

1 · Setup¶

In [ ]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import pandas_datareader.data as web

2 · Load the FRED data¶

  • CPIAUCSL — Consumer Price Index, seasonally adjusted (a level, not a rate)
  • UNRATE — unemployment rate, percent

We convert CPI into year-over-year inflation.

In [86]:
# Define date range (feel free to adjust)
start = datetime(2015, 1, 1)
end   = datetime(2025, 12, 31)

# Read series from FRED
cpi = web.DataReader("CPIAUCSL", "fred", start, end)
print(cpi.head())
unr = web.DataReader("UNRATE",   "fred", start, end)
print(unr.head())

# Compute year-over-year inflation rate (%)
cpi['Inflation'] = cpi['CPIAUCSL'].pct_change(12) * 100
print(cpi.head())

# Merge on date index
df = pd.concat([cpi['Inflation'], unr['UNRATE']], axis=1).dropna()
df.columns = ['Inflation', 'Unemployment']

df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 116 entries, 2016-01-01 to 2025-08-01
Freq: MS
Data columns (total 2 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   Inflation     116 non-null    float64
 1   Unemployment  116 non-null    float64
dtypes: float64(2)
memory usage: 2.7 KB

What the code does¶

  1. datetime(Y, M, D) sets the date range.

  2. web.DataReader(symbol, "fred", start, end) fetches each series, indexed by date.

  3. pct_change(12) takes the 12-month change; ×100 makes it percent. The first 12 rows are NaN.

    $\text{Inflation}_t = \dfrac{CPI_t - CPI_{t-12}}{CPI_{t-12}} \times 100$

  4. pd.concat(..., axis=1) merges on the shared date index; dropna() clears the leading NaNs.

Needs pip install pandas-datareader.

3 · Summary statistics and correlation¶

In [87]:
print("Summary statistics:")
display(df.describe())

corr = df['Inflation'].corr(df['Unemployment']) # Pearson correlation by default
print(f"Correlation between Inflation and Unemployment: {corr:.3f}")
Summary statistics:
Inflation Unemployment
count 116.000000 116.000000
mean 3.150491 4.587069
std 2.127807 1.753235
min 0.198201 3.400000
25% 1.731757 3.700000
50% 2.481514 4.100000
75% 3.382786 4.700000
max 8.999298 14.800000
Correlation between Inflation and Unemployment: -0.362

4 · Trends over time¶

One plot per series, for clarity.

In [88]:
# Inflation over time
plt.figure()
plt.plot(df.index, df['Inflation'])
plt.title("Inflation Rate")
plt.xlabel("Year")
plt.ylabel("Percent")
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image
In [89]:
# Unemployment over time
plt.figure()
plt.plot(df.index, df['Unemployment'])
plt.title("Unemployment Rate (%)")
plt.xlabel("Year")
plt.ylabel("Percent")
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image

5 · The Phillips curve¶

Inflation (y) against unemployment (x), with a least-squares line from numpy.polyfit.

In [90]:
x = df['Unemployment'].to_numpy()
y = df['Inflation'].to_numpy()

# Linear fit: y = m*x + b
m, b = np.polyfit(x, y, 1) # np.polyfit with degree=1 returns slope m and intercept b (least squares)

# creates 100 evenly spaced points from a to b to draw a smooth fitted line.
line_x = np.linspace(x.min(), x.max(), 100)
line_y = m * line_x + b

plt.figure()
plt.scatter(x, y, alpha=0.6, label="Monthly observations")
plt.plot(line_x, line_y, label=f"OLS fit: y = {m:.2f}x + {b:.2f}")
plt.title("Phillips Curve: Inflation vs. Unemployment")
plt.xlabel("Unemployment Rate (%)")
plt.ylabel("Inflation Rate (YoY, %)")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image
  • np.polyfit(x, y, 1) returns slope m — inflation change per 1pp of unemployment — and intercept b
  • np.linspace(a, b, 100) gives evenly spaced points to draw the fitted line
  • alpha=0.6 keeps dense clusters readable; tight_layout() fixes label spacing