| import streamlit as st |
| import pandas as pd |
| import altair as alt |
|
|
| |
| |
| |
| st.set_page_config(page_title="Transportation Trends", layout="wide") |
|
|
| |
| |
| |
| st.sidebar.title("Navigation") |
| page = st.sidebar.radio("Go to", [ |
| "Introduction", |
| "Visual 1&2", |
| "Visual 3", |
| "Visual 4", |
| "Conclusion" |
| ]) |
|
|
| |
| |
| |
| @st.cache_data |
| df = pd.read_csv("data_165037.csv") |
| df.columns = df.columns.str.strip() |
| df["Transportation Type"] = df["Transportation Type"].str.replace("Transportation Type :", "").str.strip() |
| df["Value"] = pd.to_numeric(df["Value"], errors="coerce") |
| df = df.drop(columns=["Data Comment", "Unnamed: 8"], errors="ignore") |
|
|
| |
| |
| |
| if page == "Introduction": |
| st.title("Transportation Trends in U.S.") |
|
|
| st.header("Project Overview") |
| st.markdown(""" |
| This Streamlit app presents trends in **walking and biking** across U.S. counties from 2008 to 2022. |
| |
| **Objectives:** |
| - Compare average usage of walking vs. biking |
| - Analyze changes over time |
| - Explore county-level insights |
| |
| **Data Source**: [CDC Environmental Public Health Tracking Network](https://ephtracking.cdc.gov/) |
| |
| **Team Members** |
| - Jingye Lin |
| - Jorge Yoshiyama Lee |
| - Kritika Singh |
| - Mruga Vinayak Gokhale |
| - Meghna Sarda |
| """) |
|
|
| st.header("Dataset Summary") |
| st.markdown(""" |
| This project investigates patterns in active transportation—specifically **walking** and **bicycling**—across U.S. counties using data from the CDC’s transportation health indicators. |
| By analyzing trends over time and geography, our goal is to support data-driven public health planning and identify regions with high or low physical activity rates. |
| |
| **Why This Dataset?** |
| - **Public Health Relevance**: Walking and biking are linked to improved health outcomes. |
| - **Geographic Coverage**: Data spans all 50 states at the **county level**. |
| - **Longitudinal Trends**: Covers over a **decade of data**, enabling time-based analysis. |
| - **Government-Sourced**: Derived from a credible, curated CDC dataset. |
| |
| **Quick Stats** |
| - **Total rows**: 87,994 |
| - **Columns**: 10 |
| - **Time Coverage**: 2005–2022 |
| - **Geographic Units**: U.S. counties |
| |
| **Columns** |
| | Column | Description | |
| |----------------------|---------------------------------------------| |
| | StateFIPS | Federal Information Processing Standards ID | |
| | State | State name | |
| | CountyFIPS | County FIPS code | |
| | County | County name | |
| | Start Year | Start of the 5-year period | |
| | End Year | End of the 5-year period | |
| | Value | Percentage of people using the mode | |
| | Transportation Type| Mode of transportation (Bicycle/Walking) |
| |
| **Data Quality** |
| - No missing values in main columns except `Value` (~11k missing). |
| - Dropped empty/irrelevant columns. |
| - Only two transportation types: 'Bicycle' and 'Walking'. |
| |
| **What We Expect to Learn** |
| - How has active transportation changed over time? |
| - Which states or counties have higher walking or biking rates? |
| - Is there a relationship between walking and biking rates across regions? |
| - Where can policy interventions or infrastructure investments make the biggest difference? |
| """) |
|
|
| st.markdown("### Dataset Preview") |
| st.dataframe(df.head()) |
|
|
| |
| |
| |
| elif page == "Visual 1&2": |
| st.title("Vizualization: State and County Trends") |
|
|
| |
| |
| |
| st.subheader("Grouped Bar Chart: Walking vs. Bicycling Over Time") |
| st.markdown("This chart shows walking and bicycling usage side by side for each year in the selected state.") |
|
|
| state = st.selectbox("Select a State", sorted(df["State"].dropna().unique())) |
| filtered = df[df["State"] == state] |
|
|
| chart = alt.Chart(filtered).mark_bar().encode( |
| x=alt.X('End Year:O', title='End Year'), |
| xOffset='Transportation Type:N', |
| y=alt.Y('Value:Q', title='Usage'), |
| color=alt.Color('Transportation Type:N', title='Transportation Type'), |
| tooltip=['End Year', 'Transportation Type', 'Value'] |
| ).properties( |
| width=700, |
| height=400, |
| title=f"Walking vs. Bicycling Usage in {state} (Grouped by Year)" |
| ) |
| st.altair_chart(chart, use_container_width=True) |
|
|
| st.markdown("#### Explanation") |
| st.markdown(""" |
| This interactive grouped bar chart allows users to explore and compare walking and bicycling usage trends across different U.S. states from 2009 to 2022. |
| By selecting a state from the dropdown menu, the chart dynamically updates to display year-wise usage data for both transportation types side by side. |
| |
| The goal of this visualization is to highlight patterns in active transportation over time and reveal how these trends may vary by location. |
| For example, some states show consistently higher walking usage, while others reflect a growing shift toward bicycling in more recent years. |
| This tool supports deeper insight into the effects of urban planning, public health initiatives, and infrastructure development on how people choose to move. |
| """) |
|
|
| |
| |
| |
| st.subheader("County-Level Trendlines: Top Active Counties") |
| st.markdown("Explore transportation usage in the top counties with the highest walking/biking levels.") |
|
|
| year_options = sorted(df['Start Year'].dropna().unique()) |
| default_years = [year for year in [2015, 2016, 2017, 2018] if year in year_options] |
| selected_years = st.multiselect("Select Year(s)", year_options, default=default_years) |
| selected_types = st.multiselect("Select Transportation Types", ['Bicycle', 'Walking'], default=['Bicycle', 'Walking']) |
| top_n = st.slider("Number of Top Counties", min_value=3, max_value=20, value=6) |
|
|
| df_filtered = df[df['Start Year'].isin(selected_years) & df['Transportation Type'].isin(selected_types)] |
| top_counties = df_filtered.groupby('County')['Value'].sum().nlargest(top_n).index.tolist() |
| plot_df = df_filtered[df_filtered['County'].isin(top_counties)] |
|
|
| chart2 = alt.Chart(plot_df).mark_line(point=True).encode( |
| x=alt.X('Start Year:O', title='Year'), |
| y=alt.Y('Value:Q', title='Transportation Count'), |
| color=alt.Color('Transportation Type:N', title='Type'), |
| strokeDash='Transportation Type:N', |
| tooltip=['County', 'Start Year', 'Transportation Type', 'Value'] |
| ).properties(width=250, height=200) |
|
|
| st.altair_chart(chart2.facet(facet=alt.Facet('County:N'), columns=3).resolve_scale(y='independent'), use_container_width=True) |
|
|
| st.markdown("#### Explanation") |
| st.markdown(""" |
| This faceted line chart shows year-wise walking and bicycling trends for the top U.S. counties with the highest transportation usage. |
| The facets enable users to visually compare how each county’s usage changes over time across the selected years. |
| |
| Line styles and colors distinguish between transportation modes. Some counties show consistent increases in active transportation, |
| while others fluctuate or decline. This plot is useful for identifying localized transportation trends and understanding which |
| counties are leading or lagging in promoting walking and biking. |
| """) |
|
|
|
|
| |
| |
| |
| elif page == "Visual 3": |
| st.title("Comparison Based on Ratio Based on County Pop") |
| |
| @st.cache_data |
| df = pd.read_csv('dataset.csv') |
| df = df.drop(['Data Comment'], axis=1) |
| df.drop(columns=['Unnamed: 8'], inplace=True, errors='ignore') |
| df.columns = df.columns.str.strip() |
| df['Value'] = pd.to_numeric(df['Value'], errors='coerce') |
| if 'Transportation Type' in df.columns: |
| df['Transportation Type'] = df['Transportation Type'].str.replace("Transportation Type :", "").str.strip() |
| |
| fips_df = pd.read_csv("https://raw.githubusercontent.com/kjhealy/fips-codes/refs/heads/master/state_and_county_fips_master.csv") |
| merged_df = df.merge( |
| fips_df[['fips', 'name']], |
| left_on='CountyFIPS', |
| right_on='fips', |
| how='left' |
| ) |
| merged_df['County'] = merged_df['name'] |
| merged_df.drop(columns=['fips', 'name'], inplace=True) |
| merged_df = merged_df.fillna(9999) |
| |
| @st.cache_data |
| pop_data = pd.read_csv('pop_data.csv') |
| df_info = pop_data.groupby('CTYNAME', as_index=False).first() |
| final_merged_df = pd.merge(merged_df, df_info, left_on='County', right_on='CTYNAME', how='left') |
| final_merged_df['pop_per_unit'] = final_merged_df['POPESTIMATE2019'] / final_merged_df['Value'] |
| final_merged_df['ratio'] = final_merged_df['Value'] / final_merged_df['POPESTIMATE2019'] |
| final_merged_df = final_merged_df.rename(columns={'Value': 'count'}) |
| master_df = final_merged_df |
| |
| year = st.selectbox("Select Year", [2017, 2018]) |
| t_type = st.selectbox("Select Transportation Type", ['Bicycle', 'Walking']) |
| data = st.selectbox("Select Data to Visualize", ['pop_per_unit', 'ratio', 'count']) |
| |
| if data == 'pop_per_unit': |
| value_pop = st.slider("Max Pop per Unit", min_value=0.0, max_value=1000.0, step=1.0, value=500.0) |
| st.markdown("""*pop_per_unit refers to the population amount per person that uses a bycicle or walks. It is calculated by dividing the population for that county by the # of bike users/walkers.""") |
| elif data == 'ratio': |
| value_ratio = st.slider("Max Ratio", min_value=0.0, max_value=2.0, step=0.001, value=0.001) |
| st.markdown("""*ratio refers to the ratio of bike/walkers to the population. It represents the reciprocal of pop_per_unit.""") |
| elif data == 'count': |
| value_count = st.slider("Max Count", min_value=0, max_value=1000, step=1, value=500) |
|
|
| time_df = master_df[(master_df['Start Year'] == year) & (master_df['Transportation Type'] == t_type) & master_df['County']].copy() |
| counties = alt.topo_feature("https://raw.githubusercontent.com/vega/vega/refs/heads/main/docs/data/us-10m.json", "counties") |
| alt.data_transformers.disable_max_rows() |
| |
| base = alt.Chart(counties).mark_geoshape(fill='lightgray').encode().project('albersUsa') |
| if data == 'pop_per_unit': |
| chart_data = time_df[['County','CountyFIPS', 'pop_per_unit']].copy() |
| chart_data = chart_data[chart_data['pop_per_unit'] <= value_pop] |
| color_field = 'pop_per_unit:Q' |
| elif data == 'ratio': |
| chart_data = time_df[['County','CountyFIPS', 'ratio']].copy() |
| chart_data = chart_data[chart_data['ratio'] <= value_ratio] |
| color_field = 'ratio:Q' |
| else: |
| chart_data = time_df[['County','CountyFIPS', 'count']].copy() |
| chart_data = chart_data[chart_data['count'] <= value_count] |
| color_field = 'count:Q' |
| top = alt.Chart(counties).mark_geoshape().encode( |
| color=color_field, |
| tooltip= [chart_data.columns[0]+":N", chart_data.columns[1] + ':Q',chart_data.columns[2]+":Q"] |
| ).transform_lookup( |
| lookup='id', |
| from_=alt.LookupData(chart_data, 'CountyFIPS', chart_data.columns[1:]) |
| ).project(type='albersUsa').properties(width=700, height=450) |
| st.altair_chart(base + top) |
| |
| if data == 'pop_per_unit': |
| st.markdown("#### Explanation") |
| st.markdown(""" |
| The **pop_per_unit** metric reflects the **number of people in a county per one person who walks or bicycles**. It is calculated by dividing the total population by the number of active transportation users (bicyclists or walkers). |
| |
| - A **lower pop_per_unit** value means more people walk or bike (i.e., higher active transport adoption). |
| - A **higher value** implies fewer individuals per user, indicating lower usage of active modes. |
| |
| This normalization helps account for differences in county population sizes, enabling fairer comparison across regions. Urban counties tend to have lower pop_per_unit values due to better infrastructure and accessibility. |
| """) |
| elif data == 'ratio': |
| st.markdown("#### Explanation") |
| st.markdown(""" |
| The **ratio** metric represents the **proportion of people in a county who use active transportation** (bicycling or walking). It is the **reciprocal** of `pop_per_unit`, calculated as: |
| |
| `ratio = number of walkers/bikers ÷ total population` |
| |
| - A **higher ratio** means a greater share of the population uses the selected mode. |
| - A **lower ratio** indicates low active transport engagement. |
| |
| This direct ratio makes it easier to identify counties with high engagement, and is ideal for comparing uptake across regions without raw population bias. |
| """) |
| elif data == 'count': |
| st.markdown("#### Explanation") |
| st.markdown(""" |
| The **count** metric shows the **estimated number of people in each county who walk or bicycle**, without adjusting for population size. It reflects absolute usage levels rather than proportion. |
| |
| - **Higher counts** may indicate populous counties or strong infrastructure supporting active modes. |
| - **Lower counts** could reflect either smaller populations or less frequent walking/biking. |
| |
| This raw count is useful for identifying where the largest absolute numbers of active travelers are located, but it should be interpreted with population context in mind. |
| """) |
|
|
| elif page == "Visual 4": |
| df = pd.read_csv('dataset.csv') |
| df = df.drop(['Data Comment'], axis=1) |
| df.drop(columns=['Unnamed: 8'], inplace=True, errors='ignore') |
| df.columns = df.columns.str.strip() |
| df['Value'] = pd.to_numeric(df['Value'], errors='coerce') |
| if 'Transportation Type' in df.columns: |
| df['Transportation Type'] = df['Transportation Type'].str.replace("Transportation Type :", "").str.strip() |
| |
| fips_df = pd.read_csv("https://raw.githubusercontent.com/kjhealy/fips-codes/refs/heads/master/state_and_county_fips_master.csv") |
| |
| merged_df = df.merge( |
| fips_df[['fips', 'name']], |
| left_on='CountyFIPS', |
| right_on='fips', |
| how='left' |
| ) |
| |
| merged_df['County'] = merged_df['name'] |
| merged_df.drop(columns=['fips', 'name'], inplace=True) |
| merged_df = merged_df.fillna(9999) |
| |
| pop_data = pd.read_csv('pop_data.csv') |
| df_info = pop_data.groupby('CTYNAME', as_index=False).first() |
| final_merged_df = pd.merge(merged_df, df_info, left_on='County', right_on='CTYNAME', how='left') |
| final_merged_df['pop_per_unit'] = final_merged_df['POPESTIMATE2019'] / final_merged_df['Value'] |
| final_merged_df['ratio'] = final_merged_df['Value']/final_merged_df['POPESTIMATE2019'] |
| master_df=final_merged_df |
| |
| st.sidebar.title("Year Comparison") |
| year_options = sorted(master_df['Start Year'].dropna().unique()) |
| year1 = st.sidebar.selectbox("Select first year", year_options, index=year_options.index(2017)) |
| year2 = st.sidebar.selectbox("Select second year", year_options, index=year_options.index(2018)) |
| |
| st.title("County-Level Value Change Visualization") |
| st.write(f"Showing change in total 'Value' from {year1} to {year2} by county.") |
| |
| df_filtered = master_df[master_df['Start Year'].isin([year1, year2])] |
| value_by_year = df_filtered.groupby(['CountyFIPS', 'Start Year'])['Value'].sum().unstack() |
| value_by_year['Change'] = value_by_year[year2] - value_by_year[year1] |
| value_by_year = value_by_year.reset_index() |
| |
| county_info = df[['CountyFIPS', 'County', 'State', 'StateFIPS']].drop_duplicates() |
| change_df = county_info.merge(value_by_year[['CountyFIPS', 'Change']], on='CountyFIPS') |
| |
| counties = alt.topo_feature("https://raw.githubusercontent.com/vega/vega/refs/heads/main/docs/data/us-10m.json", "counties") |
| alt.data_transformers.disable_max_rows() |
| |
| base = alt.Chart(counties).mark_geoshape(fill='lightgray').encode().project('albersUsa') |
|
|
| top = alt.Chart(counties).mark_geoshape().transform_lookup( |
| lookup='id', |
| from_=alt.LookupData(change_df, 'CountyFIPS', ['Change']) |
| ).encode( |
| color=alt.Color('Change:Q', title='Change in Value', scale=alt.Scale(scheme='purplegreen')) |
| ) |
| |
| st.altair_chart(base+top, use_container_width=True) |
|
|
| st.markdown(""" |
| This visualization was to better vizualise what is being graphed in visual 2. It better singles out which counties are going through net changes in the amount of individuals who commute to work using bicycles and by walking. |
| The net changes are calculated by subtracting the second year from the first year. This leads to a net change which is visualized by the color scale. Darker shades mean higher net values. Lighter shades mean lower net values. Positive values are shaded green and negative values are shaded purple. |
| *NOTE: counties presented in gray means that there was no datapoint for that county. |
| """) |
| |
|
|
| |
| |
| |
| elif page == "Conclusion": |
| st.title("Conclusion & Next Steps") |
|
|
| st.markdown("### Conclusion") |
| st.markdown(""" |
| This analysis highlights clear and insightful patterns in **active transportation usage (walking and bicycling)** across U.S. counties from **2008 to 2022**. |
| Nationally, walking remains the dominant form of active travel, consistently outpacing bicycling in participation rates. However, significant **geographic variation** exists—some counties show a growing interest in biking, while others maintain strong walking cultures or stagnate. |
| |
| Our **state- and county-level visualizations** revealed temporal and regional fluctuations, offering evidence that **infrastructure availability**, **urban design**, and **local policy efforts** play substantial roles in shaping transportation behaviors. |
| """) |
|
|
| st.markdown("### Key Takeaways") |
| st.markdown(""" |
| - **Walking is consistently more prevalent** than bicycling across most U.S. counties due to ease of access and infrastructure support. |
| - **County-level trends vary**—some show growth in active transport use, while others stagnate or decline. |
| - **Population-adjusted metrics** show disparities in access or behavior, with biking more common in parts of the **West and Northeast**. |
| - **Geographic and policy context matters**—state-level patterns suggest local investments significantly impact active transportation behavior. |
| - **Top counties** can be used as benchmarks or case studies to guide underperforming areas. |
| """) |
|
|
| st.markdown("### Roadblocks Faced") |
| st.markdown(""" |
| - **Missing data**: ~11,000 rows had null values in the `Value` column, limiting some trend analyses. |
| - **FIPS code mismatches**: Combining multiple datasets required careful alignment of geographic identifiers. |
| - **Inconsistent time reporting**: Some counties lacked full year or mode coverage, limiting time-series completeness. |
| - **Population normalization issues**: Handling outliers and gaps in population data introduced some estimation challenges. |
| - **Visualization complexity**: Visualizing detailed county-level data for all years occasionally led to slow rendering or oversimplification. |
| """) |
|
|
| st.markdown("### Future Scope") |
| st.markdown(""" |
| - **Correlation with health outcomes**: Link active transport rates to metrics like **obesity** and **physical inactivity** using public health datasets. |
| - **Socioeconomic overlay**: Integrate **income**, **education**, and **urban/rural splits** to identify behavioral and structural influences. |
| - **Policy simulation**: Model how adding **bike lanes** or **sidewalks** could shift future usage at the county level. |
| - **Temporal forecasting**: Use models like **ARIMA** or **Prophet** to predict active transport trends through 2030 and beyond. |
| - **Interactive stakeholder tools**: Transform this into a **dashboard** for policymakers to interactively explore and compare counties. |
| """) |
|
|
| st.success("This project lays the groundwork for evidence-based public health and transportation policies that promote safer, healthier, and more active communities.") |
| |