Excelgoodies logo +44 (0)20 3769 3689

LEARN THIS HANDS ON

Excel VBA Programming

. Live Online FILLING FAST
View all upcoming batches
Replacing Legacy VBA Macros with Python in Excel: What You Can Safely Migrate Today

Replacing Legacy VBA Macros with Python in Excel: What You Can Safely Migrate Today

You don’t need to rewrite your whole Excel automation stack to start using Python. This article shows which legacy VBA macros you can safely migrate to Python today, what should stay in VBA (for now), and how to run both side by side without breaking your users’ workbooks.

If you’re planning a gradual transition, it’s worth investing in solid VBA foundations first; structured skills from something like a focused Excel automation deep dive make the migration decisions much easier.


1. The Reality Check: Python in Excel Is Powerful, But Not a Drop‑In VBA Replacement

Before touching code, align expectations.

Python in Excel (and Python + Excel via tools like xlwings, openpyxl, pandas, etc.) is great for:

  • Data cleaning and transformation
  • Heavy calculations and algorithms
  • File and API integration
  • Reusable business logic outside the workbook

But VBA still wins for:

  • Deep UI integration (buttons, forms, ribbons)
  • Tight event handling (Workbook_Open, Worksheet_Change, etc.)
  • Legacy COM/Office automation (Word, Outlook, Access)

Think of Python as your engine for logic and data processing, and VBA as your glue for Excel’s UI and event model. Most real-world teams end up with a hybrid.


2. VBA Patterns That Migrate Very Well to Python

These are the low-risk, high-benefit migrations you can do early.

2.1 Heavy Data Crunching in Loops

If you see:

  • Nested For loops over tens of thousands of cells
  • Complex conditional logic per row
  • Performance issues and screen flicker

…it’s a perfect Python candidate.

Typical VBA:

Sub FlagLargeOrders()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim i As Long

    Set ws = ThisWorkbook.Sheets("Orders")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    For i = 2 To lastRow
        If ws.Cells(i, "C").Value > 10000 Then
            ws.Cells(i, "D").Value = "Large"
        Else
            ws.Cells(i, "D").Value = "Normal"
        End If
    Next i
End Sub

Python equivalent with pandas (reading from Excel, processing, writing back):

import pandas as pd

file_path = "orders.xlsx"

# Read
df = pd.read_excel(file_path, sheet_name="Orders")

# Transform
df["Category"] = df["Amount"].apply(lambda x: "Large" if x > 10000 else "Normal")

# Write back (replace sheet)
with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
    df.to_excel(writer, sheet_name="Orders", index=False)

When this is safe:

  • The macro is mostly data logic, not UI
  • The workbook isn’t relying on step-by-step cell updates
  • Users are comfortable running a script or using a small wrapper button

2.2 Data Cleaning and Normalisation

Anything involving:

  • Trimming, uppercasing, replacing strings
  • Splitting/merging columns
  • Deduplicating rows

…is usually cleaner and more maintainable in Python.

Example: cleaning customer names

VBA version:

Sub CleanNames()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim i As Long

    Set ws = ThisWorkbook.Sheets("Customers")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    For i = 2 To lastRow
        With ws.Cells(i, "A")
            .Value = WorksheetFunction.Trim(.Value)
            .Value = UCase(.Value)
        End With
    Next i
End Sub

Python version:

import pandas as pd

file_path = "customers.xlsx"

df = pd.read_excel(file_path, sheet_name="Customers")

df["Name"] = (
    df["Name"]
    .astype(str)
    .str.strip()
    .str.replace(r"\s+", " ", regex=True)
    .str.upper()
)

with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
    df.to_excel(writer, sheet_name="Customers", index=False)

This kind of migration is usually straightforward and gives immediate performance and readability gains.


2.3 Complex Business Rules and Reuse Across Workbooks

If you find yourself copy-pasting the same VBA module into multiple workbooks, that logic probably belongs in Python:

  • Pricing engines
  • Risk/scoring models
  • Allocation rules

Why Python helps here:

  • You can put the logic in a single package
  • Version control is easier (Git, etc.)
  • You can test logic outside Excel

Example structure:

