Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,7 +10,7 @@ from folium.plugins import HeatMap
|
|
| 10 |
from streamlit_folium import st_folium
|
| 11 |
import plotly.express as px
|
| 12 |
|
| 13 |
-
#
|
| 14 |
st.set_page_config(page_title="Nuisance Complaints Dashboard", layout="wide")
|
| 15 |
|
| 16 |
# Title and introduction
|
|
@@ -26,7 +26,7 @@ st.markdown("""
|
|
| 26 |
This dashboard analyzes nuisance complaints data from the City of Urbana. The visualizations aim to explore complaint trends, resolution efficiency, and geographic patterns to provide actionable insights for urban planning and management.
|
| 27 |
""")
|
| 28 |
|
| 29 |
-
#
|
| 30 |
@st.cache_data
|
| 31 |
def load_and_clean_data():
|
| 32 |
data = pd.read_csv('Nuisance_Complaints_20241130.csv')
|
|
@@ -35,7 +35,7 @@ def load_and_clean_data():
|
|
| 35 |
data['Date Notice Mailed or Given'] = pd.to_datetime(data['Date Notice Mailed or Given'])
|
| 36 |
data['File Close Date'] = pd.to_datetime(data['File Close Date'], errors='coerce')
|
| 37 |
|
| 38 |
-
#
|
| 39 |
median_delay = (data['Date Notice Mailed or Given'] - data['Date Reported']).dt.days.median()
|
| 40 |
data.loc[data['Date Notice Mailed or Given'].isna(), 'Date Notice Mailed or Given'] = \
|
| 41 |
data.loc[data['Date Notice Mailed or Given'].isna(), 'Date Reported'] + pd.Timedelta(days=median_delay)
|
|
@@ -64,79 +64,20 @@ def load_and_clean_data():
|
|
| 64 |
data['Month Reported'] = data['Date Reported'].dt.month
|
| 65 |
return data
|
| 66 |
|
| 67 |
-
#
|
| 68 |
data = load_and_clean_data()
|
| 69 |
|
| 70 |
-
# # Sidebar for controls
|
| 71 |
-
# st.sidebar.header("Dashboard Controls")
|
| 72 |
-
|
| 73 |
-
# # First, define the visualization type (viz_type) selection
|
| 74 |
-
# viz_type = st.sidebar.selectbox("Select Visualization", [
|
| 75 |
-
# "Complaint Types", "Geographic Distribution", "Complaints by Disposition", "Submission Methods","Monthly Trends by Complaint Type",
|
| 76 |
-
# "Complaints Over Time", "Complaints by Housing Block and Type"
|
| 77 |
-
# ])
|
| 78 |
-
|
| 79 |
-
# # Remove the year selection when certain visualizations are selected
|
| 80 |
-
# if viz_type not in ["Complaints Over Time", "Complaints by Housing Block and Type"]:
|
| 81 |
-
# year_options = ['All Time'] + sorted(data['Year Reported'].unique().tolist())
|
| 82 |
-
# selected_year = st.sidebar.selectbox("Select Year", options=year_options)
|
| 83 |
-
# else:
|
| 84 |
-
# selected_year = 'All Time' # Default to 'All Time' if visualization doesn't require year
|
| 85 |
-
|
| 86 |
-
# # Date Range Selector for Complaints Over Time (only show when Complaints Over Time is selected)
|
| 87 |
-
# if viz_type == "Complaints Over Time":
|
| 88 |
-
# start_date = st.sidebar.date_input("Start Date", pd.to_datetime("2020-01-01"))
|
| 89 |
-
# end_date = st.sidebar.date_input("End Date", pd.to_datetime("2024-12-31"))
|
| 90 |
-
|
| 91 |
-
# # Allow the user to select the type of chart (Bar or Pie) for Submission Methods
|
| 92 |
-
# if viz_type == "Submission Methods":
|
| 93 |
-
# plot_type = st.sidebar.selectbox("Select Plot Type", options=["Bar Chart", "Pie Chart"])
|
| 94 |
-
|
| 95 |
-
# # Dropdown for Housing Block (only show when Complaints by Housing Block and Type is selected)
|
| 96 |
-
# if viz_type == "Complaints by Housing Block and Type":
|
| 97 |
-
# block_options = ['All Blocks'] + sorted(data['Housing Block'].unique().tolist())
|
| 98 |
-
# selected_block = st.sidebar.selectbox("Select Housing Block", options=block_options)
|
| 99 |
-
|
| 100 |
-
# # Ensure selected_block is only used if defined
|
| 101 |
-
# if viz_type == "Complaints by Housing Block and Type" and 'selected_block' not in locals():
|
| 102 |
-
# selected_block = 'All Blocks' # Default to 'All Blocks' if no selection made
|
| 103 |
-
|
| 104 |
-
# # Filter data based on selected year
|
| 105 |
-
# if selected_year != 'All Time':
|
| 106 |
-
# filtered_data = data[data['Year Reported'] == selected_year]
|
| 107 |
-
# else:
|
| 108 |
-
# filtered_data = data
|
| 109 |
-
|
| 110 |
-
# # Further filter by Housing Block
|
| 111 |
-
# if 'selected_block' in locals() and selected_block != 'All Blocks':
|
| 112 |
-
# filtered_data = filtered_data[filtered_data['Housing Block'] == selected_block]
|
| 113 |
-
|
| 114 |
-
# # Filter data based on date range (only for Complaints Over Time visualization)
|
| 115 |
-
# if viz_type == "Complaints Over Time":
|
| 116 |
-
# filtered_data_time = filtered_data[
|
| 117 |
-
# (filtered_data['Date Reported'] >= pd.to_datetime(start_date)) &
|
| 118 |
-
# (filtered_data['Date Reported'] <= pd.to_datetime(end_date))
|
| 119 |
-
# ]
|
| 120 |
-
# else:
|
| 121 |
-
# filtered_data_time = filtered_data
|
| 122 |
-
|
| 123 |
-
# # Filter data based on selected housing block (only for Complaints by Housing Block visualization)
|
| 124 |
-
# if viz_type == "Complaints by Housing Block and Type" and selected_block != 'All Blocks':
|
| 125 |
-
# filtered_data_time = filtered_data_time[filtered_data_time['Housing Block'] == selected_block]
|
| 126 |
-
|
| 127 |
-
# # Header for selected year
|
| 128 |
-
# st.header(f"Analysis for {'All Time' if selected_year == 'All Time' else selected_year}")
|
| 129 |
|
| 130 |
# Sidebar for controls
|
| 131 |
st.sidebar.header("Dashboard Controls")
|
| 132 |
|
| 133 |
-
#
|
| 134 |
viz_type = st.sidebar.selectbox("Select Visualization", [
|
| 135 |
"Complaint Types", "Geographic Distribution", "Complaints by Disposition", "Submission Methods",
|
| 136 |
"Monthly Trends by Complaint Type", "Complaints Over Time", "Complaints by Housing Block and Type"
|
| 137 |
])
|
| 138 |
|
| 139 |
-
#
|
| 140 |
if viz_type not in ["Complaints Over Time", "Complaints by Housing Block and Type"]:
|
| 141 |
year_options = ['All Time'] + sorted(data['Year Reported'].unique().tolist())
|
| 142 |
selected_year = st.sidebar.selectbox("Select Year", options=year_options, key="year_select")
|
|
@@ -153,25 +94,21 @@ if viz_type == "Complaints by Housing Block and Type":
|
|
| 153 |
block_options = ['All Blocks'] + sorted(data['Housing Block'].unique().tolist())
|
| 154 |
selected_block = st.sidebar.selectbox("Select Housing Block", options=block_options, key="block_select")
|
| 155 |
|
| 156 |
-
#
|
| 157 |
-
# if viz_type == "Submission Methods":
|
| 158 |
-
# plot_type = st.sidebar.selectbox("Select Plot Type", options=["Bar Chart", "Pie Chart"], key="plot_type_select")
|
| 159 |
-
|
| 160 |
-
# Ensure selected_block is only used if defined
|
| 161 |
if viz_type == "Complaints by Housing Block and Type" and 'selected_block' not in locals():
|
| 162 |
selected_block = 'All Blocks' # Default to 'All Blocks' if no selection made
|
| 163 |
|
| 164 |
-
#
|
| 165 |
if selected_year != 'All Time':
|
| 166 |
filtered_data = data[data['Year Reported'] == selected_year]
|
| 167 |
else:
|
| 168 |
filtered_data = data
|
| 169 |
|
| 170 |
-
# Further
|
| 171 |
if 'selected_block' in locals() and selected_block != 'All Blocks':
|
| 172 |
filtered_data = filtered_data[filtered_data['Housing Block'] == selected_block]
|
| 173 |
|
| 174 |
-
#
|
| 175 |
if viz_type == "Complaints Over Time":
|
| 176 |
filtered_data_time = filtered_data[
|
| 177 |
(filtered_data['Date Reported'] >= pd.to_datetime(start_date)) &
|
|
@@ -180,7 +117,7 @@ if viz_type == "Complaints Over Time":
|
|
| 180 |
else:
|
| 181 |
filtered_data_time = filtered_data
|
| 182 |
|
| 183 |
-
#
|
| 184 |
if viz_type == "Complaints by Housing Block and Type" and selected_block != 'All Blocks':
|
| 185 |
filtered_data_time = filtered_data_time[filtered_data_time['Housing Block'] == selected_block]
|
| 186 |
|
|
@@ -189,7 +126,7 @@ st.header(f"Analysis for {'All Time' if selected_year == 'All Time' else selecte
|
|
| 189 |
|
| 190 |
|
| 191 |
|
| 192 |
-
#
|
| 193 |
col1, col2 = st.columns(2)
|
| 194 |
with col1:
|
| 195 |
st.metric("Total Complaints", len(filtered_data))
|
|
@@ -339,6 +276,7 @@ elif viz_type == "Submission Methods":
|
|
| 339 |
# **Color Scheme:**
|
| 340 |
# The 'viridis' color palette highlights differences in complaint resolution status, with lighter shades indicating a higher frequency of resolved complaints.
|
| 341 |
# """)
|
|
|
|
| 342 |
|
| 343 |
elif viz_type == "Complaints by Disposition":
|
| 344 |
st.subheader("Complaints by Disposition")
|
|
@@ -369,7 +307,6 @@ elif viz_type == "Complaints by Disposition":
|
|
| 369 |
""")
|
| 370 |
|
| 371 |
|
| 372 |
-
|
| 373 |
elif viz_type == "Monthly Trends by Complaint Type":
|
| 374 |
st.subheader("Monthly Trends Grouped by Complaint Types")
|
| 375 |
monthly_trends = filtered_data.groupby(['Month Reported', 'Type of Complaint']).size().reset_index(name='Count')
|
|
@@ -385,7 +322,7 @@ elif viz_type == "Monthly Trends by Complaint Type":
|
|
| 385 |
This line chart visualizes the monthly trends in complaint counts, grouped by complaint type. It allows tracking changes in complaint frequencies over time and identifying patterns or spikes in specific categories.
|
| 386 |
|
| 387 |
**Why it's interesting:**
|
| 388 |
-
By visualizing these trends,
|
| 389 |
|
| 390 |
**Color Scheme:**
|
| 391 |
Different complaint types are represented by distinct colors, enabling easy comparison of trends across categories.
|
|
@@ -409,6 +346,8 @@ elif viz_type == "Monthly Trends by Complaint Type":
|
|
| 409 |
# **Color Scheme:**
|
| 410 |
# The 'inferno' palette is used to emphasize the frequency of each complaint type, with darker shades representing higher frequencies.
|
| 411 |
# """)
|
|
|
|
|
|
|
| 412 |
|
| 413 |
elif viz_type == "Complaints Over Time":
|
| 414 |
st.subheader("Complaints Over Time")
|
|
@@ -420,10 +359,15 @@ elif viz_type == "Complaints Over Time":
|
|
| 420 |
st.pyplot(fig)
|
| 421 |
|
| 422 |
st.write("""
|
| 423 |
-
**
|
| 424 |
-
It shows the number of complaints reported
|
| 425 |
-
|
| 426 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
|
| 428 |
elif viz_type == "Complaints by Housing Block and Type":
|
| 429 |
st.subheader("Complaints by Housing Block and Type")
|
|
@@ -455,14 +399,14 @@ elif viz_type == "Complaints by Housing Block and Type":
|
|
| 455 |
|
| 456 |
st.write("""
|
| 457 |
**What this visualization shows:**
|
| 458 |
-
This
|
| 459 |
|
| 460 |
**Why it's interesting:**
|
| 461 |
-
By
|
| 462 |
|
| 463 |
-
**
|
| 464 |
-
|
| 465 |
-
|
| 466 |
|
| 467 |
# Footer
|
| 468 |
st.markdown("---")
|
|
|
|
| 10 |
from streamlit_folium import st_folium
|
| 11 |
import plotly.express as px
|
| 12 |
|
| 13 |
+
# Setting page config
|
| 14 |
st.set_page_config(page_title="Nuisance Complaints Dashboard", layout="wide")
|
| 15 |
|
| 16 |
# Title and introduction
|
|
|
|
| 26 |
This dashboard analyzes nuisance complaints data from the City of Urbana. The visualizations aim to explore complaint trends, resolution efficiency, and geographic patterns to provide actionable insights for urban planning and management.
|
| 27 |
""")
|
| 28 |
|
| 29 |
+
# Loading and cleaning data
|
| 30 |
@st.cache_data
|
| 31 |
def load_and_clean_data():
|
| 32 |
data = pd.read_csv('Nuisance_Complaints_20241130.csv')
|
|
|
|
| 35 |
data['Date Notice Mailed or Given'] = pd.to_datetime(data['Date Notice Mailed or Given'])
|
| 36 |
data['File Close Date'] = pd.to_datetime(data['File Close Date'], errors='coerce')
|
| 37 |
|
| 38 |
+
# Filling missing dates
|
| 39 |
median_delay = (data['Date Notice Mailed or Given'] - data['Date Reported']).dt.days.median()
|
| 40 |
data.loc[data['Date Notice Mailed or Given'].isna(), 'Date Notice Mailed or Given'] = \
|
| 41 |
data.loc[data['Date Notice Mailed or Given'].isna(), 'Date Reported'] + pd.Timedelta(days=median_delay)
|
|
|
|
| 64 |
data['Month Reported'] = data['Date Reported'].dt.month
|
| 65 |
return data
|
| 66 |
|
| 67 |
+
# Loading the data
|
| 68 |
data = load_and_clean_data()
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
# Sidebar for controls
|
| 72 |
st.sidebar.header("Dashboard Controls")
|
| 73 |
|
| 74 |
+
# Defining the visualization type (viz_type) selection
|
| 75 |
viz_type = st.sidebar.selectbox("Select Visualization", [
|
| 76 |
"Complaint Types", "Geographic Distribution", "Complaints by Disposition", "Submission Methods",
|
| 77 |
"Monthly Trends by Complaint Type", "Complaints Over Time", "Complaints by Housing Block and Type"
|
| 78 |
])
|
| 79 |
|
| 80 |
+
# Removing the year selection when certain visualizations are selected
|
| 81 |
if viz_type not in ["Complaints Over Time", "Complaints by Housing Block and Type"]:
|
| 82 |
year_options = ['All Time'] + sorted(data['Year Reported'].unique().tolist())
|
| 83 |
selected_year = st.sidebar.selectbox("Select Year", options=year_options, key="year_select")
|
|
|
|
| 94 |
block_options = ['All Blocks'] + sorted(data['Housing Block'].unique().tolist())
|
| 95 |
selected_block = st.sidebar.selectbox("Select Housing Block", options=block_options, key="block_select")
|
| 96 |
|
| 97 |
+
# Ensuring selected_block is only used if defined
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
if viz_type == "Complaints by Housing Block and Type" and 'selected_block' not in locals():
|
| 99 |
selected_block = 'All Blocks' # Default to 'All Blocks' if no selection made
|
| 100 |
|
| 101 |
+
# Filtering data based on selected year
|
| 102 |
if selected_year != 'All Time':
|
| 103 |
filtered_data = data[data['Year Reported'] == selected_year]
|
| 104 |
else:
|
| 105 |
filtered_data = data
|
| 106 |
|
| 107 |
+
# Further filtering by Housing Block
|
| 108 |
if 'selected_block' in locals() and selected_block != 'All Blocks':
|
| 109 |
filtered_data = filtered_data[filtered_data['Housing Block'] == selected_block]
|
| 110 |
|
| 111 |
+
# Filtering data based on date range (only for Complaints Over Time visualization)
|
| 112 |
if viz_type == "Complaints Over Time":
|
| 113 |
filtered_data_time = filtered_data[
|
| 114 |
(filtered_data['Date Reported'] >= pd.to_datetime(start_date)) &
|
|
|
|
| 117 |
else:
|
| 118 |
filtered_data_time = filtered_data
|
| 119 |
|
| 120 |
+
# Filtering data based on selected housing block (only for Complaints by Housing Block visualization)
|
| 121 |
if viz_type == "Complaints by Housing Block and Type" and selected_block != 'All Blocks':
|
| 122 |
filtered_data_time = filtered_data_time[filtered_data_time['Housing Block'] == selected_block]
|
| 123 |
|
|
|
|
| 126 |
|
| 127 |
|
| 128 |
|
| 129 |
+
# Displaying metrics
|
| 130 |
col1, col2 = st.columns(2)
|
| 131 |
with col1:
|
| 132 |
st.metric("Total Complaints", len(filtered_data))
|
|
|
|
| 276 |
# **Color Scheme:**
|
| 277 |
# The 'viridis' color palette highlights differences in complaint resolution status, with lighter shades indicating a higher frequency of resolved complaints.
|
| 278 |
# """)
|
| 279 |
+
# We collectively decided to opt for more engaging and interactive charts instead of the above chart.
|
| 280 |
|
| 281 |
elif viz_type == "Complaints by Disposition":
|
| 282 |
st.subheader("Complaints by Disposition")
|
|
|
|
| 307 |
""")
|
| 308 |
|
| 309 |
|
|
|
|
| 310 |
elif viz_type == "Monthly Trends by Complaint Type":
|
| 311 |
st.subheader("Monthly Trends Grouped by Complaint Types")
|
| 312 |
monthly_trends = filtered_data.groupby(['Month Reported', 'Type of Complaint']).size().reset_index(name='Count')
|
|
|
|
| 322 |
This line chart visualizes the monthly trends in complaint counts, grouped by complaint type. It allows tracking changes in complaint frequencies over time and identifying patterns or spikes in specific categories.
|
| 323 |
|
| 324 |
**Why it's interesting:**
|
| 325 |
+
By visualizing these trends, we can identify whether certain complaint types are seasonal or are influenced by specific events. This information helps prioritize resources and refine strategies for complaint management.
|
| 326 |
|
| 327 |
**Color Scheme:**
|
| 328 |
Different complaint types are represented by distinct colors, enabling easy comparison of trends across categories.
|
|
|
|
| 346 |
# **Color Scheme:**
|
| 347 |
# The 'inferno' palette is used to emphasize the frequency of each complaint type, with darker shades representing higher frequencies.
|
| 348 |
# """)
|
| 349 |
+
# We collectively decided to opt for more engaging and interactive charts instead of the above chart.
|
| 350 |
+
|
| 351 |
|
| 352 |
elif viz_type == "Complaints Over Time":
|
| 353 |
st.subheader("Complaints Over Time")
|
|
|
|
| 359 |
st.pyplot(fig)
|
| 360 |
|
| 361 |
st.write("""
|
| 362 |
+
**What this visualization shows:**
|
| 363 |
+
This visualization displays the trend of complaints over time using a line chart. It shows the number of complaints reported , making it easy to spot peaks or declines in complaints.
|
| 364 |
+
|
| 365 |
+
**Why it's interesting:**
|
| 366 |
+
By examining the complaints over time, users can identify patterns, such as specific days or periods with higher or lower complaint volumes. This can inform decision-making and help allocate resources more effectively during high-complaint periods.
|
| 367 |
+
|
| 368 |
+
**Color Scheme:**
|
| 369 |
+
The use of a purple color scheme highlights the flow and continuity of the data, providing a clear view of the patterns over time.
|
| 370 |
+
""")
|
| 371 |
|
| 372 |
elif viz_type == "Complaints by Housing Block and Type":
|
| 373 |
st.subheader("Complaints by Housing Block and Type")
|
|
|
|
| 399 |
|
| 400 |
st.write("""
|
| 401 |
**What this visualization shows:**
|
| 402 |
+
This bar chart displays the distribution of complaints by Housing Block and Complaint Type. The data is stacked to show the total number of complaints per block, categorized by type. This allows for a quick comparison of the most common complaint types across different housing blocks.
|
| 403 |
|
| 404 |
**Why it's interesting:**
|
| 405 |
+
By analyzing the distribution of complaints by both block and type, organizations can identify specific areas where certain complaint types are more prevalent. This insight helps target interventions and allocate resources more efficiently based on the most common issues in different housing blocks.
|
| 406 |
|
| 407 |
+
**Color Scheme:**
|
| 408 |
+
The 'inferno' color palette is used to represent different complaint types, with darker shades indicating a higher frequency of complaints. The stacked bar chart makes it easy to compare the distribution of complaints by block and type.
|
| 409 |
+
""")
|
| 410 |
|
| 411 |
# Footer
|
| 412 |
st.markdown("---")
|