Lesson 4 · Pandas Projects
Build a Personal Finance Budget Tracker in Python with Pandas
A complete, standalone tutorial: build six months of synthetic transactions, then categorize, summarize, and track savings with pandas. No prior pandas…
- CoursePandas Projects
- Lesson4 of 10
- Video17 min
- FormatJupyter notebook · 15 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPandas for Personal Finance: Building a Budget Tracker#
- A complete, standalone tutorial: build six months of synthetic transactions, then categorize, summarize, and track savings with pandas.
- No prior pandas experience needed. Let's jump straight in.
Before You Start#
- Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
- If pandas isn't installed yet, open a terminal in VS Code and run: pip install pandas
Part 1: Building the Dataset#
import pandas as pd
import numpy as np
print(pd.__version__)
Generating Six Months of Transactions#
rng = np.random.default_rng(seed=44)
expense_categories = {
'Rent': (1400, 1400),
'Groceries': (60, 160),
'Utilities': (80, 220),
'Dining Out': (15, 90),
'Entertainment': (10, 70),
'Transport': (20, 100)
}
print(list(expense_categories.keys()))
rows = []
months = pd.date_range('2026-01-01', periods=6, freq='MS')
for month_start in months:
rows.append([month_start + pd.Timedelta(days=0), 'Salary Deposit', 'Income', round(float(rng.uniform(3800, 4200)), 2)])
rows.append([month_start + pd.Timedelta(days=14), 'Salary Deposit', 'Income', round(float(rng.uniform(3800, 4200)), 2)])
rows.append([month_start + pd.Timedelta(days=1), 'Monthly Rent', 'Rent', -1400.0])
n_extra = int(rng.integers(10, 18))
for _ in range(n_extra):
category = rng.choice([c for c in expense_categories if c != 'Rent'])
low, high = expense_categories[category]
day_offset = int(rng.integers(0, 28))
amount = -round(float(rng.uniform(low, high)), 2)
rows.append([month_start + pd.Timedelta(days=day_offset), f'{category} purchase', category, amount])
transactions = pd.DataFrame(rows, columns=['date', 'description', 'category', 'amount'])
print(transactions.shape)
transactions.to_csv('personal_transactions.csv', index=False)
transactions = pd.read_csv('personal_transactions.csv', parse_dates=['date'])
transactions = transactions.sort_values('date').reset_index(drop=True)
transactions.head()
Part 2: First Look at the Data#
print(transactions.shape)
transactions.info()
transactions['category'].value_counts()
Part 3: Income vs. Expense#
transactions['type'] = np.where(transactions['amount'] > 0, 'Income', 'Expense')
transactions[['description', 'category', 'amount', 'type']].head()
total_income = transactions.loc[transactions['type'] == 'Income', 'amount'].sum()
total_expense = transactions.loc[transactions['type'] == 'Expense', 'amount'].sum()
print(f'Total income: ${total_income:,.2f}')
print(f'Total expenses: ${total_expense:,.2f}')
print(f'Net over 6 months: ${total_income + total_expense:,.2f}')
Part 4: Monthly Summary#
transactions['month'] = transactions['date'].dt.to_period('M')
monthly_summary = transactions.groupby(['month', 'type'])['amount'].sum().unstack(fill_value=0)
monthly_summary['net'] = monthly_summary['Income'] + monthly_summary['Expense']
monthly_summary.round(2)
Savings Rate#
monthly_summary['savings_rate_pct'] = (monthly_summary['net'] / monthly_summary['Income'] * 100).round(1)
monthly_summary[['Income', 'Expense', 'net', 'savings_rate_pct']]
Part 5: Category Breakdown#
expenses_only = transactions[transactions['type'] == 'Expense']
category_totals = expenses_only.groupby('category')['amount'].sum().abs().sort_values(ascending=False)
category_totals
pivot = pd.pivot_table(expenses_only, values='amount', index='month', columns='category', aggfunc='sum', fill_value=0).abs().round(2)
pivot
Part 6: Running Balance#
starting_balance = 2000
transactions['running_balance'] = starting_balance + transactions['amount'].cumsum()
transactions[['date', 'description', 'amount', 'running_balance']].tail()
Part 7: Visualizing the Results#
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
colors = ['seagreen' if v >= 0 else 'crimson' for v in monthly_summary['net']]
monthly_summary['net'].plot(kind='bar', ax=ax, color=colors)
ax.set_title('Net Income by Month')
ax.set_ylabel('Net ($)')
ax.axhline(0, color='black', linewidth=0.8)
plt.tight_layout()
plt.savefig('monthly_net.png', dpi=150)
plt.close(fig)
print('Saved monthly_net.png')
fig, ax = plt.subplots(figsize=(8, 5))
category_totals.plot(kind='bar', ax=ax, color='darkorange')
ax.set_title('Total Spending by Category (6 Months)')
ax.set_ylabel('Spending ($)')
plt.tight_layout()
plt.savefig('spending_by_category.png', dpi=150)
plt.close(fig)
print('Saved spending_by_category.png')
Wrap-Up: What You Learned#
- Generating a realistic synthetic six months of income and expenses, then saving and reloading with to_csv and read_csv.
- Labeling income versus expense with np.where, based on the sign of a column.
- Monthly grouping with dt.to_period, unstack, and a computed savings rate.
- Category breakdowns with groupby and a month-by-category pivot_table.
- Reconstructing a running account balance with cumsum.
- Color-coded, presentation-ready budget charts with matplotlib.
- You went from raw synthetic transactions to a full personal budget report with a savings rate and running balance. If you want the next dataset in this series to land in your feed automatically, subscribing is the move see you in the next one.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



