Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jcunni/fin trend chart #11

Merged
merged 5 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.env
.idea
.idea
.streamlit
3 changes: 3 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[theme]
base="light"
primaryColor="#66b1e2"
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ Once you are within you desired working python env (or conda, venv), run the fol
```pip install -r requirements.txt```

## Configuration
Currently, this app uses a `.env` file to store secrets and sensitive information needed for runtime. Create a `.env`
Currently, this app uses a `secrets.toml` file in the `.streamlit/` folder to store secrets and sensitive information needed for runtime. Create a `.env`
file in the repo root folder and add the following key/value pairs to the file.
```
SNOWFLAKE_USER = <username goes here>
SNOWFLAKE_PASSWORD = <password goes here>
SNOWFLAKE_ACCOUNT = <snowflake account url>
SNOWFLAKE_WH = <name of desired warehouse to use>
SNOWFLAKE_ROLE = <name of desired role to use>
SNOWFLAKE_USER = "<username goes here>"
SNOWFLAKE_PASSWORD = "<password goes here>"
SNOWFLAKE_ACCOUNT = "<snowflake account url>"
SNOWFLAKE_WH = "<name of desired warehouse to use>"
SNOWFLAKE_ROLE = "<name of desired role to use>"
```

## App Start Up
Expand Down
31 changes: 12 additions & 19 deletions components.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import util


def financial_bans(summary_stats_data):
def financial_bans(summary_stats_data, direction="horizontal"):
# TODO: Add Year filter
"""Takes dataframe of financial summary data at the year level and displays BANs
for med spend, pharm spend, member months and average pmpm. Can handle dataframe with
Expand All @@ -16,29 +16,22 @@ def financial_bans(summary_stats_data):
summary_stats_data = summary_stats_data.loc[
summary_stats_data["year"].isin(year_values)
]
if len(year_values) == 1:
year_string = year_values[0]
else:
year_string = "{} - {}".format(year_values[0], year_values[-1])
st.markdown(
f"""
These financial summary charts offer a concise and comprehensive snapshot of your organization's financial
performance, providing key metrics and insights at a glance.

The top three metrics you need to know about your data at all times are medical paid amount, pharmacy
paid amount and pmpm."""
)
st.markdown(f"### Spend Summary in {year_string}")

med_spend = summary_stats_data["medical_paid_amount"].sum()
pharm_spend = summary_stats_data["pharmacy_paid_amount"].sum()
member_mon_count = summary_stats_data["member_month_count"].sum()
avg_pmpm = med_spend / member_mon_count
col1, col2, col3, col4 = st.columns(4)
col1.metric("Medical Spend", util.human_format(med_spend))
col2.metric("Pharmacy Spend", util.human_format(pharm_spend))
col3.metric("Member Months", util.human_format(member_mon_count))
col4.metric("Average PMPM", util.human_format(avg_pmpm))
if direction == "vertical":
st.metric("Medical Spend", util.human_format(med_spend))
st.metric("Pharmacy Spend", util.human_format(pharm_spend))
st.metric("Member Months", util.human_format(member_mon_count))
st.metric("Average PMPM", util.human_format(avg_pmpm))
else:
col1, col2, col3, col4 = st.columns(4)
col1.metric("Medical Spend", util.human_format(med_spend))
col2.metric("Pharmacy Spend", util.human_format(pharm_spend))
col3.metric("Member Months", util.human_format(member_mon_count))
col4.metric("Average PMPM", util.human_format(avg_pmpm))


def year_slider(year_values):
Expand Down
140 changes: 104 additions & 36 deletions pages/02_financial_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import plost
import util
import components as comp
from streamlit_echarts import st_echarts
import time

conn = util.connection(database="dev_lipsa")

Expand Down Expand Up @@ -156,28 +158,79 @@ def pmpm_by_service_category_1_2():
return data


def claim_type_line_chart(df, animated=True):
if animated:
t = st.session_state["iteration"]
month_list = sorted(list(set(pmpm_claim_type_data["year_month"])))
anim_data = df.loc[df["year_month"] <= month_list[t], :]
list_data = [anim_data.columns.to_list()] + anim_data.values.tolist()
else:
list_data = [df.columns.to_list()] + df.values.tolist()
series = list(set(df["claim_type"]))
datasetWithFilters = [
{
"id": f"dataset_{s}",
"fromDatasetId": "dataset_raw",
"transform": {
"type": "filter",
"config": {
"and": [
{"dimension": "claim_type", "=": s},
]
},
},
}
for s in series
]
seriesList = [
{
"type": "line",
"datasetId": f"dataset_{s}",
"showSymbol": False,
"name": s,
"labelLayout": {"moveOverlap": "shiftY"},
"emphasis": {"focus": "series"},
"encode": {
"x": "year_month",
"y": "paid_amount_pmpm",
"label": ["claim_type", "paid_amount_pmpm"],
"itemName": "year_month",
"tooltip": ["paid_amount_pmpm"],
},
}
for s in series
]
option = {
"color": ["#06405C", "#FFCC05", "#66B1E2"],
"dataset": [{"id": "dataset_raw", "source": list_data}] + datasetWithFilters,
"title": {"text": "Paid Amount PMPM by Claim Type"},
"tooltip": {"order": "valueDesc", "trigger": "axis"},
"xAxis": {"type": "category", "nameLocation": "middle"},
"yAxis": {"name": "PMPM"},
"grid": {"right": 140},
"series": seriesList,
}
st_echarts(options=option, height="400px", key="chart")


year_month_values = sorted(list(set(year_months()["year_month"])))
year_values = sorted(list(set([x[:4] for x in year_month_values])))

## --------------------------------- ##
## Header
## --- --- ##
## --------------------------------- ##
st.markdown("# Financial Overview")

summary_stats_data = summary_stats()
summary_stats_data = summary_stats_data.loc[
summary_stats_data["year"] == year_values[-1]
]

comp.financial_bans(summary_stats_data)

st.divider()
pmpm_claim_type_data = pmpm_by_claim_type()
pmpm_claim_type_data.sort_values(by="year_month", inplace=True)
st.markdown("## Claim Type")
st.markdown(
"""
Use the following time slider to cut the following charts by the year range of your interest.
Explore the per member per month costs across different claim types to gain insights into healthcare expenditure patterns. Inpatient spend will tend to be much higher than professional spend. Dig deeper to find out what is hidden in these costs.
"""
)

## --------------------------------- ##
## Header
## --------------------------------- ##
st.markdown("# Financial Overview")
start_year, end_year = st.select_slider(
label="Select a range of years",
options=year_values,
Expand All @@ -187,36 +240,51 @@ def pmpm_by_service_category_1_2():
selected_range = year_values[
year_values.index(start_year) : year_values.index(end_year) + 1
]
if len(year_values) == 1:
year_string = year_values[0]
else:
year_string = "{} - {}".format(year_values[0], year_values[-1])
st.markdown(
f"""
These financial summary charts offer a concise and comprehensive snapshot of your organization's financial
performance, providing key metrics and insights at a glance.

