vgosavi2 commited on
Commit
a290e78
·
verified ·
1 Parent(s): 0ee9bae

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +130 -34
src/streamlit_app.py CHANGED
@@ -1,40 +1,136 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  """
7
- # Welcome to Streamlit!
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import altair as alt
4
+ from pathlib import Path
5
+ import plotly.express as px
6
 
7
+ # ── 0. Page configuration ──
8
+ st.set_page_config(
9
+ page_title="Analyze Crime Distributions",
10
+ page_icon="📊",
11
+ layout="wide"
12
+ )
13
+ st.markdown("""
14
+ <style>
15
+ .title {
16
+ text-align: center;
17
+ padding: 25px;
18
+ }
19
+ </style>
20
+ """, unsafe_allow_html=True)
21
+
22
+ st.markdown("<div class='title'><h1> LAPD Crime Insights Dashboard </h1></div>", unsafe_allow_html=True)
23
+
24
+ # 1. Page title
25
+ st.markdown(""" This application provides a suite of interactive visualizations—pie charts, bar charts, scatter plots, and more—that let you explore crime patterns in the LAPD dataset from multiple angles. Quickly see which offense categories dominate, compare arrest rates against non-arrests, track how crime volumes change over time, and examine geographic hotspots. These insights can help police departments, community organizations, and policymakers allocate resources more effectively and design targeted strategies to improve public safety.""")
26
+
27
+ # 2. Data info & load
28
+ st.header("Dataset Information")
29
+ st.markdown(
30
+ """
31
+ - **Source:** LAPD crime incidents dataset
32
+ - **Rows:** incidents (one per row)
33
+ - **Columns:** e.g. `crm_cd_desc` (crime type), `arrest` (boolean), `date`, `location_description`, etc.
34
+ - **Purpose:** Interactive exploration of top crime categories and arrest rates.
35
  """
36
+ )
37
 
38
+ # 1. Resolve the path to the CSV next to this script
39
+ DATA_PATH = Path(__file__).parent / "crime_data.csv" # /app/src/crime_data.csv
 
40
 
41
+ @st.cache_data
42
+ def load_data():
43
+ return pd.read_csv(DATA_PATH)
44
+
45
+ if st.button("🔄"):
46
+ st.cache_data.clear() # Clear the cache
47
+ st.toast("Data is refreshed",icon="✅") # Reload the data
48
+
49
+ # 2. Load and early‐exit if missing
50
+ df = load_data()
51
+ if df.empty:
52
+ st.stop()
53
+
54
+ # 3. Data preview
55
+ st.header("Data Preview")
56
+ st.write(f"Total records: {df.shape[0]} | Total columns: {df.shape[1]}")
57
+ st.dataframe(df.head())
58
+
59
+ # Pie Chart 1: Top 10 Crime Types
60
+ st.markdown("<div class='title'><h1> Top 10 Crime Type </h1></div>", unsafe_allow_html=True)
61
+
62
+ years = sorted(df["year"].dropna().astype(int).unique())
63
+ # Prepend an “All” option
64
+ options = ["All"] + years
65
+
66
+ # Year filter (shorter, above chart)
67
+ col_empty, col_filter = st.columns([3,1])
68
+ with col_filter:
69
+ selected_year = st.selectbox(
70
+ "Select Year",
71
+ options=options,
72
+ index=0, # default to “All”
73
+ key="year_filter"
74
+ )
75
+
76
+ # Filter according to selection
77
+ if selected_year == "All":
78
+ filtered = df.copy()
79
+ else:
80
+ filtered = df[df["year"] == selected_year]
81
+
82
+ # Compute top 10 crime types for that year ──
83
+ top_crimes = (
84
+ filtered["crm_cd_desc"]
85
+ .value_counts()
86
+ .nlargest(10)
87
+ .rename_axis("Crime Type")
88
+ .reset_index(name="Count")
89
+ )
90
+ top_crimes["Percentage"] = top_crimes["Count"] / top_crimes["Count"].sum()
91
+
92
+ #Key Metrics
93
+ st.markdown("### Key Metrics", unsafe_allow_html=True)
94
+ col1, col2, col3 = st.columns(3)
95
+ col1.metric(
96
+ label="Total Incidents",
97
+ value=f"{len(filtered):,}"
98
+ )
99
+ col2.metric(
100
+ label="Unique Crime Types",
101
+ value=f"{filtered['crm_cd_desc'].nunique():,}"
102
+ )
103
+ # compute share of the top crime
104
+ top_share = top_crimes.iloc[0]["Percentage"]
105
+ col3.metric(
106
+ label=f"Share of Top Crime ({top_crimes.iloc[0]['Crime Type']})",
107
+ value=f"{top_share:.1%}"
108
+ )
109
+
110
+ # Plotly donut chart ──
111
+ fig = px.pie(
112
+ top_crimes,
113
+ names="Crime Type",
114
+ values="Count",
115
+ hole=0.4,
116
+ color_discrete_sequence=px.colors.sequential.Agsunset,
117
+ title=" "
118
+ )
119
+
120
+ fig.update_traces(
121
+ textposition="outside",
122
+ textinfo="label+percent",
123
+ pull=[0.05] * len(top_crimes),
124
+ marker=dict(line=dict(color="white", width=2))
125
+ )
126
+
127
+ fig.update_layout(
128
+ legend_title_text="Crime Type",
129
+ margin=dict(t=60, b=20, l=20, r=20),
130
+ height=700,
131
+ title_x=0.5
132
+ )
133
+
134
+ st.plotly_chart(fig, use_container_width=True)
135
+ st.markdown(""" The donut chart shows the share of the ten most frequent crime categories in the selected year. At the center, you can see that Vehicle ­– Stolen is the single largest slice, accounting for roughly 18.7% of all incidents, The remaining five categories each represent between 3%–5% of total incidents—these include miscellaneous crimes, criminal threats, assault with a deadly weapon, burglary, and minor vandalism. By displaying both slice size and percentage labels, the chart makes it easy to compare how dominant property‐related offenses are, versus violent or lesser‐common crimes, in that year’s LAPD data. """)
136