auditapp 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audit/__init__.py +0 -0
- audit/app/.streamlit/config.toml +7 -0
- audit/app/APP.py +74 -0
- audit/app/__init__.py +0 -0
- audit/app/launcher.py +38 -0
- audit/app/util/__init__.py +0 -0
- audit/app/util/commons/__init__.py +0 -0
- audit/app/util/commons/checks.py +24 -0
- audit/app/util/commons/data_preprocessing.py +98 -0
- audit/app/util/commons/sidebars.py +330 -0
- audit/app/util/commons/utils.py +11 -0
- audit/app/util/constants/__init__.py +0 -0
- audit/app/util/constants/descriptions.py +371 -0
- audit/app/util/constants/features.py +168 -0
- audit/app/util/constants/metrics.py +19 -0
- audit/app/util/images/AUDIT.png +0 -0
- audit/app/util/images/AUDIT_big.jpeg +0 -0
- audit/app/util/images/AUDIT_medium.jpeg +0 -0
- audit/app/util/images/AUDIT_transparent.png +0 -0
- audit/app/util/images/artorg_logo.gif +0 -0
- audit/app/util/images/audit_schema.png +0 -0
- audit/app/util/images/uniovi_logo.png +0 -0
- audit/app/util/pages/Home_Page.py +134 -0
- audit/app/util/pages/Longitudinal_Measurements.py +98 -0
- audit/app/util/pages/Model_Performance_Analysis.py +146 -0
- audit/app/util/pages/Multi_Model_Performance_Comparison.py +93 -0
- audit/app/util/pages/Multivariate_Feature_Analysis.py +96 -0
- audit/app/util/pages/Pairwise_Model_Performance_Comparison.py +221 -0
- audit/app/util/pages/Segmentation_Error_Matrix.py +198 -0
- audit/app/util/pages/Subjects_Exploration.py +119 -0
- audit/app/util/pages/Univariate_Feature_Analysis.py +149 -0
- audit/app/util/pages/__init__.py +0 -0
- audit/cli.py +46 -0
- audit/configs/app.yml +42 -0
- audit/configs/feature_extractor.yml +31 -0
- audit/configs/itk_labels.txt +6 -0
- audit/configs/metric_extractor.yml +34 -0
- audit/feature_extractor.py +69 -0
- audit/features/__init__.py +0 -0
- audit/features/main.py +177 -0
- audit/features/spatial.py +107 -0
- audit/features/statistical.py +122 -0
- audit/features/texture.py +111 -0
- audit/features/tumor.py +257 -0
- audit/metric_extractor.py +68 -0
- audit/metrics/__init__.py +0 -0
- audit/metrics/commons.py +64 -0
- audit/metrics/error_matrix.py +43 -0
- audit/metrics/main.py +230 -0
- audit/metrics/segmentation_metrics.py +251 -0
- audit/metrics/statistical_tests.py +224 -0
- audit/utils/__init__.py +0 -0
- audit/utils/commons/__init__.py +0 -0
- audit/utils/commons/file_manager.py +516 -0
- audit/utils/commons/strings.py +115 -0
- audit/utils/external_tools/__init__.py +0 -0
- audit/utils/external_tools/itk_snap.py +103 -0
- audit/utils/sequences/__init__.py +0 -0
- audit/utils/sequences/sequences.py +290 -0
- audit/visualization/__init__.py +0 -0
- audit/visualization/barplots.py +79 -0
- audit/visualization/boxplot.py +227 -0
- audit/visualization/confusion_matrices.py +207 -0
- audit/visualization/constants.py +11 -0
- audit/visualization/histograms.py +182 -0
- audit/visualization/scatter_plots.py +238 -0
- audit/visualization/sequences.py +9 -0
- audit/visualization/time_series.py +123 -0
- auditapp-0.0.7.dist-info/LICENSE.md +201 -0
- auditapp-0.0.7.dist-info/METADATA +347 -0
- auditapp-0.0.7.dist-info/RECORD +73 -0
- auditapp-0.0.7.dist-info/WHEEL +4 -0
- auditapp-0.0.7.dist-info/entry_points.txt +3 -0
audit/__init__.py
ADDED
|
File without changes
|
audit/app/APP.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import warnings
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import streamlit as st
|
|
5
|
+
from PIL import Image
|
|
6
|
+
|
|
7
|
+
from audit.utils.commons.file_manager import load_config_file
|
|
8
|
+
from audit.app.util.pages.Home_Page import home_page
|
|
9
|
+
from audit.app.util.pages.Longitudinal_Measurements import longitudinal
|
|
10
|
+
from audit.app.util.pages.Model_Performance_Analysis import performance
|
|
11
|
+
from audit.app.util.pages.Multi_Model_Performance_Comparison import multi_model
|
|
12
|
+
from audit.app.util.pages.Multivariate_Feature_Analysis import multivariate
|
|
13
|
+
from audit.app.util.pages.Pairwise_Model_Performance_Comparison import pairwise_comparison
|
|
14
|
+
from audit.app.util.pages.Segmentation_Error_Matrix import matrix
|
|
15
|
+
from audit.app.util.pages.Subjects_Exploration import subjects
|
|
16
|
+
from audit.app.util.pages.Univariate_Feature_Analysis import univariate
|
|
17
|
+
|
|
18
|
+
warnings.simplefilter(action="ignore", category=FutureWarning)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AUDIT:
|
|
22
|
+
def __init__(self, config):
|
|
23
|
+
self.apps = []
|
|
24
|
+
self.config = config
|
|
25
|
+
|
|
26
|
+
def add_page(self, title, func):
|
|
27
|
+
self.apps.append({"title": title, "function": func})
|
|
28
|
+
|
|
29
|
+
def run(self):
|
|
30
|
+
st.set_page_config(page_title="AUDIT", page_icon=":brain", layout="wide")
|
|
31
|
+
|
|
32
|
+
# Resolve the absolute path for the logo
|
|
33
|
+
base_dir = Path(__file__).resolve().parent
|
|
34
|
+
audit_logo_path = base_dir / "util/images/AUDIT_transparent.png"
|
|
35
|
+
|
|
36
|
+
# Load the image
|
|
37
|
+
if audit_logo_path.exists():
|
|
38
|
+
audit_logo = Image.open(audit_logo_path)
|
|
39
|
+
st.sidebar.image(audit_logo, use_column_width=True)
|
|
40
|
+
else:
|
|
41
|
+
st.sidebar.error(f"Logo not found: {audit_logo_path}")
|
|
42
|
+
|
|
43
|
+
st.sidebar.markdown("## Main Menu")
|
|
44
|
+
page = st.sidebar.selectbox("Select Page", self.apps, format_func=lambda page: page["title"])
|
|
45
|
+
st.sidebar.markdown("---")
|
|
46
|
+
page["function"](self.config)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main():
|
|
50
|
+
# Extract the config path from sys.argv (Streamlit passes arguments this way)
|
|
51
|
+
config_path = "./configs/app.yml" # Default config path
|
|
52
|
+
if len(sys.argv) > 2 and sys.argv[1] == "--config":
|
|
53
|
+
config_path = sys.argv[2]
|
|
54
|
+
|
|
55
|
+
# Load the configuration file
|
|
56
|
+
config = load_config_file(config_path)
|
|
57
|
+
|
|
58
|
+
# Initialize and run the app
|
|
59
|
+
app = AUDIT(config)
|
|
60
|
+
app.add_page("Home Page", home_page)
|
|
61
|
+
app.add_page("Univariate Analysis", univariate)
|
|
62
|
+
app.add_page("Multivariate Analysis", multivariate)
|
|
63
|
+
app.add_page("Segmentation Error Matrix", matrix)
|
|
64
|
+
app.add_page("Model Performance Analysis", performance)
|
|
65
|
+
app.add_page("Pairwise Model Performance Comparison", pairwise_comparison)
|
|
66
|
+
app.add_page("Multi-model Performance Comparison", multi_model)
|
|
67
|
+
app.add_page("Longitudinal Measurements", longitudinal)
|
|
68
|
+
app.add_page("Subjects Exploration", subjects)
|
|
69
|
+
|
|
70
|
+
app.run()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
main()
|
audit/app/__init__.py
ADDED
|
File without changes
|
audit/app/launcher.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def run_streamlit_app(config):
|
|
7
|
+
|
|
8
|
+
# Get the path to the APP.py file
|
|
9
|
+
app_path = Path(__file__).resolve().parent / "APP.py"
|
|
10
|
+
|
|
11
|
+
# Ensure the app exists
|
|
12
|
+
if not app_path.exists():
|
|
13
|
+
raise FileNotFoundError(f"Streamlit app not found at: {app_path}")
|
|
14
|
+
|
|
15
|
+
# Build the command to launch Streamlit with the provided config
|
|
16
|
+
command = f"streamlit run {app_path} -- --config {config}"
|
|
17
|
+
|
|
18
|
+
# Print and execute the command
|
|
19
|
+
print(f"Running command: {command}")
|
|
20
|
+
os.system(command)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
# Command-line argument parsing
|
|
25
|
+
parser = argparse.ArgumentParser(description="AUDIT web APP.")
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
'--config',
|
|
28
|
+
type=str,
|
|
29
|
+
default='./configs/app.yml', # Path relative to the script location
|
|
30
|
+
help="Path to the configuration file for web app."
|
|
31
|
+
)
|
|
32
|
+
args = parser.parse_args()
|
|
33
|
+
|
|
34
|
+
run_streamlit_app(args.config)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def health_checks(selected_sets, select_feature_names: list = []):
|
|
5
|
+
if len(selected_sets) == 0:
|
|
6
|
+
return False, "Please, select a dataset from the left sidebar."
|
|
7
|
+
elif any(k is None for k in select_feature_names):
|
|
8
|
+
return False, f'Please, select an available category.'
|
|
9
|
+
else:
|
|
10
|
+
return True, ''
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def dataset_sanity_check(selected_sets):
|
|
14
|
+
if len(selected_sets) == 0:
|
|
15
|
+
return False
|
|
16
|
+
else:
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def models_sanity_check(baseline_model, benchmark_model):
|
|
21
|
+
if baseline_model == benchmark_model:
|
|
22
|
+
return False
|
|
23
|
+
else:
|
|
24
|
+
return True
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def select_datasets(data, sets=None):
|
|
8
|
+
if isinstance(sets, list):
|
|
9
|
+
return data[data.set.isin(sets)]
|
|
10
|
+
elif isinstance(sets, str):
|
|
11
|
+
return data[data.set == sets]
|
|
12
|
+
else:
|
|
13
|
+
return data
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def select_features(data, features=None):
|
|
17
|
+
if isinstance(features, list):
|
|
18
|
+
return data[features]
|
|
19
|
+
elif isinstance(features, str):
|
|
20
|
+
return data[[features]]
|
|
21
|
+
else:
|
|
22
|
+
return data
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def select_regions(data, regions=None):
|
|
26
|
+
if isinstance(regions, list):
|
|
27
|
+
return data[data.region.isin(regions)]
|
|
28
|
+
elif isinstance(regions, str):
|
|
29
|
+
return data[data.region == regions]
|
|
30
|
+
else:
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def select_models(data, models=None):
|
|
35
|
+
if isinstance(models, list):
|
|
36
|
+
return data[data.model.isin(models)]
|
|
37
|
+
elif isinstance(models, str):
|
|
38
|
+
return data[data.model == models]
|
|
39
|
+
else:
|
|
40
|
+
return data
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def select_subjects(data, subjects=None):
|
|
44
|
+
if isinstance(subjects, list):
|
|
45
|
+
return data[data.ID.isin(subjects)]
|
|
46
|
+
elif isinstance(subjects, str):
|
|
47
|
+
return data[data.ID == subjects]
|
|
48
|
+
else:
|
|
49
|
+
return data
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def filter_outliers(
|
|
53
|
+
data: pd.DataFrame,
|
|
54
|
+
filtering_method: str = None,
|
|
55
|
+
filtering_feature: str = None,
|
|
56
|
+
remove_low: float = None,
|
|
57
|
+
remove_up: float = None,
|
|
58
|
+
clip_low: float = None,
|
|
59
|
+
clip_up: float = None,
|
|
60
|
+
num_std_devs: int = None
|
|
61
|
+
):
|
|
62
|
+
if filtering_method:
|
|
63
|
+
mean, std_dev = data[filtering_feature].mean(), data[filtering_feature].std()
|
|
64
|
+
if filtering_method == "Removing outliers":
|
|
65
|
+
data = data[data[filtering_feature].between(remove_low, remove_up)]
|
|
66
|
+
elif filtering_method == "Clipping outliers":
|
|
67
|
+
data[filtering_feature] = data[filtering_feature].clip(clip_low, clip_up)
|
|
68
|
+
elif filtering_method == "Standard deviations":
|
|
69
|
+
upper_bound = mean + int(num_std_devs) * std_dev
|
|
70
|
+
lower_bound = mean - int(num_std_devs) * std_dev
|
|
71
|
+
data = data[data[filtering_feature].between(lower_bound, upper_bound)]
|
|
72
|
+
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def processing_data(
|
|
77
|
+
data: pd.DataFrame,
|
|
78
|
+
sets: Union[List[str], str] = None,
|
|
79
|
+
models: Union[List[str], str] = None,
|
|
80
|
+
features: Union[List[str], str] = None,
|
|
81
|
+
regions: Union[List[str], str] = None,
|
|
82
|
+
subjects: Union[List[str], str] = None,
|
|
83
|
+
filtering_method: str = None,
|
|
84
|
+
filtering_feature: str = None,
|
|
85
|
+
remove_low: float = None,
|
|
86
|
+
remove_up: float = None,
|
|
87
|
+
clip_low: float = None,
|
|
88
|
+
clip_up: float = None,
|
|
89
|
+
num_std_devs: int = None
|
|
90
|
+
):
|
|
91
|
+
data = select_datasets(data, sets=sets)
|
|
92
|
+
data = select_models(data, models=models)
|
|
93
|
+
data = select_features(data, features=features)
|
|
94
|
+
data = select_regions(data, regions=regions)
|
|
95
|
+
data = select_subjects(data, subjects=subjects)
|
|
96
|
+
data = filter_outliers(data, filtering_method, filtering_feature, remove_low, remove_up, clip_low, clip_up, num_std_devs)
|
|
97
|
+
|
|
98
|
+
return data
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import streamlit as st
|
|
2
|
+
|
|
3
|
+
from audit.app.util.constants.features import Features
|
|
4
|
+
from audit.app.util.constants.metrics import Metrics
|
|
5
|
+
from audit.utils.commons.strings import pretty_string
|
|
6
|
+
|
|
7
|
+
# Load constants
|
|
8
|
+
const_metrics = Metrics()
|
|
9
|
+
metrics_dict = const_metrics.get_metrics()
|
|
10
|
+
orderby_dict = const_metrics.orderby
|
|
11
|
+
const_features = Features()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def setup_sidebar_multi_datasets(data_paths):
|
|
15
|
+
with st.sidebar.expander("Datasets", expanded=True):
|
|
16
|
+
selected_sets = st.multiselect(
|
|
17
|
+
label="Select datasets to visualize:",
|
|
18
|
+
options=data_paths.keys(),
|
|
19
|
+
default=data_paths.keys()
|
|
20
|
+
)
|
|
21
|
+
return selected_sets
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def setup_sidebar_single_dataset(data):
|
|
25
|
+
with st.sidebar.expander("Datasets", expanded=True):
|
|
26
|
+
selected_set = st.selectbox("Select dataset to analyze:", options=list(data.set.unique()), index=0)
|
|
27
|
+
|
|
28
|
+
return selected_set
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def setup_sidebar_single_metric(data):
|
|
32
|
+
available_metrics = [k for k, v in metrics_dict.items() if v in data.columns]
|
|
33
|
+
with st.sidebar.expander("Metrics", expanded=True):
|
|
34
|
+
selected_metric = st.selectbox("Select metric to analyze:", options=available_metrics, index=0)
|
|
35
|
+
|
|
36
|
+
return selected_metric
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def setup_sidebar_multi_metrics(data):
|
|
40
|
+
available_metrics = [k for k, v in metrics_dict.items() if v in data.columns]
|
|
41
|
+
with st.sidebar.expander("Metrics", expanded=True):
|
|
42
|
+
selected_metrics = st.multiselect(
|
|
43
|
+
label="Select metrics to analyze:",
|
|
44
|
+
options=available_metrics,
|
|
45
|
+
default=available_metrics[0]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return selected_metrics
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def setup_sidebar_single_model(data):
|
|
52
|
+
with st.sidebar.expander("Models", expanded=True):
|
|
53
|
+
selected_model = st.selectbox("Select model to analyze:", options=list(data.model.unique()), index=0)
|
|
54
|
+
|
|
55
|
+
return selected_model
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def setup_sidebar_multi_model(data):
|
|
59
|
+
with st.sidebar.expander("Models", expanded=True):
|
|
60
|
+
selected_models = st.multiselect(
|
|
61
|
+
label="Select models to analyze:",
|
|
62
|
+
options=list(data.model.unique()),
|
|
63
|
+
default=list(data.model.unique())
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return selected_models
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def setup_sidebar_pairwise_models(data, selected_set):
|
|
70
|
+
with st.sidebar.expander("Models", expanded=True):
|
|
71
|
+
# models_available = [capitalizer(pretty_string(m)) for m in data[data.set == selected_set].model.unique()]
|
|
72
|
+
models_available = data[data.set == selected_set].model.unique()
|
|
73
|
+
baseline_model = st.selectbox("Select the baseline model:", options=models_available, index=0)
|
|
74
|
+
benchmark_model = st.selectbox("Select the benchmark model:", options=models_available, index=1)
|
|
75
|
+
if baseline_model == benchmark_model:
|
|
76
|
+
st.error("Models selected must be different to make a performance comparison", icon="🚨")
|
|
77
|
+
|
|
78
|
+
return baseline_model, benchmark_model
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def setup_sidebar_features(data, name, c_index=0, f_index=0, key=None):
|
|
82
|
+
with st.sidebar.expander(name, expanded=True):
|
|
83
|
+
select_category = st.selectbox(
|
|
84
|
+
label="Feature category:",
|
|
85
|
+
options=const_features.categories,
|
|
86
|
+
index=c_index,
|
|
87
|
+
key=f"c_{key}"
|
|
88
|
+
)
|
|
89
|
+
available_features = [k for k, v in const_features.get_features(select_category).items() if v in data.columns]
|
|
90
|
+
selected_feature = st.selectbox(
|
|
91
|
+
label="Feature name:",
|
|
92
|
+
options=available_features,
|
|
93
|
+
index=f_index,
|
|
94
|
+
key=f"f_{key}"
|
|
95
|
+
)
|
|
96
|
+
selected_feature = const_features.get_features(select_category).get(selected_feature, None)
|
|
97
|
+
|
|
98
|
+
return selected_feature
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def setup_sidebar_color(data, name, c_index=0, f_index=0, key=None):
|
|
102
|
+
with st.sidebar.expander(name, expanded=True):
|
|
103
|
+
select_color_category = st.selectbox(
|
|
104
|
+
label="Feature category:",
|
|
105
|
+
options=["Dataset"] + const_features.categories,
|
|
106
|
+
index=c_index,
|
|
107
|
+
key=f"c_{key}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if select_color_category == "Dataset":
|
|
111
|
+
select_color_axis = 'Dataset'
|
|
112
|
+
else:
|
|
113
|
+
available_features = [k for k, v in const_features.get_features(select_color_category).items() if
|
|
114
|
+
v in data.columns]
|
|
115
|
+
select_color_axis = st.selectbox(
|
|
116
|
+
label="Feature name:",
|
|
117
|
+
options=available_features,
|
|
118
|
+
index=f_index,
|
|
119
|
+
key=f"f_{key}"
|
|
120
|
+
)
|
|
121
|
+
select_color_axis = const_features.get_features(select_color_category).get(select_color_axis, None)
|
|
122
|
+
|
|
123
|
+
return select_color_axis
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def setup_highlight_subject(data):
|
|
127
|
+
with st.sidebar.expander(label="Highlight subject"):
|
|
128
|
+
selected_sets = st.selectbox(
|
|
129
|
+
label="Dataset:", options=data.set.unique(), index=0
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
highlight_subject = st.selectbox(
|
|
133
|
+
label="Enter subject ID to highlight",
|
|
134
|
+
options=[None] + list(data[data.set == selected_sets].ID.unique()),
|
|
135
|
+
index=0
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return highlight_subject
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def setup_histogram_options(plot_type):
|
|
142
|
+
"""
|
|
143
|
+
Set up histogram customization options based on plot type.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
plot_type (str): Type of plot ("Histogram" or "Probability").
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
tuple: Number of bins or bin size based on user selection.
|
|
150
|
+
"""
|
|
151
|
+
n_bins, bins_size = None, None
|
|
152
|
+
if plot_type == "Histogram":
|
|
153
|
+
with st.sidebar.expander("Customization", expanded=True):
|
|
154
|
+
option = st.selectbox("Define number of bins or bins size", ("Number of bins", "Bins size"))
|
|
155
|
+
if option == "Number of bins":
|
|
156
|
+
n_bins = st.number_input(
|
|
157
|
+
"Select the number of bins",
|
|
158
|
+
min_value=1,
|
|
159
|
+
max_value=200,
|
|
160
|
+
value=100,
|
|
161
|
+
step=1,
|
|
162
|
+
placeholder="Type a number...",
|
|
163
|
+
help="The actual number of bins will be the closest value to your selection based on distribution.",
|
|
164
|
+
)
|
|
165
|
+
elif option == "Bins size":
|
|
166
|
+
bins_size = st.number_input(
|
|
167
|
+
"Select bins size", min_value=1, max_value=None, value=1, step=1, placeholder="Type a number..."
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return n_bins, bins_size
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def setup_filtering_options(df, feature):
|
|
174
|
+
"""
|
|
175
|
+
Set up filtering options based on selected features.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
df (DataFrame): DataFrame containing the data.
|
|
179
|
+
feature (dict): Feature selected
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
tuple: Filtering method and corresponding parameters.
|
|
183
|
+
"""
|
|
184
|
+
with st.sidebar.expander("Filtering", expanded=False):
|
|
185
|
+
filtering_method = st.radio(
|
|
186
|
+
label="Filter data based on",
|
|
187
|
+
options=["No filter", "Removing outliers", "Clipping outliers", "Standard deviations"],
|
|
188
|
+
captions=[
|
|
189
|
+
"",
|
|
190
|
+
"It remove values outside a specified range",
|
|
191
|
+
"It restricts the range of data by capping values below and above a threshold to the lower "
|
|
192
|
+
"and upper bound selected.",
|
|
193
|
+
"Filtering data based on standard deviations",
|
|
194
|
+
],
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
remove_low, remove_up = None, None
|
|
198
|
+
clip_low, clip_up = None, None
|
|
199
|
+
num_std_devs = None
|
|
200
|
+
|
|
201
|
+
if filtering_method == "Removing outliers":
|
|
202
|
+
remove_low, remove_up = st.slider(
|
|
203
|
+
"Remove outliers within a range of values",
|
|
204
|
+
min_value=df[feature].min(),
|
|
205
|
+
max_value=df[feature].max(),
|
|
206
|
+
value=(df[feature].min(), df[feature].max()),
|
|
207
|
+
)
|
|
208
|
+
elif filtering_method == "Clipping outliers":
|
|
209
|
+
clip_low, clip_up = st.slider(
|
|
210
|
+
"Clip outliers within a range of values",
|
|
211
|
+
min_value=df[feature].min(),
|
|
212
|
+
max_value=df[feature].max(),
|
|
213
|
+
value=(df[feature].min(), df[feature].max()),
|
|
214
|
+
)
|
|
215
|
+
elif filtering_method == "Standard deviations":
|
|
216
|
+
# mean, std_dev = df[feature].mean(), df[feature].std()
|
|
217
|
+
num_std_devs = st.number_input(label="Number of standard deviations", min_value=1, step=1, value=3)
|
|
218
|
+
|
|
219
|
+
return filtering_method, remove_low, remove_up, clip_low, clip_up, num_std_devs
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def setup_metrics_customization(baseline_model, benchmark_model, aggregated):
|
|
223
|
+
mapping_performance = {
|
|
224
|
+
f"subject ID": "ID",
|
|
225
|
+
f"Performance ({baseline_model})": f"{baseline_model}",
|
|
226
|
+
f"Performance ({benchmark_model})": f"{benchmark_model}",
|
|
227
|
+
}
|
|
228
|
+
num_max_subjects, selected_sorted, selected_order = None, None, None
|
|
229
|
+
if not aggregated:
|
|
230
|
+
with st.sidebar.expander("Customization", expanded=True):
|
|
231
|
+
num_max_subjects = st.number_input("Maximum subjects to visualize", min_value=1, value=5, step=1)
|
|
232
|
+
mapping_buttons_columns_perf = {
|
|
233
|
+
**const_features.get_multiple_features(["common"]).copy(),
|
|
234
|
+
**mapping_performance,
|
|
235
|
+
}
|
|
236
|
+
selected_sorted = st.selectbox("Sorted by:", options=mapping_buttons_columns_perf)
|
|
237
|
+
selected_order = st.radio("Order by:", options=orderby_dict.keys())
|
|
238
|
+
|
|
239
|
+
return num_max_subjects, mapping_performance.get(selected_sorted), orderby_dict.get(selected_order)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def setup_improvement_button():
|
|
243
|
+
improvement_type = st.selectbox(
|
|
244
|
+
label="Type of comparison",
|
|
245
|
+
options=["relative", "absolute", "ratio"],
|
|
246
|
+
format_func=pretty_string,
|
|
247
|
+
index=0
|
|
248
|
+
)
|
|
249
|
+
return improvement_type
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def setup_aggregation_button():
|
|
253
|
+
return st.checkbox("Aggregated.", value=True, help="It aggregates all the subjects, if enabled.")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def setup_clip_sidebar(data, feature):
|
|
257
|
+
clip_low, clip_up = None, None
|
|
258
|
+
with st.sidebar:
|
|
259
|
+
metric_clip = st.checkbox(
|
|
260
|
+
"Clip the metric",
|
|
261
|
+
help="It restricts the range of the metrics by capping values below and "
|
|
262
|
+
"above a threshold to the lower and upper bound selected, if "
|
|
263
|
+
"enabled.",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if metric_clip:
|
|
267
|
+
clip_low, clip_up = st.slider(
|
|
268
|
+
label="Clip metric",
|
|
269
|
+
min_value=data[feature].min(),
|
|
270
|
+
max_value=data[feature].max(),
|
|
271
|
+
value=(data[feature].min(), data[feature].max()),
|
|
272
|
+
label_visibility="collapsed",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return clip_low, clip_up
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def setup_statistical_test():
|
|
279
|
+
statistical_test = st.checkbox(
|
|
280
|
+
label="Perform statistical test",
|
|
281
|
+
help="It performs statistical tests to evaluate whether exist statistical "
|
|
282
|
+
"differences between the model performance, if enabled.",
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return statistical_test
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def setup_button_data_download(df):
|
|
289
|
+
st.download_button(
|
|
290
|
+
label="Download data used in the statistical tests as CSV",
|
|
291
|
+
data=df.to_csv().encode("utf-8"),
|
|
292
|
+
file_name="raw_data_statistical_test.csv",
|
|
293
|
+
mime="text/csv",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def setup_sidebar_regions(data, aggregated):
|
|
298
|
+
selected_regions = None
|
|
299
|
+
if not aggregated:
|
|
300
|
+
with st.sidebar.expander("Regions", expanded=True):
|
|
301
|
+
available_regions = list(data.region.unique())
|
|
302
|
+
selected_regions = st.multiselect(
|
|
303
|
+
label="Select the regions to visualize:",
|
|
304
|
+
options=available_regions,
|
|
305
|
+
default=available_regions
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return selected_regions
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def setup_sidebar_longitudinal_subject(data):
|
|
312
|
+
with st.sidebar.expander("Subjects", expanded=True):
|
|
313
|
+
subject_selected = st.selectbox(
|
|
314
|
+
label="Select a subject to visualize:",
|
|
315
|
+
options=sorted(data.longitudinal_id.unique()),
|
|
316
|
+
index=0
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
return subject_selected
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def setup_sidebar_single_subjects(data):
|
|
323
|
+
with st.sidebar.expander("Subjects", expanded=True):
|
|
324
|
+
subject_selected = st.selectbox(
|
|
325
|
+
label="Select a subject to visualize:",
|
|
326
|
+
options=sorted(data.ID.unique()),
|
|
327
|
+
index=0
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
return subject_selected
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import plotly.io as pio
|
|
2
|
+
import streamlit as st
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def download_plot(fig, label="", filename="image"):
|
|
6
|
+
st.download_button(
|
|
7
|
+
label=f"Download {label} plot",
|
|
8
|
+
data=pio.to_image(fig, format="svg"),
|
|
9
|
+
file_name=f"{filename}.svg",
|
|
10
|
+
mime="/image/svg"
|
|
11
|
+
)
|
|
File without changes
|