|
from collections import Counter |
|
from typing import List |
|
import numpy as np |
|
import streamlit as st |
|
import pandas as pd |
|
|
|
|
|
class Collapsable: |
|
""" |
|
Creates a collapsable text composed of a preamble (clickable section of text) |
|
and epilogue (collapsable text). |
|
""" |
|
|
|
def __init__(self, preamble="", epilogue=""): |
|
self.preamble = preamble |
|
self.epilogue = epilogue |
|
self.small_font = 18 |
|
self.large_font = 18 |
|
self.sections = [] |
|
|
|
def add_section(self, heading, text): |
|
|
|
if isinstance(text, list): |
|
text = ( |
|
"<ul>" |
|
+ "".join( |
|
[ |
|
f'<li style="font-size:{self.small_font}px;" align="justify">{x}</li>' |
|
for x in text |
|
] |
|
) |
|
+ "</ul>" |
|
) |
|
|
|
|
|
self.sections.append((heading, text)) |
|
|
|
def deploy(self): |
|
|
|
secs = "".join( |
|
[ |
|
( |
|
"<details>" |
|
f"<summary style='font-size:{self.large_font}px;'>{heading}</summary>" |
|
f"<blockquote style='font-size:{self.small_font}px;max-width: 80%;'" |
|
f"align='justify'>{text}</details>" |
|
) |
|
for heading, text in self.sections |
|
] |
|
) |
|
collapsable_sec = f""" |
|
<ol> |
|
{self.preamble} |
|
{secs} |
|
{self.epilogue} |
|
</ol> |
|
""" |
|
st.markdown(collapsable_sec, unsafe_allow_html=True) |
|
|
|
|
|
def add_filter( |
|
data_frame_list: List[pd.DataFrame], |
|
name: str, |
|
label: str, |
|
options: List[str] = None, |
|
num_cols: int = 1, |
|
last_is_others: bool = True, |
|
): |
|
""" |
|
Creates a filter on the side bar using checkboxes |
|
""" |
|
|
|
|
|
all_options = set(data_frame_list[-1][label]) |
|
if "-" in all_options: |
|
all_options.remove("-") |
|
if len(all_options) == 0: |
|
return data_frame_list |
|
|
|
st.markdown(f"#### {name}") |
|
|
|
|
|
if options is None: |
|
options_dict = Counter(data_frame_list[-1][label]) |
|
sorted_options = sorted(options_dict, key=options_dict.get, reverse=True) |
|
if "-" in sorted_options: |
|
sorted_options.remove("-") |
|
if len(sorted_options) > 8: |
|
options = list(sorted_options[:7]) + ["others"] |
|
last_is_others = True |
|
else: |
|
options = list(sorted_options) |
|
last_is_others = False |
|
|
|
cols = st.columns(num_cols) |
|
instantiated_checkbox = [] |
|
for idx in range(len(options)): |
|
with cols[idx % num_cols]: |
|
instantiated_checkbox.append( |
|
st.checkbox(options[idx], False, key=f"{label}_{options[idx]}") |
|
) |
|
|
|
selected_options = [ |
|
options[idx] for idx, checked in enumerate(instantiated_checkbox) if checked |
|
] |
|
|
|
|
|
if instantiated_checkbox[-1] and last_is_others: |
|
selected_options = selected_options[:-1] |
|
other_options = [x for x in all_options if x not in options] |
|
selected_options = set(selected_options + other_options) |
|
|
|
if len(selected_options) > 0: |
|
for idx, _ in enumerate(data_frame_list): |
|
data_frame_list[idx] = data_frame_list[idx][ |
|
[ |
|
any([x == model_entry for x in selected_options]) |
|
for model_entry in data_frame_list[idx][label] |
|
] |
|
] |
|
return data_frame_list |
|
|
|
|
|
def slider_filter( |
|
data_frame_list: List[pd.DataFrame], |
|
title: str, |
|
filter_by: str, |
|
max_val: int = 1000, |
|
): |
|
""" |
|
Creates slider to filter dataframes according to a given label. |
|
label must be numeric. Values are in millions. |
|
""" |
|
|
|
start_val, end_val = st.select_slider( |
|
title, |
|
options=[str(x) for x in np.arange(0, max_val + 1, 10, dtype=int)], |
|
value=("0", str(max_val)), |
|
) |
|
|
|
for idx in range(len(data_frame_list)): |
|
data_frame_list[idx] = data_frame_list[idx][ |
|
[ |
|
int(model_entry) >= int(start_val) * 1000000 |
|
and int(model_entry) <= int(end_val) * 1000000 |
|
for model_entry in data_frame_list[idx][filter_by] |
|
] |
|
] |
|
|
|
return data_frame_list |
|
|