
Generate Visualizations and Graphs with Your AI Agent
Episode 6: Build A Personal AI Agent
A Practical Guide for Evaluators and M&E Professionals
Why Visualizations Matter
Visualizations transform complex indicator data into clear, actionable insights. This tutorial shows you how to build an AI agent that automatically generates charts, graphs, and dashboards from your M&E data, making reporting faster and more impactful.
Part 1: Understanding Visualization Types for M&E
Common M&E Visualization Types:
| Chart Type | Best Used For | Example Use Case |
|---|---|---|
| Bar Chart | Comparing values across categories | Target vs Actual by region |
| Line Chart | Showing trends over time | Monthly indicator progress |
| Pie Chart | Showing composition/percentages | Beneficiary demographics |
| Scatter Plot | Showing relationships between variables | Budget vs Outcome correlation |
| Gauge Chart | Showing progress toward target | Overall program achievement |
Part 2: Setting Up the Visualization Environment
Step 1: Install Required Libraries
pip install matplotlib seaborn pandas plotly openpyxlLibrary Guide:
matplotlib: Basic plotting library
seaborn: Enhanced statistical visualizations
plotly: Interactive web-based charts
pandas: Data manipulation for plotting
Step 2: Data Preparation Functions
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime
import os
def load_indicator_data(file_path='indicator_data.xlsx'):
"""Load indicator data from Excel file."""
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found: {file_path}")
df = pd.read_excel(file_path)
return df
def prepare_data_for_plotting(df):
"""Clean and prepare data for visualization."""
# Convert date column
if 'date' in df.columns:
df['date'] = pd.to_datetime(df['date'])
# Calculate achievement if not present
if 'achievement' not in df.columns and 'target' in df.columns and 'actual' in df.columns:
df['achievement'] = (df['actual'] / df['target'] * 100).round(1)
# Normalize status
if 'status' in df.columns:
status_map = {
'on track': 'on_track', 'ontrack': 'on_track',
'at risk': 'at_risk', 'atrisk': 'at_risk',
'off track': 'off_track', 'offtrack': 'off_track',
'complete': 'completed', 'done': 'completed'
}
df['status'] = df['status'].str.lower().replace(status_map)
return dfPart 3: Building the Visualization Agent
1. Bar Chart: Target vs Actual Comparison
def create_target_vs_actual(df, output_dir='charts'):
"""Create bar chart comparing target and actual values."""
os.makedirs(output_dir, exist_ok=True)
# Sort by achievement for better visualization
df_sorted = df.sort_values('achievement', ascending=False)
# Set up the figure
plt.figure(figsize=(12, 8))
x = range(len(df_sorted))
width = 0.35
# Create bars
bars1 = plt.bar(x, df_sorted['target'], width, label='Target', color='#4a9eff')
bars2 = plt.bar([i + width for i in x], df_sorted['actual'], width, label='Actual', color='#6c5ce7')
# Customize chart
plt.xlabel('Indicator', fontsize=12)
plt.ylabel('Values', fontsize=12)
plt.title('Target vs Actual by Indicator', fontsize=16, fontweight='bold')
plt.xticks([i + width/2 for i in x], df_sorted['indicator_name'], rotation=45, ha='right')
plt.legend()
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
# Add value labels on bars
for i, (target, actual) in enumerate(zip(df_sorted['target'], df_sorted['actual'])):
plt.text(i, target + 5, str(target), ha='center', va='bottom', fontsize=9)
plt.text(i + width, actual + 5, str(actual), ha='center', va='bottom', fontsize=9)
# Save chart
filename = f"{output_dir}/target_vs_actual_{datetime.now().strftime('%Y%m%d')}.png"
plt.savefig(filename, dpi=300, bbox_inches='tight')
plt.close()
print(f"Chart saved: {filename}")
return filename2. Line Chart: Trends Over Time
def create_trend_chart(df, indicator_name=None, output_dir='charts'):
"""Create line chart showing trends over time."""
os.makedirs(output_dir, exist_ok=True)
# Filter for specific indicator if provided
if indicator_name:
df_plot = df[df['indicator_name'] == indicator_name].copy()
title = f"{indicator_name} - Progress Over Time"
else:
# Aggregate by date (average achievement)
df_plot = df.groupby('date')['achievement'].mean().reset_index()
title = "Overall Program Progress Over Time"
# Sort by date
df_plot = df_plot.sort_values('date')
# Create figure
plt.figure(figsize=(12, 6))
if indicator_name:
# Plot target and actual lines
plt.plot(df_plot['date'], df_plot['target'], marker='o', label='Target', color='#4a9eff', linewidth=2)
plt.plot(df_plot['date'], df_plot['actual'], marker='s', label='Actual', color='#6c5ce7', linewidth=2)
# Add achievement annotations
for _, row in df_plot.iterrows():
plt.annotate(f"{row['achievement']:.0f}%",
(row['date'], row['actual']),
textcoords="offset points", xytext=(0,10),
ha='center', fontsize=9)
else:
# Plot average achievement
plt.plot(df_plot['date'], df_plot['achievement'], marker='o', color='#4a9eff', linewidth=2)
# Add target line (100%)
plt.axhline(y=100, color='#dc3545', linestyle='--', label='Target (100%)', alpha=0.7)
plt.xlabel('Date', fontsize=12)
plt.ylabel('Achievement (%)', fontsize=12)
plt.title(title, fontsize=16, fontweight='bold')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
filename = f"{output_dir}/trend_{datetime.now().strftime('%Y%m%d')}.png"
plt.savefig(filename, dpi=300, bbox_inches='tight')
plt.close()
print(f"Trend chart saved: {filename}")
return filename3. Achievement Heatmap
def create_achievement_heatmap(df, output_dir='charts'):
"""Create heatmap showing achievement across indicators and categories."""
os.makedirs(output_dir, exist_ok=True)
# Pivot table for heatmap
if 'category' in df.columns:
pivot = df.pivot_table(
values='achievement',
index='category',
columns='indicator_name',
aggfunc='mean'
)
title = "Achievement Heatmap by Category and Indicator"
else:
# Use date and indicator if category not available
df['date_str'] = df['date'].dt.strftime('%Y-%m')
pivot = df.pivot_table(
values='achievement',
index='date_str',
columns='indicator_name',
aggfunc='mean'
)
title = "Achievement Heatmap by Month and Indicator"
# Create figure
plt.figure(figsize=(14, 8))
sns.heatmap(pivot, annot=True, fmt='.0f', cmap='RdYlGn',
cbar_kws={'label': 'Achievement (%)'},
linewidths=0.5, linecolor='white')
plt.title(title, fontsize=16, fontweight='bold')
plt.xlabel('Indicator', fontsize=12)
plt.ylabel('Category' if 'category' in df.columns else 'Month', fontsize=12)
plt.tight_layout()
filename = f"{output_dir}/heatmap_{datetime.now().strftime('%Y%m%d')}.png"
plt.savefig(filename, dpi=300, bbox_inches='tight')
plt.close()
print(f"Heatmap saved: {filename}")
return filename4. Dashboard Summary Charts
def create_dashboard_summary(df, output_dir='charts'):
"""Create a dashboard with multiple charts in one figure."""
os.makedirs(output_dir, exist_ok=True)
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# Chart 1: Status distribution (pie)
if 'status' in df.columns:
status_counts = df['status'].value_counts()
axes[0, 0].pie(status_counts.values, labels=status_counts.index,
autopct='%1.1f%%', colors=['#28a745', '#ffc107', '#dc3545'])
axes[0, 0].set_title('Status Distribution', fontsize=14, fontweight='bold')
# Chart 2: Top 5 achievers (bar)
top_achievers = df.nlargest(5, 'achievement')
axes[0, 1].barh(top_achievers['indicator_name'], top_achievers['achievement'],
color='#4a9eff')
axes[0, 1].set_xlabel('Achievement (%)')
axes[0, 1].set_title('Top 5 Performing Indicators', fontsize=14, fontweight='bold')
# Chart 3: Bottom 5 achievers (bar)
bottom_achievers = df.nsmallest(5, 'achievement')
axes[1, 0].barh(bottom_achievers['indicator_name'], bottom_achievers['achievement'],
color='#dc3545')
axes[1, 0].set_xlabel('Achievement (%)')
axes[1, 0].set_title('Bottom 5 Performing Indicators', fontsize=14, fontweight='bold')
# Chart 4: Category performance (bar)
if 'category' in df.columns:
category_perf = df.groupby('category')['achievement'].mean().sort_values()
axes[1, 1].bar(category_perf.index, category_perf.values, color='#6c5ce7')
axes[1, 1].set_ylabel('Average Achievement (%)')
axes[1, 1].set_title('Category Performance', fontsize=14, fontweight='bold')
axes[1, 1].axhline(y=80, color='#dc3545', linestyle='--', alpha=0.5)
plt.suptitle('M&E Performance Dashboard', fontsize=20, fontweight='bold', y=0.98)
plt.tight_layout()
filename = f"{output_dir}/dashboard_{datetime.now().strftime('%Y%m%d')}.png"
plt.savefig(filename, dpi=300, bbox_inches='tight')
plt.close()
print(f"Dashboard saved: {filename}")
return filenamePart 4: Interactive Charts with Plotly
import plotly.express as px
import plotly.graph_objects as go
def create_interactive_dashboard(df, output_dir='charts'):
"""Create interactive HTML dashboard using Plotly."""
os.makedirs(output_dir, exist_ok=True)
# Create interactive bar chart
fig = px.bar(df, x='indicator_name', y=['target', 'actual'],
title='Target vs Actual Comparison',
barmode='group',
color_discrete_sequence=['#4a9eff', '#6c5ce7'])
fig.update_layout(xaxis_tickangle=-45)
# Save as HTML
filename_html = f"{output_dir}/interactive_dashboard_{datetime.now().strftime('%Y%m%d')}.html"
fig.write_html(filename_html)
print(f"Interactive dashboard saved: {filename_html}")
# Create scatter plot
fig2 = px.scatter(df, x='target', y='actual',
color='status',
hover_data=['indicator_name'],
title='Target vs Actual Relationship',
labels={'target': 'Target', 'actual': 'Actual'})
filename_html2 = f"{output_dir}/scatter_{datetime.now().strftime('%Y%m%d')}.html"
fig2.write_html(filename_html2)
print(f"Interactive scatter plot saved: {filename_html2}")
return filename_htmlPart 5: Complete Visualization Agent
class VisualizationAgent:
"""Complete visualization agent for M&E data."""
def __init__(self, file_path='indicator_data.xlsx', output_dir='charts'):
self.file_path = file_path
self.output_dir = output_dir
self.df = None
os.makedirs(output_dir, exist_ok=True)
def load_and_prepare(self):
"""Load and prepare data for visualization."""
self.df = load_indicator_data(self.file_path)
self.df = prepare_data_for_plotting(self.df)
print(f"Loaded {len(self.df)} records")
return self.df
def generate_all_charts(self):
"""Generate all visualization types."""
if self.df is None:
self.load_and_prepare()
charts = {}
# 1. Target vs Actual
charts['target_actual'] = create_target_vs_actual(self.df, self.output_dir)
# 2. Trend Chart
charts['trend'] = create_trend_chart(self.df, output_dir=self.output_dir)
# 3. Heatmap
charts['heatmap'] = create_achievement_heatmap(self.df, self.output_dir)
# 4. Dashboard
charts['dashboard'] = create_dashboard_summary(self.df, self.output_dir)
# 5. Interactive charts
charts['interactive'] = create_interactive_dashboard(self.df, self.output_dir)
return charts
def generate_chart_by_type(self, chart_type='bar'):
"""Generate specific chart type."""
if self.df is None:
self.load_and_prepare()
chart_map = {
'bar': create_target_vs_actual,
'trend': create_trend_chart,
'heatmap': create_achievement_heatmap,
'dashboard': create_dashboard_summary
}
if chart_type in chart_map:
return chart_map[chart_type](self.df, self.output_dir)
else:
print(f"Unknown chart type: {chart_type}")
return None
def export_chart_summary(self):
"""Export a summary of all charts created."""
if self.df is None:
self.load_and_prepare()
summary = f"""
========================================
CHART GENERATION SUMMARY
========================================
Date: {datetime.now().strftime('%Y-%m-%d %H:%M')}
Records: {len(self.df)}
Indicators: {self.df['indicator_name'].nunique()}
Categories: {self.df['category'].nunique() if 'category' in self.df.columns else 'N/A'}
Average Achievement: {self.df['achievement'].mean():.1f}%
Max Achievement: {self.df['achievement'].max():.1f}%
Min Achievement: {self.df['achievement'].min():.1f}%
Status Distribution:
{self.df['status'].value_counts().to_string() if 'status' in self.df.columns else 'N/A'}
Charts generated in: {self.output_dir}
========================================
"""
filename = f"{self.output_dir}/summary_{datetime.now().strftime('%Y%m%d')}.txt"
with open(filename, 'w') as f:
f.write(summary)
print(f"Summary saved: {filename}")
return summaryPart 6: Complete Usage Examples
Example 1: Generate All Charts
# Complete workflow
def main():
# Initialize agent
agent = VisualizationAgent('indicator_data.xlsx', 'my_report_charts')
# Load data
print("Loading data...")
agent.load_and_prepare()
# Generate all charts
print("\nGenerating charts...")
charts = agent.generate_all_charts()
# Export summary
print("\nExporting summary...")
summary = agent.export_chart_summary()
print(summary)
print(f"\nAll charts saved in: {agent.output_dir}")
return charts
if __name__ == "__main__":
# Create sample data
sample_data = pd.DataFrame({
'indicator_name': ['Indicator A', 'Indicator B', 'Indicator C', 'Indicator D', 'Indicator E'],
'target': [100, 150, 80, 200, 120],
'actual': [92, 135, 55, 190, 80],
'date': ['2024-01-15', '2024-01-15', '2024-01-15', '2024-01-15', '2024-01-15'],
'status': ['on_track', 'on_track', 'at_risk', 'on_track', 'off_track'],
'category': ['Health', 'Education', 'Health', 'Livelihood', 'Education']
})
sample_data.to_excel('indicator_data.xlsx', index=False)
# Run the agent
charts = main()
Example 2: Generate Specific Chart Type
# Generate only dashboard
def generate_dashboard_only():
agent = VisualizationAgent('indicator_data.xlsx', 'dashboard_charts')
agent.load_and_prepare()
# Generate only dashboard
dashboard_file = agent.generate_chart_by_type('dashboard')
print(f"Dashboard generated: {dashboard_file}")
# Also generate trend chart
trend_file = agent.generate_chart_by_type('trend')
print(f"Trend chart generated: {trend_file}")
generate_dashboard_only()
Troubleshooting Common Visualization Issues
| Issue | Solution |
|---|---|
| Chart not showing | Check if plt.savefig() is called before plt.show() |
| Font too small | Adjust fontsize in plt.title(), plt.xlabel(), etc. |
| Overlapping labels | Use plt.tight_layout() or adjust figure size |
| Missing data columns | Check required columns: indicator_name, target, actual |
| Interactive chart not opening | Open HTML file in web browser |
Best Practices for M&E Visualizations
- Keep it Simple: Avoid cluttered charts with too many elements
- Use Clear Labels: Always label axes, include titles, and add legends
- Color Consistency: Use consistent colors (green=good, red=bad)
- Show Context: Include targets, benchmarks, and relevant comparisons
- Interactive Elements: Use Plotly for dashboards that allow drilling down
- Accessibility: Consider color-blind friendly palettes
Next Steps: Advanced Visualization Topics
- Automated Reporting: Integrate charts into weekly/monthly reports
- Geographic Maps: Plot indicator performance on regional maps
- Real-Time Dashboards: Use tools like Dash or Streamlit for live updates
- Export to PDF: Generate complete report PDFs with embedded charts
- Chart Interpretation: Use AI to add narrative insights to charts
Master Data Visualization for M&E
The AI Agents for Evaluators Certificate teaches you to build complete M&E solutions including automated chart generation, dashboard creation, and report automation.
What you will learn:
- Build automated visualization pipelines
- Create interactive dashboards
- Integrate charts into reports
- Design custom chart types for M&E
Course Features: 32 lectures · Lifetime access · Certificate included · 241 students enrolled
Enroll Now – $249 Lifetime Access
Bundle with AI in M&E course and save 30%
This guide is part of the AI Agents for Evaluators series. Continue your learning journey with the full certificate course above.
