File size: 6,296 Bytes
cf54ce1
e985fcd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

import streamlit as st
import pandas as pd
import altair as alt
from vega_datasets import data
import us




# -------------------------------------------------
# PAGE CONFIG + TITLE
# -------------------------------------------------
st.set_page_config(page_title="University Donor Dashboard", layout="wide")
st.title("University Donor Dashboard")
alt.data_transformers.enable("default", max_rows=None)

# -------------------------------------------------
# DATA
# -------------------------------------------------
@st.cache_data
def load_data():
    df = pd.read_csv("university-donations.csv")
    df["Gift Date"] = pd.to_datetime(
        df["Gift Date"],
        format="%m/%d/%y",
        errors="coerce"
    )
    df["Year"]      = df["Gift Date"].dt.year.astype("Int64")
    df["YearMonth"] = df["Gift Date"].dt.to_period("M").astype(str)

    # 2-letter state → FIPS
    state_id = {s.abbr: int(s.fips) for s in us.states.STATES}
    df["state_fips"] = df["State"].map(state_id)
    return df

df = load_data()
st.write("Loaded rows:", len(df))   # quick sanity check

# -------------------------------------------------
# SIDEBAR FILTERS
# -------------------------------------------------
st.sidebar.header("Filters")
col_opts = ["All"] + sorted(df["College"].dropna().unique())
mot_opts = ["All"] + sorted(df["Gift Allocation"].dropna().unique())

col_pick = st.sidebar.selectbox("College", col_opts, index=0)
mot_pick = st.sidebar.selectbox("Motivation (Gift Allocation)", mot_opts, index=0)

st.sidebar.write(f"Selected College: {col_pick}")
st.sidebar.write(f"Selected Motivation: {mot_pick}")

mask = pd.Series(True, index=df.index)
if col_pick != "All":
    mask &= df["College"] == col_pick
if mot_pick != "All":
    mask &= df["Gift Allocation"] == mot_pick
df_filt = df[mask]

# Check if the filtered dataframe is empty
if df_filt.empty:
    st.warning("No data matches the selected filters.")
else:
    # -------------------------------------------------
    # SELECTIONS (empty="all" to show all data initially)
    # -------------------------------------------------
    state_select = alt.selection_point(fields=["state_fips"], toggle=False, empty="all")
    brush = alt.selection_interval(encodings=["x"], empty="all")
    subcategory_select = alt.selection_point(
        fields=["Allocation Subcategory"],
        toggle="event.shiftKey",
        empty="all"
    )

    # -------------------------------------------------
    # CHOROPLETH MAP
    # -------------------------------------------------

    state_totals = (
        df_filt.groupby("state_fips", as_index=False)
               .agg(Gift_Amount_sum=("Gift Amount", "sum"),
                    Gift_Count=("Gift Amount", "count"))
    )


    states = alt.topo_feature(data.us_10m.url, "states")
    map_chart = (
        alt.Chart(states)
        .mark_geoshape(stroke="white", strokeWidth=0.5)
        .encode(
            color=alt.condition(
                state_select,
                alt.Color("Gift_Amount_sum:Q",
                          scale=alt.Scale(scheme="blues"),
                          title="Total Gifts ($)"),
                alt.value("lightgray")
            ),
            tooltip=[
                alt.Tooltip("Gift_Amount_sum:Q", title="Total Gifts ($)", format=",.0f"),
                alt.Tooltip("Gift_Count:Q",       title="# Gifts")
            ]
        )
        .transform_lookup(
            lookup="id",
            from_=alt.LookupData(state_totals,
                                 key="state_fips",
                                 fields=["Gift_Amount_sum", "Gift_Count"])
        )
        .add_params(state_select)
        .project(type="albersUsa")
        .properties(width=380, height=250)
    )

    # -------------------------------------------------
    # LINE – GIFTS BY YEAR
    # -------------------------------------------------
    line_chart = (
        alt.Chart(df_filt)
        .transform_filter(state_select)
        .mark_line(point=True)
        .encode(
            x=alt.X("Year:O", sort="ascending"),
            y=alt.Y("sum(Gift Amount):Q", title="Total Gifts ($)"),
            tooltip=[
                alt.Tooltip("Year:O", title="Year"),
                alt.Tooltip("sum(Gift Amount):Q", title="Total Gifts ($)", format=",.0f")
            ]
        )
        .add_params(state_select, brush)
        .properties(width=380, height=250)
    )

    # -------------------------------------------------
    # BAR – TOTAL BY COLLEGE
    # -------------------------------------------------
    bar_college = (
        alt.Chart(df_filt)
        .transform_filter(state_select)
        .mark_bar()
        .encode(
            y=alt.Y("College:N", sort="-x", title="College"),
            x=alt.X("sum(Gift Amount):Q", title="Total Gifts ($)"),
            tooltip=[
                alt.Tooltip("College:N", title="College"),
                alt.Tooltip("sum(Gift Amount):Q", title="Total Gifts ($)", format=",.0f")
            ]
        )
        .add_params(state_select)
        .properties(width=380, height=400)
    )

    # -------------------------------------------------
    # BAR – TOTAL BY SUB-CATEGORY
    # -------------------------------------------------
    bar_sub = (
        alt.Chart(df_filt)
        .transform_filter(state_select)
        .transform_filter(brush)
        .mark_bar()
        .encode(
            y=alt.Y("Allocation Subcategory:N", sort="-x", title="Allocation Sub-category"),
            x=alt.X("sum(Gift Amount):Q", title="Total Gifts ($)"),
            color=alt.condition(subcategory_select, alt.value("#1f77b4"), alt.value("lightgray")),
            tooltip=[
                alt.Tooltip("Allocation Subcategory:N", title="Sub-category"),
                alt.Tooltip("sum(Gift Amount):Q", title="Total Gifts ($)", format=",.0f")
            ]
        )
        .add_params(state_select, brush, subcategory_select)
        .properties(width=380, height=400)
    )

    # -------------------------------------------------
    # LAYOUT
    # -------------------------------------------------
    upper = alt.hconcat(map_chart, line_chart).resolve_scale(color="independent")
    lower = alt.hconcat(bar_college, bar_sub)
    st.altair_chart(alt.vconcat(upper, lower), use_container_width=True)