|
| 1 | +""" |
| 2 | +visualize.py — Create plots of the Keeling Curve data. |
| 3 | +""" |
| 4 | + |
| 5 | +import pandas as pd |
| 6 | +import matplotlib.pyplot as plt |
| 7 | +import matplotlib.dates as mdates |
| 8 | + |
| 9 | + |
| 10 | +INPUT_FILE = "co2_clean.csv" |
| 11 | + |
| 12 | + |
| 13 | +def plot_full_curve(df: pd.DataFrame) -> None: |
| 14 | + """Plot the complete Keeling Curve.""" |
| 15 | + fig, ax = plt.subplots(figsize=(12, 6)) |
| 16 | + |
| 17 | + ax.plot(df["date"], df["co2"], color="#2196F3", linewidth=0.8, alpha=0.8, label="Monthly average") |
| 18 | + |
| 19 | + # Add a trend line using the adjusted/fit values if available |
| 20 | + if "co2_fit" in df.columns: |
| 21 | + ax.plot(df["date"], df["co2_fit"], color="#F44336", linewidth=1.5, label="Trend (fit)") |
| 22 | + |
| 23 | + ax.set_xlabel("Year", fontsize=12) |
| 24 | + ax.set_ylabel("CO₂ Concentration (ppm)", fontsize=12) |
| 25 | + ax.set_title("The Keeling Curve — Atmospheric CO₂ at Mauna Loa", fontsize=14, fontweight="bold") |
| 26 | + ax.legend(loc="upper left") |
| 27 | + ax.grid(True, alpha=0.3) |
| 28 | + |
| 29 | + # Format x-axis |
| 30 | + ax.xaxis.set_major_locator(mdates.YearLocator(10)) |
| 31 | + ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y")) |
| 32 | + |
| 33 | + plt.tight_layout() |
| 34 | + plt.savefig("keeling_curve_full.png", dpi=150) |
| 35 | + print("Saved: keeling_curve_full.png") |
| 36 | + plt.close() |
| 37 | + |
| 38 | + |
| 39 | +def plot_seasonal_cycle(df: pd.DataFrame) -> None: |
| 40 | + """Plot the average seasonal CO₂ cycle.""" |
| 41 | + monthly_avg = df.groupby("month")["co2"].mean() |
| 42 | + |
| 43 | + fig, ax = plt.subplots(figsize=(8, 5)) |
| 44 | + months = range(1, 13) |
| 45 | + month_labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 46 | + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
| 47 | + |
| 48 | + ax.bar(months, monthly_avg.values, color="#4CAF50", alpha=0.7, edgecolor="white") |
| 49 | + ax.set_xticks(months) |
| 50 | + ax.set_xticklabels(month_labels) |
| 51 | + ax.set_xlabel("Month", fontsize=12) |
| 52 | + ax.set_ylabel("Average CO₂ (ppm)", fontsize=12) |
| 53 | + ax.set_title("Average Seasonal CO₂ Cycle", fontsize=14, fontweight="bold") |
| 54 | + ax.grid(True, axis="y", alpha=0.3) |
| 55 | + |
| 56 | + plt.tight_layout() |
| 57 | + plt.savefig("keeling_curve_seasonal.png", dpi=150) |
| 58 | + print("Saved: keeling_curve_seasonal.png") |
| 59 | + plt.close() |
| 60 | + |
| 61 | + |
| 62 | +def plot_decade_comparison(df: pd.DataFrame) -> None: |
| 63 | + """Plot CO₂ trend for each decade.""" |
| 64 | + fig, ax = plt.subplots(figsize=(12, 6)) |
| 65 | + |
| 66 | + colors = plt.cm.viridis_r # Color map: darker = more recent |
| 67 | + decades = sorted(df["year"].apply(lambda y: int(y) // 10 * 10).unique()) |
| 68 | + |
| 69 | + for i, decade in enumerate(decades): |
| 70 | + mask = (df["year"] >= decade) & (df["year"] < decade + 10) |
| 71 | + subset = df[mask] |
| 72 | + if len(subset) > 0: |
| 73 | + color = colors(i / len(decades)) |
| 74 | + ax.plot(subset["month"], subset["co2"], alpha=0.3, color=color) |
| 75 | + |
| 76 | + # Plot the mean for first and last decades |
| 77 | + for decade, style, label in [(decades[0], "--", f"{decades[0]}s avg"), |
| 78 | + (decades[-1], "-", f"{decades[-1]}s avg")]: |
| 79 | + mask = (df["year"] >= decade) & (df["year"] < decade + 10) |
| 80 | + subset = df[mask] |
| 81 | + if len(subset) > 0: |
| 82 | + monthly = subset.groupby("month")["co2"].mean() |
| 83 | + ax.plot(monthly.index, monthly.values, style, linewidth=2.5, label=label) |
| 84 | + |
| 85 | + ax.set_xticks(range(1, 13)) |
| 86 | + ax.set_xticklabels(["Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 87 | + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]) |
| 88 | + ax.set_xlabel("Month", fontsize=12) |
| 89 | + ax.set_ylabel("CO₂ (ppm)", fontsize=12) |
| 90 | + ax.set_title("CO₂ Seasonal Cycle by Decade", fontsize=14, fontweight="bold") |
| 91 | + ax.legend() |
| 92 | + ax.grid(True, alpha=0.3) |
| 93 | + |
| 94 | + plt.tight_layout() |
| 95 | + plt.savefig("keeling_curve_decades.png", dpi=150) |
| 96 | + print("Saved: keeling_curve_decades.png") |
| 97 | + plt.close() |
| 98 | + |
| 99 | + |
| 100 | +if __name__ == "__main__": |
| 101 | + df = pd.read_csv(INPUT_FILE, parse_dates=["date"]) |
| 102 | + plot_full_curve(df) |
| 103 | + plot_seasonal_cycle(df) |
| 104 | + plot_decade_comparison(df) |
| 105 | + print("\nAll plots saved!") |
0 commit comments