# pricing_engine.py

DISCOUNT_TIERS = [
    (0, 1000, 0.00),
    (1000, 5000, 0.05),
    (5000, float("inf"), 0.1),
]

def calculate_discount(amount: float) -> float:
    for lower, upper, rate in DISCOUNT_TIERS:
        if lower <= amount < upper:
            return amount * rate
    return 0.0

Then your Excel-facing script just:

import pandas as pd
from pricing_engine import calculate_discount

file_path = "orders.xlsx"

df = pd.read_excel(file_path, sheet_name="Orders")

df["Discount"] = df["Amount"].apply(calculate_discount)

with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
    df.to_excel(writer, sheet_name="Orders", index=False)

The workbook becomes a thin shell around a tested Python library.


2.4 External Integrations (Files, APIs, Databases)

Python is usually a better fit than VBA for:

  • REST APIs (authentication, paging, JSON handling)
  • SFTP/FTP and cloud storage
  • Databases beyond simple ODBC

VBA can do all of this, but the code is more verbose and fragile. Python’s libraries are mature and easier to maintain.

Example: Pulling data from a REST API to Excel

import requests
import pandas as pd

url = "https://api.example.com/sales"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()

data = response.json()

df = pd.json_normalize(data)

with pd.ExcelWriter("sales.xlsx", engine="openpyxl") as writer:
    df.to_excel(writer, sheet_name="API_Data", index=False)

3. VBA Areas You Should NOT Rush to Replace

Some VBA is still the right tool. Replacing it with Python today either isn’t possible or adds more complexity than value.

3.1 Deep Excel UI Automation

VBA is still the native language for:

  • Command buttons on sheets
  • Shapes, charts, and form controls events
  • Custom ribbon buttons tied directly to macros

Python can integrate with Excel (e.g., xlwings can bind to buttons), but:

  • Deployment is heavier (users need Python + packages)
  • IT sign-off can be harder
  • Debugging for non-developers is tougher

Keep in VBA:

  • Simple button click handlers that call Python indirectly
  • Dialogs that are purely Excel-based (InputBox, MsgBox) for casual users

Hybrid pattern:

Use VBA for the UI and call Python for the heavy work.

' VBA macro that calls a Python script
Sub RunPythonProcessing()
    Dim shell As Object
    Dim pythonExe As String
    Dim scriptPath As String

    pythonExe = "C:\Python311\python.exe"
    scriptPath = ThisWorkbook.Path & "\process_orders.py"

    Set shell = CreateObject("WScript.Shell")
    shell.Run """" & pythonExe & """ """" & scriptPath & """"", 1, True

    MsgBox "Processing finished.", vbInformation
End Sub

3.2 Event-Driven Workbook Logic

Anything that relies on Excel events is hard to fully move to Python right now:

  • Worksheet_Change, Worksheet_BeforeDoubleClick
  • Workbook_Open, Workbook_BeforeClose

You can sometimes simulate this with:

  • A VBA event that calls Python
  • Scheduled Python jobs outside Excel

…but full replacement is rarely worth it unless you’re moving away from Excel entirely.

Keep in VBA when:

  • Logic must fire immediately on user action
  • Users expect instant feedback in the sheet

3.3 Tight Office COM Automation

If your VBA:

  • Generates Word reports from Excel
  • Sends Outlook emails with attachments and formatting
  • Pushes data into Access or PowerPoint via COM

…you can technically do this in Python via pywin32, but you gain little and add complexity.

Guideline:

  • If it’s working and not a performance bottleneck, leave it in VBA
  • Only move to Python when you’re centralising the whole process on a server or in a service (where Office automation may not even be supported)

4. A Practical Migration Strategy: Hybrid, Not Big Bang

4.1 Classify Your Existing Macros

Go through your key workbooks and tag each macro:

  • Type A – Data logic / batch processing
    • Loops over ranges, data cleaning, calculations
  • Type B – UI / events
    • Buttons, forms, event handlers
  • Type C – External automation
    • Outlook, Word, Access, file system

Plan:

  1. Migrate Type A to Python first
  2. Keep Type B mostly in VBA, possibly calling Python
  3. Leave Type C in VBA unless you have a strong reason to move it

