LeonceNsh's picture
Update app.py
4b3015b verified
import pandas as pd
import geopandas as gpd
import gradio as gr
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.cluster.hierarchy import linkage, leaves_list
# ========================
# Data Loading
# ========================
conus_data = pd.read_csv("conus27.csv")
county_geojson = gpd.read_file("county.geojson")
county_embeddings = pd.read_csv("county_embeddings.csv")
county_unemployment = pd.read_csv("county_unemployment.csv")
zcta_poverty = pd.read_csv("zcta_poverty.csv")
zcta_geojson = gpd.read_file("zcta.geojson")
# Prepare unemployment data
county_unemployment_melted = county_unemployment.melt(
id_vars=['place'], var_name='date', value_name='unemployment_rate'
)
county_unemployment_melted['place'] = county_unemployment_melted['place'].astype(str)
county_geojson_unemployment = county_geojson.merge(
county_unemployment_melted, left_on='place', right_on='place', how='left'
)
# Prepare poverty data
zcta_poverty_melted = zcta_poverty.melt(
id_vars=['place'], var_name='year', value_name='poverty_rate'
)
zcta_poverty_melted['place'] = zcta_poverty_melted['place'].astype(str)
zcta_geojson['place'] = zcta_geojson['place'].astype(str)
zcta_geojson_poverty = zcta_geojson.merge(
zcta_poverty_melted, left_on='place', right_on='place', how='left'
)
# Identify health metrics
health_metrics = [col for col in conus_data.columns if col.startswith('Percent_Person_')]
simplified_metrics = [col.replace('Percent_Person_', '') for col in health_metrics]
metric_mapping = dict(zip(simplified_metrics, health_metrics))
# Create a merged geodataframe for health metrics visualization
# Assuming conus_data has a 'place' or 'GEOID' matching the county_geojson
if 'place' in conus_data.columns:
merged_health = county_geojson.merge(conus_data, on='place', how='left')
else:
# If another key needed, adjust here. Assuming 'GEOID' would match, as example.
if 'GEOID' in county_geojson.columns and 'GEOID' in conus_data.columns:
merged_health = county_geojson.merge(conus_data, on='GEOID', how='left')
else:
raise ValueError("No matching key found to merge health data with geodata.")
# ========================
# Utility Functions
# ========================
def plot_health_metric(metric):
"""
Plots the geographical distribution of a selected health metric using a better colormap.
"""
metric_full_name = metric_mapping[metric]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
merged_health.plot(
column=metric_full_name,
cmap='viridis',
markersize=50,
legend=True,
legend_kwds={'label': f"{metric} (%)"},
ax=ax,
alpha=0.7,
edgecolor='black',
linewidth=0.5,
missing_kwds={"color": "lightgrey", "label": "No Data"}
)
ax.set_title(f'Geographical Distribution of {metric}', fontsize=15)
ax.axis('off')
plt.tight_layout()
return fig
def plot_health_histogram(metric):
"""
Plots the distribution (histogram) of a selected health metric to understand its spread.
"""
metric_full_name = metric_mapping[metric]
data = conus_data[metric_full_name].dropna()
fig, ax = plt.subplots(figsize=(8, 6))
sns.histplot(data, kde=True, color='teal', ax=ax)
ax.set_title(f'Distribution of {metric} (%)', fontsize=15)
ax.set_xlabel(f'{metric} (%)')
ax.set_ylabel('Count')
plt.tight_layout()
return fig
def summarize_health_metrics(metric):
"""
Generates more detailed summary statistics for a selected health metric.
Includes median and IQR along with standard describe().
"""
metric_full_name = metric_mapping[metric]
data = conus_data[metric_full_name].dropna()
desc = data.describe().to_frame().reset_index()
desc.columns = ['Statistic', 'Value']
# Add median and IQR if not already present
median_val = data.median()
q1, q3 = data.quantile([0.25, 0.75])
iqr = q3 - q1
# Insert median and IQR below mean row
extra_stats = pd.DataFrame({
'Statistic': ['Median', 'IQR'],
'Value': [median_val, iqr]
})
summary = pd.concat([desc, extra_stats], ignore_index=True)
return summary
def plot_correlation_matrix(selected_metrics):
"""
Plots a correlation matrix for selected health metrics and reorders the axes using hierarchical clustering.
"""
selected_columns = [metric_mapping[m] for m in selected_metrics]
corr = conus_data[selected_columns].corr()
# Hierarchical clustering to reorder correlation matrix
linkage_matrix = linkage(1 - corr, method='average')
idx = leaves_list(linkage_matrix)
corr = corr.iloc[idx, :].iloc[:, idx]
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
corr, annot=True, cmap='coolwarm', square=True, ax=ax,
xticklabels=corr.columns, yticklabels=corr.columns,
cbar_kws={"shrink": .8}
)
ax.set_title('Correlation Matrix (Hierarchically Clustered)', fontsize=15)
plt.xticks(rotation=45, ha='right')
plt.yticks(rotation=0)
plt.tight_layout()
return fig
def plot_unemployment_map(date):
"""
Plots the unemployment rate map for a selected date with an improved colormap.
"""
date = str(date)
data = county_geojson_unemployment[county_geojson_unemployment['date'] == date]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
data.plot(
column='unemployment_rate',
cmap='YlGnBu',
linewidth=0.5,
ax=ax,
edgecolor='0.8',
legend=True,
missing_kwds={"color": "lightgrey", "label": "Missing values"},
)
ax.set_title(f'Unemployment Rate by County ({date})', fontsize=15)
ax.axis('off')
plt.tight_layout()
return fig
def plot_poverty_map(year):
"""
Plots the poverty rate map for a selected year with improved colormap.
"""
year = str(year)
data = zcta_geojson_poverty[zcta_geojson_poverty['year'] == year]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
data.plot(
column='poverty_rate',
cmap='YlOrRd',
linewidth=0.5,
ax=ax,
edgecolor='0.8',
legend=True,
missing_kwds={"color": "lightgrey", "label": "Missing values"},
)
ax.set_title(f'Poverty Rate by ZCTA ({year})', fontsize=15)
ax.axis('off')
plt.tight_layout()
return fig
# ========================
# Gradio Interface Functions
# ========================
def health_metric_interface(metric):
map_fig = plot_health_metric(metric)
summary = summarize_health_metrics(metric)
hist_fig = plot_health_histogram(metric)
return map_fig, summary, hist_fig
def correlation_interface(metrics):
# Require at least two metrics to show correlation
if len(metrics) < 2:
return "Please select at least two metrics to see a correlation matrix."
fig = plot_correlation_matrix(metrics)
return fig
def unemployment_interface(date):
fig = plot_unemployment_map(date)
return fig
def poverty_interface(year):
fig = plot_poverty_map(year)
return fig
# ========================
# Gradio App Setup
# ========================
with gr.Blocks(title="US Population Health Dashboard") as demo:
gr.Markdown("# US Population Health Dashboard")
gr.Markdown("""
Explore health metrics, socioeconomic data, and their geospatial distributions across the United States.
Use the tabs below to select different datasets and visualizations.
""")
with gr.Tab("Health Metrics"):
gr.Markdown("### Explore a Selected Health Metric")
gr.Markdown("Select a health metric to view its geographical distribution, summary statistics, and distribution histogram.")
health_metric = gr.Dropdown(label="Select a Health Metric", choices=simplified_metrics, value=simplified_metrics[0])
health_plot = gr.Plot(label="Health Metric Map")
health_summary = gr.Dataframe(label="Summary Statistics", headers=["Statistic", "Value"])
health_hist = gr.Plot(label="Metric Distribution Histogram")
health_metric.change(health_metric_interface, inputs=health_metric, outputs=[health_plot, health_summary, health_hist])
with gr.Tab("Health Metrics Correlation"):
gr.Markdown("### Correlation Between Health Metrics")
gr.Markdown("Select multiple health metrics to see how they correlate with each other. The matrix is reordered using hierarchical clustering.")
correlation_metrics = gr.CheckboxGroup(
label="Select Health Metrics",
choices=simplified_metrics,
value=simplified_metrics[:5]
)
correlation_plot = gr.Plot(label="Correlation Matrix")
correlation_metrics.change(correlation_interface, inputs=correlation_metrics, outputs=correlation_plot)
with gr.Tab("Unemployment Rates Over Time"):
gr.Markdown("### View Unemployment Rates by County")
gr.Markdown("Select a date to see the unemployment rate distribution across counties.")
unique_dates = sorted(county_unemployment_melted['date'].unique())
unemployment_date = gr.Dropdown(label="Select a Date", choices=unique_dates, value=unique_dates[0])
unemployment_plot = gr.Plot(label="Unemployment Rate Map")
unemployment_date.change(unemployment_interface, inputs=unemployment_date, outputs=unemployment_plot)
with gr.Tab("Poverty Rates Over Time"):
gr.Markdown("### View Poverty Rates by ZCTA")
gr.Markdown("Select a year to see the poverty rate distribution across ZIP Code Tabulation Areas.")
unique_years = sorted(zcta_poverty_melted['year'].unique())
poverty_year = gr.Dropdown(label="Select a Year", choices=unique_years, value=unique_years[0])
poverty_plot = gr.Plot(label="Poverty Rate Map")
poverty_year.change(poverty_interface, inputs=poverty_year, outputs=poverty_plot)
if __name__ == "__main__":
demo.launch()