Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -3,9 +3,21 @@ import pandas as pd
|
|
3 |
import plotly.express as px
|
4 |
import altair as alt
|
5 |
import folium
|
|
|
6 |
from folium.plugins import HeatMap, MarkerCluster
|
7 |
from streamlit_folium import st_folium
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
@st.cache_data
|
10 |
def load_and_preprocess_data(file_path):
|
11 |
# Read the data
|
@@ -69,21 +81,44 @@ def create_severity_violation_chart(df, age_group=None):
|
|
69 |
color='Severity',
|
70 |
title=f'Crash Severity Distribution by Violation Type - {age_group}',
|
71 |
labels={'count': 'Number of Incidents', 'Violation': 'Violation Type'},
|
72 |
-
|
|
|
73 |
)
|
74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
fig.update_layout(
|
76 |
xaxis_tickangle=-45,
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
)
|
80 |
|
81 |
-
|
|
|
|
|
|
|
82 |
|
83 |
def get_top_violations(df, age_group):
|
84 |
# Calculate total incidents for the age group
|
85 |
if age_group == 'All Ages':
|
86 |
-
total_incidents = len(df)
|
87 |
# Get violations for all ages
|
88 |
violations = pd.concat([
|
89 |
df['Violation1_Drv1'].value_counts(),
|
@@ -116,18 +151,69 @@ def get_top_violations(df, age_group):
|
|
116 |
return violations_df.head()
|
117 |
|
118 |
@st.cache_data
|
119 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
filtered_df = df[df['Year'] == selected_year]
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
m = folium.Map(
|
123 |
-
location=[33.4255, -111.9400],
|
124 |
zoom_start=12,
|
125 |
control_scale=True,
|
126 |
tiles='CartoDB positron'
|
127 |
)
|
128 |
|
129 |
-
|
130 |
-
|
|
|
|
|
131 |
for _, row in filtered_df.iterrows():
|
132 |
folium.Marker(
|
133 |
location=[row['Latitude'], row['Longitude']],
|
@@ -135,9 +221,11 @@ def create_map(df, selected_year):
|
|
135 |
icon=folium.Icon(color='red')
|
136 |
).add_to(marker_cluster)
|
137 |
|
|
|
138 |
heat_data = filtered_df[['Latitude', 'Longitude']].values.tolist()
|
139 |
-
HeatMap(heat_data, radius=15, max_zoom=13, min_opacity=0.3).add_to(m)
|
140 |
|
|
|
141 |
return m
|
142 |
|
143 |
def create_injuries_fatalities_chart(crash_data, unit_type):
|
@@ -308,7 +396,7 @@ def create_category_distribution_chart(df, selected_category, selected_year):
|
|
308 |
barmode='stack',
|
309 |
xaxis_tickangle=-45,
|
310 |
legend_title='Injury Severity',
|
311 |
-
margin=dict(t=50, b=
|
312 |
)
|
313 |
|
314 |
return fig
|
@@ -345,16 +433,16 @@ def main():
|
|
345 |
|
346 |
st.markdown("""
|
347 |
**Team Members:**
|
348 |
-
- Janhavi Tushar Zarapkar
|
349 |
- Hangyue Zhang ([email protected])
|
350 |
- Andrew Nam ([email protected])
|
351 |
- Nirmal Attarde ([email protected])
|
352 |
-
- Maanas Sandeep
|
353 |
""")
|
354 |
|
355 |
|
356 |
st.markdown("""
|
357 |
-
|
358 |
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.
|
359 |
|
360 |
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.
|
@@ -367,14 +455,21 @@ def main():
|
|
367 |
|
368 |
if 'Weather' not in df.columns:
|
369 |
df['Weather'] = 'Unknown'
|
|
|
|
|
|
|
370 |
|
|
|
|
|
|
|
|
|
371 |
# Create tabs for different visualizations
|
372 |
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
373 |
"Crash Trend",
|
374 |
-
"
|
375 |
"Distribution by Category",
|
376 |
"Crash Injuries/Fatalities",
|
377 |
-
"
|
378 |
])
|
379 |
|
380 |
with tab1:
|
@@ -392,6 +487,8 @@ def main():
|
|
392 |
margin=dict(l=50, r=50, t=50, b=50)
|
393 |
)
|
394 |
st.plotly_chart(trend_fig, use_container_width=True)
|
|
|
|
|
395 |
|
396 |
with desc_col:
|
397 |
st.markdown("""
|
@@ -408,6 +505,7 @@ def main():
|
|
408 |
* **Dynamic Title**: The chart updates its title to reflect the selected weather condition or "All Conditions" for the overall trend.
|
409 |
|
410 |
**Insights:**
|
|
|
411 |
This chart helps uncover:
|
412 |
* Annual fluctuations in crash incidents.
|
413 |
* Correlations between weather conditions and crash frequencies.
|
@@ -419,12 +517,24 @@ def main():
|
|
419 |
age_groups = ['All Ages', '16-25', '26-35', '36-45', '46-55', '56-65', '65+']
|
420 |
selected_age = st.selectbox('Select Age Group:', age_groups)
|
421 |
|
422 |
-
chart_col, desc_col = st.columns([7,
|
423 |
|
424 |
with chart_col:
|
425 |
# Create and display chart
|
426 |
-
fig = create_severity_violation_chart(df, selected_age)
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
|
429 |
# Display statistics
|
430 |
if selected_age == 'All Ages':
|
@@ -435,37 +545,46 @@ def main():
|
|
435 |
(df['Age_Group_Drv2'] == selected_age)
|
436 |
])
|
437 |
|
438 |
-
# Create two columns for statistics
|
439 |
-
col1, col2 = st.columns(2)
|
440 |
-
|
441 |
-
with col1:
|
442 |
-
st.markdown(f"### Total Incidents")
|
443 |
-
st.markdown(f"**{total_incidents:,}** incidents for {selected_age}")
|
444 |
-
|
445 |
-
with col2:
|
446 |
-
st.markdown("### Top Violations")
|
447 |
-
top_violations = get_top_violations(df, selected_age)
|
448 |
-
st.table(top_violations)
|
449 |
|
450 |
with desc_col:
|
451 |
st.markdown("""
|
452 |
-
|
453 |
-
|
454 |
-
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.
|
455 |
|
456 |
-
**
|
457 |
-
1. **Age Group Analysis**:
|
458 |
-
* Select specific age groups (e.g., "16-25", "65+") or analyze all ages to explore correlations between age, violation type, and crash severity.
|
459 |
-
* Understand how different age groups are involved in various types of violations.
|
460 |
|
461 |
-
|
462 |
-
* Examine the most frequent violations contributing to traffic accidents for each age group.
|
463 |
-
* View detailed statistics showing the distribution of violation types.
|
464 |
|
465 |
-
**
|
466 |
-
|
467 |
-
|
468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
469 |
""")
|
470 |
|
471 |
with tab3:
|
@@ -500,7 +619,7 @@ def main():
|
|
500 |
## Distribution of Incidents by {selected_category}
|
501 |
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).
|
502 |
|
503 |
-
**Key
|
504 |
* Interactive Filters: Select a category and filter by year to analyze trends over time.
|
505 |
* Insightful Tooltips: Hover over each segment to view the exact count and percentage of incidents for a given severity level.
|
506 |
* Comparative Analysis: Quickly identify how different conditions or behaviors correlate with injury severity.
|
@@ -562,37 +681,83 @@ def main():
|
|
562 |
years = sorted(df['Year'].unique())
|
563 |
selected_year = st.selectbox('Select Year:', years)
|
564 |
|
565 |
-
|
|
|
566 |
|
567 |
-
with
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
568 |
map_placeholder = st.empty()
|
569 |
with map_placeholder:
|
570 |
-
m = create_map(df, selected_year)
|
571 |
map_data = st_folium(
|
572 |
m,
|
573 |
width=None,
|
574 |
-
height=
|
575 |
-
key=f"map_{selected_year}",
|
576 |
returned_objects=["null_drawing"]
|
577 |
)
|
578 |
-
|
579 |
with desc_col:
|
580 |
st.markdown("""
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
**
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
595 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
596 |
|
597 |
|
598 |
if __name__ == "__main__":
|
|
|
3 |
import plotly.express as px
|
4 |
import altair as alt
|
5 |
import folium
|
6 |
+
from streamlit_plotly_events import plotly_events # added for part3 interactivity
|
7 |
from folium.plugins import HeatMap, MarkerCluster
|
8 |
from streamlit_folium import st_folium
|
9 |
|
10 |
+
# To fix the color scheme in crash stats plot (asked ChatGPT for appropriate colors)
|
11 |
+
severity_colors = {
|
12 |
+
"No Injury": "#1f77b4",
|
13 |
+
"Possible Injury": "#aec7e8",
|
14 |
+
"Non Incapacitating Injury": "#ff7f0e",
|
15 |
+
"Incapacitating Injury": "#ffbb78",
|
16 |
+
"Suspected Minor Injury": "#2ca02c",
|
17 |
+
"Suspected Serious Injury": "#98df8a",
|
18 |
+
"Fatal": "#d62728",
|
19 |
+
}
|
20 |
+
|
21 |
@st.cache_data
|
22 |
def load_and_preprocess_data(file_path):
|
23 |
# Read the data
|
|
|
81 |
color='Severity',
|
82 |
title=f'Crash Severity Distribution by Violation Type - {age_group}',
|
83 |
labels={'count': 'Number of Incidents', 'Violation': 'Violation Type'},
|
84 |
+
color_discrete_map=severity_colors,
|
85 |
+
|
86 |
)
|
87 |
|
88 |
+
# fig.update_layout(
|
89 |
+
# xaxis_tickangle=-45,
|
90 |
+
# legend_title='Severity Level',
|
91 |
+
# barmode='stack'
|
92 |
+
# )
|
93 |
+
|
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=250,r=20), # Increase bottom margin to avoid pruning
|
107 |
+
xaxis=dict(
|
108 |
+
tickangle=-45,
|
109 |
+
tickfont=dict(size=9),
|
110 |
+
dtick=1,)
|
111 |
)
|
112 |
|
113 |
+
|
114 |
+
|
115 |
+
# return fig
|
116 |
+
return fig, violations
|
117 |
|
118 |
def get_top_violations(df, age_group):
|
119 |
# Calculate total incidents for the age group
|
120 |
if age_group == 'All Ages':
|
121 |
+
total_incidents = len(df)
|
122 |
# Get violations for all ages
|
123 |
violations = pd.concat([
|
124 |
df['Violation1_Drv1'].value_counts(),
|
|
|
151 |
return violations_df.head()
|
152 |
|
153 |
@st.cache_data
|
154 |
+
def create_interactive_pie_chart(violations, selected_violation, selected_age):
|
155 |
+
# Filter data based on selected violation
|
156 |
+
filtered_data = violations[violations['Violation'] == selected_violation]
|
157 |
+
|
158 |
+
# Create a pie chart for severity distribution of the selected violation type
|
159 |
+
fig = px.pie(
|
160 |
+
filtered_data,
|
161 |
+
names='Severity',
|
162 |
+
values='count',
|
163 |
+
# title=f'Severity Level Distribution for Violation: {selected_violation}',
|
164 |
+
title=f'Severity Level Distribution for Violation: {selected_violation} - {selected_age}', # dynamically update pie chart's title
|
165 |
+
height=600,
|
166 |
+
color_discrete_map=severity_colors
|
167 |
+
)
|
168 |
+
|
169 |
+
return fig
|
170 |
+
|
171 |
+
def create_map_bar_chart(df, selected_year):
|
172 |
+
# Create severity count bar chart
|
173 |
filtered_df = df[df['Year'] == selected_year]
|
174 |
+
severity_count = filtered_df['Injuryseverity'].value_counts().reset_index()
|
175 |
+
severity_count.columns = ['Injuryseverity', 'Count']
|
176 |
+
|
177 |
+
fig = px.bar(
|
178 |
+
severity_count,
|
179 |
+
x='Injuryseverity',
|
180 |
+
y='Count',
|
181 |
+
title="Accidents by Severity",
|
182 |
+
labels={'Injuryseverity': 'Severity', 'Count': 'Number of Accidents'} # Adjust height as needed
|
183 |
+
)
|
184 |
+
fig.update_traces(marker_color='blue')
|
185 |
+
fig.update_layout(
|
186 |
+
clickmode='event+select', # Enable interactivity
|
187 |
+
xaxis_tickangle=45, # Rotate x-axis labels 45 degrees
|
188 |
+
margin=dict(t=50, b=150, r=20), # Add bottom margin to prevent label cutoff
|
189 |
+
)
|
190 |
+
return fig
|
191 |
+
|
192 |
+
|
193 |
+
@st.cache_data
|
194 |
+
def create_map(df, selected_year, selected_severity=None):
|
195 |
+
# Filter data by selected year
|
196 |
+
filtered_df = df[df['Year'] == selected_year]
|
197 |
+
|
198 |
+
# Filter further by selected severity if provided
|
199 |
+
if selected_severity:
|
200 |
+
filtered_df = filtered_df[filtered_df['Injuryseverity'] == selected_severity]
|
201 |
+
|
202 |
+
# Remove rows with missing latitude or longitude
|
203 |
+
filtered_df = filtered_df.dropna(subset=['Latitude', 'Longitude'])
|
204 |
+
|
205 |
+
# Create the map
|
206 |
m = folium.Map(
|
207 |
+
location=[33.4255, -111.9400], # Default location (can be customized)
|
208 |
zoom_start=12,
|
209 |
control_scale=True,
|
210 |
tiles='CartoDB positron'
|
211 |
)
|
212 |
|
213 |
+
# Add marker cluster
|
214 |
+
marker_cluster = MarkerCluster(name="Accident Locations").add_to(m)
|
215 |
+
|
216 |
+
# Add accident markers
|
217 |
for _, row in filtered_df.iterrows():
|
218 |
folium.Marker(
|
219 |
location=[row['Latitude'], row['Longitude']],
|
|
|
221 |
icon=folium.Icon(color='red')
|
222 |
).add_to(marker_cluster)
|
223 |
|
224 |
+
# Add heatmap
|
225 |
heat_data = filtered_df[['Latitude', 'Longitude']].values.tolist()
|
226 |
+
HeatMap(heat_data, radius=15, max_zoom=13, min_opacity=0.3, name="Heat Map").add_to(m)
|
227 |
|
228 |
+
folium.LayerControl().add_to(m)
|
229 |
return m
|
230 |
|
231 |
def create_injuries_fatalities_chart(crash_data, unit_type):
|
|
|
396 |
barmode='stack',
|
397 |
xaxis_tickangle=-45,
|
398 |
legend_title='Injury Severity',
|
399 |
+
margin=dict(t=50, b=150, l=50, r=50),
|
400 |
)
|
401 |
|
402 |
return fig
|
|
|
433 |
|
434 |
st.markdown("""
|
435 |
**Team Members:**
|
436 |
+
- Janhavi Tushar Zarapkar ([email protected])
|
437 |
- Hangyue Zhang ([email protected])
|
438 |
- Andrew Nam ([email protected])
|
439 |
- Nirmal Attarde ([email protected])
|
440 |
+
- Maanas Sandeep Agrawal ([email protected])
|
441 |
""")
|
442 |
|
443 |
|
444 |
st.markdown("""
|
445 |
+
# Introduction to the Traffic Accident Dataset
|
446 |
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.
|
447 |
|
448 |
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.
|
|
|
455 |
|
456 |
if 'Weather' not in df.columns:
|
457 |
df['Weather'] = 'Unknown'
|
458 |
+
|
459 |
+
if 'selected_violation' not in st.session_state:
|
460 |
+
st.session_state['selected_violation'] = None
|
461 |
|
462 |
+
if "selected_severity" not in st.session_state:
|
463 |
+
st.session_state["selected_severity"] = None
|
464 |
+
|
465 |
+
|
466 |
# Create tabs for different visualizations
|
467 |
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
468 |
"Crash Trend",
|
469 |
+
"Violation-Severity Analysis",
|
470 |
"Distribution by Category",
|
471 |
"Crash Injuries/Fatalities",
|
472 |
+
"Severity-Location Analysis"
|
473 |
])
|
474 |
|
475 |
with tab1:
|
|
|
487 |
margin=dict(l=50, r=50, t=50, b=50)
|
488 |
)
|
489 |
st.plotly_chart(trend_fig, use_container_width=True)
|
490 |
+
|
491 |
+
|
492 |
|
493 |
with desc_col:
|
494 |
st.markdown("""
|
|
|
505 |
* **Dynamic Title**: The chart updates its title to reflect the selected weather condition or "All Conditions" for the overall trend.
|
506 |
|
507 |
**Insights:**
|
508 |
+
|
509 |
This chart helps uncover:
|
510 |
* Annual fluctuations in crash incidents.
|
511 |
* Correlations between weather conditions and crash frequencies.
|
|
|
517 |
age_groups = ['All Ages', '16-25', '26-35', '36-45', '46-55', '56-65', '65+']
|
518 |
selected_age = st.selectbox('Select Age Group:', age_groups)
|
519 |
|
520 |
+
chart_col, desc_col = st.columns([7, 4])
|
521 |
|
522 |
with chart_col:
|
523 |
# Create and display chart
|
524 |
+
fig, violations = create_severity_violation_chart(df, selected_age)
|
525 |
+
|
526 |
+
clicked_points = plotly_events(fig, click_event=True, override_height=600, override_width="100%")
|
527 |
+
|
528 |
+
if clicked_points:
|
529 |
+
selected_violation = clicked_points[0]['x']
|
530 |
+
if selected_violation != st.session_state['selected_violation']:
|
531 |
+
st.session_state['selected_violation'] = selected_violation
|
532 |
+
|
533 |
+
# If a violation is selected, display the pie chart --> added for part3 (interactive pie chart)
|
534 |
+
if st.session_state['selected_violation']:
|
535 |
+
# pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'])
|
536 |
+
pie_chart = create_interactive_pie_chart(violations, st.session_state['selected_violation'], selected_age) # dynamically update pie chart's title
|
537 |
+
st.plotly_chart(pie_chart, use_container_width=True)
|
538 |
|
539 |
# Display statistics
|
540 |
if selected_age == 'All Ages':
|
|
|
545 |
(df['Age_Group_Drv2'] == selected_age)
|
546 |
])
|
547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
548 |
|
549 |
with desc_col:
|
550 |
st.markdown("""
|
551 |
+
# Severity of Violations Across Age Groups
|
|
|
|
|
552 |
|
553 |
+
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.
|
|
|
|
|
|
|
554 |
|
555 |
+
---
|
|
|
|
|
556 |
|
557 |
+
## **Key Features**
|
558 |
+
|
559 |
+
### 1. **Age Group Analysis**
|
560 |
+
- Select specific age groups (e.g., "16-25", "65+") or analyze all ages to explore correlations between:
|
561 |
+
- Age
|
562 |
+
- Violation type
|
563 |
+
- Crash severity
|
564 |
+
- Understand how different age groups are involved in various types of violations.
|
565 |
+
|
566 |
+
### 2. **Violation Breakdown**
|
567 |
+
- Examine the most frequent violations contributing to traffic accidents for each age group.
|
568 |
+
- View detailed statistics showing the distribution of violation types.
|
569 |
+
|
570 |
+
### 3. **Understanding Severity Level**
|
571 |
+
- Identify the proportion of severity levels for a specific violation type based on different age groups.
|
572 |
+
- Investigate detailed severity patterns for each violation type across age groups.
|
573 |
+
|
574 |
+
---
|
575 |
+
|
576 |
+
## **Insights**
|
577 |
+
|
578 |
+
- **Identifies High-Risk Behaviors:**
|
579 |
+
- Highlights risky behaviors such as reckless driving in younger drivers or impaired driving in older groups.
|
580 |
+
|
581 |
+
- **Highlights Severity Associations:**
|
582 |
+
- Shows which violations are associated with more severe outcomes, aiding targeted safety interventions and public awareness campaigns.
|
583 |
+
|
584 |
+
- **Supports Data-Driven Decision Making:**
|
585 |
+
- Provides insights for designing **age-specific traffic safety programs**.
|
586 |
+
|
587 |
+
---
|
588 |
""")
|
589 |
|
590 |
with tab3:
|
|
|
619 |
## Distribution of Incidents by {selected_category}
|
620 |
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).
|
621 |
|
622 |
+
**Key Features:**
|
623 |
* Interactive Filters: Select a category and filter by year to analyze trends over time.
|
624 |
* Insightful Tooltips: Hover over each segment to view the exact count and percentage of incidents for a given severity level.
|
625 |
* Comparative Analysis: Quickly identify how different conditions or behaviors correlate with injury severity.
|
|
|
681 |
years = sorted(df['Year'].unique())
|
682 |
selected_year = st.selectbox('Select Year:', years)
|
683 |
|
684 |
+
# Create two columns for visualization and description
|
685 |
+
viz_col, desc_col = st.columns([6, 4])
|
686 |
|
687 |
+
with viz_col:
|
688 |
+
# First add bar chart
|
689 |
+
st.subheader("Severity-Location Analysis")
|
690 |
+
bar_fig = create_map_bar_chart(df, selected_year)
|
691 |
+
|
692 |
+
# Capture click events with bar chart
|
693 |
+
clicked_points = plotly_events(
|
694 |
+
bar_fig,
|
695 |
+
click_event=True,
|
696 |
+
override_height=300,
|
697 |
+
override_width="100%"
|
698 |
+
)
|
699 |
+
|
700 |
+
if clicked_points:
|
701 |
+
selected_severity = clicked_points[0]['x']
|
702 |
+
st.session_state["selected_severity"] = selected_severity
|
703 |
+
|
704 |
+
# Show currently selected severity
|
705 |
+
st.write(f"Selected Severity: {st.session_state['selected_severity'] if st.session_state['selected_severity'] else 'All'}")
|
706 |
+
|
707 |
+
# Add map below bar chart
|
708 |
+
st.subheader("Accident Locations")
|
709 |
map_placeholder = st.empty()
|
710 |
with map_placeholder:
|
711 |
+
m = create_map(df, selected_year, st.session_state["selected_severity"])
|
712 |
map_data = st_folium(
|
713 |
m,
|
714 |
width=None,
|
715 |
+
height=600, # Reduced height since it's now below bar chart
|
716 |
+
key=f"map_{selected_year}_{st.session_state['selected_severity']}",
|
717 |
returned_objects=["null_drawing"]
|
718 |
)
|
719 |
+
|
720 |
with desc_col:
|
721 |
st.markdown("""
|
722 |
+
# Exploring Traffic Accident Severity and Location
|
723 |
+
The two linked graphs show an interactive platform for exploring traffic accident data, featuring a **bar chart** and a **dynamic map**.
|
724 |
+
- The **bar chart** displays the distribution of accidents by severity.
|
725 |
+
- The **map** combines marker clustering and heatmaps to highlight accident locations.
|
726 |
+
- Users can filter data by year and severity to explore patterns.
|
727 |
+
---
|
728 |
+
## **Key Features**
|
729 |
+
- **Interactive Bar Chart:**
|
730 |
+
Displays accident counts by severity, updating the map based on selected severity.
|
731 |
+
- **Map with Dual Layers:**
|
732 |
+
Includes marker clustering for individual accidents and a heatmap to visualize accident density.
|
733 |
+
- **Year-Based Filtering:**
|
734 |
+
Allows users to filter data by year and severity for focused analysis.
|
735 |
+
- **Seamless Integration:**
|
736 |
+
Combines Streamlit and Folium, with Plotly events linking the visualizations.
|
737 |
+
---
|
738 |
+
## **Design**
|
739 |
+
- **Bar Chart:**
|
740 |
+
- Uses a calm blue color for clarity.
|
741 |
+
- **Map:**
|
742 |
+
- Uses **CartoDB tiles** with red markers and heatmaps for visibility.
|
743 |
+
---
|
744 |
+
## **Insights**
|
745 |
+
- **Severity Patterns:**
|
746 |
+
The bar chart reveals accident trends across severity levels.
|
747 |
+
- **Spatial Trends:**
|
748 |
+
The map identifies high-risk accident hotspots.
|
749 |
+
- **Yearly and Severity Insights:**
|
750 |
+
Filters help uncover temporal and severity-related patterns, aiding traffic safety analysis.
|
751 |
""")
|
752 |
+
st.markdown("---")
|
753 |
+
|
754 |
+
# Add conclusion section
|
755 |
+
st.markdown("# FP3 Conclusion")
|
756 |
+
|
757 |
+
st.markdown("""
|
758 |
+
TODO
|
759 |
+
""")
|
760 |
+
|
761 |
|
762 |
|
763 |
if __name__ == "__main__":
|