4.2 Start with One High-Impact Use Case

Pick a macro that:

  • Runs slowly or fails on large files
  • Is business-critical but logically simple
  • Doesn’t depend heavily on UI

Rewrite it in Python, but keep the workbook structure intact. Use VBA only as a launcher if needed.

4.3 Build a Simple Run Pattern

You need a predictable way for users (and yourself) to run Python code.

Options:

  1. Direct script execution

    • Pros: simple
    • Cons: users must leave Excel
  2. VBA button that calls Python (as shown earlier)

    • Pros: feels like a normal macro
    • Cons: needs a consistent Python path
  3. Tools like xlwings

    • Pros: tight integration, can call Python functions as if they were macros
    • Cons: extra dependency, some setup overhead

Choose one approach and standardise it across your team.


5. Technical Considerations: Avoiding Pain Later

5.1 Environment and Dependencies

Decide early:

  • Which Python version you support
  • How packages will be installed and updated
  • Where scripts live (shared drive, Git repo, local copies)

Write this down. Half of the “Python doesn’t work” tickets are environment issues.

5.2 Data Exchange Between Excel and Python

Common patterns:

  • Read/write entire sheets with pandas.read_excel / to_excel
  • Use a dedicated “Data” sheet as the interface
  • Avoid fragile cell-level coupling; pass tables, not scattered cells

Design the workbook so that:

  • VBA handles user input and validation
  • Python reads a clean table and writes a clean output table

5.3 Testing and Version Control

For logic-heavy Python, add:

  • Basic unit tests for core functions
  • A simple versioning scheme (e.g., script header with version/date)

For VBA that calls Python, keep:

  • A clear mapping of which macro calls which script
  • A simple way to roll back to a previous script version

6. Concrete Example: Hybrid Workflow for a Monthly Report

Imagine a monthly sales report workbook with this legacy flow:

  1. User clicks a button
  2. VBA imports CSVs from a folder
  3. VBA cleans and merges data
  4. VBA refreshes pivot tables and charts
  5. VBA emails a summary via Outlook

Modern hybrid approach:

  • Step 2–3 (data import + clean): move to Python
  • Step 1, 4, 5 (UI + pivots + email): keep in VBA

VBA button:

Sub RunMonthlyProcess()
    Application.ScreenUpdating = False

    ' 1. Call Python to import & clean data
    Call RunPythonProcessing

    ' 2. Refresh pivots
    Dim pc As PivotCache
    For Each pc In ThisWorkbook.PivotCaches
        pc.Refresh
    Next pc

    ' 3. Send email summary (still in VBA)
    Call SendSummaryEmail

    Application.ScreenUpdating = True

    MsgBox "Monthly report updated.", vbInformation
End Sub

Python script (simplified):

import pandas as pd
from pathlib import Path

BASE = Path(__file__).parent
csv_folder = BASE / "data"
excel_file = BASE / "MonthlyReport.xlsx"

files = list(csv_folder.glob("sales_*.csv"))

dfs = [pd.read_csv(f) for f in files]

df = pd.concat(dfs, ignore_index=True)

# Basic cleaning
cols = {c: c.strip() for c in df.columns}

df = df.rename(columns=cols)

# Example transformation

df["Amount"] = df["Amount"].fillna(0)

with pd.ExcelWriter(excel_file, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
    df.to_excel(writer, sheet_name="Data", index=False)

You get the performance and maintainability of Python where it matters, without rewriting the whole user experience.


One Practical Takeaway

Don’t try to replace VBA with Python everywhere. Start by identifying the 1–3 macros that are mostly data logic and slow or hard to maintain, migrate only those to Python, and keep VBA as the orchestration and UI layer. Once that hybrid pattern is stable, expand it gradually instead of planning a risky full rewrite.

VBA & Python

New

Next Batches Now Live

Power BIPower BI
SQLSQL
Power AppsPower Apps
Power AutomatePower Automate
Microsoft FabricMicrosoft Fabrics
AzureAzure Data Engineering
Explore Dates & Reserve Your Spot → Reserve Your Spot →