File size: 21,012 Bytes
f380222
 
4e77e27
f380222
cc91495
 
 
4a22336
 
cc91495
 
 
4a22336
 
0b92951
5352fe0
 
e1339f0
0b92951
4a22336
 
cc91495
5352fe0
cc91495
33e30d3
a7e6b33
f380222
4a22336
 
 
f380222
cc91495
5352fe0
cc91495
0b92951
1979a89
5352fe0
0b92951
4a22336
8783af9
cc91495
4a22336
 
 
 
cc91495
4a22336
09fae9d
 
 
 
 
 
 
4a22336
f380222
5352fe0
da247bb
cc17af7
 
5352fe0
 
 
 
 
 
da247bb
5352fe0
cc17af7
 
 
 
da247bb
5352fe0
cc17af7
 
 
 
 
 
 
 
 
5352fe0
cc17af7
5352fe0
 
 
cc17af7
 
5352fe0
 
 
 
 
cc17af7
 
5352fe0
4a22336
f380222
cc91495
5352fe0
cc91495
5352fe0
 
cc0c380
5352fe0
 
 
 
 
8783af9
09fae9d
55a60af
8783af9
73a1d9b
23739ff
 
 
 
 
dc8d514
23739ff
 
 
eaece8a
8783af9
f380222
5352fe0
 
 
 
cc0c380
5352fe0
 
 
 
23739ff
5352fe0
 
 
 
 
17a4c5e
b987809
9c9ccd6
76ad067
ecc2170
 
b987809
cc91495
5352fe0
04c3978
17a4c5e
5352fe0
04c3978
17a4c5e
571f359
17a4c5e
 
cc91495
b987809
5352fe0
 
 
 
 
 
 
 
 
 
 
 
f380222
cc91495
5352fe0
cc91495
5352fe0
1979a89
33e30d3
 
1979a89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33e30d3
1979a89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600da76
1979a89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19139e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229e069
e1339f0
351a216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e1339f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1bdedb
e1339f0
 
9b94197
4a633b7
890052a
 
 
 
c97c1dd
890052a
e1339f0
4a633b7
b87628b
 
 
 
 
 
e1339f0
 
cc91495
 
 
00d8d79
5352fe0
cc17af7
19139e8
4a22336
368cb53
 
 
 
 
 
19139e8
368cb53
19139e8
 
 
 
 
cc17af7
d346422
19139e8
cc17af7
19139e8
 
 
 
 
cc17af7
d346422
19139e8
cc17af7
19139e8
 
 
 
 
d346422
cc17af7
3f1f231
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
import streamlit as st
import pandas as pd
import altair as alt

# ----------------------------
# App configuration
# ----------------------------
st.set_page_config(page_title="Transportation Trends", layout="wide")

# ----------------------------
# Sidebar Navigation
# ----------------------------
st.sidebar.title("Navigation")
page = st.sidebar.radio("Go to", [
    "Introduction", 
    "Visual 1&2", 
    "Visual 3", 
    "Visual 4",
    "Conclusion"
])

# ----------------------------
# Load and clean dataset
# ----------------------------
@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")

# ----------------------------
# Page: Introduction (Merged Intro + Dataset)
# ----------------------------
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())

# ----------------------------
# Page: Visual 1&2 (Merged Viz 1 + Viz 2)
# ----------------------------
elif page == "Visual 1&2":
    st.title("Vizualization: State and County Trends")

    # ----------------------------
    # Grouped Bar Chart by State
    # ----------------------------
    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.
    """)

    # ----------------------------
    # County-Level Line Trends
    # ----------------------------
    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.
    """)


# ----------------------------
# Page: Visual 3 (Previously Viz 3)
# ----------------------------
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:  # count
        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. 
    """)
    

# ----------------------------
# Page: Conclusion
# ----------------------------
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.")