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.
Files changed (73) hide show
  1. audit/__init__.py +0 -0
  2. audit/app/.streamlit/config.toml +7 -0
  3. audit/app/APP.py +74 -0
  4. audit/app/__init__.py +0 -0
  5. audit/app/launcher.py +38 -0
  6. audit/app/util/__init__.py +0 -0
  7. audit/app/util/commons/__init__.py +0 -0
  8. audit/app/util/commons/checks.py +24 -0
  9. audit/app/util/commons/data_preprocessing.py +98 -0
  10. audit/app/util/commons/sidebars.py +330 -0
  11. audit/app/util/commons/utils.py +11 -0
  12. audit/app/util/constants/__init__.py +0 -0
  13. audit/app/util/constants/descriptions.py +371 -0
  14. audit/app/util/constants/features.py +168 -0
  15. audit/app/util/constants/metrics.py +19 -0
  16. audit/app/util/images/AUDIT.png +0 -0
  17. audit/app/util/images/AUDIT_big.jpeg +0 -0
  18. audit/app/util/images/AUDIT_medium.jpeg +0 -0
  19. audit/app/util/images/AUDIT_transparent.png +0 -0
  20. audit/app/util/images/artorg_logo.gif +0 -0
  21. audit/app/util/images/audit_schema.png +0 -0
  22. audit/app/util/images/uniovi_logo.png +0 -0
  23. audit/app/util/pages/Home_Page.py +134 -0
  24. audit/app/util/pages/Longitudinal_Measurements.py +98 -0
  25. audit/app/util/pages/Model_Performance_Analysis.py +146 -0
  26. audit/app/util/pages/Multi_Model_Performance_Comparison.py +93 -0
  27. audit/app/util/pages/Multivariate_Feature_Analysis.py +96 -0
  28. audit/app/util/pages/Pairwise_Model_Performance_Comparison.py +221 -0
  29. audit/app/util/pages/Segmentation_Error_Matrix.py +198 -0
  30. audit/app/util/pages/Subjects_Exploration.py +119 -0
  31. audit/app/util/pages/Univariate_Feature_Analysis.py +149 -0
  32. audit/app/util/pages/__init__.py +0 -0
  33. audit/cli.py +46 -0
  34. audit/configs/app.yml +42 -0
  35. audit/configs/feature_extractor.yml +31 -0
  36. audit/configs/itk_labels.txt +6 -0
  37. audit/configs/metric_extractor.yml +34 -0
  38. audit/feature_extractor.py +69 -0
  39. audit/features/__init__.py +0 -0
  40. audit/features/main.py +177 -0
  41. audit/features/spatial.py +107 -0
  42. audit/features/statistical.py +122 -0
  43. audit/features/texture.py +111 -0
  44. audit/features/tumor.py +257 -0
  45. audit/metric_extractor.py +68 -0
  46. audit/metrics/__init__.py +0 -0
  47. audit/metrics/commons.py +64 -0
  48. audit/metrics/error_matrix.py +43 -0
  49. audit/metrics/main.py +230 -0
  50. audit/metrics/segmentation_metrics.py +251 -0
  51. audit/metrics/statistical_tests.py +224 -0
  52. audit/utils/__init__.py +0 -0
  53. audit/utils/commons/__init__.py +0 -0
  54. audit/utils/commons/file_manager.py +516 -0
  55. audit/utils/commons/strings.py +115 -0
  56. audit/utils/external_tools/__init__.py +0 -0
  57. audit/utils/external_tools/itk_snap.py +103 -0
  58. audit/utils/sequences/__init__.py +0 -0
  59. audit/utils/sequences/sequences.py +290 -0
  60. audit/visualization/__init__.py +0 -0
  61. audit/visualization/barplots.py +79 -0
  62. audit/visualization/boxplot.py +227 -0
  63. audit/visualization/confusion_matrices.py +207 -0
  64. audit/visualization/constants.py +11 -0
  65. audit/visualization/histograms.py +182 -0
  66. audit/visualization/scatter_plots.py +238 -0
  67. audit/visualization/sequences.py +9 -0
  68. audit/visualization/time_series.py +123 -0
  69. auditapp-0.0.7.dist-info/LICENSE.md +201 -0
  70. auditapp-0.0.7.dist-info/METADATA +347 -0
  71. auditapp-0.0.7.dist-info/RECORD +73 -0
  72. auditapp-0.0.7.dist-info/WHEEL +4 -0
  73. auditapp-0.0.7.dist-info/entry_points.txt +3 -0
audit/__init__.py ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ [theme]
2
+ #base="light"
3
+ primaryColor="#45ad87"
4
+ backgroundColor="#FFFFFF"
5
+ secondaryBackgroundColor="#f1ebe6"
6
+ textColor="#262730"
7
+ font="sans serif"
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