|
| 1 | +""" pyplots.ai |
| 2 | +choropleth-basic: Choropleth Map with Regional Coloring |
| 3 | +Library: seaborn 0.13.2 | Python 3.13.11 |
| 4 | +Quality: 88/100 | Created: 2025-12-31 |
| 5 | +""" |
| 6 | + |
| 7 | +import matplotlib.patches as mpatches |
| 8 | +import matplotlib.pyplot as plt |
| 9 | +import numpy as np |
| 10 | +import pandas as pd |
| 11 | +import seaborn as sns |
| 12 | + |
| 13 | + |
| 14 | +# Data: US states tile grid map with economic data (GDP growth rate %) |
| 15 | +# Tile grid maps are a recognized cartogram technique that ensures equal visual weight per region |
| 16 | +np.random.seed(42) |
| 17 | + |
| 18 | +# State data with grid positions (row, col) approximating US map layout |
| 19 | +# Values represent GDP growth rate (%) - None indicates missing data |
| 20 | +states_data = { |
| 21 | + # Row 0 (top - Pacific Northwest, Northern states) |
| 22 | + "WA": (0, 1, 3.2), |
| 23 | + "MT": (0, 3, 1.8), |
| 24 | + "ND": (0, 5, 2.1), |
| 25 | + "MN": (0, 6, 2.9), |
| 26 | + "WI": (0, 7, 2.3), |
| 27 | + "MI": (0, 8, 1.9), |
| 28 | + "NY": (0, 10, 3.5), |
| 29 | + "VT": (0, 11, 1.5), |
| 30 | + "ME": (0, 12, 1.2), |
| 31 | + # Row 1 |
| 32 | + "OR": (1, 1, 2.8), |
| 33 | + "ID": (1, 2, 3.1), |
| 34 | + "WY": (1, 3, 0.9), |
| 35 | + "SD": (1, 5, 1.7), |
| 36 | + "IA": (1, 6, 2.0), |
| 37 | + "IL": (1, 7, 2.5), |
| 38 | + "IN": (1, 8, 2.2), |
| 39 | + "OH": (1, 9, 1.8), |
| 40 | + "PA": (1, 10, 2.1), |
| 41 | + "MA": (1, 11, 3.8), |
| 42 | + "NH": (1, 12, 2.4), |
| 43 | + # Row 2 |
| 44 | + "NV": (2, 1, 4.1), |
| 45 | + "UT": (2, 2, 4.5), |
| 46 | + "CO": (2, 3, 3.9), |
| 47 | + "NE": (2, 5, 1.6), |
| 48 | + "KS": (2, 6, 1.4), |
| 49 | + "MO": (2, 7, 1.9), |
| 50 | + "KY": (2, 8, 2.0), |
| 51 | + "WV": (2, 9, 0.5), |
| 52 | + "VA": (2, 10, 3.2), |
| 53 | + "MD": (2, 11, 2.8), |
| 54 | + "NJ": (2, 12, 2.6), |
| 55 | + # Row 3 |
| 56 | + "CA": (3, 1, 3.7), |
| 57 | + "AZ": (3, 2, 4.2), |
| 58 | + "NM": (3, 3, 1.3), |
| 59 | + "OK": (3, 5, 1.1), |
| 60 | + "AR": (3, 6, 1.5), |
| 61 | + "TN": (3, 7, 3.0), |
| 62 | + "NC": (3, 9, 3.4), |
| 63 | + "SC": (3, 10, 2.7), |
| 64 | + "DE": (3, 11, 2.3), |
| 65 | + "CT": (3, 12, 2.9), |
| 66 | + # Row 4 (bottom - Southern states) |
| 67 | + "TX": (4, 3, 3.6), |
| 68 | + "LA": (4, 5, 0.8), |
| 69 | + "MS": (4, 6, 0.6), |
| 70 | + "AL": (4, 7, 1.7), |
| 71 | + "GA": (4, 8, 3.3), |
| 72 | + "FL": (4, 10, 3.8), |
| 73 | + "RI": (4, 12, 2.0), |
| 74 | + # Missing data examples (show as gray/hatched pattern per spec) |
| 75 | + "PR": (4, 13, None), # Puerto Rico - no data |
| 76 | + # Alaska and Hawaii (offset) |
| 77 | + "AK": (5, 0, 0.4), |
| 78 | + "HI": (5, 2, 2.5), |
| 79 | + "DC": (3, 13, None), # DC - no data available |
| 80 | +} |
| 81 | + |
| 82 | +# Create DataFrame |
| 83 | +rows = [] |
| 84 | +for state, (r, c, val) in states_data.items(): |
| 85 | + rows.append({"state": state, "row": r, "col": c, "gdp_growth": val}) |
| 86 | +df = pd.DataFrame(rows) |
| 87 | + |
| 88 | +# Create grid matrix for heatmap (6 rows x 14 cols) |
| 89 | +n_rows, n_cols = 6, 14 |
| 90 | +grid = np.full((n_rows, n_cols), np.nan) |
| 91 | +state_labels = np.full((n_rows, n_cols), "", dtype=object) |
| 92 | +missing_mask = np.zeros((n_rows, n_cols), dtype=bool) |
| 93 | + |
| 94 | +for _, row in df.iterrows(): |
| 95 | + r, c = int(row["row"]), int(row["col"]) |
| 96 | + if row["gdp_growth"] is not None and not pd.isna(row["gdp_growth"]): |
| 97 | + grid[r, c] = row["gdp_growth"] |
| 98 | + else: |
| 99 | + missing_mask[r, c] = True # Mark as missing data |
| 100 | + state_labels[r, c] = row["state"] |
| 101 | + |
| 102 | +# Set up seaborn styling |
| 103 | +sns.set_theme(style="white", context="talk", font_scale=1.2) |
| 104 | + |
| 105 | +# Create figure with appropriate size for 4800x2700 output |
| 106 | +fig, ax = plt.subplots(figsize=(16, 9)) |
| 107 | + |
| 108 | +# Create heatmap using seaborn with masked values for empty cells |
| 109 | +empty_mask = np.isnan(grid) & ~missing_mask # True for truly empty cells (no state) |
| 110 | +heatmap = sns.heatmap( |
| 111 | + grid, |
| 112 | + mask=empty_mask, |
| 113 | + cmap="YlGnBu", |
| 114 | + annot=False, # We'll add custom annotations |
| 115 | + cbar=True, |
| 116 | + cbar_kws={"label": "GDP Growth Rate (%)", "shrink": 0.7, "aspect": 20, "pad": 0.02}, |
| 117 | + linewidths=1.5, # Reduced from 3 to be less overwhelming |
| 118 | + linecolor="white", |
| 119 | + square=True, |
| 120 | + vmin=0, |
| 121 | + vmax=5, |
| 122 | + ax=ax, |
| 123 | +) |
| 124 | + |
| 125 | +# Add gray cells for missing data with hatching pattern |
| 126 | +for i in range(n_rows): |
| 127 | + for j in range(n_cols): |
| 128 | + if missing_mask[i, j]: |
| 129 | + # Draw gray rectangle with hatching for missing data |
| 130 | + rect = mpatches.Rectangle( |
| 131 | + (j, i), 1, 1, fill=True, facecolor="#d0d0d0", edgecolor="white", linewidth=1.5, hatch="///", zorder=2 |
| 132 | + ) |
| 133 | + ax.add_patch(rect) |
| 134 | + |
| 135 | +# Customize colorbar |
| 136 | +cbar = heatmap.collections[0].colorbar |
| 137 | +cbar.ax.tick_params(labelsize=18) |
| 138 | +cbar.set_label("GDP Growth Rate (%)", fontsize=20, labelpad=10) |
| 139 | + |
| 140 | +# Add state code and value annotations |
| 141 | +for i in range(n_rows): |
| 142 | + for j in range(n_cols): |
| 143 | + if state_labels[i, j]: # If there's a state here |
| 144 | + # State code (larger, bold) |
| 145 | + ax.text( |
| 146 | + j + 0.5, |
| 147 | + i + 0.35, |
| 148 | + state_labels[i, j], |
| 149 | + ha="center", |
| 150 | + va="center", |
| 151 | + fontsize=20, |
| 152 | + fontweight="bold", |
| 153 | + color="#1a1a1a" if not missing_mask[i, j] else "#666666", |
| 154 | + ) |
| 155 | + # Value or "N/A" for missing data (increased from 13pt to 16pt) |
| 156 | + if not missing_mask[i, j] and not np.isnan(grid[i, j]): |
| 157 | + ax.text( |
| 158 | + j + 0.5, |
| 159 | + i + 0.7, |
| 160 | + f"{grid[i, j]:.1f}%", |
| 161 | + ha="center", |
| 162 | + va="center", |
| 163 | + fontsize=16, |
| 164 | + color="#333333", |
| 165 | + fontweight="medium", |
| 166 | + ) |
| 167 | + else: |
| 168 | + ax.text( |
| 169 | + j + 0.5, i + 0.7, "N/A", ha="center", va="center", fontsize=16, color="#888888", fontstyle="italic" |
| 170 | + ) |
| 171 | + |
| 172 | +# Styling |
| 173 | +ax.set_title("choropleth-basic · seaborn · pyplots.ai", fontsize=26, pad=20, fontweight="bold") |
| 174 | + |
| 175 | +# Remove axis labels and ticks (tile grid doesn't need them) |
| 176 | +ax.set_xticks([]) |
| 177 | +ax.set_yticks([]) |
| 178 | +ax.set_xlabel("") |
| 179 | +ax.set_ylabel("") |
| 180 | + |
| 181 | +# Add legend for missing data |
| 182 | +missing_patch = mpatches.Patch(facecolor="#d0d0d0", edgecolor="white", hatch="///", label="No Data Available") |
| 183 | +ax.legend(handles=[missing_patch], loc="lower left", fontsize=16, framealpha=0.9) |
| 184 | + |
| 185 | +# Add subtitle explaining the visualization |
| 186 | +ax.text( |
| 187 | + 0.5, |
| 188 | + -0.06, |
| 189 | + "US States GDP Growth Rate (%) — Tile Grid Choropleth Map", |
| 190 | + ha="center", |
| 191 | + va="top", |
| 192 | + fontsize=18, |
| 193 | + color="#555555", |
| 194 | + transform=ax.transAxes, |
| 195 | +) |
| 196 | + |
| 197 | +plt.tight_layout() |
| 198 | +plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
0 commit comments