File size: 4,631 Bytes
6cfa539
54fa991
 
ac01220
acb177a
54fa991
167e956
 
 
 
 
 
840e1cf
 
 
 
 
 
 
 
 
188b003
167e956
54fa991
761a702
167e956
54fa991
 
 
 
afef622
54fa991
 
167e956
6cfa539
54fa991
a174729
4f2b40c
 
6cfa539
54fa991
4f2b40c
 
ea6bf86
 
 
3d0b2c0
ea6bf86
4f2b40c
54fa991
47ccdf0
4f2b40c
47ccdf0
54fa991
 
 
 
6cfa539
acb177a
3d6e51e
784e8fe
92c4e53
784e8fe
 
c55a4cb
acb177a
36629c4
 
683f503
 
784e8fe
 
36629c4
683f503
 
784e8fe
 
 
 
76e95ba
683f503
acb177a
9f7e69b
9dffac3
92c4e53
 
 
 
4f7fa58
9dffac3
4f7fa58
3d6e51e
a20d9a1
3d6e51e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
acb177a
e6da3fa
9f7e69b
9dffac3
 
c55a4cb
24ecf2d
 
9f7e69b
 
 
9dffac3
 
 
 
4f7fa58
 
9f7e69b
9dffac3
c55a4cb
f3db840
9dffac3
3397ec7
167e956
4d0e424
f3db840
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import streamlit as st
import pandas as pd
import altair as alt
from pathlib import Path
import plotly.express as px

# ── 0. Page configuration ──
st.set_page_config(
    page_title="Analyze Crime Distributions", 
    page_icon="📊", 
    layout="wide"
)
st.markdown("""
        <style>
        .title {
            text-align: center;
            padding: 25px;
        }
        </style>
        """, unsafe_allow_html=True)
    
st.markdown("<div class='title'><h1> LAPD Crime Insights Dashboard </h1></div>", unsafe_allow_html=True)

# 1. Page title
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.""")
            
# 2. Data info & load
st.header("Dataset Information")
st.markdown(
    """
- **Source:** LAPD crime incidents dataset  
- **Rows:** incidents (one per row)  
- **Columns:** e.g. `crm_cd_desc` (crime type), `arrest` (boolean), `date`, `location_description`, etc.  
- **Purpose:** Interactive exploration of top crime categories and arrest rates.
"""
)

# 1. Resolve the path to the CSV next to this script
DATA_PATH = Path(__file__).parent / "crime_data.csv"        # /app/src/crime_data.csv

@st.cache_data
def load_data():
    return pd.read_csv(DATA_PATH)
    
if st.button("🔄"):
    st.cache_data.clear()  # Clear the cache
    st.toast("Data is refreshed",icon="✅")  # Reload the data
    
# 2. Load and early‐exit if missing
df = load_data()
if df.empty:
    st.stop()
    
# 3. Data preview
st.header("Data Preview")
st.write(f"Total records: {df.shape[0]}  |  Total columns: {df.shape[1]}")
st.dataframe(df.head())

# Pie Chart 1: Top 10 Crime Types
st.markdown("<div class='title'><h1> Top 10 Crime Type </h1></div>", unsafe_allow_html=True)

years = sorted(df["year"].dropna().astype(int).unique())
# Prepend an “All” option
options = ["All"] + years

# Year filter (shorter, above chart)
col_empty, col_filter = st.columns([3,1])
with col_filter:
    selected_year = st.selectbox(
        "Select Year",
        options=options,
        index=0,  # default to “All”
        key="year_filter"
    )

# Filter according to selection
if selected_year == "All":
    filtered = df.copy()
else:
    filtered = df[df["year"] == selected_year]

# Compute top 10 crime types for that year ──
top_crimes = (
    filtered["crm_cd_desc"]
      .value_counts()
      .nlargest(10)
      .rename_axis("Crime Type")
      .reset_index(name="Count")
)
top_crimes["Percentage"] = top_crimes["Count"] / top_crimes["Count"].sum()

#Key Metrics
st.markdown("### Key Metrics", unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
col1.metric(
    label="Total Incidents",
    value=f"{len(filtered):,}"
)
col2.metric(
    label="Unique Crime Types",
    value=f"{filtered['crm_cd_desc'].nunique():,}"
)
# compute share of the top crime
top_share = top_crimes.iloc[0]["Percentage"]  
col3.metric(
    label=f"Share of Top Crime ({top_crimes.iloc[0]['Crime Type']})",
    value=f"{top_share:.1%}"
)

# Plotly donut chart ──
fig = px.pie(
    top_crimes,
    names="Crime Type",
    values="Count",
    hole=0.4,
    color_discrete_sequence=px.colors.sequential.Agsunset,
    title="  "
)

fig.update_traces(
    textposition="outside",
    textinfo="label+percent",
    pull=[0.05] * len(top_crimes),
    marker=dict(line=dict(color="white", width=2))
)

fig.update_layout(
    legend_title_text="Crime Type",
    margin=dict(t=60, b=20, l=20, r=20),
    height=700,
    title_x=0.5
)

st.plotly_chart(fig, use_container_width=True)
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. """)