Smart Narrative in Power BI is one of those features that feels like magic the first time you try it. It automatically creates textual summaries of your visuals and helps users interpret data without having to scan every chart. For dashboards aimed at non-technical audiences, or for stakeholders who prefer a clear explanation over visual clutter, it’s a real win.
The problem is that Smart Narrative is not available if you are on Premium Per User. It’s locked behind full Premium capacity. So what happens if you still want narrative-driven insights but don’t have access to Premium?
You build your own using Python.
Why Python Works So Well with Power BI
Python visuals in Power BI unlock far more than most people realise. You’re no longer limited to native visuals or DAX summaries. With a few lines of Python you can generate custom text blocks, annotate trends, and even replicate something close to Smart Narrative, right inside your report.
The best part is flexibility. You decide how the narrative is written, which figures to highlight, and how the text should be styled. That means you can adapt it to your audience and keep things consistent across different months or metrics.
Let’s go through a real example using the AdventureWorks DW dataset.
Use Case: Spotting Top Movers in Sales
Imagine you want to answer a simple question:
Which three products saw the biggest increase in sales this month compared to last, and which three dropped the most?
That’s exactly the kind of insight stakeholders love. Here’s how to build it in Power BI with a Python visual.
Step 1. Add a Python visual
Drop a Python visual onto your report page. Power BI will prompt you to enable Python scripting and select your interpreter.
Step 2. Add your fields
From your model, drag in EnglishProductName, SalesAmount, and OrderDate. Power BI passes these into a DataFrame called dataset.
Step 3. Paste the Python code
Copy the script below into the Python editor. This code calculates the month-over-month changes, finds the top three increases and decreases, and then displays them as a neat text block.
import pandas as pd
import matplotlib.pyplot as plt
def format_dollars(value):
abs_val = abs(value)
if abs_val >= 1_000_000_000:
return f"${value / 1_000_000_000:.2f}B"
elif abs_val >= 1_000_000:
return f"${value / 1_000_000:.2f}M"
elif abs_val >= 1_000:
return f"${value / 1_000:.2f}K"
else:
return f"${value:.2f}"
# Prepare data
df = dataset.copy()
df.rename(columns={
"EnglishProductName": "Product",
"SalesAmount": "Amount",
"OrderDate": "Date"
}, inplace=True)
df["Date"] = pd.to_datetime(df["Date"], errors="coerce")
df.dropna(subset=["Date"], inplace=True)
df["Month"] = df["Date"].dt.to_period("M")
df.sort_values("Date", inplace=True)
# Summarise and calculate change
monthly_sales = df.groupby(["Product", "Month"])["Amount"].sum().unstack(fill_value=0)
change = monthly_sales.diff(axis=1)
recent_month = change.columns[-1]
previous_month = change.columns[-2]
title_text = (
f"Month-over-Month Trend ("
f"{previous_month.to_timestamp().strftime('%B %Y')} → "
f"{recent_month.to_timestamp().strftime('%B %Y')})"
)
recent_change = change[recent_month].dropna()
top_increases = recent_change.sort_values(ascending=False).head(3)
top_decreases = recent_change.sort_values().head(3)
summary_lines = ["Top 3 Increases in Sales Amount:"]
for prod, val in top_increases.items():
summary_lines.append(f"- {prod}: +{format_dollars(val)}")
summary_lines.append("")
summary_lines.append("Top 3 Decreases in Sales Amount:")
for prod, val in top_decreases.items():
summary_lines.append(f"- {prod}: {format_dollars(val)}")
summary_text = "\n".join(summary_lines)
# Render visual
fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
ax.axis("off")
ax.text(0, 1, title_text, fontsize=22, weight="bold", va="top", ha="left")
ax.text(0, 0.82, summary_text, fontsize=18, va="top", ha="left", wrap=True, linespacing=1.6)
plt.show()
Output:

What the Script Does
Here’s the logic in plain English.
- Dollar amounts are reformatted into easier-to-read K, M, or B values.
- The dataset is cleaned, columns renamed, and order dates converted into proper date objects.
- Sales are grouped by product and month, then compared using a simple month-over-month difference.
- The latest two months are isolated, and the top three increases and decreases are identified.
- A narrative text block is created, and matplotlib renders it cleanly onto the canvas.
Beyond the technical breakdown, this is also a good reminder of why Python for Data Science is such a crucial skill. It is not just about statistics or machine learning. In this case Python is being used as a narrative engine, transforming raw numbers into clear, readable insights that decision makers can instantly understand. That is essentially what Smart Narrative does out of the box, and here we are recreating the same idea with a more open and flexible approach.
If you are aiming to build a career in data, I would strongly suggest putting Python on your learning path. It gives you the freedom to extend Power BI, automate repetitive tasks, and create insights in ways that the standard tools simply cannot match. For anyone who wants to become a well-rounded data professional, Python is a skill that pays dividends.
If you’re on PPU and feel left out of Smart Narrative, don’t worry. With a small amount of Python you can build your own narrative layer that is just as effective, and arguably more flexible.
Power BI and Python together create a fantastic canvas for storytelling. Whether you want to highlight anomalies, surface trends, or simply guide users through a complex set of metrics, this approach goes beyond charts and into genuine insight.
So next time someone asks, “What’s changed this month?”, you’ll have the answer ready – in words.

