Lesson 15 · Python for Banking and Finance
How to Build Core Banking Views with Python for Financial Data Management
We will learn how to create core banking views using Python and pandas. This problem is important because banks need to aggregate data for regulatory…
- CoursePython for Banking and Finance
- Lesson15 of 24
- Video22 min
- FormatJupyter notebook · 20 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbCore Banking Views: Building Foundational Analytics and Summaries#
- We will learn how to create core banking views using Python and pandas.
- This problem is important because banks need to aggregate data for regulatory reporting, customer insights, and risk management.
- By working through business-driven examples, you will learn how to assemble transaction, customer, and account data into meaningful analytics.
- We will build a simple but powerful foundation for real-world banking data analysis.
- You will gain skill at using Python to create data aggregations, summaries, and tables essential for everyday banking operations.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)
Understanding Core Bank Data: Transactions, Customers, Accounts#
- Transactions are records of customer payments or deposits and contain dates, types, and channels.
- Customers have attributes like region and segment, and are linked to their accounts.
- Accounts track balances and account types like Savings or Cheque, with unique identifiers.
- Beginners often miss that real banking data is always relational: all tables are linked by IDs.
- Data quality is critical: duplicated, missing, or mismatched IDs cause broken joins and bad analytics.
n_transactions = 1000
n_customers = 200
df = pd.DataFrame({
'transaction_id': range(1, n_transactions + 1),
'customer_id': np.random.choice([f'CUST_{i:04d}' for i in range(1, n_customers + 1)], n_transactions),
'amount': np.round(np.random.normal(150, 60, n_transactions), 2),
'transaction_type': np.random.choice(['Debit', 'Credit'], n_transactions),
'channel': np.random.choice(['ATM', 'Online', 'Branch', 'POS'], n_transactions),
'date': pd.date_range(start='2024-01-01', periods=n_transactions, freq='h')
})
print(df.shape)
print(df.head(3))
customer_ids = [f'CUST_{i:04d}' for i in range(1, 201)]
customers = pd.DataFrame({
'customer_id': customer_ids,
'segment': ['Retail'] * 150 + ['Business'] * 50,
'region': ['Metro'] * 100 + ['Regional'] * 100
})
print(customers.shape)
print(customers.head(3))
accounts = pd.DataFrame({
'account_id': [f'ACC_{i:05d}' for i in range(1, 201)],
'customer_id': customer_ids,
'account_type': np.random.choice(['Savings', 'Cheque', 'Credit'], size=200),
'open_date': pd.date_range(start='2015-01-01', periods=200, freq='30D')
})
print(accounts.shape)
print(accounts.head(3))
print(df['channel'].value_counts())
print(df.groupby('transaction_type')['amount'].mean())
df['amount'].hist(bins=30)
daily_totals = df.groupby(df['date'].dt.date)['amount'].sum()
print(daily_totals.head())
channel_crosstab = pd.crosstab(df['channel'], df['transaction_type'])
print(channel_crosstab)
merged = df.merge(customers, on='customer_id', how='left')
channel_by_seg = merged.groupby(['segment', 'channel']).size().unstack()
print(channel_by_seg)
avg_amount_region = merged.groupby('region')['amount'].mean()
print(avg_amount_region)
pivot = merged.pivot_table(index='region', columns='transaction_type', values='amount', aggfunc='sum')
print(pivot)
accounts_per_region = accounts.merge(customers, on='customer_id').groupby('region')['account_id'].count()
print(accounts_per_region)
activity_per_customer = df.groupby('customer_id').size()
top10_customers = activity_per_customer.sort_values(ascending=False).head(10)
print(top10_customers)
Handling Data Quality Issues in Banking Views#
- Missing or duplicated IDs cause broken joins.
- Negative or zero amounts can signal data capture errors.
- Always check for unexpected nulls or out-of-bounds values after merging.
- Print samples early and often to catch mistakes before deeper analysis.
print(df[df['amount'] <= 0].head())
print(merged[merged['customer_id'].isnull()].head())
Best Practices in Building Bank Data Views#
- Always start by profiling data for nulls and range errors.
- Use explicit joins and always check output shapes.
- Verify all ID relationships before business analysis.
- Use meaningful column names so business partners understand your work.
- Save clean, intermediate tables as checkpoints.
merged.to_csv('customer_transactions.csv', index=False)
print('Saved cleaned customer transaction dataset.')
# End-to-end Example: Calculate total monthly debit value by customer segment
df['month'] = df['date'].dt.to_period('M')
enriched = df.merge(customers, on='customer_id', how='left')
monthly_debit = enriched[enriched['transaction_type'] == 'Debit'].groupby(['month', 'segment'])['amount'].sum().unstack()
print(monthly_debit.head())
# Final Practice: Calculate share of business customers by region with at least one credit transaction
business_credits = merged[(merged['segment'] == 'Business') & (merged['transaction_type'] == 'Credit')]
business_region_counts = business_credits.groupby('region')['customer_id'].nunique()
all_business_counts = customers[customers['segment'] == 'Business'].groupby('region')['customer_id'].nunique()
share = (business_region_counts / all_business_counts * 100).round(2)
print(share)
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



