Step 6: Matplotlib Visualization¶
Create professional static plots with matplotlib.
1. Add Imports¶
import matplotlib.pyplot as plt
import seaborn as sns
# Plot settings
sns.set_style("whitegrid")
sns.set_palette("husl")
plt.rcParams["figure.figsize"] = (14, 8)
plt.rcParams["font.size"] = 10
2. Implement generate_plots()¶
def generate_plots(results, config):
"""Generate matplotlib plots"""
output_dir = Path(config["output"]["output_dir"])
output_dir.mkdir(parents=True, exist_ok=True)
fmt = config["output"]["plot_format"]
print(f"\n{'='*60}")
print("PLOTS")
print(f"{'='*60}\n")
df = results["data"]
stats = results["stats"]
# Generate 4 plots
plot_timeseries(df, stats, output_dir, fmt)
plot_distribution(df, stats, output_dir, fmt)
plot_heatmap(df, output_dir, fmt)
plot_hourly_pattern(df, output_dir, fmt)
print("✓ All plots generated")
3. Time-Series Plot¶
def plot_timeseries(df, stats, output_dir, fmt):
"""Plot temperatures and differential over time"""
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
# Plot 1: Supply and Return
ax1.plot(df.index, df["supply"], label="Supply", color="#e74c3c", linewidth=1.5)
ax1.plot(df.index, df["return"], label="Return", color="#3498db", linewidth=1.5)
ax1.set_ylabel("Temperature (°C)")
ax1.set_title("Supply and Return Temperatures", fontweight="bold")
ax1.legend()
ax1.grid(True, alpha=0.3)
# Plot 2: Differential
ax2.plot(df.index, df["temp_diff"], color="#9b59b6", linewidth=1.5)
ax2.axhline(stats["mean_temp_diff"], color="#27ae60", linestyle="--",
label=f"Mean: {stats['mean_temp_diff']:.2f}°C")
ax2.set_xlabel("Time")
ax2.set_ylabel("Temperature Difference (°C)")
ax2.set_title("Temperature Differential", fontweight="bold")
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(output_dir / f"timeseries.{fmt}", dpi=300, bbox_inches="tight")
plt.close()
print(f"✓ timeseries.{fmt}")
4. Distribution Histogram¶
def plot_distribution(df, stats, output_dir, fmt):
"""Plot distribution histogram"""
fig, ax = plt.subplots(figsize=(12, 6))
ax.hist(df["temp_diff"], bins=50, color="#3498db", alpha=0.7, edgecolor="black")
ax.axvline(stats["mean_temp_diff"], color="#e74c3c", linestyle="--",
linewidth=2, label=f"Mean: {stats['mean_temp_diff']:.2f}°C")
ax.axvline(stats["median_temp_diff"], color="#27ae60", linestyle="--",
linewidth=2, label=f"Median: {stats['median_temp_diff']:.2f}°C")
ax.set_xlabel("Temperature Difference (°C)")
ax.set_ylabel("Frequency")
ax.set_title("Distribution", fontweight="bold")
ax.legend()
ax.grid(True, alpha=0.3, axis="y")
plt.tight_layout()
plt.savefig(output_dir / f"distribution.{fmt}", dpi=300, bbox_inches="tight")
plt.close()
print(f"✓ distribution.{fmt}")
5. Heatmap¶
def plot_heatmap(df, output_dir, fmt):
"""Plot heatmap by hour and weekday"""
pivot = df.pivot_table(values="temp_diff", index="hour",
columns="weekday", aggfunc="mean")
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(pivot, annot=True, fmt=".1f", cmap="RdYlBu_r",
center=pivot.values.mean(),
cbar_kws={"label": "Mean Temp Diff (°C)"}, ax=ax)
ax.set_xlabel("Day of Week (0=Mon, 6=Sun)")
ax.set_ylabel("Hour of Day")
ax.set_title("Average by Hour and Weekday", fontweight="bold")
plt.tight_layout()
plt.savefig(output_dir / f"heatmap.{fmt}", dpi=300, bbox_inches="tight")
plt.close()
print(f"✓ heatmap.{fmt}")
6. Hourly Pattern¶
def plot_hourly_pattern(df, output_dir, fmt):
"""Plot hourly pattern with error bars"""
hourly = df.groupby("hour")["temp_diff"].agg(["mean", "std"])
fig, ax = plt.subplots(figsize=(12, 6))
ax.bar(hourly.index, hourly["mean"], yerr=hourly["std"],
color="#3498db", alpha=0.7, error_kw={"elinewidth": 2, "capsize": 5})
ax.set_xlabel("Hour of Day")
ax.set_ylabel("Mean Temperature Difference (°C)")
ax.set_title("Hourly Pattern", fontweight="bold")
ax.set_xticks(range(24))
ax.grid(True, alpha=0.3, axis="y")
plt.tight_layout()
plt.savefig(output_dir / f"hourly_pattern.{fmt}", dpi=300, bbox_inches="tight")
plt.close()
print(f"✓ hourly_pattern.{fmt}")
Checkpoint¶
- Matplotlib imports added
- 4 plot types implemented
- Plots save in correct format
- Test generates all plots
Next Step¶
👉 Step 7: Plotly HTML Visualization
In this step, you'll create professional static plots using matplotlib and seaborn.
Goal of This Step¶
- Generate time-series plots
- Create distribution histograms
- Build heatmaps for pattern analysis
- Save plots in multiple formats (PNG, PDF, SVG)
Step 6.1: Add Matplotlib Imports¶
At the top of your app.py, ensure you have these imports:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# Plot settings
sns.set_style("whitegrid")
sns.set_palette("husl")
plt.rcParams["figure.figsize"] = (14, 8)
plt.rcParams["font.size"] = 10
Understanding:
- sns.set_style("whitegrid") - Clean background with grid
- sns.set_palette("husl") - Colorful, distinguishable colors
- plt.rcParams - Default figure size and font
Step 6.2: Implement generate_plots()¶
Create the main plotting function:
def generate_plots(results, config):
"""Generate matplotlib visualizations"""
output_dir = Path(config["output"]["output_dir"])
output_dir.mkdir(parents=True, exist_ok=True)
plot_format = config["output"]["plot_format"]
print(f"\n{'='*60}")
print(f"PLOTS: Generating matplotlib plots")
print(f"{'='*60}\n")
df = results["data"]
stats = results["stats"]
# Generate all plots
plot_timeseries(df, stats, output_dir, plot_format)
plot_distribution(df, stats, output_dir, plot_format)
plot_heatmap(df, output_dir, plot_format)
plot_hourly_pattern(df, output_dir, plot_format)
print(f"\n[OK] All plots generated")
Step 6.3: Time-Series Plot¶
Create a time-series visualization:
def plot_timeseries(df, stats, output_dir, plot_format):
"""Plot temperature differential over time"""
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
# Plot 1: Supply and Return temperatures
ax1.plot(df.index, df["supply"], label="Supply Temp", color="#e74c3c", linewidth=1.5)
ax1.plot(df.index, df["return"], label="Return Temp", color="#3498db", linewidth=1.5)
ax1.set_ylabel("Temperature (°C)", fontsize=12)
ax1.set_title("Supply and Return Temperatures", fontsize=14, fontweight="bold")
ax1.legend(loc="upper right")
ax1.grid(True, alpha=0.3)
# Plot 2: Temperature differential
ax2.plot(df.index, df["temp_diff"], label="Temp Diff", color="#9b59b6", linewidth=1.5)
ax2.axhline(y=stats["mean_temp_diff"], color="#27ae60", linestyle="--",
label=f"Mean: {stats['mean_temp_diff']:.2f}°C")
ax2.set_xlabel("Time", fontsize=12)
ax2.set_ylabel("Temperature Difference (°C)", fontsize=12)
ax2.set_title("Temperature Differential", fontsize=14, fontweight="bold")
ax2.legend(loc="upper right")
ax2.grid(True, alpha=0.3)
plt.tight_layout()
filepath = output_dir / f"timeseries.{plot_format}"
plt.savefig(filepath, dpi=300, bbox_inches="tight")
plt.close()
print(f" [OK] {filepath.name}")
Understanding:
- subplots(2, 1) - Create 2 rows, 1 column of plots
- sharex=True - Share x-axis between plots
- axhline() - Horizontal line for mean
- dpi=300 - High resolution for publication
- bbox_inches="tight" - Remove extra whitespace
Step 6.4: Distribution Histogram¶
Create a histogram showing value distribution:
def plot_distribution(df, stats, output_dir, plot_format):
"""Plot distribution of temperature differential"""
fig, ax = plt.subplots(figsize=(12, 6))
# Histogram
ax.hist(df["temp_diff"], bins=50, color="#3498db", alpha=0.7, edgecolor="black")
# Add vertical lines for statistics
ax.axvline(stats["mean_temp_diff"], color="#e74c3c", linestyle="--",
linewidth=2, label=f"Mean: {stats['mean_temp_diff']:.2f}°C")
ax.axvline(stats["median_temp_diff"], color="#27ae60", linestyle="--",
linewidth=2, label=f"Median: {stats['median_temp_diff']:.2f}°C")
# Labels and title
ax.set_xlabel("Temperature Difference (°C)", fontsize=12)
ax.set_ylabel("Frequency", fontsize=12)
ax.set_title("Distribution of Temperature Differential", fontsize=14, fontweight="bold")
ax.legend()
ax.grid(True, alpha=0.3, axis="y")
plt.tight_layout()
filepath = output_dir / f"distribution.{plot_format}"
plt.savefig(filepath, dpi=300, bbox_inches="tight")
plt.close()
print(f" [OK] {filepath.name}")
Understanding:
- bins=50 - Number of histogram bars
- alpha=0.7 - Transparency
- edgecolor="black" - Outline for each bar
- axvline() - Vertical line for mean/median
Step 6.5: Heatmap¶
Create a heatmap showing patterns by hour and day:
def plot_heatmap(df, output_dir, plot_format):
"""Plot heatmap of temperature differential by hour and weekday"""
# Create pivot table
pivot = df.pivot_table(
values="temp_diff",
index="hour",
columns="weekday",
aggfunc="mean"
)
# Create heatmap
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
pivot,
annot=True,
fmt=".1f",
cmap="RdYlBu_r",
center=pivot.values.mean(),
cbar_kws={"label": "Mean Temp Diff (°C)"},
ax=ax
)
# Customize
ax.set_xlabel("Day of Week (0=Monday, 6=Sunday)", fontsize=12)
ax.set_ylabel("Hour of Day", fontsize=12)
ax.set_title("Average Temperature Differential by Hour and Weekday",
fontsize=14, fontweight="bold")
plt.tight_layout()
filepath = output_dir / f"heatmap.{plot_format}"
plt.savefig(filepath, dpi=300, bbox_inches="tight")
plt.close()
print(f" [OK] {filepath.name}")
Understanding:
- pivot_table() - Reorganize data into matrix
- sns.heatmap() - Create colored grid
- annot=True - Show values in cells
- fmt=".1f" - Format numbers to 1 decimal
- cmap="RdYlBu_r" - Color scheme (red-yellow-blue reversed)
Step 6.6: Hourly Pattern Plot¶
Show how values change by hour:
def plot_hourly_pattern(df, output_dir, plot_format):
"""Plot hourly pattern of temperature differential"""
hourly_stats = df.groupby("hour")["temp_diff"].agg(["mean", "std", "count"])
fig, ax = plt.subplots(figsize=(12, 6))
# Bar plot with error bars
ax.bar(hourly_stats.index, hourly_stats["mean"],
yerr=hourly_stats["std"],
color="#3498db", alpha=0.7,
error_kw={"elinewidth": 2, "capsize": 5})
ax.set_xlabel("Hour of Day", fontsize=12)
ax.set_ylabel("Mean Temperature Difference (°C)", fontsize=12)
ax.set_title("Hourly Pattern of Temperature Differential",
fontsize=14, fontweight="bold")
ax.set_xticks(range(24))
ax.grid(True, alpha=0.3, axis="y")
plt.tight_layout()
filepath = output_dir / f"hourly_pattern.{plot_format}"
plt.savefig(filepath, dpi=300, bbox_inches="tight")
plt.close()
print(f" [OK] {filepath.name}")
Understanding:
- groupby("hour") - Group data by hour
- .agg(["mean", "std"]) - Calculate multiple statistics
- yerr= - Error bars showing standard deviation
- set_xticks() - Set x-axis tick marks
Step 6.7: Update Config for Plot Format¶
In your config.yaml, users can choose output format:
Formats: - PNG: Best for web, presentations, reports - PDF: Best for publications, vector graphics - SVG: Best for editing in vector graphics software
Step 6.8: Test the Plots¶
Update your test script to check plots:
def test_plots():
"""Test plotting functions"""
from hhw_brick.applications.my_first_app.app import analyze, load_config
fixtures = Path(__file__).parent.parent.parent.parent / "tests" / "fixtures"
model_file = fixtures / "Brick_Model_File" / "building_29.ttl"
data_file = fixtures / "TimeSeriesData" / "29hhw_system_data.csv"
config = load_config()
config["output"]["output_dir"] = "./test_plots"
config["output"]["generate_plots"] = True
config["output"]["plot_format"] = "png"
results = analyze(str(model_file), str(data_file), config)
if results:
print("\n✅ Plots generated successfully!")
print(f"Check folder: {config['output']['output_dir']}")
else:
print("\n❌ Plot generation failed")
if __name__ == "__main__":
test_plots()
Run the test:
Expected output: 4 plot files in test_plots/ directory
Step 6.9: Complete generate_plots() Implementation¶
Your final app.py should have all these functions:
def generate_plots(results, config):
"""Generate matplotlib visualizations"""
# ...implementation from Step 6.2...
def plot_timeseries(df, stats, output_dir, plot_format):
"""Plot temperature differential over time"""
# ...implementation from Step 6.3...
def plot_distribution(df, stats, output_dir, plot_format):
"""Plot distribution of temperature differential"""
# ...implementation from Step 6.4...
def plot_heatmap(df, output_dir, plot_format):
"""Plot heatmap of temperature differential by hour and weekday"""
# ...implementation from Step 6.5...
def plot_hourly_pattern(df, output_dir, plot_format):
"""Plot hourly pattern of temperature differential"""
# ...implementation from Step 6.6...
Checkpoint¶
Before proceeding, verify:
- All imports added (matplotlib, seaborn)
- generate_plots() function created
- 4 plot types implemented:
- Time-series plot
- Distribution histogram
- Heatmap
- Hourly pattern
- Plots save in configured format
- Test script generates all plots
Next Steps¶
✅ Matplotlib visualization complete!
👉 Continue to Step 7: Plotly HTML Visualization
Common Issues¶
Issue: ImportError: No module named 'matplotlib'
Solution: pip install matplotlib seaborn
Issue: Plots are blank or don't show
Solution: Make sure to call plt.close() after plt.savefig()
Issue: Labels overlap or are cut off
Solution: Use plt.tight_layout() before saving
Issue: Low quality images
Solution: Set dpi=300 in savefig()
Customization Tips¶
Change Colors¶
# Use different color palettes
sns.set_palette("Set2") # Pastel colors
sns.set_palette("dark") # Dark colors
sns.set_palette(["#FF6B6B", "#4ECDC4", "#45B7D1"]) # Custom colors
Change Figure Size¶
Add More Statistics¶
# Add min/max lines
ax.axhline(stats["min_temp_diff"], color="blue", linestyle=":", label="Min")
ax.axhline(stats["max_temp_diff"], color="red", linestyle=":", label="Max")
Summary¶
You've created professional visualizations:
✅ Time-Series: Temperature over time
✅ Distribution: Histogram with statistics
✅ Heatmap: Patterns by hour and day
✅ Hourly Pattern: Average by hour with error bars
✅ Multiple Formats: PNG, PDF, SVG support
Next: Interactive Plotly HTML visualizations!