Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- app.py +103 -0
- requirements.txt +4 -0
- screenshot.png +0 -0
app.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Data Source: https://public.tableau.com/app/profile/federal.trade.commission/viz/FraudandIDTheftMaps/AllReportsbyState
|
2 |
+
# US State Boundaries: https://public.opendatasoft.com/explore/dataset/us-state-boundaries/export/
|
3 |
+
|
4 |
+
import streamlit as st
|
5 |
+
import pandas as pd
|
6 |
+
import folium
|
7 |
+
from streamlit_folium import st_folium
|
8 |
+
|
9 |
+
APP_TITLE = 'Fraud and Identity Theft Report'
|
10 |
+
APP_SUB_TITLE = 'Source: Federal Trade Commission'
|
11 |
+
|
12 |
+
def display_time_filters(df):
|
13 |
+
year_list = list(df['Year'].unique())
|
14 |
+
year_list.sort()
|
15 |
+
year = st.sidebar.selectbox('Year', year_list, len(year_list)-1)
|
16 |
+
quarter = st.sidebar.radio('Quarter', [1, 2, 3, 4])
|
17 |
+
st.header(f'{year} Q{quarter}')
|
18 |
+
return year, quarter
|
19 |
+
|
20 |
+
def display_state_filter(df, state_name):
|
21 |
+
state_list = [''] + list(df['State Name'].unique())
|
22 |
+
state_list.sort()
|
23 |
+
state_index = state_list.index(state_name) if state_name and state_name in state_list else 0
|
24 |
+
return st.sidebar.selectbox('State', state_list, state_index)
|
25 |
+
|
26 |
+
def display_report_type_filter():
|
27 |
+
return st.sidebar.radio('Report Type', ['Fraud', 'Other'])
|
28 |
+
|
29 |
+
def display_map(df, year, quarter):
|
30 |
+
df = df[(df['Year'] == year) & (df['Quarter'] == quarter)]
|
31 |
+
|
32 |
+
map = folium.Map(location=[38, -96.5], zoom_start=4, scrollWheelZoom=False, tiles='CartoDB positron')
|
33 |
+
|
34 |
+
choropleth = folium.Choropleth(
|
35 |
+
geo_data='data/us-state-boundaries.geojson',
|
36 |
+
data=df,
|
37 |
+
columns=('State Name', 'State Total Reports Quarter'),
|
38 |
+
key_on='feature.properties.name',
|
39 |
+
line_opacity=0.8,
|
40 |
+
highlight=True
|
41 |
+
)
|
42 |
+
choropleth.geojson.add_to(map)
|
43 |
+
|
44 |
+
df_indexed = df.set_index('State Name')
|
45 |
+
for feature in choropleth.geojson.data['features']:
|
46 |
+
state_name = feature['properties']['name']
|
47 |
+
feature['properties']['population'] = 'Population: ' + '{:,}'.format(df_indexed.loc[state_name, 'State Pop'][0]) if state_name in list(df_indexed.index) else ''
|
48 |
+
feature['properties']['per_100k'] = 'Reports/100K Population: ' + str(round(df_indexed.loc[state_name, 'Reports per 100K-F&O together'][0])) if state_name in list(df_indexed.index) else ''
|
49 |
+
|
50 |
+
choropleth.geojson.add_child(
|
51 |
+
folium.features.GeoJsonTooltip(['name', 'population', 'per_100k'], labels=False)
|
52 |
+
)
|
53 |
+
|
54 |
+
st_map = st_folium(map, width=700, height=450)
|
55 |
+
|
56 |
+
state_name = ''
|
57 |
+
if st_map['last_active_drawing']:
|
58 |
+
state_name = st_map['last_active_drawing']['properties']['name']
|
59 |
+
return state_name
|
60 |
+
|
61 |
+
def display_fraud_facts(df, year, quarter, report_type, state_name, field, title, string_format='${:,}', is_median=False):
|
62 |
+
df = df[(df['Year'] == year) & (df['Quarter'] == quarter)]
|
63 |
+
df = df[df['Report Type'] == report_type]
|
64 |
+
if state_name:
|
65 |
+
df = df[df['State Name'] == state_name]
|
66 |
+
df.drop_duplicates(inplace=True)
|
67 |
+
if is_median:
|
68 |
+
total = df[field].sum() / len(df[field]) if len(df) else 0
|
69 |
+
else:
|
70 |
+
total = df[field].sum()
|
71 |
+
st.metric(title, string_format.format(round(total)))
|
72 |
+
|
73 |
+
def main():
|
74 |
+
st.set_page_config(APP_TITLE)
|
75 |
+
st.title(APP_TITLE)
|
76 |
+
st.caption(APP_SUB_TITLE)
|
77 |
+
|
78 |
+
#Load Data
|
79 |
+
df_continental = pd.read_csv('data/AxS-Continental_Full Data_data.csv')
|
80 |
+
df_fraud = pd.read_csv('data/AxS-Fraud Box_Full Data_data.csv')
|
81 |
+
df_median = pd.read_csv('data/AxS-Median Box_Full Data_data.csv')
|
82 |
+
df_loss = pd.read_csv('data/AxS-Losses Box_Full Data_data.csv')
|
83 |
+
|
84 |
+
#Display Filters and Map
|
85 |
+
year, quarter = display_time_filters(df_continental)
|
86 |
+
state_name = display_map(df_continental, year, quarter)
|
87 |
+
state_name = display_state_filter(df_continental, state_name)
|
88 |
+
report_type = display_report_type_filter()
|
89 |
+
|
90 |
+
#Display Metrics
|
91 |
+
st.subheader(f'{state_name} {report_type} Facts')
|
92 |
+
|
93 |
+
col1, col2, col3 = st.columns(3)
|
94 |
+
with col1:
|
95 |
+
display_fraud_facts(df_fraud, year, quarter, report_type, state_name, 'State Fraud/Other Count', f'# of {report_type} Reports', string_format='{:,}')
|
96 |
+
with col2:
|
97 |
+
display_fraud_facts(df_median, year, quarter, report_type, state_name, 'Overall Median Losses Qtr', 'Median $ Loss', is_median=True)
|
98 |
+
with col3:
|
99 |
+
display_fraud_facts(df_loss, year, quarter, report_type, state_name, 'Total Losses', 'Total $ Loss')
|
100 |
+
|
101 |
+
|
102 |
+
if __name__ == "__main__":
|
103 |
+
main()
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pandas==1.2.4
|
2 |
+
folium==0.12.1.post1
|
3 |
+
streamlit==1.10.0
|
4 |
+
streamlit_folium==0.6.13
|
screenshot.png
ADDED
![]() |