Download src/streamlit_app.py from vgosavi2/FinalV1: direct link, hf CLI and curl.
- Browser
- Download file 6.66 kB
-
https://huggingface.co/spaces/vgosavi2/FinalV1/resolve/main/src/streamlit_app.py
- Command line
-
hf download hf://spaces/vgosavi2/FinalV1/src/streamlit_app.py
-
curl -L -o streamlit_app.py https://huggingface.co/spaces/vgosavi2/FinalV1/resolve/main/src/streamlit_app.py
6.66 kB
| 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; | |
| color: #2c3e50; | |
| font-family: 'Source Sans Pro', sans-serif; | |
| } | |
| /* Paragraph/write-up styling */ | |
| .description { | |
| font-size: 18px; /* comfortable reading size */ | |
| line-height: 1.6; /* good spacing */ | |
| color: #4b4b4b; /* dark grey text */ | |
| text-align: justify; /* nice full-justified look */ | |
| padding: 0 10px 20px; /* side & bottom padding */ | |
| font-family: 'Helvetica Neue', Arial, sans-serif; | |
| } | |
| .sectionheader { | |
| font-family: 'Source Sans Pro', sans-serif; | |
| font-size: 32px; | |
| color: #2c3e50; | |
| margin-top: 15px; | |
| margin-bottom: 10px; | |
| border-bottom: 3px solid #ccc; | |
| padding-bottom: 8px; | |
| } | |
| </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("""<div class='description'> 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.</div>""",unsafe_allow_html=True) | |
| # 2. Data info & load | |
| st.markdown("<div class='sectionheader'> Dataset Information </div>", unsafe_allow_html=True) | |
| st.markdown( | |
| """ | |
| <div class="description"> | |
| <ul> | |
| <li><strong>Source:</strong> LAPD crime incidents dataset</li> | |
| <li><strong>Rows:</strong> one incident per row</li> | |
| <li><strong>Columns:</strong> e.g. <code>crm_cd_desc</code> (crime type), <code>arrest</code> (boolean), <code>year</code>, <code>location_description</code>, etc.</li> | |
| <li><strong>Purpose:</strong> Interactive exploration of top crime categories and arrest rates.</li> | |
| </ul> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| # 1. Resolve the path to the CSV next to this script | |
| DATA_PATH = Path(__file__).parent / "crime_data.csv" # /app/src/crime_data.csv | |
| REGION_DATA_PATH = Path(__file__).parent / "area_lookup.csv" # /app/src/crime_data.csv | |
| def load_data(): | |
| return pd.read_csv(DATA_PATH) | |
| def region_load_data(): | |
| return pd.read_csv(REGION_DATA_PATH) | |
| if st.button("🔄 Refresh Data"): | |
| 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() | |
| lookup = region_load_data() | |
| map_region = dict(zip(lookup["OBJECTID"], lookup["APREC"])) | |
| map_precinct = dict(zip(lookup["OBJECTID"], lookup["PREC"])) | |
| # Map into new columns | |
| df["RegionName"] = df["area"].map(map_region) | |
| df["PrecinctCode"] = df["area"].map(map_precinct) | |
| # Inspect | |
| print(df[["area", "RegionName", "PrecinctCode"]].head()) | |
| if df.empty: | |
| st.stop() | |
| # 3. Data preview | |
| st.markdown("<div class='sectionheader'> Data Preview </div>", unsafe_allow_html=True) | |
| st.markdown( | |
| f"<div class='description'>" | |
| f"Total records: <strong>{df.shape[0]:,}</strong> | " | |
| f"Total columns: <strong>{df.shape[1]:,}</strong>" | |
| f"</div>", | |
| unsafe_allow_html=True | |
| ) | |
| st.dataframe(df.head()) | |
| # Pie Chart 1: Top 10 Crime Types | |
| st.markdown("<div class='sectionheader'> Top 10 Crime Types by Year </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) | |
| selected_year = st.selectbox("Select Year", options, index=0) | |
| # 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.02] * len(top_crimes), | |
| marker=dict(line=dict(color="white", width=1)) | |
| ) | |
| fig.update_layout( | |
| legend_title_text="Crime Type", | |
| margin=dict(t=40, b=40, l=20, r=20), | |
| height=600, | |
| width=450, | |
| title_x=0.5 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("""<div class="description"> 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.</div>""",unsafe_allow_html=True) | |