Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -80,8 +80,8 @@ def create_severity_violation_chart(df, age_group=None):
|
|
80 |
y='count',
|
81 |
color='Severity',
|
82 |
title=f'Crash Severity Distribution by Violation Type - {age_group}',
|
83 |
-
labels={'count': 'Number of Incidents', 'Violation': 'Violation Type'},
|
84 |
height=600,
|
|
|
85 |
color_discrete_map=severity_colors, # --> for part 3
|
86 |
)
|
87 |
|
@@ -94,9 +94,16 @@ def create_severity_violation_chart(df, age_group=None):
|
|
94 |
# modified the above code because x-axis labels were partially pruned
|
95 |
fig.update_layout(
|
96 |
xaxis_tickangle=-45,
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
barmode='stack',
|
99 |
-
margin=dict(t=50, b=150), # Increase bottom margin to avoid pruning
|
100 |
xaxis=dict(automargin=True)
|
101 |
)
|
102 |
|
@@ -104,16 +111,22 @@ def create_severity_violation_chart(df, age_group=None):
|
|
104 |
return fig, violations
|
105 |
|
106 |
def get_top_violations(df, age_group):
|
|
|
107 |
if age_group == 'All Ages':
|
|
|
|
|
108 |
violations = pd.concat([
|
109 |
df['Violation1_Drv1'].value_counts(),
|
110 |
df['Violation1_Drv2'].value_counts()
|
111 |
]).groupby(level=0).sum()
|
112 |
else:
|
|
|
113 |
filtered_df = df[
|
114 |
(df['Age_Group_Drv1'] == age_group) |
|
115 |
(df['Age_Group_Drv2'] == age_group)
|
116 |
]
|
|
|
|
|
117 |
violations = pd.concat([
|
118 |
filtered_df['Violation1_Drv1'].value_counts(),
|
119 |
filtered_df['Violation1_Drv2'].value_counts()
|
@@ -122,13 +135,17 @@ def get_top_violations(df, age_group):
|
|
122 |
# Convert to DataFrame and format
|
123 |
violations_df = violations.reset_index()
|
124 |
violations_df.columns = ['Violation Type', 'Count']
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
126 |
violations_df['Percentage'] = violations_df['Percentage'].map('{:.2f}%'.format)
|
127 |
|
128 |
return violations_df.head()
|
129 |
|
130 |
-
|
131 |
-
# def create_interactive_pie_chart(violations, selected_violation):
|
132 |
def create_interactive_pie_chart(violations, selected_violation, selected_age):
|
133 |
# Filter data based on selected violation
|
134 |
filtered_data = violations[violations['Violation'] == selected_violation]
|
@@ -145,20 +162,53 @@ def create_interactive_pie_chart(violations, selected_violation, selected_age):
|
|
145 |
)
|
146 |
|
147 |
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
@st.cache_data
|
150 |
-
def create_map(df, selected_year):
|
|
|
151 |
filtered_df = df[df['Year'] == selected_year]
|
152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
m = folium.Map(
|
154 |
-
location=[33.4255, -111.9400],
|
155 |
zoom_start=12,
|
156 |
control_scale=True,
|
157 |
tiles='CartoDB positron'
|
158 |
)
|
159 |
|
160 |
-
|
161 |
-
|
|
|
|
|
162 |
for _, row in filtered_df.iterrows():
|
163 |
folium.Marker(
|
164 |
location=[row['Latitude'], row['Longitude']],
|
@@ -166,15 +216,17 @@ def create_map(df, selected_year):
|
|
166 |
icon=folium.Icon(color='red')
|
167 |
).add_to(marker_cluster)
|
168 |
|
|
|
169 |
heat_data = filtered_df[['Latitude', 'Longitude']].values.tolist()
|
170 |
-
HeatMap(heat_data, radius=15, max_zoom=13, min_opacity=0.3).add_to(m)
|
171 |
|
|
|
172 |
return m
|
173 |
|
174 |
def create_injuries_fatalities_chart(crash_data, unit_type):
|
175 |
|
176 |
# 5th visualization title
|
177 |
-
st.header("Total Injuries and Fatalities by Month")
|
178 |
|
179 |
# Filter rows where we have valid data for all necessary columns
|
180 |
crash_data = crash_data[['DateTime', 'Totalinjuries', 'Totalfatalities', 'Unittype_One', 'Unittype_Two']].dropna()
|
@@ -213,10 +265,10 @@ def create_injuries_fatalities_chart(crash_data, unit_type):
|
|
213 |
|
214 |
# Reshape the data for easier plotting
|
215 |
injuries = monthly_sum[['Month', 'Totalinjuries']].rename(columns={'Totalinjuries': 'Value'})
|
216 |
-
injuries['
|
217 |
|
218 |
fatalities = monthly_sum[['Month', 'Totalfatalities']].rename(columns={'Totalfatalities': 'Value'})
|
219 |
-
fatalities['
|
220 |
|
221 |
combined_data = pd.concat([injuries, fatalities])
|
222 |
|
@@ -263,8 +315,8 @@ def create_injuries_fatalities_chart(crash_data, unit_type):
|
|
263 |
line_chart = alt.Chart(combined_data).mark_line(point=True).encode(
|
264 |
x=alt.X('Month:N', sort=month_order, title='Month'),
|
265 |
y=alt.Y('Value:Q', title='Total Injuries & Fatalities'),
|
266 |
-
color=alt.Color('
|
267 |
-
tooltip=['Month', '
|
268 |
).properties(
|
269 |
title=f'Total Injuries and Fatalities by Month for Unit Type Pair: {unit_type}',
|
270 |
width=600,
|
@@ -339,7 +391,7 @@ def create_category_distribution_chart(df, selected_category, selected_year):
|
|
339 |
barmode='stack',
|
340 |
xaxis_tickangle=-45,
|
341 |
legend_title='Injury Severity',
|
342 |
-
margin=dict(t=50, b=
|
343 |
)
|
344 |
|
345 |
return fig
|
@@ -371,21 +423,21 @@ def main():
|
|
371 |
</style>
|
372 |
""", unsafe_allow_html=True)
|
373 |
|
374 |
-
st.markdown("<div class='title'><h1> Accident Analysis for City of Tempe,
|
375 |
|
376 |
|
377 |
st.markdown("""
|
378 |
**Team Members:**
|
379 |
-
- Janhavi Tushar Zarapkar
|
380 |
- Hangyue Zhang ([email protected])
|
381 |
- Andrew Nam ([email protected])
|
382 |
- Nirmal Attarde ([email protected])
|
383 |
-
- Maanas Sandeep
|
384 |
""")
|
385 |
|
386 |
|
387 |
st.markdown("""
|
388 |
-
|
389 |
This dataset contains detailed information about traffic accidents in the city of **Tempe**. It includes various attributes of the accidents, such as the severity of injuries, the demographics of the drivers involved, the locations of the incidents, and the conditions at the time of the accidents. The dataset covers accidents that occurred over several years, with data on factors like **weather conditions**, **road surface conditions**, the **time of day**, and the type of **violations** (e.g., alcohol or drug use) that may have contributed to the accident.
|
390 |
|
391 |
The data was sourced from **Tempe City's traffic incident reports** and provides a comprehensive view of the factors influencing road safety and accident severity in the city. By analyzing this dataset, we can gain insights into the key contributors to traffic incidents and uncover trends that could help improve traffic safety measures, urban planning, and law enforcement policies in the city.
|
@@ -399,137 +451,176 @@ def main():
|
|
399 |
if 'Weather' not in df.columns:
|
400 |
df['Weather'] = 'Unknown'
|
401 |
|
402 |
-
# Initialize session state to store selected violation --> added for part3 (interactive pie chart)
|
403 |
if 'selected_violation' not in st.session_state:
|
404 |
st.session_state['selected_violation'] = None
|
405 |
|
|
|
|
|
|
|
|
|
406 |
# Create tabs for different visualizations
|
407 |
-
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
|
409 |
with tab1:
|
410 |
-
#
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
# Create and display chart
|
415 |
-
# fig = create_severity_violation_chart(df, selected_age)
|
416 |
-
fig, violations = create_severity_violation_chart(df, selected_age) # --> for part3 (interactive pie chart)
|
417 |
-
# st.plotly_chart(fig, use_container_width=True)
|
418 |
-
|
419 |
-
# Display the first bar chart and capture click events using plotly_events
|
420 |
-
clicked_points = plotly_events(fig, click_event=True, override_height=600, override_width="100%") # added for part3 (interactive pie chart)
|
421 |
-
|
422 |
-
# If a bar is clicked, update the selected_violation in session_state --> added for part3 (interactive pie chart)
|
423 |
-
if clicked_points:
|
424 |
-
selected_violation = clicked_points[0]['x']
|
425 |
-
if selected_violation != st.session_state['selected_violation']:
|
426 |
-
st.session_state['selected_violation'] = selected_violation
|
427 |
-
|
428 |
-
# If a violation is selected, display the pie chart --> added for part3 (interactive pie chart)
|
429 |
-
if st.session_state['selected_violation']:
|
430 |
-
# pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'])
|
431 |
-
pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'], selected_age) # dynamically update pie chart's title
|
432 |
-
st.plotly_chart(pie_chart, use_container_width=True)
|
433 |
-
|
434 |
-
# Display statistics
|
435 |
-
if selected_age == 'All Ages':
|
436 |
-
total_incidents = len(df)
|
437 |
-
else:
|
438 |
-
total_incidents = len(df[
|
439 |
-
(df['Age_Group_Drv1'] == selected_age) |
|
440 |
-
(df['Age_Group_Drv2'] == selected_age)
|
441 |
-
])
|
442 |
-
|
443 |
-
# Create two columns for statistics
|
444 |
-
col1, col2 = st.columns(2)
|
445 |
-
|
446 |
-
with col1:
|
447 |
-
st.markdown(f"### Total Incidents")
|
448 |
-
st.markdown(f"**{total_incidents:,}** incidents for {selected_age}")
|
449 |
|
450 |
-
|
451 |
-
st.markdown("### Top Violations")
|
452 |
-
top_violations = get_top_violations(df, selected_age)
|
453 |
-
st.table(top_violations)
|
454 |
-
|
455 |
-
with tab2:
|
456 |
-
# Year selection for map
|
457 |
-
years = sorted(df['Year'].unique())
|
458 |
-
selected_year = st.selectbox('Select Year:', years)
|
459 |
|
460 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
461 |
|
462 |
-
with map_col:
|
463 |
-
# Create and display map
|
464 |
-
st.markdown("### Crash Location Map")
|
465 |
-
map_placeholder = st.empty()
|
466 |
-
with map_placeholder:
|
467 |
-
m = create_map(df, selected_year)
|
468 |
-
map_data = st_folium(
|
469 |
-
m,
|
470 |
-
width=None,
|
471 |
-
height=800,
|
472 |
-
key=f"map_{selected_year}",
|
473 |
-
returned_objects=["null_drawing"]
|
474 |
-
)
|
475 |
|
476 |
with desc_col:
|
477 |
st.markdown("""
|
478 |
-
|
479 |
-
This interactive
|
480 |
-
1. **Accident Markers**: Red markers indicate individual accidents, with popups displaying the coordinates, date/time, and severity of each incident.
|
481 |
-
2. **Heatmap**: The heatmap highlights accident hotspots with colors ranging from blue (low frequency) to yellow (moderate) and red (high frequency), showing areas with more frequent accidents.
|
482 |
|
483 |
**Key Features:**
|
484 |
-
* **
|
485 |
-
* **
|
|
|
|
|
|
|
|
|
|
|
486 |
|
487 |
-
**
|
488 |
-
* **Red**: Individual accident markers.
|
489 |
-
* **Blue to Red**: Heatmap colors indicate accident frequency, from low (blue) to high (red).
|
490 |
|
491 |
-
This
|
|
|
|
|
|
|
492 |
""")
|
|
|
|
|
|
|
|
|
|
|
493 |
|
494 |
-
|
495 |
-
with tab3:
|
496 |
-
# Weather condition filter
|
497 |
-
weather = ['All Conditions'] + sorted(df['Weather'].unique())
|
498 |
-
selected_weather = st.selectbox('Select Weather Condition:', weather)
|
499 |
|
500 |
-
|
|
|
|
|
501 |
|
502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
503 |
|
504 |
-
with
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
height=800, # Increased height
|
510 |
-
width=None, # Let width be responsive
|
511 |
margin=dict(l=50, r=50, t=50, b=50)
|
512 |
)
|
513 |
-
st.plotly_chart(
|
514 |
|
515 |
with desc_col:
|
516 |
-
st.markdown("""
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
This chart helps uncover:
|
528 |
-
* Annual fluctuations in crash incidents.
|
529 |
-
* Correlations between weather conditions and crash frequencies.
|
530 |
-
* Historical patterns that can guide future safety measures and urban planning decisions
|
531 |
-
""")
|
532 |
-
|
533 |
|
534 |
with tab4:
|
535 |
# Dropdown for Unit Type selection
|
@@ -541,27 +632,12 @@ def main():
|
|
541 |
unit_type_pairs = sorted(list(unit_type_pairs))
|
542 |
unit_type = st.selectbox("Select Unit Type Pair", options=['Total'] + unit_type_pairs)
|
543 |
|
544 |
-
# # Create 5th Visualization: Injuries and fatalities chart
|
545 |
-
# injuries_fatalities_chart = create_injuries_fatalities_chart(df, unit_type)
|
546 |
-
# st.altair_chart(injuries_fatalities_chart, use_container_width=True)
|
547 |
-
|
548 |
-
# st.markdown("""
|
549 |
-
# This line chart shows the **total number of injuries and fatalities by month for the selected unit type pair**. The blue line represents total injuries, while the red line represents total fatalities. Observing the trends over the months can help identify any seasonal patterns or peaks in traffic incidents involving specific unit types.
|
550 |
-
|
551 |
-
# - **Total Injuries**: The blue line indicates how injuries vary over different months, highlighting any particular spikes or declines.
|
552 |
-
# - **Total Fatalities**: The red line shows the trend for fatalities, which is generally much lower compared to injuries.
|
553 |
-
# - **Unit Types**: The dropdown selection allows users to filter the data by specific unit type pairs (e.g., Driver vs Pedestrian) or view the overall trend across all types.
|
554 |
-
|
555 |
-
# This visualization aims to provide an intuitive understanding of how injuries and fatalities are distributed across the year, helping stakeholders develop targeted safety measures.
|
556 |
-
# """)
|
557 |
-
|
558 |
chart_col, desc_col = st.columns([7, 3])
|
559 |
|
560 |
with chart_col:
|
561 |
-
# Create 5th Visualization: Injuries and fatalities chart
|
562 |
injuries_fatalities_chart = create_injuries_fatalities_chart(df, unit_type)
|
563 |
injuries_fatalities_chart = injuries_fatalities_chart.properties(
|
564 |
-
height=800
|
565 |
)
|
566 |
st.altair_chart(injuries_fatalities_chart, use_container_width=True)
|
567 |
|
@@ -595,50 +671,88 @@ def main():
|
|
595 |
|
596 |
This visualization aids stakeholders in developing effective safety measures and resource allocation strategies throughout the year.
|
597 |
""")
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
with tab5:
|
602 |
-
|
603 |
-
|
604 |
-
'Collisionmanner',
|
605 |
-
'Lightcondition',
|
606 |
-
'Weather',
|
607 |
-
'SurfaceCondition',
|
608 |
-
'AlcoholUse_Drv1',
|
609 |
-
'Gender_Drv1',
|
610 |
-
]
|
611 |
-
selected_category = st.selectbox("Select Category:", categories)
|
612 |
-
|
613 |
-
# Dropdown for year selection
|
614 |
-
years = ['All Years'] + sorted(df['Year'].dropna().unique().astype(int).tolist())
|
615 |
-
selected_year = st.selectbox("Select Year:", years)
|
616 |
-
|
617 |
-
chart_col, desc_col = st.columns([7, 3])
|
618 |
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
627 |
)
|
628 |
-
|
629 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
with desc_col:
|
631 |
st.markdown("""
|
632 |
-
|
633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
|
635 |
-
|
636 |
-
|
637 |
-
* Insightful Tooltips: Hover over each segment to view the exact count and percentage of incidents for a given severity level.
|
638 |
-
* Comparative Analysis: Quickly identify how different conditions or behaviors correlate with injury severity.
|
639 |
|
640 |
-
|
641 |
-
|
|
|
|
|
642 |
|
643 |
|
644 |
if __name__ == "__main__":
|
|
|
80 |
y='count',
|
81 |
color='Severity',
|
82 |
title=f'Crash Severity Distribution by Violation Type - {age_group}',
|
|
|
83 |
height=600,
|
84 |
+
labels={'count': 'Number of Incidents', 'Violation': 'Violation Type'},
|
85 |
color_discrete_map=severity_colors, # --> for part 3
|
86 |
)
|
87 |
|
|
|
94 |
# modified the above code because x-axis labels were partially pruned
|
95 |
fig.update_layout(
|
96 |
xaxis_tickangle=-45,
|
97 |
+
legend=dict(
|
98 |
+
orientation='v',
|
99 |
+
x=1,
|
100 |
+
y=1,
|
101 |
+
yanchor='top',
|
102 |
+
xanchor='left',
|
103 |
+
title="Severity Level"
|
104 |
+
),
|
105 |
barmode='stack',
|
106 |
+
margin=dict(t=50, b=150,r=0), # Increase bottom margin to avoid pruning
|
107 |
xaxis=dict(automargin=True)
|
108 |
)
|
109 |
|
|
|
111 |
return fig, violations
|
112 |
|
113 |
def get_top_violations(df, age_group):
|
114 |
+
# Calculate total incidents for the age group
|
115 |
if age_group == 'All Ages':
|
116 |
+
total_incidents = len(df)
|
117 |
+
# Get violations for all ages
|
118 |
violations = pd.concat([
|
119 |
df['Violation1_Drv1'].value_counts(),
|
120 |
df['Violation1_Drv2'].value_counts()
|
121 |
]).groupby(level=0).sum()
|
122 |
else:
|
123 |
+
# Filter for specific age group
|
124 |
filtered_df = df[
|
125 |
(df['Age_Group_Drv1'] == age_group) |
|
126 |
(df['Age_Group_Drv2'] == age_group)
|
127 |
]
|
128 |
+
total_incidents = len(filtered_df)
|
129 |
+
# Get violations for specific age group
|
130 |
violations = pd.concat([
|
131 |
filtered_df['Violation1_Drv1'].value_counts(),
|
132 |
filtered_df['Violation1_Drv2'].value_counts()
|
|
|
135 |
# Convert to DataFrame and format
|
136 |
violations_df = violations.reset_index()
|
137 |
violations_df.columns = ['Violation Type', 'Count']
|
138 |
+
|
139 |
+
# Sort by Count in descending order
|
140 |
+
violations_df = violations_df.sort_values('Count', ascending=False)
|
141 |
+
|
142 |
+
# Calculate percentage of total incidents
|
143 |
+
violations_df['Percentage'] = (violations_df['Count'] / total_incidents * 100).round(2)
|
144 |
violations_df['Percentage'] = violations_df['Percentage'].map('{:.2f}%'.format)
|
145 |
|
146 |
return violations_df.head()
|
147 |
|
148 |
+
|
|
|
149 |
def create_interactive_pie_chart(violations, selected_violation, selected_age):
|
150 |
# Filter data based on selected violation
|
151 |
filtered_data = violations[violations['Violation'] == selected_violation]
|
|
|
162 |
)
|
163 |
|
164 |
return fig
|
165 |
+
|
166 |
+
def create_map_bar_chart(df, selected_year):
|
167 |
+
# Create severity count bar chart
|
168 |
+
filtered_df = df[df['Year'] == selected_year]
|
169 |
+
severity_count = filtered_df['Injuryseverity'].value_counts().reset_index()
|
170 |
+
severity_count.columns = ['Injuryseverity', 'Count']
|
171 |
|
172 |
+
fig = px.bar(
|
173 |
+
severity_count,
|
174 |
+
x='Injuryseverity',
|
175 |
+
y='Count',
|
176 |
+
title="Accidents by Severity",
|
177 |
+
labels={'Injuryseverity': 'Severity', 'Count': 'Number of Accidents'} # Adjust height as needed
|
178 |
+
)
|
179 |
+
fig.update_traces(marker_color='blue')
|
180 |
+
fig.update_layout(
|
181 |
+
clickmode='event+select', # Enable interactivity
|
182 |
+
xaxis_tickangle=45, # Rotate x-axis labels 45 degrees
|
183 |
+
margin=dict(t=50, b=150, r=20), # Add bottom margin to prevent label cutoff
|
184 |
+
)
|
185 |
+
return fig
|
186 |
+
|
187 |
+
|
188 |
@st.cache_data
|
189 |
+
def create_map(df, selected_year, selected_severity=None):
|
190 |
+
# Filter data by selected year
|
191 |
filtered_df = df[df['Year'] == selected_year]
|
192 |
+
|
193 |
+
# Filter further by selected severity if provided
|
194 |
+
if selected_severity:
|
195 |
+
filtered_df = filtered_df[filtered_df['Injuryseverity'] == selected_severity]
|
196 |
+
|
197 |
+
# Remove rows with missing latitude or longitude
|
198 |
+
filtered_df = filtered_df.dropna(subset=['Latitude', 'Longitude'])
|
199 |
+
|
200 |
+
# Create the map
|
201 |
m = folium.Map(
|
202 |
+
location=[33.4255, -111.9400], # Default location (can be customized)
|
203 |
zoom_start=12,
|
204 |
control_scale=True,
|
205 |
tiles='CartoDB positron'
|
206 |
)
|
207 |
|
208 |
+
# Add marker cluster
|
209 |
+
marker_cluster = MarkerCluster(name="Accident Locations").add_to(m)
|
210 |
+
|
211 |
+
# Add accident markers
|
212 |
for _, row in filtered_df.iterrows():
|
213 |
folium.Marker(
|
214 |
location=[row['Latitude'], row['Longitude']],
|
|
|
216 |
icon=folium.Icon(color='red')
|
217 |
).add_to(marker_cluster)
|
218 |
|
219 |
+
# Add heatmap
|
220 |
heat_data = filtered_df[['Latitude', 'Longitude']].values.tolist()
|
221 |
+
HeatMap(heat_data, radius=15, max_zoom=13, min_opacity=0.3, name="Heat Map").add_to(m)
|
222 |
|
223 |
+
folium.LayerControl().add_to(m)
|
224 |
return m
|
225 |
|
226 |
def create_injuries_fatalities_chart(crash_data, unit_type):
|
227 |
|
228 |
# 5th visualization title
|
229 |
+
# st.header("5. Total Injuries and Fatalities by Month")
|
230 |
|
231 |
# Filter rows where we have valid data for all necessary columns
|
232 |
crash_data = crash_data[['DateTime', 'Totalinjuries', 'Totalfatalities', 'Unittype_One', 'Unittype_Two']].dropna()
|
|
|
265 |
|
266 |
# Reshape the data for easier plotting
|
267 |
injuries = monthly_sum[['Month', 'Totalinjuries']].rename(columns={'Totalinjuries': 'Value'})
|
268 |
+
injuries['Measure'] = 'Total Injuries'
|
269 |
|
270 |
fatalities = monthly_sum[['Month', 'Totalfatalities']].rename(columns={'Totalfatalities': 'Value'})
|
271 |
+
fatalities['Measure'] = 'Total Fatalities'
|
272 |
|
273 |
combined_data = pd.concat([injuries, fatalities])
|
274 |
|
|
|
315 |
line_chart = alt.Chart(combined_data).mark_line(point=True).encode(
|
316 |
x=alt.X('Month:N', sort=month_order, title='Month'),
|
317 |
y=alt.Y('Value:Q', title='Total Injuries & Fatalities'),
|
318 |
+
color=alt.Color('Measure:N', title='', scale=alt.Scale(domain=['Total Injuries', 'Total Fatalities'], range=['blue', 'red'])),
|
319 |
+
tooltip=['Month', 'Measure:N', 'Value:Q']
|
320 |
).properties(
|
321 |
title=f'Total Injuries and Fatalities by Month for Unit Type Pair: {unit_type}',
|
322 |
width=600,
|
|
|
391 |
barmode='stack',
|
392 |
xaxis_tickangle=-45,
|
393 |
legend_title='Injury Severity',
|
394 |
+
margin=dict(t=50, b=150, l=50, r=50),
|
395 |
)
|
396 |
|
397 |
return fig
|
|
|
423 |
</style>
|
424 |
""", unsafe_allow_html=True)
|
425 |
|
426 |
+
st.markdown("<div class='title'><h1> Accident Analysis for City of Tempe,Arizona </h1></div>", unsafe_allow_html=True)
|
427 |
|
428 |
|
429 |
st.markdown("""
|
430 |
**Team Members:**
|
431 |
+
- Janhavi Tushar Zarapkar ([email protected])
|
432 |
- Hangyue Zhang ([email protected])
|
433 |
- Andrew Nam ([email protected])
|
434 |
- Nirmal Attarde ([email protected])
|
435 |
+
- Maanas Sandeep Agrawal ([email protected])
|
436 |
""")
|
437 |
|
438 |
|
439 |
st.markdown("""
|
440 |
+
# Introduction to the Traffic Accident Dataset
|
441 |
This dataset contains detailed information about traffic accidents in the city of **Tempe**. It includes various attributes of the accidents, such as the severity of injuries, the demographics of the drivers involved, the locations of the incidents, and the conditions at the time of the accidents. The dataset covers accidents that occurred over several years, with data on factors like **weather conditions**, **road surface conditions**, the **time of day**, and the type of **violations** (e.g., alcohol or drug use) that may have contributed to the accident.
|
442 |
|
443 |
The data was sourced from **Tempe City's traffic incident reports** and provides a comprehensive view of the factors influencing road safety and accident severity in the city. By analyzing this dataset, we can gain insights into the key contributors to traffic incidents and uncover trends that could help improve traffic safety measures, urban planning, and law enforcement policies in the city.
|
|
|
451 |
if 'Weather' not in df.columns:
|
452 |
df['Weather'] = 'Unknown'
|
453 |
|
|
|
454 |
if 'selected_violation' not in st.session_state:
|
455 |
st.session_state['selected_violation'] = None
|
456 |
|
457 |
+
if "selected_severity" not in st.session_state:
|
458 |
+
st.session_state["selected_severity"] = None
|
459 |
+
|
460 |
+
|
461 |
# Create tabs for different visualizations
|
462 |
+
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
463 |
+
"Crash Trend",
|
464 |
+
"Violation-Severity Analysis",
|
465 |
+
"Distribution by Category",
|
466 |
+
"Crash Injuries/Fatalities",
|
467 |
+
"Severity-Location Analysis"
|
468 |
+
])
|
469 |
|
470 |
with tab1:
|
471 |
+
# Weather condition filter
|
472 |
+
weather = ['All Conditions'] + sorted(df['Weather'].unique())
|
473 |
+
selected_weather = st.selectbox('Select Weather Condition:', weather)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
474 |
|
475 |
+
trend_col, desc_col = st.columns([7, 3])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
476 |
|
477 |
+
with trend_col:
|
478 |
+
trend_fig = create_crash_trend_chart(df, selected_weather)
|
479 |
+
trend_fig.update_layout(
|
480 |
+
height=800,
|
481 |
+
width=None,
|
482 |
+
margin=dict(l=50, r=50, t=50, b=50)
|
483 |
+
)
|
484 |
+
st.plotly_chart(trend_fig, use_container_width=True)
|
485 |
+
|
486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
|
488 |
with desc_col:
|
489 |
st.markdown("""
|
490 |
+
## **Crash Trend Over Time**
|
491 |
+
This interactive line chart visualizes the trend of unique traffic crashes over the years, optionally filtered by weather conditions. It highlights how crash frequency changes over time, helping identify trends and potential contributing factors.
|
|
|
|
|
492 |
|
493 |
**Key Features:**
|
494 |
+
* **Time Trend Analysis**: Displays the total number of unique crashes for each year, showing long-term patterns.
|
495 |
+
* **Weather Filter**: Users can filter the data by weather conditions (e.g., "Rainy", "Sunny") to analyze how weather impacts crash trends.
|
496 |
+
* **Interactive Tooltips**: Hovering over data points reveals the exact crash count for each year, providing detailed insights.
|
497 |
+
|
498 |
+
**Color Scheme and Design:**
|
499 |
+
* **Line and Markers**: A smooth line connects data points, with prominent markers for each year to highlight trends clearly.
|
500 |
+
* **Dynamic Title**: The chart updates its title to reflect the selected weather condition or "All Conditions" for the overall trend.
|
501 |
|
502 |
+
**Insights:**
|
|
|
|
|
503 |
|
504 |
+
This chart helps uncover:
|
505 |
+
* Annual fluctuations in crash incidents.
|
506 |
+
* Correlations between weather conditions and crash frequencies.
|
507 |
+
* Historical patterns that can guide future safety measures and urban planning decisions
|
508 |
""")
|
509 |
+
|
510 |
+
with tab2:
|
511 |
+
# Age group selection
|
512 |
+
age_groups = ['All Ages', '16-25', '26-35', '36-45', '46-55', '56-65', '65+']
|
513 |
+
selected_age = st.selectbox('Select Age Group:', age_groups)
|
514 |
|
515 |
+
chart_col, desc_col = st.columns([6, 4])
|
|
|
|
|
|
|
|
|
516 |
|
517 |
+
with chart_col:
|
518 |
+
# Create and display chart
|
519 |
+
fig, violations = create_severity_violation_chart(df, selected_age)
|
520 |
|
521 |
+
clicked_points = plotly_events(fig, click_event=True, override_height=600, override_width="100%")
|
522 |
+
|
523 |
+
if clicked_points:
|
524 |
+
selected_violation = clicked_points[0]['x']
|
525 |
+
if selected_violation != st.session_state['selected_violation']:
|
526 |
+
st.session_state['selected_violation'] = selected_violation
|
527 |
+
|
528 |
+
# If a violation is selected, display the pie chart --> added for part3 (interactive pie chart)
|
529 |
+
if st.session_state['selected_violation']:
|
530 |
+
# pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'])
|
531 |
+
pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'], selected_age) # dynamically update pie chart's title
|
532 |
+
st.plotly_chart(pie_chart, use_container_width=True)
|
533 |
+
|
534 |
+
# Display statistics
|
535 |
+
if selected_age == 'All Ages':
|
536 |
+
total_incidents = len(df)
|
537 |
+
else:
|
538 |
+
total_incidents = len(df[
|
539 |
+
(df['Age_Group_Drv1'] == selected_age) |
|
540 |
+
(df['Age_Group_Drv2'] == selected_age)
|
541 |
+
])
|
542 |
+
|
543 |
+
|
544 |
+
with desc_col:
|
545 |
+
st.markdown("""
|
546 |
+
# Severity of Violations Across Age Groups
|
547 |
+
|
548 |
+
This section provides an interactive visualization of **crash severities** linked to specific violation types, segmented by driver age groups. It enables a comprehensive analysis of how **age influences crash severity and violation trends**. The visualization is linked to an **interactive pie chart** that updates when a specific bar is selected, displaying the detailed distribution of the selected violation type based on the selected age group.
|
549 |
+
|
550 |
+
---
|
551 |
+
|
552 |
+
## **Key Features**
|
553 |
+
|
554 |
+
### 1. **Age Group Analysis**
|
555 |
+
- Select specific age groups (e.g., "16-25", "65+") or analyze all ages to explore correlations between:
|
556 |
+
- Age
|
557 |
+
- Violation type
|
558 |
+
- Crash severity
|
559 |
+
- Understand how different age groups are involved in various types of violations.
|
560 |
+
|
561 |
+
### 2. **Violation Breakdown**
|
562 |
+
- Examine the most frequent violations contributing to traffic accidents for each age group.
|
563 |
+
- View detailed statistics showing the distribution of violation types.
|
564 |
+
|
565 |
+
### 3. **Understanding Severity Level**
|
566 |
+
- Identify the proportion of severity levels for a specific violation type based on different age groups.
|
567 |
+
- Investigate detailed severity patterns for each violation type across age groups.
|
568 |
+
|
569 |
+
---
|
570 |
+
|
571 |
+
## **Insights**
|
572 |
+
|
573 |
+
- **Identifies High-Risk Behaviors:**
|
574 |
+
- Highlights risky behaviors such as reckless driving in younger drivers or impaired driving in older groups.
|
575 |
+
|
576 |
+
- **Highlights Severity Associations:**
|
577 |
+
- Shows which violations are associated with more severe outcomes, aiding targeted safety interventions and public awareness campaigns.
|
578 |
+
|
579 |
+
- **Supports Data-Driven Decision Making:**
|
580 |
+
- Provides insights for designing **age-specific traffic safety programs**.
|
581 |
+
|
582 |
+
---
|
583 |
+
""")
|
584 |
+
|
585 |
+
with tab3:
|
586 |
+
# Dropdown for category selection
|
587 |
+
categories = [
|
588 |
+
'Collisionmanner',
|
589 |
+
'Lightcondition',
|
590 |
+
'Weather',
|
591 |
+
'SurfaceCondition',
|
592 |
+
'AlcoholUse_Drv1',
|
593 |
+
'Gender_Drv1',
|
594 |
+
]
|
595 |
+
selected_category = st.selectbox("Select Category:", categories)
|
596 |
+
|
597 |
+
# Dropdown for year selection
|
598 |
+
years = ['All Years'] + sorted(df['Year'].dropna().unique().astype(int).tolist())
|
599 |
+
selected_year = st.selectbox("Select Year:", years)
|
600 |
+
|
601 |
+
chart_col, desc_col = st.columns([7, 3])
|
602 |
|
603 |
+
with chart_col:
|
604 |
+
distribution_chart = create_category_distribution_chart(df, selected_category, selected_year)
|
605 |
+
distribution_chart.update_layout(
|
606 |
+
height=800,
|
607 |
+
width=None,
|
|
|
|
|
608 |
margin=dict(l=50, r=50, t=50, b=50)
|
609 |
)
|
610 |
+
st.plotly_chart(distribution_chart, use_container_width=True)
|
611 |
|
612 |
with desc_col:
|
613 |
+
st.markdown(f"""
|
614 |
+
## Distribution of Incidents by {selected_category}
|
615 |
+
This visualization explores the distribution of traffic incidents across various categories, such as Collision Manner, Weather, Surface Condition, Alcohol Use, and Driver Gender. Each bar represents a specific category value (e.g., "Male" or "Female" for Gender), and the bars are divided into segments based on Injury Severity (e.g., Minor, Moderate, Serious, Fatal).
|
616 |
+
|
617 |
+
**Key Features:**
|
618 |
+
* Interactive Filters: Select a category and filter by year to analyze trends over time.
|
619 |
+
* Insightful Tooltips: Hover over each segment to view the exact count and percentage of incidents for a given severity level.
|
620 |
+
* Comparative Analysis: Quickly identify how different conditions or behaviors correlate with injury severity.
|
621 |
+
|
622 |
+
This chart provides actionable insights into factors contributing to traffic incidents and their outcomes, helping stakeholders target interventions and improve road safety.
|
623 |
+
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
624 |
|
625 |
with tab4:
|
626 |
# Dropdown for Unit Type selection
|
|
|
632 |
unit_type_pairs = sorted(list(unit_type_pairs))
|
633 |
unit_type = st.selectbox("Select Unit Type Pair", options=['Total'] + unit_type_pairs)
|
634 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
635 |
chart_col, desc_col = st.columns([7, 3])
|
636 |
|
637 |
with chart_col:
|
|
|
638 |
injuries_fatalities_chart = create_injuries_fatalities_chart(df, unit_type)
|
639 |
injuries_fatalities_chart = injuries_fatalities_chart.properties(
|
640 |
+
height=800
|
641 |
)
|
642 |
st.altair_chart(injuries_fatalities_chart, use_container_width=True)
|
643 |
|
|
|
671 |
|
672 |
This visualization aids stakeholders in developing effective safety measures and resource allocation strategies throughout the year.
|
673 |
""")
|
674 |
+
|
|
|
|
|
675 |
with tab5:
|
676 |
+
years = sorted(df['Year'].unique())
|
677 |
+
selected_year = st.selectbox('Select Year:', years)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
678 |
|
679 |
+
# Create two columns for visualization and description
|
680 |
+
viz_col, desc_col = st.columns([6, 4])
|
681 |
+
|
682 |
+
with viz_col:
|
683 |
+
# First add bar chart
|
684 |
+
st.subheader("Severity-Location Analysis")
|
685 |
+
bar_fig = create_map_bar_chart(df, selected_year)
|
686 |
+
|
687 |
+
# Capture click events with bar chart
|
688 |
+
clicked_points = plotly_events(
|
689 |
+
bar_fig,
|
690 |
+
click_event=True,
|
691 |
+
override_height=300,
|
692 |
+
override_width="100%"
|
693 |
)
|
694 |
+
|
695 |
+
if clicked_points:
|
696 |
+
selected_severity = clicked_points[0]['x']
|
697 |
+
st.session_state["selected_severity"] = selected_severity
|
698 |
+
|
699 |
+
# Show currently selected severity
|
700 |
+
st.write(f"Selected Severity: {st.session_state['selected_severity'] if st.session_state['selected_severity'] else 'All'}")
|
701 |
+
|
702 |
+
# Add map below bar chart
|
703 |
+
st.subheader("Accident Locations")
|
704 |
+
map_placeholder = st.empty()
|
705 |
+
with map_placeholder:
|
706 |
+
m = create_map(df, selected_year, st.session_state["selected_severity"])
|
707 |
+
map_data = st_folium(
|
708 |
+
m,
|
709 |
+
width=None,
|
710 |
+
height=600, # Reduced height since it's now below bar chart
|
711 |
+
key=f"map_{selected_year}_{st.session_state['selected_severity']}",
|
712 |
+
returned_objects=["null_drawing"]
|
713 |
+
)
|
714 |
+
|
715 |
with desc_col:
|
716 |
st.markdown("""
|
717 |
+
# Exploring Traffic Accident Severity and Location
|
718 |
+
The two linked graphs show an interactive platform for exploring traffic accident data, featuring a **bar chart** and a **dynamic map**.
|
719 |
+
- The **bar chart** displays the distribution of accidents by severity.
|
720 |
+
- The **map** combines marker clustering and heatmaps to highlight accident locations.
|
721 |
+
- Users can filter data by year and severity to explore patterns.
|
722 |
+
---
|
723 |
+
## **Key Features**
|
724 |
+
- **Interactive Bar Chart:**
|
725 |
+
Displays accident counts by severity, updating the map based on selected severity.
|
726 |
+
- **Map with Dual Layers:**
|
727 |
+
Includes marker clustering for individual accidents and a heatmap to visualize accident density.
|
728 |
+
- **Year-Based Filtering:**
|
729 |
+
Allows users to filter data by year and severity for focused analysis.
|
730 |
+
- **Seamless Integration:**
|
731 |
+
Combines Streamlit and Folium, with Plotly events linking the visualizations.
|
732 |
+
---
|
733 |
+
## **Design**
|
734 |
+
- **Bar Chart:**
|
735 |
+
- Uses a calm blue color for clarity.
|
736 |
+
- **Map:**
|
737 |
+
- Uses **CartoDB tiles** with red markers and heatmaps for visibility.
|
738 |
+
---
|
739 |
+
## **Insights**
|
740 |
+
- **Severity Patterns:**
|
741 |
+
The bar chart reveals accident trends across severity levels.
|
742 |
+
- **Spatial Trends:**
|
743 |
+
The map identifies high-risk accident hotspots.
|
744 |
+
- **Yearly and Severity Insights:**
|
745 |
+
Filters help uncover temporal and severity-related patterns, aiding traffic safety analysis.
|
746 |
+
""")
|
747 |
+
st.markdown("---")
|
748 |
|
749 |
+
# Add conclusion section
|
750 |
+
st.markdown("# FP3 Conclusion")
|
|
|
|
|
751 |
|
752 |
+
st.markdown("""
|
753 |
+
TODO
|
754 |
+
""")
|
755 |
+
|
756 |
|
757 |
|
758 |
if __name__ == "__main__":
|