Matplotlib vs. AI-Generated Charts: A Visual Comparison
Same dataset, same story — charted manually with Matplotlib and automatically by DataStoryBot. A honest comparison of effort, quality, and control.
Matplotlib vs. AI-Generated Charts: A Visual Comparison
Matplotlib is the most widely used plotting library in the Python ecosystem. It ships with pandas, it runs everywhere, and it can produce nearly any visualization you can imagine. It is also, by universal agreement, tedious to configure for anything beyond the defaults.
DataStoryBot generates charts automatically — you upload a CSV, select a story angle, and get back publication-ready PNGs. Under the hood, it's still Matplotlib (running inside OpenAI's Code Interpreter), but the chart type selection, styling, and labeling decisions are made by the AI.
This article puts them side by side. Same dataset, same story, same final output goal — but built two different ways. The point isn't to declare a winner. It's to show where each approach earns its keep and where it falls short, so you can choose the right tool for the job.
The Dataset
We'll use a straightforward CSV: 12 months of revenue data across four regions.
month,north,south,east,west
2025-01,1240000,890000,1100000,1560000
2025-02,1180000,920000,1050000,1620000
2025-03,1310000,870000,1130000,1710000
2025-04,1290000,950000,1080000,1800000
2025-05,1350000,910000,1150000,1890000
2025-06,1400000,880000,1120000,1950000
2025-07,1380000,940000,1200000,2100000
2025-08,1420000,960000,1180000,2250000
2025-09,1460000,990000,1210000,2400000
2025-10,1510000,1020000,1250000,2580000
2025-11,1550000,1050000,1290000,2710000
2025-12,1620000,1100000,1340000,2890000
The story here is obvious even in the raw numbers: the West region is growing significantly faster than the others. Let's see how each approach visualizes that finding.
The Matplotlib Approach
Here's what it takes to produce a polished line chart with Matplotlib:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
# Load and prepare data
df = pd.read_csv("regional_revenue.csv")
df["month"] = pd.to_datetime(df["month"])
# Set up the figure with dark theme
plt.style.use("dark_background")
fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
fig.patch.set_facecolor("#141414")
ax.set_facecolor("#141414")
# Define colors
colors = {
"north": "#5B8DEF",
"south": "#F2994A",
"east": "#6FCF97",
"west": "#EB5757"
}
# Plot each region
for region in ["north", "south", "east", "west"]:
ax.plot(df["month"], df[region], label=region.capitalize(),
color=colors[region], linewidth=2.2, marker="o", markersize=4)
# Format y-axis as currency
ax.yaxis.set_major_formatter(
mticker.FuncFormatter(lambda x, p: f"${x/1e6:.1f}M")
)
# Styling
ax.set_title("Monthly Revenue by Region (2025)",
fontsize=16, color="#ffffff", pad=15, fontweight="bold")
ax.set_xlabel("Month", fontsize=12, color="#cccccc")
ax.set_ylabel("Revenue", fontsize=12, color="#cccccc")
ax.tick_params(colors="#999999", labelsize=10)
ax.legend(framealpha=0.3, fontsize=11, loc="upper left")
# Grid
ax.grid(True, alpha=0.15, linestyle="--")
ax.set_axisbelow(True)
# Clean up spines
for spine in ax.spines.values():
spine.set_color("#333333")
# Annotate the West region's acceleration
ax.annotate("West: +85% YoY",
xy=(df["month"].iloc[-1], df["west"].iloc[-1]),
xytext=(-120, -25), textcoords="offset points",
fontsize=10, color="#EB5757",
arrowprops=dict(arrowstyle="->", color="#EB5757", lw=1.5))
plt.tight_layout()
plt.savefig("revenue_matplotlib.png", transparent=True, bbox_inches="tight")
plt.close()
That's 47 lines of code. It produces a clean, professional chart. The dark theme matches a modern presentation aesthetic. The annotation calls out the key insight. Every detail — colors, font sizes, axis formatting, grid opacity — was chosen deliberately.
Time to write this code from scratch, including looking up the FuncFormatter syntax and fiddling with the annotation positioning: 20-30 minutes for an experienced developer. Longer if you haven't used Matplotlib recently.
The DataStoryBot Approach
Here's the same visualization generated through the API:
import requests
BASE_URL = "https://datastory.bot/api"
# Step 1: Upload
with open("regional_revenue.csv", "rb") as f:
upload = requests.post(f"{BASE_URL}/upload", files={"file": f}).json()
container_id = upload["containerId"]
# Step 2: Analyze with focus on regional comparison
stories = requests.post(f"{BASE_URL}/analyze", json={
"containerId": container_id,
"steeringPrompt": "Compare revenue trends across regions. Highlight the fastest-growing region."
}).json()
print("Story angles:")
for s in stories:
print(f" - {s['title']}")
# Step 3: Refine
result = requests.post(f"{BASE_URL}/refine", json={
"containerId": container_id,
"selectedStoryTitle": stories[0]["title"]
}).json()
# Download charts
for i, chart in enumerate(result["charts"]):
img = requests.get(f"{BASE_URL}/files/{container_id}/{chart['fileId']}")
with open(f"revenue_ai_{i+1}.png", "wb") as f:
f.write(img.content)
print(f"Saved: {chart['caption']}")
That's 25 lines, and most of them are boilerplate API calls. No chart type decision. No color palette selection. No axis formatting. No annotation positioning.
Time to write and run: 2-3 minutes. Most of that is waiting for the API response.
What You Get: A Side-by-Side Comparison
Both approaches produce dark-themed, high-DPI PNG charts suitable for presentations. But the outputs differ in important ways.
Chart Type Selection
Matplotlib: You chose a line chart because you know time-series data works well as lines. That decision took experience and maybe 10 seconds of thought.
DataStoryBot: The Code Interpreter examines the data shape — a date column and four numeric columns — and arrives at the same conclusion: line chart. But it might also generate a stacked area chart showing cumulative revenue, or a bar chart comparing total annual revenue by region. You get 2-3 charts per refine call, each chosen to highlight a different aspect of the story.
The AI sometimes picks chart types you wouldn't have chosen — and occasionally that's a better choice than what you'd have made manually. A grouped bar chart of Q1 vs. Q4 revenue by region might highlight the West's growth more dramatically than a line chart. The AI tries multiple approaches and keeps the ones that support the narrative.
Styling and Polish
Matplotlib: You have total control. Every pixel is configurable. The chart looks exactly the way you want it — but only because you spent 20 minutes making it so. Change the dataset and you need to re-tune: adjust axis limits, move annotations, maybe switch from a legend to direct labels.
DataStoryBot: The styling is consistent but not customizable at the CSS level. Dark background (#141414), consistent color palette, proper labels and legends. It handles axis formatting automatically — currency columns get dollar signs, percentage columns get % symbols, dates get readable labels. The output is good enough for 90% of use cases. For the other 10%, you need Matplotlib.
Annotations and Callouts
Matplotlib: You added the "West: +85% YoY" annotation manually. You knew where to place it, what to say, and how to style the arrow. This is domain knowledge expressed as code.
DataStoryBot: The generated charts include annotations when the story warrants them — but the AI decides what to annotate. It might highlight the growth rate, or the crossover point where West surpassed North, or the month with the largest month-over-month jump. The annotation content is often good, sometimes great, occasionally misses what you'd have emphasized.
Captions
Matplotlib: No caption unless you add one yourself (via fig.text() or in the document where you embed the chart).
DataStoryBot: Every chart comes with a descriptive caption in the API response: "Monthly revenue by region, Jan-Dec 2025, showing West region outperformance (+85% YoY) versus single-digit growth in other regions." These captions are generated from the analysis and are designed to be used as <figcaption> elements or alt text.
The Honest Tradeoffs
Here's where this comparison gets useful. Neither approach is universally better — they optimize for different things.
Where Matplotlib Wins
Full creative control. If you need a specific layout — dual y-axes, inset plots, custom tick positions, corporate brand colors with exact hex codes — Matplotlib can do it. DataStoryBot gives you a house style. Matplotlib gives you an empty canvas.
Reproducibility. The same Matplotlib script produces the same chart every time. DataStoryBot's Code Interpreter might make slightly different styling decisions on repeated runs. For publications, regulatory filings, or any context where exact reproducibility matters, write your own code.
Custom chart types. Sankey diagrams, chord diagrams, geographic maps, network graphs — Matplotlib (with extensions like networkx, geopandas, or plotly) handles specialized visualizations that DataStoryBot won't attempt.
Iteration speed on a known chart. If you've already built the chart and just need to update it with new data, re-running a Matplotlib script takes seconds. DataStoryBot re-analyzes from scratch every time because containers are ephemeral.
No API dependency. Matplotlib runs locally. No network calls, no container spin-up time, no 20-minute TTL. For air-gapped environments or latency-sensitive pipelines, local execution wins.
Where DataStoryBot Wins
Speed for new analyses. Going from "I have a CSV" to "I have a polished chart with a caption" takes 30 seconds via the API versus 20-30 minutes with Matplotlib. For exploratory work where you're charting many datasets quickly, the time savings compound.
Chart type intelligence. The AI picks appropriate chart types based on the data shape and the story angle. It won't create a pie chart for 50 categories or a line chart for non-temporal data. These are judgment calls that require experience — or an AI that's seen thousands of datasets.
Narrative context. The charts come with a written narrative and captions. You don't just get a visualization — you get the story it tells. The chart and the narrative are designed together, so the visual evidence matches the written claims.
No configuration debt. Matplotlib charts accumulate configuration: color definitions, font sizes, spacing values, formatter functions. Change the data shape and half the configuration breaks. DataStoryBot adapts to whatever data you upload.
Non-technical accessibility. You can explain "upload a CSV and get charts" to anyone. You cannot explain ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, p: f"${x/1e6:.1f}M")) to anyone outside your engineering team.
A Hybrid Approach
In practice, the two approaches complement each other. Here's a workflow that uses both:
import requests
import matplotlib.pyplot as plt
import pandas as pd
BASE_URL = "https://datastory.bot/api"
# Step 1: Use DataStoryBot for exploration and narrative
with open("regional_revenue.csv", "rb") as f:
upload = requests.post(f"{BASE_URL}/upload", files={"file": f}).json()
stories = requests.post(f"{BASE_URL}/analyze", json={
"containerId": upload["containerId"]
}).json()
result = requests.post(f"{BASE_URL}/refine", json={
"containerId": upload["containerId"],
"selectedStoryTitle": stories[0]["title"]
}).json()
# Use the AI-generated narrative
print(result["narrative"])
# Step 2: Use Matplotlib for the final publication chart
# Now you know the story — West region dominance
# Build the exact chart your publication needs
df = pd.read_csv("regional_revenue.csv")
df["month"] = pd.to_datetime(df["month"])
fig, ax = plt.subplots(figsize=(10, 6), dpi=300) # Higher DPI for print
# ... your custom Matplotlib code here, informed by the AI's analysis
Use DataStoryBot to find the story and draft the narrative. Use Matplotlib to build the exact chart your deliverable requires. The AI handles the analysis and writing; you handle the pixel-perfect final visualization.
Cost Comparison
A practical comparison for a team that produces 10 charts per week:
| Factor | Matplotlib | DataStoryBot |
|---|---|---|
| Time per chart | 20-30 min | 30 sec |
| Weekly time (10 charts) | 3-5 hours | 5 min |
| Setup cost | Library install | None (API) |
| Ongoing maintenance | Update scripts when data changes | None |
| Customization ceiling | Unlimited | Moderate |
| Narrative included | No | Yes |
| API key required | No | No (open beta) |
The time savings are real, but they come with a customization ceiling. If your team needs 10 quick charts for a weekly standup, DataStoryBot is the obvious choice. If your team needs 2 highly polished charts for a board presentation, Matplotlib (or a design tool) is worth the investment.
Requesting Specific Chart Styles
You can influence DataStoryBot's chart output through the refinementPrompt:
result = requests.post(f"{BASE_URL}/refine", json={
"containerId": container_id,
"selectedStoryTitle": stories[0]["title"],
"refinementPrompt": "For charts: use a bar chart comparing total annual revenue by region. Annotate the percentage growth for each region. Use a sequential blue palette."
}).json()
This doesn't give you Matplotlib-level control, but it covers the most common requests: chart type, annotation content, and color direction. The Code Interpreter interprets these instructions and adjusts its generated code accordingly.
When to Use Which
Use Matplotlib when:
- You need exact visual specifications (brand guidelines, publication standards).
- The chart type is specialized (geographic, network, 3D).
- Reproducibility matters — the same input must produce the identical output.
- You're building a production dashboard with a fixed chart design that updates with new data.
Use DataStoryBot when:
- You're exploring a new dataset and want quick visual insights.
- You need charts and a written narrative together.
- The audience cares about the story, not the chart styling.
- You're producing many charts across different datasets (weekly reports, client deliverables).
- You don't have a data visualization specialist on the team.
Use both when:
- You need the AI to find the story and Matplotlib to tell it with pixel-perfect precision.
- You're building a report pipeline where AI drafts the first version and humans refine the visuals for publication.
What to Read Next
For a deeper look at the chart types DataStoryBot generates and how to embed them in applications, read how to generate charts from CSV automatically.
For controlling chart quality and resolution in API-generated outputs, see publication-quality charts via API.
Or try the comparison yourself: upload a CSV to the DataStoryBot playground, download the generated charts, and see how they compare to what you'd build manually.
Ready to find your data story?
Upload a CSV and DataStoryBot will uncover the narrative in seconds.
Try DataStoryBot →