Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,11 +4,11 @@ import numpy as np
|
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import seaborn as sns
|
| 6 |
import altair as alt
|
|
|
|
| 7 |
import folium
|
| 8 |
from folium.plugins import HeatMap
|
| 9 |
from streamlit_folium import st_folium
|
| 10 |
import plotly.express as px
|
| 11 |
-
from datetime import datetime
|
| 12 |
|
| 13 |
# Set page config
|
| 14 |
st.set_page_config(page_title="Nuisance Complaints Dashboard", layout="wide")
|
|
@@ -73,7 +73,8 @@ year_options = ['All Time'] + sorted(data['Year Reported'].unique().tolist())
|
|
| 73 |
selected_year = st.sidebar.selectbox("Select Year", options=year_options)
|
| 74 |
viz_type = st.sidebar.selectbox("Select Visualization", [
|
| 75 |
"Complaint Types", "Geographic Distribution", "Resolution Status",
|
| 76 |
-
"Submission Methods", "Complaints by Disposition", "Monthly Trends by Complaint Type"
|
|
|
|
| 77 |
])
|
| 78 |
|
| 79 |
# Filter data based on year
|
|
@@ -99,10 +100,6 @@ if viz_type == "Complaint Types":
|
|
| 99 |
complaint_counts.columns = ['Complaint Type', 'Count']
|
| 100 |
fig = px.pie(complaint_counts, names='Complaint Type', values='Count', hole=0.4)
|
| 101 |
st.plotly_chart(fig, use_container_width=True)
|
| 102 |
-
st.write("""
|
| 103 |
-
**Write-up:** This visualization shows the distribution of complaint types as a donut chart.
|
| 104 |
-
It provides a quick overview of the most common complaints. The warm color palette helps
|
| 105 |
-
highlight differences between complaint categories.""")
|
| 106 |
|
| 107 |
elif viz_type == "Geographic Distribution":
|
| 108 |
st.subheader("Clustered Heatmap of Complaints")
|
|
@@ -111,68 +108,68 @@ elif viz_type == "Geographic Distribution":
|
|
| 111 |
heat_data = filtered_data[['Latitude', 'Longitude']].dropna().values.tolist()
|
| 112 |
HeatMap(heat_data).add_to(m)
|
| 113 |
st_folium(m, width=700, height=500)
|
| 114 |
-
st.write("""
|
| 115 |
-
**Write-up:** This heatmap visualizes complaint hotspots geographically. Areas with
|
| 116 |
-
higher complaint density are highlighted, helping policymakers focus resources effectively.""")
|
| 117 |
|
| 118 |
elif viz_type == "Resolution Status":
|
| 119 |
st.subheader("Interactive Complaint Resolution Status")
|
| 120 |
resolution_counts = filtered_data['Disposition'].value_counts().reset_index()
|
| 121 |
resolution_counts.columns = ['Disposition', 'Count']
|
| 122 |
-
resolution_counts['Percentage'] = (resolution_counts['Count'] / resolution_counts['Count'].sum()) * 100
|
| 123 |
chart = alt.Chart(resolution_counts).mark_arc(innerRadius=50).encode(
|
| 124 |
theta=alt.Theta(field="Count", type="quantitative"),
|
| 125 |
-
color=alt.Color(field="Disposition", type="nominal")
|
| 126 |
-
tooltip=[
|
| 127 |
-
alt.Tooltip("Disposition", title="Resolution"),
|
| 128 |
-
alt.Tooltip("Count", title="Count"),
|
| 129 |
-
alt.Tooltip("Percentage", title="Percentage", format=".2f")
|
| 130 |
-
]
|
| 131 |
-
)
|
| 132 |
-
st.altair_chart(chart, use_container_width=True)
|
| 133 |
-
st.write("""
|
| 134 |
-
**Write-up:** This chart visualizes resolution status using a donut chart.
|
| 135 |
-
It provides insights into the efficiency of complaint resolutions.""")
|
| 136 |
-
|
| 137 |
-
elif viz_type == "Monthly Trends by Complaint Type":
|
| 138 |
-
st.subheader("Monthly Trends Grouped by Complaint Types")
|
| 139 |
-
monthly_trends = (
|
| 140 |
-
filtered_data.groupby(['Month Reported', 'Type of Complaint'])
|
| 141 |
-
.size()
|
| 142 |
-
.reset_index(name='Count')
|
| 143 |
-
)
|
| 144 |
-
monthly_trends['Month'] = monthly_trends['Month Reported'].apply(
|
| 145 |
-
lambda x: datetime(2023, x, 1).strftime('%B')
|
| 146 |
-
)
|
| 147 |
-
chart = alt.Chart(monthly_trends).mark_line(point=True).encode(
|
| 148 |
-
x=alt.X('Month Reported:O', title='Month'),
|
| 149 |
-
y=alt.Y('Count:Q', title='Number of Complaints'),
|
| 150 |
-
color=alt.Color('Type of Complaint:N', title='Complaint Type'),
|
| 151 |
-
tooltip=["Type of Complaint:N", "Month:N", "Count:Q"]
|
| 152 |
)
|
| 153 |
st.altair_chart(chart, use_container_width=True)
|
| 154 |
-
st.write("""
|
| 155 |
-
**Write-up:** This line chart visualizes monthly trends in complaints grouped by type.
|
| 156 |
-
The use of vibrant colors helps distinguish trends across different complaint types.""")
|
| 157 |
|
| 158 |
elif viz_type == "Submission Methods":
|
| 159 |
st.subheader("Submission Methods Analysis")
|
| 160 |
submission_counts = filtered_data['Method Submitted'].value_counts()
|
| 161 |
fig, ax = plt.subplots(figsize=(10, 6))
|
| 162 |
sns.barplot(x=submission_counts.values, y=submission_counts.index, palette='inferno', ax=ax)
|
| 163 |
-
ax.set_title(f"Submission Methods in {selected_year}")
|
| 164 |
st.pyplot(fig)
|
| 165 |
-
st.write("""
|
| 166 |
-
**Write-up:** This bar chart illustrates the preferred methods for complaint submission.
|
| 167 |
-
The `inferno` color palette highlights differences across submission types.""")
|
| 168 |
|
| 169 |
elif viz_type == "Complaints by Disposition":
|
| 170 |
st.subheader("Complaints by Disposition")
|
| 171 |
disposition_counts = filtered_data['Disposition'].value_counts()
|
| 172 |
fig, ax = plt.subplots(figsize=(10, 6))
|
| 173 |
sns.barplot(x=disposition_counts.values, y=disposition_counts.index, palette='viridis', ax=ax)
|
| 174 |
-
ax.set_title(f"Complaints by Disposition in {selected_year}")
|
| 175 |
st.pyplot(fig)
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import seaborn as sns
|
| 6 |
import altair as alt
|
| 7 |
+
from datetime import datetime
|
| 8 |
import folium
|
| 9 |
from folium.plugins import HeatMap
|
| 10 |
from streamlit_folium import st_folium
|
| 11 |
import plotly.express as px
|
|
|
|
| 12 |
|
| 13 |
# Set page config
|
| 14 |
st.set_page_config(page_title="Nuisance Complaints Dashboard", layout="wide")
|
|
|
|
| 73 |
selected_year = st.sidebar.selectbox("Select Year", options=year_options)
|
| 74 |
viz_type = st.sidebar.selectbox("Select Visualization", [
|
| 75 |
"Complaint Types", "Geographic Distribution", "Resolution Status",
|
| 76 |
+
"Submission Methods", "Complaints by Disposition", "Monthly Trends by Complaint Type",
|
| 77 |
+
"Top Complaint Types", "Complaints Over Time", "Complaints by Housing Block and Type"
|
| 78 |
])
|
| 79 |
|
| 80 |
# Filter data based on year
|
|
|
|
| 100 |
complaint_counts.columns = ['Complaint Type', 'Count']
|
| 101 |
fig = px.pie(complaint_counts, names='Complaint Type', values='Count', hole=0.4)
|
| 102 |
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
elif viz_type == "Geographic Distribution":
|
| 105 |
st.subheader("Clustered Heatmap of Complaints")
|
|
|
|
| 108 |
heat_data = filtered_data[['Latitude', 'Longitude']].dropna().values.tolist()
|
| 109 |
HeatMap(heat_data).add_to(m)
|
| 110 |
st_folium(m, width=700, height=500)
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
elif viz_type == "Resolution Status":
|
| 113 |
st.subheader("Interactive Complaint Resolution Status")
|
| 114 |
resolution_counts = filtered_data['Disposition'].value_counts().reset_index()
|
| 115 |
resolution_counts.columns = ['Disposition', 'Count']
|
|
|
|
| 116 |
chart = alt.Chart(resolution_counts).mark_arc(innerRadius=50).encode(
|
| 117 |
theta=alt.Theta(field="Count", type="quantitative"),
|
| 118 |
+
color=alt.Color(field="Disposition", type="nominal")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
)
|
| 120 |
st.altair_chart(chart, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
elif viz_type == "Submission Methods":
|
| 123 |
st.subheader("Submission Methods Analysis")
|
| 124 |
submission_counts = filtered_data['Method Submitted'].value_counts()
|
| 125 |
fig, ax = plt.subplots(figsize=(10, 6))
|
| 126 |
sns.barplot(x=submission_counts.values, y=submission_counts.index, palette='inferno', ax=ax)
|
|
|
|
| 127 |
st.pyplot(fig)
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
elif viz_type == "Complaints by Disposition":
|
| 130 |
st.subheader("Complaints by Disposition")
|
| 131 |
disposition_counts = filtered_data['Disposition'].value_counts()
|
| 132 |
fig, ax = plt.subplots(figsize=(10, 6))
|
| 133 |
sns.barplot(x=disposition_counts.values, y=disposition_counts.index, palette='viridis', ax=ax)
|
|
|
|
| 134 |
st.pyplot(fig)
|
| 135 |
+
|
| 136 |
+
elif viz_type == "Monthly Trends by Complaint Type":
|
| 137 |
+
st.subheader("Monthly Trends Grouped by Complaint Types")
|
| 138 |
+
monthly_trends = filtered_data.groupby(['Month Reported', 'Type of Complaint']).size().reset_index(name='Count')
|
| 139 |
+
chart = alt.Chart(monthly_trends).mark_line(point=True).encode(
|
| 140 |
+
x=alt.X('Month Reported:O', title='Month'),
|
| 141 |
+
y=alt.Y('Count:Q', title='Number of Complaints'),
|
| 142 |
+
color='Type of Complaint:N'
|
| 143 |
+
)
|
| 144 |
+
st.altair_chart(chart, use_container_width=True)
|
| 145 |
+
|
| 146 |
+
elif viz_type == "Top Complaint Types":
|
| 147 |
+
st.subheader("Top Complaint Types")
|
| 148 |
+
complaint_counts = filtered_data['Type of Complaint'].value_counts().head(10)
|
| 149 |
+
fig, ax = plt.subplots()
|
| 150 |
+
sns.barplot(x=complaint_counts.values, y=complaint_counts.index, palette="inferno", ax=ax)
|
| 151 |
+
st.pyplot(fig)
|
| 152 |
+
|
| 153 |
+
elif viz_type == "Complaints Over Time":
|
| 154 |
+
st.subheader("Complaints Over Time")
|
| 155 |
+
complaints_over_time = filtered_data.groupby(filtered_data['Date Reported'].dt.date).size()
|
| 156 |
+
fig, ax = plt.subplots()
|
| 157 |
+
ax.plot(complaints_over_time.index, complaints_over_time.values, marker='o')
|
| 158 |
+
ax.set_title("Complaints Over Time")
|
| 159 |
+
st.pyplot(fig)
|
| 160 |
+
|
| 161 |
+
elif viz_type == "Complaints by Housing Block and Type":
|
| 162 |
+
st.subheader("Complaints by Housing Block and Type")
|
| 163 |
+
complaint_pivot = filtered_data.pivot_table(
|
| 164 |
+
index='Housing Block',
|
| 165 |
+
columns='Type of Complaint',
|
| 166 |
+
values='Disposition',
|
| 167 |
+
aggfunc='count',
|
| 168 |
+
fill_value=0
|
| 169 |
+
)
|
| 170 |
+
fig = complaint_pivot.plot(kind='bar', stacked=True, colormap='inferno', figsize=(10, 6)).get_figure()
|
| 171 |
+
st.pyplot(fig)
|
| 172 |
+
|
| 173 |
+
# Footer
|
| 174 |
+
st.markdown("---")
|
| 175 |
+
st.markdown("Dataset provided by the City of Urbana Open Data Portal.")
|