## --------------------------------- ##
## --- --- ##
## --------------------------------- ##
pmpm_claim_type_data = pmpm_by_claim_type()
pmpm_claim_type_data = pmpm_claim_type_data.loc[
pmpm_claim_type_data["year_month"].str[:4].isin(selected_range)
]
pmpm_claim_type_data = (
pmpm_claim_type_data.groupby("claim_type", as_index=False)[
["paid_amount_sum", "member_month_count"]
]
.sum()
.assign(paid_amount_pmpm=lambda x: x["paid_amount_sum"] / x["member_month_count"])
The top three metrics you need to know about your data at all times are medical paid amount, pharmacy
paid amount and pmpm."""
)
st.markdown(f"### Spend Summary in {year_string}")
summary_stats_data = summary_stats()
summary_stats_data = summary_stats_data.loc[
summary_stats_data["year"] == year_values[-1]
]

st.markdown("## Claim Type")
if "iteration" not in st.session_state:
st.session_state["iteration"] = 0

col1, col2 = st.columns([1, 3])
with col1:
comp.financial_bans(summary_stats_data, direction="vertical")
with col2:
animate = False
month_list = sorted(list(set(pmpm_claim_type_data["year_month"])))
if animate:
while st.session_state["iteration"] < len(month_list):
claim_type_line_chart(pmpm_claim_type_data, True)
time.sleep(0.05)
st.session_state["iteration"] += 1

if st.session_state["iteration"] < len(month_list) and animate:
st.experimental_rerun()
else:
claim_type_line_chart(pmpm_claim_type_data, False)


st.divider()
st.markdown(
"""
Explore the per member per month costs across different claim types to gain insights into healthcare expenditure patterns. Inpatient spend will tend to be much higher than professional spend. Dig deeper to find out what is hidden in these costs.
Use the following time slider to cut the following charts by the year range of your interest.
"""
)
plost.bar_chart(
data=pmpm_claim_type_data,
bar="claim_type",
value="paid_amount_pmpm",
direction="horizontal",
legend="top",
height=200,
)


## --------------------------------- ##
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ streamlit
plost
snowflake-connector-python
boto3
python-dotenv
tomli
pre-commit
black
streamlit-echarts
16 changes: 8 additions & 8 deletions util.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import snowflake.connector as sn
import pandas as pd
from dotenv import load_dotenv
import os
import tomli

load_dotenv()
with open(".streamlit/secrets.toml", mode="rb") as fp:
config = tomli.load(fp)


def connection(database=None):
return sn.connect(
user=os.getenv("SNOWFLAKE_USER"),
password=os.getenv("SNOWFLAKE_PASSWORD"),
account=os.getenv("SNOWFLAKE_ACCOUNT"),
warehouse=os.getenv("SNOWFLAKE_WH"),
role=os.getenv("SNOWFLAKE_ROLE"),
user=config["SNOWFLAKE_USER"],
password=config["SNOWFLAKE_PASSWORD"],
account=config["SNOWFLAKE_ACCOUNT"],
warehouse=config["SNOWFLAKE_WH"],
role=config["SNOWFLAKE_ROLE"],
database=database or "tuva_project_demo",
)

Expand Down