Add enhanced data center cluster map

This commit is contained in:
2026-05-18 08:37:21 -07:00
parent a005b2eab0
commit aef9d99e18
5 changed files with 66795 additions and 0 deletions

View File

@@ -0,0 +1,527 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# Enhanced Data Center Cluster Map\n",
"\n",
"This notebook starts from the spatial clustering outputs created by `spatial_clustering_master_data_centers.ipynb` and adds contextual layers from the demographic/RUCA/energy analysis.\n",
"\n",
"Current features:\n",
"- Loads point and cluster summary CSVs from `output/`.\n",
"- Recreates the cluster-colored Folium map.\n",
"- Enriches point popups with HUC8 watershed, RUCA, tract demographics, and state energy context where available.\n",
"- Adds separate layers for clustered points, isolated/noise points, cluster centroids, HUC8 watersheds, and state IM3 projected demand.\n",
"- Saves a standalone HTML map to `output/enhanced_master_data_center_spatial_clusters_map.html`.\n",
"\n",
"Notes from `output/data_center_demographic_ruca_energy_summary.md`:\n",
"- HUC8 watershed join is a recommended next step for water-context analysis.\n",
"- `im3_state_projected_moderate_50` is populated and used for state projected demand context.\n",
"- `seds_state_msn_year` is checked through the state context export, but it currently has no rows, so SEDS fields are blank until that table is populated.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"from html import escape\n",
"from pathlib import Path\n",
"\n",
"os.environ.setdefault('MPLCONFIGDIR', '/tmp/matplotlib')\n",
"Path(os.environ['MPLCONFIGDIR']).mkdir(parents=True, exist_ok=True)\n",
"\n",
"import pandas as pd\n",
"import folium\n",
"from folium import plugins\n",
"\n",
"print('pandas:', pd.__version__)\n",
"print('folium:', folium.__version__)\n"
]
},
{
"cell_type": "markdown",
"id": "2",
"metadata": {},
"source": [
"## Paths And Display Settings"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3",
"metadata": {},
"outputs": [],
"source": [
"OUTPUT_DIR = Path('output')\n",
"POINTS_CSV = OUTPUT_DIR / 'master_data_center_spatial_cluster_points.csv'\n",
"CLUSTERS_CSV = OUTPUT_DIR / 'master_data_center_spatial_cluster_summary.csv'\n",
"POINT_CONTEXT_CSV = OUTPUT_DIR / 'master_data_center_map_context.csv'\n",
"HUC8_GEOJSON = OUTPUT_DIR / 'master_data_center_huc8_watersheds.geojson'\n",
"STATE_ENERGY_CSV = OUTPUT_DIR / 'master_data_center_state_energy_context.csv'\n",
"MAP_HTML = OUTPUT_DIR / 'enhanced_master_data_center_spatial_clusters_map.html'\n",
"\n",
"MAP_CENTER = [39, -98]\n",
"MAP_ZOOM = 4\n",
"BASE_TILES = 'CartoDB positron'\n",
"\n",
"MAX_POINTS = None\n",
"\n",
"CLUSTERED_RADIUS = 5\n",
"NOISE_RADIUS = 3\n",
"CENTROID_RADIUS = 7\n",
"SHOW_CENTROID_P90_CIRCLES = True\n",
"SHOW_HUC8_LAYER = True\n",
"SHOW_STATE_ENERGY_LAYER = True\n",
"\n",
"OUTPUT_DIR.mkdir(exist_ok=True)\n",
"print('points:', POINTS_CSV)\n",
"print('clusters:', CLUSTERS_CSV)\n",
"print('point context:', POINT_CONTEXT_CSV)\n",
"print('HUC8 GeoJSON:', HUC8_GEOJSON)\n",
"print('state energy context:', STATE_ENERGY_CSV)\n",
"print('html output:', MAP_HTML)\n"
]
},
{
"cell_type": "markdown",
"id": "4",
"metadata": {},
"source": [
"## Load Cluster Outputs"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5",
"metadata": {},
"outputs": [],
"source": [
"required_files = [POINTS_CSV, CLUSTERS_CSV]\n",
"missing = [str(p) for p in required_files if not p.exists()]\n",
"if missing:\n",
" raise FileNotFoundError('Missing required cluster output CSV(s): ' + ', '.join(missing))\n",
"\n",
"points = pd.read_csv(POINTS_CSV)\n",
"clusters = pd.read_csv(CLUSTERS_CSV)\n",
"point_context = pd.read_csv(POINT_CONTEXT_CSV) if POINT_CONTEXT_CSV.exists() else pd.DataFrame()\n",
"state_energy = pd.read_csv(STATE_ENERGY_CSV) if STATE_ENERGY_CSV.exists() else pd.DataFrame()\n",
"\n",
"if MAX_POINTS is not None:\n",
" points = points.head(MAX_POINTS).copy()\n",
"\n",
"points['cluster_id'] = pd.to_numeric(points['cluster_id'], errors='coerce').fillna(-1).astype(int)\n",
"points['is_noise'] = points['cluster_id'].eq(-1)\n",
"points['is_clustered'] = ~points['is_noise']\n",
"points['name'] = points['name'].fillna('')\n",
"points['operator'] = points['operator'].fillna('Unknown').replace('', 'Unknown')\n",
"points['city'] = points['city'].fillna('Unknown').replace('', 'Unknown')\n",
"points['state'] = points['state'].fillna('UNK').replace('', 'UNK')\n",
"points['source'] = points['source'].fillna('unknown').replace('', 'unknown')\n",
"\n",
"if not point_context.empty:\n",
" context_cols = [c for c in point_context.columns if c != 'master_id']\n",
" points = points.merge(point_context[['master_id'] + context_cols], on='master_id', how='left')\n",
"\n",
"if not state_energy.empty:\n",
" state_cols = [c for c in state_energy.columns if c != 'state_code']\n",
" points = points.merge(state_energy[['state_code'] + state_cols], left_on='state', right_on='state_code', how='left')\n",
"\n",
"clusters['cluster_id'] = pd.to_numeric(clusters['cluster_id'], errors='coerce').astype(int)\n",
"clusters = clusters.sort_values(['point_count', 'radius_km_p90'], ascending=[False, True]).reset_index(drop=True)\n",
"clusters['cluster_rank'] = clusters.index + 1\n",
"\n",
"huc8_geojson = None\n",
"if HUC8_GEOJSON.exists():\n",
" huc8_geojson = json.loads(HUC8_GEOJSON.read_text())\n",
"\n",
"n_clusters = points.loc[points['cluster_id'].ne(-1), 'cluster_id'].nunique()\n",
"print(f'Loaded {len(points):,} points and {n_clusters:,} clusters')\n",
"print('point context columns:', 0 if point_context.empty else len(point_context.columns))\n",
"print('HUC8 features:', 0 if huc8_geojson is None else len(huc8_geojson.get('features', [])))\n",
"if not state_energy.empty:\n",
" seds_available = state_energy['seds_series_count'].notna().sum() if 'seds_series_count' in state_energy.columns else 0\n",
" print(f'state energy rows: {len(state_energy):,}; SEDS rows represented: {seds_available:,}')\n",
"else:\n",
" print('state energy context file not found')\n",
"display(points.head())\n",
"display(clusters.head(10))\n",
"if not state_energy.empty:\n",
" display(state_energy.head(10))\n"
]
},
{
"cell_type": "markdown",
"id": "6",
"metadata": {},
"source": [
"## Map Helpers"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"CLUSTER_COLORS = [\n",
" '#2563eb', '#dc2626', '#16a34a', '#9333ea', '#ea580c', '#0891b2',\n",
" '#be123c', '#4f46e5', '#65a30d', '#c026d3', '#0f766e', '#b45309',\n",
"]\n",
"NOISE_COLOR = '#9ca3af'\n",
"CENTROID_COLOR = '#111827'\n",
"STATE_ENERGY_COLOR = '#f59e0b'\n",
"\n",
"cluster_info = clusters.set_index('cluster_id').to_dict('index')\n",
"\n",
"\n",
"def clean_value(value):\n",
" if pd.isna(value):\n",
" return ''\n",
" return escape(str(value))\n",
"\n",
"\n",
"def fmt_number(value, decimals=0, prefix='', suffix=''):\n",
" if pd.isna(value):\n",
" return ''\n",
" try:\n",
" value = float(value)\n",
" except (TypeError, ValueError):\n",
" return clean_value(value)\n",
" return f\"{prefix}{value:,.{decimals}f}{suffix}\"\n",
"\n",
"\n",
"def cluster_color(cluster_id):\n",
" if cluster_id == -1:\n",
" return NOISE_COLOR\n",
" info = cluster_info.get(cluster_id, {})\n",
" rank = int(info.get('cluster_rank', cluster_id + 1))\n",
" return CLUSTER_COLORS[(rank - 1) % len(CLUSTER_COLORS)]\n",
"\n",
"\n",
"def cluster_label_and_size(cluster_id):\n",
" if cluster_id == -1:\n",
" return 'Noise / isolated', '1', ''\n",
" info = cluster_info.get(cluster_id, {})\n",
" rank = int(info.get('cluster_rank', cluster_id + 1))\n",
" point_count = int(info.get('point_count', 0))\n",
" return f'Cluster ID {cluster_id}', f'{point_count:,}', f'Rank {rank} of {n_clusters} by size'\n",
"\n",
"\n",
"def point_popup(row):\n",
" cluster_label, cluster_size, cluster_rank = cluster_label_and_size(row.cluster_id)\n",
" nearest = row.nearest_neighbor_km\n",
" nearest_text = f'{nearest:.2f} km' if pd.notna(nearest) else ''\n",
" title = clean_value(row.name) or clean_value(row.master_id)\n",
"\n",
" huc8_lines = ''\n",
" if hasattr(row, 'huc8') and pd.notna(row.huc8):\n",
" huc8_lines = f'''\n",
" <hr style=\"margin: 6px 0;\">\n",
" <strong>Watershed</strong><br>\n",
" HUC8: {clean_value(row.huc8)}<br>\n",
" Name: {clean_value(row.huc8_name)}<br>\n",
" States: {clean_value(row.huc8_states)}<br>\n",
" '''\n",
"\n",
" ruca_lines = ''\n",
" if hasattr(row, 'primary_ruca') and pd.notna(row.primary_ruca):\n",
" ruca_lines = f'''\n",
" <hr style=\"margin: 6px 0;\">\n",
" <strong>RUCA / tract context</strong><br>\n",
" RUCA band: {clean_value(row.ruca_band)}<br>\n",
" RUCA code: {fmt_number(row.primary_ruca)}<br>\n",
" {clean_value(row.primary_ruca_description)}<br>\n",
" Median HH income: {fmt_number(row.median_household_income, prefix='$')}<br>\n",
" Bachelor's+: {fmt_number(row.bachelor_or_higher_pct, 1, suffix='%')}<br>\n",
" Poverty: {fmt_number(row.poverty_rate, 1, suffix='%')}<br>\n",
" Non-Hispanic white: {fmt_number(row.non_hispanic_white_pct, 1, suffix='%')}<br>\n",
" '''\n",
"\n",
" energy_lines = ''\n",
" if hasattr(row, 'im3_projected_it_power_mw') and pd.notna(row.im3_projected_it_power_mw):\n",
" if hasattr(row, 'seds_series_count') and pd.notna(row.seds_series_count):\n",
" seds_note = f\"SEDS year: {fmt_number(row.seds_latest_year)}; series: {fmt_number(row.seds_series_count)}<br>\"\n",
" else:\n",
" seds_note = 'SEDS context: unavailable in seds_state_msn_year<br>'\n",
" energy_lines = f'''\n",
" <hr style=\"margin: 6px 0;\">\n",
" <strong>State energy demand context</strong><br>\n",
" IM3 projected IT power: {fmt_number(row.im3_projected_it_power_mw, suffix=' MW')}<br>\n",
" IM3 cooling water demand: {fmt_number(row.im3_cooling_water_demand_mgy, 1, suffix=' MGY')}<br>\n",
" IM3 water consumption: {fmt_number(row.im3_cooling_water_consumption_mgy, 1, suffix=' MGY')}<br>\n",
" IM3 avg siting score: {fmt_number(row.im3_avg_weighted_siting_score, 3)}<br>\n",
" {seds_note}\n",
" '''\n",
"\n",
" return folium.Popup(f'''\n",
" <div style=\"font-family: system-ui, sans-serif; min-width: 310px; max-width: 420px;\">\n",
" <strong>{title}</strong><br>\n",
" {clean_value(row.city)}, {clean_value(row.state)}<br>\n",
" <hr style=\"margin: 6px 0;\">\n",
" <strong>{cluster_label}</strong><br>\n",
" {cluster_rank}<br>\n",
" Cluster size: {cluster_size} data center(s)<br>\n",
" Source: {clean_value(row.source)}<br>\n",
" Operator: {clean_value(row.operator)}<br>\n",
" Nearest neighbor: {nearest_text}<br>\n",
" Master ID: {clean_value(row.master_id)}\n",
" {huc8_lines}\n",
" {ruca_lines}\n",
" {energy_lines}\n",
" </div>\n",
" ''', max_width=460)\n",
"\n",
"\n",
"def centroid_popup(row):\n",
" return folium.Popup(f'''\n",
" <div style=\"font-family: system-ui, sans-serif; min-width: 280px;\">\n",
" <strong>Cluster ID {int(row.cluster_id)}</strong><br>\n",
" Rank {int(row.cluster_rank)} of {n_clusters} by size<br>\n",
" <hr style=\"margin: 6px 0;\">\n",
" Points: {int(row.point_count):,}<br>\n",
" p50 radius: {row.radius_km_p50:.1f} km<br>\n",
" p90 radius: {row.radius_km_p90:.1f} km<br>\n",
" Max radius: {row.radius_km_max:.1f} km<br>\n",
" States: {clean_value(row.states)}<br>\n",
" Cities: {clean_value(row.cities)}<br>\n",
" Operators: {clean_value(row.operators)}\n",
" </div>\n",
" ''', max_width=420)\n",
"\n",
"\n",
"def huc8_style(feature):\n",
" count = feature['properties'].get('data_center_count') or 0\n",
" if count >= 100:\n",
" fill = '#075985'\n",
" elif count >= 50:\n",
" fill = '#0284c7'\n",
" elif count >= 20:\n",
" fill = '#38bdf8'\n",
" elif count >= 10:\n",
" fill = '#7dd3fc'\n",
" else:\n",
" fill = '#bae6fd'\n",
" return {'fillColor': fill, 'color': '#0369a1', 'weight': 1, 'fillOpacity': 0.22}\n",
"\n",
"\n",
"def huc8_popup(feature):\n",
" p = feature['properties']\n",
" return folium.Popup(f'''\n",
" <div style=\"font-family: system-ui, sans-serif; min-width: 280px;\">\n",
" <strong>{clean_value(p.get('name'))}</strong><br>\n",
" HUC8: {clean_value(p.get('huc8'))}<br>\n",
" States: {clean_value(p.get('states'))}<br>\n",
" <hr style=\"margin: 6px 0;\">\n",
" Data centers: {fmt_number(p.get('data_center_count'))}<br>\n",
" Clustered DCs: {fmt_number(p.get('clustered_data_center_count'))}<br>\n",
" Distinct clusters: {fmt_number(p.get('cluster_count'))}<br>\n",
" Area: {fmt_number(p.get('areasqkm'), 0, suffix=' sq km')}\n",
" </div>\n",
" ''', max_width=360)\n",
"\n",
"\n",
"def state_energy_popup(row):\n",
" if hasattr(row, 'seds_series_count') and pd.notna(row.seds_series_count):\n",
" seds_note = f\"SEDS latest year: {fmt_number(row.seds_latest_year)}; series: {fmt_number(row.seds_series_count)}\"\n",
" else:\n",
" seds_note = 'SEDS context: unavailable in seds_state_msn_year'\n",
" return folium.Popup(f'''\n",
" <div style=\"font-family: system-ui, sans-serif; min-width: 280px;\">\n",
" <strong>{clean_value(row.state_code)} state energy context</strong><br>\n",
" Current data centers: {fmt_number(row.current_data_center_count)}<br>\n",
" <hr style=\"margin: 6px 0;\">\n",
" IM3 projected sites: {fmt_number(row.im3_project_count)}<br>\n",
" IM3 projected IT power: {fmt_number(row.im3_projected_it_power_mw, suffix=' MW')}<br>\n",
" IM3 cooling water demand: {fmt_number(row.im3_cooling_water_demand_mgy, 1, suffix=' MGY')}<br>\n",
" IM3 water consumption: {fmt_number(row.im3_cooling_water_consumption_mgy, 1, suffix=' MGY')}<br>\n",
" IM3 avg siting score: {fmt_number(row.im3_avg_weighted_siting_score, 3)}<br>\n",
" {seds_note}\n",
" </div>\n",
" ''', max_width=380)\n"
]
},
{
"cell_type": "markdown",
"id": "8",
"metadata": {},
"source": [
"## Build The Map"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9",
"metadata": {},
"outputs": [],
"source": [
"def build_cluster_map(points_df: pd.DataFrame, clusters_df: pd.DataFrame) -> folium.Map:\n",
" m = folium.Map(location=MAP_CENTER, zoom_start=MAP_ZOOM, tiles=BASE_TILES, control_scale=True)\n",
" plugins.Fullscreen(position='topleft').add_to(m)\n",
" plugins.MeasureControl(position='topleft', primary_length_unit='kilometers').add_to(m)\n",
" plugins.MiniMap(toggle_display=True, minimized=True).add_to(m)\n",
"\n",
" huc8_layer = folium.FeatureGroup(name='HUC8 watersheds with data centers', show=False)\n",
" state_energy_layer = folium.FeatureGroup(name='State energy demand context (IM3 / SEDS)', show=False)\n",
" clustered_layer = folium.FeatureGroup(name='Data centers: clustered', show=True)\n",
" noise_layer = folium.FeatureGroup(name='Data centers: noise / isolated', show=True)\n",
" centroid_layer = folium.FeatureGroup(name='Cluster centroids and p90 radius', show=True)\n",
"\n",
" if SHOW_HUC8_LAYER and huc8_geojson is not None:\n",
" folium.GeoJson(\n",
" huc8_geojson,\n",
" name='HUC8 watersheds with data centers',\n",
" style_function=huc8_style,\n",
" highlight_function=lambda feature: {'weight': 3, 'fillOpacity': 0.35},\n",
" tooltip=folium.GeoJsonTooltip(\n",
" fields=['name', 'huc8', 'data_center_count', 'cluster_count'],\n",
" aliases=['HUC8', 'Code', 'Data centers', 'Clusters'],\n",
" localize=True,\n",
" sticky=False,\n",
" ),\n",
" popup=huc8_popup,\n",
" ).add_to(huc8_layer)\n",
"\n",
" if SHOW_STATE_ENERGY_LAYER and not state_energy.empty:\n",
" for row in state_energy.dropna(subset=['map_latitude', 'map_longitude']).itertuples(index=False):\n",
" power = getattr(row, 'im3_projected_it_power_mw')\n",
" radius = 6 if pd.isna(power) else max(6, min(28, 4 + float(power) ** 0.5 / 2.4))\n",
" folium.CircleMarker(\n",
" location=[row.map_latitude, row.map_longitude],\n",
" radius=radius,\n",
" color='#92400e',\n",
" fill=True,\n",
" fill_color=STATE_ENERGY_COLOR,\n",
" fill_opacity=0.55,\n",
" weight=1.5,\n",
" popup=state_energy_popup(row),\n",
" tooltip=f'{row.state_code}: IM3 {fmt_number(power, suffix=\" MW\")}',\n",
" ).add_to(state_energy_layer)\n",
"\n",
" bounds = []\n",
" for row in points_df.itertuples(index=False):\n",
" cluster_label, cluster_size, _ = cluster_label_and_size(row.cluster_id)\n",
" marker = folium.CircleMarker(\n",
" location=[row.latitude, row.longitude],\n",
" radius=NOISE_RADIUS if row.cluster_id == -1 else CLUSTERED_RADIUS,\n",
" color=cluster_color(row.cluster_id),\n",
" fill=True,\n",
" fill_opacity=0.75,\n",
" weight=1,\n",
" popup=point_popup(row),\n",
" tooltip=f'{cluster_label}; size={cluster_size}',\n",
" )\n",
" if row.cluster_id == -1:\n",
" marker.add_to(noise_layer)\n",
" else:\n",
" marker.add_to(clustered_layer)\n",
" bounds.append([row.latitude, row.longitude])\n",
"\n",
" for row in clusters_df.itertuples(index=False):\n",
" color = cluster_color(int(row.cluster_id))\n",
" location = [row.centroid_latitude, row.centroid_longitude]\n",
" if SHOW_CENTROID_P90_CIRCLES and pd.notna(row.radius_km_p90):\n",
" folium.Circle(\n",
" location=location,\n",
" radius=float(row.radius_km_p90) * 1000,\n",
" color=color,\n",
" fill=False,\n",
" weight=1,\n",
" opacity=0.45,\n",
" ).add_to(centroid_layer)\n",
" folium.CircleMarker(\n",
" location=location,\n",
" radius=CENTROID_RADIUS,\n",
" color=CENTROID_COLOR,\n",
" fill=True,\n",
" fill_color=color,\n",
" fill_opacity=0.95,\n",
" weight=2,\n",
" popup=centroid_popup(row),\n",
" tooltip=f'Cluster {int(row.cluster_id)} centroid; {int(row.point_count):,} points',\n",
" ).add_to(centroid_layer)\n",
"\n",
" huc8_layer.add_to(m)\n",
" state_energy_layer.add_to(m)\n",
" clustered_layer.add_to(m)\n",
" noise_layer.add_to(m)\n",
" centroid_layer.add_to(m)\n",
" folium.LayerControl(collapsed=False).add_to(m)\n",
" if bounds:\n",
" m.fit_bounds(bounds, padding=(20, 20))\n",
" return m\n",
"\n",
"\n",
"cluster_map = build_cluster_map(points, clusters)\n",
"cluster_map\n"
]
},
{
"cell_type": "markdown",
"id": "10",
"metadata": {},
"source": [
"## Export HTML"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "11",
"metadata": {},
"outputs": [],
"source": [
"cluster_map.save(MAP_HTML)\n",
"print('Wrote:', MAP_HTML.resolve())"
]
},
{
"cell_type": "markdown",
"id": "12",
"metadata": {},
"source": [
"## Feature Staging Area\n",
"\n",
"Tell me what you want to add next and I will build it here. Good candidates:\n",
"- filters by source/operator/state/cluster size\n",
"- toggle layers for top-N clusters\n",
"- water-stress overlays on top of the HUC8 layer\n",
"- generator capacity / fuel mix overlays around each DC\n",
"- opposition cases overlay\n",
"- cluster labels or summary panels\n",
"- downloadable GeoJSON exports\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
state_code,current_data_center_count,map_latitude,map_longitude,im3_project_count,im3_projected_it_power_mw,im3_cooling_energy_demand_mwh,im3_cooling_water_demand_mgy,im3_cooling_water_consumption_mgy,im3_avg_weighted_siting_score,im3_avg_normalized_locational_cost,im3_avg_normalized_gravity_score,seds_latest_year,seds_series_count,seds_selected_total_value
VA,378,38.81180123513644,-77.48445358366453,76.0,2736.0,0.0,3307.495679999999,2645.9966960000033,0.16464524066748473,0.2366938493068311,0.09259663202813818,,,
TX,162,31.509920265059563,-97.62641372338256,49.0,1764.0,0.0,2132.4643200000023,1705.9715539999997,0.00350041121207623,0.00018486843693128847,0.00681595398722118,,,
CA,147,36.6743835566224,-121.03746691674534,21.0,756.0,0.0,913.91328,731.1306659999999,0.009498709102457833,0.00963112411543682,0.009366294089478828,,,
IL,61,41.83977411585855,-87.98518544988711,16.0,576.0,0.0,696.31488,557.051936,0.06235921050437614,0.06746532657235553,0.05725309443639674,,,
OR,145,45.4770347075944,-120.9390505417634,14.0,504.0,0.0,609.27552,487.4204439999999,0.016703061443370994,0.010005638753125727,0.023400484133616232,,,
AZ,69,33.35143139388246,-111.8599097648615,14.0,504.0,685908.0,293.75784,235.00628579999997,0.22894142277116208,0.2160051752565124,0.24187767028581172,,,
IA,65,41.44566071909779,-94.26149737259419,14.0,504.0,0.0,609.27552,487.4204439999999,0.09821083960768505,0.1497288191285527,0.0466928600868174,,,
GA,50,33.75917632869643,-84.34900369690746,14.0,504.0,0.0,609.27552,487.4204439999999,0.07573816651050262,0.08431831295122912,0.06715802006977611,,,
WA,93,47.40058038575267,-120.65389054931781,11.0,396.0,189216.0,391.67712,313.34171399999997,0.008602897054563028,0.012114925824482491,0.005090868284643536,,,
PA,17,40.51861689987747,-77.14067159955545,10.0,360.0,0.0,435.1968,348.15745999999996,0.025611264607888918,0.046432172534256695,0.00479035668152121,,,
NJ,62,40.63657863750276,-74.33774092959604,9.0,324.0,0.0,391.67712,313.34171399999997,0.07951105237280956,0.04415559548391192,0.11486650926170719,,,
NY,48,42.0986494544888,-75.86702411728405,9.0,324.0,0.0,391.67712,313.34171399999997,0.033844266542779576,0.015562967240912412,0.052125565844646754,,,
NE,26,41.19044423571503,-96.21083342167539,8.0,288.0,0.0,348.15744,278.525968,0.023213461680844788,0.00994173426451155,0.036485189097178045,,,
ND,3,46.6009985,-97.43026833333334,8.0,288.0,0.0,348.15744,278.525968,0.1754766419137694,0.2637904066656519,0.08716287716188698,,,
NV,41,37.52887284950246,-116.95999289168654,7.0,252.0,0.0,304.63776,243.71022199999996,0.09370158681927372,0.13838366134523744,0.049019512293309996,,,
NC,31,35.515951937711286,-80.45619313915424,6.0,216.0,0.0,261.11808,208.89447599999997,0.017336791819723883,0.02155215545264995,0.013121428186797816,,,
OH,103,40.11289672434338,-82.9632959538231,5.0,180.0,0.0,217.5984,174.07872999999998,0.028173405916738482,0.027854599205723286,0.02849221262775366,,,
UT,17,40.27992287878221,-111.9244948339973,5.0,180.0,0.0,217.5984,174.07872999999998,0.071711762460987,0.045980594538791664,0.09744293038318233,,,
WY,22,41.2694301778197,-104.88440403454051,4.0,144.0,283824.0,43.51968,34.815746,0.14755687340545556,0.21318301702746534,0.08193072978344579,,,
SC,13,33.14909429058469,-80.10080479843208,4.0,144.0,0.0,174.07872,139.262984,0.1016155103641099,0.06996685870746874,0.13326416202075103,,,
TN,32,36.04624181258759,-86.94623740179134,3.0,108.0,0.0,130.55904,104.447238,0.10169703586035657,0.18104455608133466,0.02234951563937843,,,
FL,29,27.411439250882815,-81.13143447486134,3.0,108.0,283824.0,0.0,0.0,0.08202502654847077,0.1294847364539594,0.0345653166429822,,,
CO,27,39.51836862226617,-104.85288139276082,3.0,108.0,141912.0,65.27952,52.223619,0.005559275742971499,0.0064795144918877,0.004639036994055333,,,
AL,12,34.30076806872618,-86.35712687934489,3.0,108.0,0.0,130.55904,104.447238,0.0009726334592856833,0.00014293747388154497,0.001802329444689833,,,
KY,5,37.94511731391368,-85.43993183815397,3.0,108.0,0.0,130.55904,104.447238,0.0698208727274249,0.023908323736558768,0.11573342171829104,,,
MO,17,39.117889133868196,-93.5372434088145,2.0,72.0,0.0,87.03936,69.631492,0.0382817539994971,0.047006409422381296,0.0295570985766129,,,
MA,12,42.37838745843559,-71.37421289245025,2.0,72.0,0.0,87.03936,69.631492,0.0727448359176194,0.0192291585456955,0.12626051328954324,,,
OK,11,35.77427623773164,-96.24089812456369,2.0,72.0,0.0,87.03936,69.631492,0.05236513716444575,0.0689640790147675,0.035766195314124,,,
MN,15,44.980069700569416,-93.17958896920497,1.0,36.0,0.0,43.51968,34.815746,0.1809237101049204,0.2082387823395508,0.1536086378702902,,,
MI,13,42.46082727383233,-83.57434337241001,1.0,36.0,0.0,43.51968,34.815746,0.0566642628531991,0.0423349445013257,0.0709935812050726,,,
MT,3,46.16069827338828,-109.23879441517472,1.0,36.0,0.0,43.51968,34.815746,0.1285720285331266,0.2250467365641875,0.0320973205020656,,,
NM,17,34.84316652063309,-106.64757455359201,,,,,,,,,,,
WI,17,43.21578182334325,-88.79371779090025,,,,,,,,,,,
MD,16,39.252795209689744,-76.78697385304773,,,,,,,,,,,
IN,10,40.603351555794575,-86.52645175699342,,,,,,,,,,,
KS,8,38.71977107866478,-95.84686174722735,,,,,,,,,,,
CT,7,41.257283034952174,-73.19770117764624,,,,,,,,,,,
MS,5,32.39151784524035,-90.32332510585482,,,,,,,,,,,
ID,4,43.267254168631325,-113.90666627597611,,,,,,,,,,,
NH,4,43.031734736446104,-71.04302199169723,,,,,,,,,,,
WV,3,38.77825380000001,-80.518261,,,,,,,,,,,
LA,3,30.956981986997324,-91.664033994836,,,,,,,,,,,
DC,2,38.89899305000002,-77.032767,,,,,,,,,,,
ME,2,43.77645494131252,-70.09226452273563,,,,,,,,,,,
PR,2,18.432131450000014,-66.08033840000002,,,,,,,,,,,
AR,2,35.002725817804865,-92.0928146845728,,,,,,,,,,,
SD,2,43.60344233895939,-96.80197609920154,,,,,,,,,,,
1 state_code current_data_center_count map_latitude map_longitude im3_project_count im3_projected_it_power_mw im3_cooling_energy_demand_mwh im3_cooling_water_demand_mgy im3_cooling_water_consumption_mgy im3_avg_weighted_siting_score im3_avg_normalized_locational_cost im3_avg_normalized_gravity_score seds_latest_year seds_series_count seds_selected_total_value
2 VA 378 38.81180123513644 -77.48445358366453 76.0 2736.0 0.0 3307.495679999999 2645.9966960000033 0.16464524066748473 0.2366938493068311 0.09259663202813818
3 TX 162 31.509920265059563 -97.62641372338256 49.0 1764.0 0.0 2132.4643200000023 1705.9715539999997 0.00350041121207623 0.00018486843693128847 0.00681595398722118
4 CA 147 36.6743835566224 -121.03746691674534 21.0 756.0 0.0 913.91328 731.1306659999999 0.009498709102457833 0.00963112411543682 0.009366294089478828
5 IL 61 41.83977411585855 -87.98518544988711 16.0 576.0 0.0 696.31488 557.051936 0.06235921050437614 0.06746532657235553 0.05725309443639674
6 OR 145 45.4770347075944 -120.9390505417634 14.0 504.0 0.0 609.27552 487.4204439999999 0.016703061443370994 0.010005638753125727 0.023400484133616232
7 AZ 69 33.35143139388246 -111.8599097648615 14.0 504.0 685908.0 293.75784 235.00628579999997 0.22894142277116208 0.2160051752565124 0.24187767028581172
8 IA 65 41.44566071909779 -94.26149737259419 14.0 504.0 0.0 609.27552 487.4204439999999 0.09821083960768505 0.1497288191285527 0.0466928600868174
9 GA 50 33.75917632869643 -84.34900369690746 14.0 504.0 0.0 609.27552 487.4204439999999 0.07573816651050262 0.08431831295122912 0.06715802006977611
10 WA 93 47.40058038575267 -120.65389054931781 11.0 396.0 189216.0 391.67712 313.34171399999997 0.008602897054563028 0.012114925824482491 0.005090868284643536
11 PA 17 40.51861689987747 -77.14067159955545 10.0 360.0 0.0 435.1968 348.15745999999996 0.025611264607888918 0.046432172534256695 0.00479035668152121
12 NJ 62 40.63657863750276 -74.33774092959604 9.0 324.0 0.0 391.67712 313.34171399999997 0.07951105237280956 0.04415559548391192 0.11486650926170719
13 NY 48 42.0986494544888 -75.86702411728405 9.0 324.0 0.0 391.67712 313.34171399999997 0.033844266542779576 0.015562967240912412 0.052125565844646754
14 NE 26 41.19044423571503 -96.21083342167539 8.0 288.0 0.0 348.15744 278.525968 0.023213461680844788 0.00994173426451155 0.036485189097178045
15 ND 3 46.6009985 -97.43026833333334 8.0 288.0 0.0 348.15744 278.525968 0.1754766419137694 0.2637904066656519 0.08716287716188698
16 NV 41 37.52887284950246 -116.95999289168654 7.0 252.0 0.0 304.63776 243.71022199999996 0.09370158681927372 0.13838366134523744 0.049019512293309996
17 NC 31 35.515951937711286 -80.45619313915424 6.0 216.0 0.0 261.11808 208.89447599999997 0.017336791819723883 0.02155215545264995 0.013121428186797816
18 OH 103 40.11289672434338 -82.9632959538231 5.0 180.0 0.0 217.5984 174.07872999999998 0.028173405916738482 0.027854599205723286 0.02849221262775366
19 UT 17 40.27992287878221 -111.9244948339973 5.0 180.0 0.0 217.5984 174.07872999999998 0.071711762460987 0.045980594538791664 0.09744293038318233
20 WY 22 41.2694301778197 -104.88440403454051 4.0 144.0 283824.0 43.51968 34.815746 0.14755687340545556 0.21318301702746534 0.08193072978344579
21 SC 13 33.14909429058469 -80.10080479843208 4.0 144.0 0.0 174.07872 139.262984 0.1016155103641099 0.06996685870746874 0.13326416202075103
22 TN 32 36.04624181258759 -86.94623740179134 3.0 108.0 0.0 130.55904 104.447238 0.10169703586035657 0.18104455608133466 0.02234951563937843
23 FL 29 27.411439250882815 -81.13143447486134 3.0 108.0 283824.0 0.0 0.0 0.08202502654847077 0.1294847364539594 0.0345653166429822
24 CO 27 39.51836862226617 -104.85288139276082 3.0 108.0 141912.0 65.27952 52.223619 0.005559275742971499 0.0064795144918877 0.004639036994055333
25 AL 12 34.30076806872618 -86.35712687934489 3.0 108.0 0.0 130.55904 104.447238 0.0009726334592856833 0.00014293747388154497 0.001802329444689833
26 KY 5 37.94511731391368 -85.43993183815397 3.0 108.0 0.0 130.55904 104.447238 0.0698208727274249 0.023908323736558768 0.11573342171829104
27 MO 17 39.117889133868196 -93.5372434088145 2.0 72.0 0.0 87.03936 69.631492 0.0382817539994971 0.047006409422381296 0.0295570985766129
28 MA 12 42.37838745843559 -71.37421289245025 2.0 72.0 0.0 87.03936 69.631492 0.0727448359176194 0.0192291585456955 0.12626051328954324
29 OK 11 35.77427623773164 -96.24089812456369 2.0 72.0 0.0 87.03936 69.631492 0.05236513716444575 0.0689640790147675 0.035766195314124
30 MN 15 44.980069700569416 -93.17958896920497 1.0 36.0 0.0 43.51968 34.815746 0.1809237101049204 0.2082387823395508 0.1536086378702902
31 MI 13 42.46082727383233 -83.57434337241001 1.0 36.0 0.0 43.51968 34.815746 0.0566642628531991 0.0423349445013257 0.0709935812050726
32 MT 3 46.16069827338828 -109.23879441517472 1.0 36.0 0.0 43.51968 34.815746 0.1285720285331266 0.2250467365641875 0.0320973205020656
33 NM 17 34.84316652063309 -106.64757455359201
34 WI 17 43.21578182334325 -88.79371779090025
35 MD 16 39.252795209689744 -76.78697385304773
36 IN 10 40.603351555794575 -86.52645175699342
37 KS 8 38.71977107866478 -95.84686174722735
38 CT 7 41.257283034952174 -73.19770117764624
39 MS 5 32.39151784524035 -90.32332510585482
40 ID 4 43.267254168631325 -113.90666627597611
41 NH 4 43.031734736446104 -71.04302199169723
42 WV 3 38.77825380000001 -80.518261
43 LA 3 30.956981986997324 -91.664033994836
44 DC 2 38.89899305000002 -77.032767
45 ME 2 43.77645494131252 -70.09226452273563
46 PR 2 18.432131450000014 -66.08033840000002
47 AR 2 35.002725817804865 -92.0928146845728
48 SD 2 43.60344233895939 -96.80197609920154