fair-ca-visualization 0.4.0__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.
- fair_ca_visualization-0.4.0.dist-info/METADATA +84 -0
- fair_ca_visualization-0.4.0.dist-info/RECORD +26 -0
- fair_ca_visualization-0.4.0.dist-info/WHEEL +4 -0
- fair_ca_visualization-0.4.0.dist-info/entry_points.txt +2 -0
- fair_ca_visualization-0.4.0.dist-info/licenses/LICENSE +7 -0
- fairvis/__init__.py +4 -0
- fairvis/__main__.py +6 -0
- fairvis/app.py +208 -0
- fairvis/cli.py +46 -0
- fairvis/console.py +17 -0
- fairvis/data/FAIR_assessment_template.xlsx +0 -0
- fairvis/data/assessments/Alejandro2026_empagliflozin.xlsx +0 -0
- fairvis/data/assessments/BioModels_C19_curated.xlsx +0 -0
- fairvis/data/assessments/BioModels_curated.xlsx +0 -0
- fairvis/data/assessments/BioModels_non_curated.xlsx +0 -0
- fairvis/data/assessments/Casabianca2026_rivaroxaban.xlsx +0 -0
- fairvis/data/assessments/Elias2025_glimepiride.xlsx +0 -0
- fairvis/data/assessments/Nemitz2026_dapagliflozin.xlsx +0 -0
- fairvis/data/assessments/PMR_curated.xlsx +0 -0
- fairvis/data/assessments/PMR_non_curated.xlsx +0 -0
- fairvis/data/assessments/Tensil2026_losartan.xlsx +0 -0
- fairvis/data/assessments/Tereshchuk2026_canagliflozin.xlsx +0 -0
- fairvis/data_io.py +163 -0
- fairvis/example.py +10 -0
- fairvis/settings.py +18 -0
- fairvis/visualization.py +109 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fair-ca-visualization
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Interactive visualization of the FAIR COMBINE Archive Indicators (FAIR-CA) for computational models.
|
|
5
|
+
Project-URL: Homepage, https://faircombine.streamlit.app
|
|
6
|
+
Project-URL: Documentation, https://matthiaskoenig.github.io/fair-ca-visualization
|
|
7
|
+
Project-URL: Repository, https://github.com/matthiaskoenig/fair-ca-visualization
|
|
8
|
+
Project-URL: Issues, https://github.com/matthiaskoenig/fair-ca-visualization/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/matthiaskoenig/fair-ca-visualization/tree/develop/release-notes
|
|
10
|
+
Project-URL: Download, https://pypi.org/project/fair-ca-visualization
|
|
11
|
+
Author-email: Matthias König <konigmatt@googlemail.com>
|
|
12
|
+
Maintainer-email: Matthias König <konigmatt@googlemail.com>
|
|
13
|
+
License-Expression: MIT
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Keywords: COMBINE,FAIR,indicators,modeling,standardization,streamlit
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
24
|
+
Requires-Python: >=3.14
|
|
25
|
+
Requires-Dist: numpy>=2.4.3
|
|
26
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
27
|
+
Requires-Dist: pandas<3,>=2.3.3
|
|
28
|
+
Requires-Dist: plotly>=6.6.0
|
|
29
|
+
Requires-Dist: pyarrow>=23.0.1
|
|
30
|
+
Requires-Dist: rich>=14.3.3
|
|
31
|
+
Requires-Dist: streamlit>=1.55.0
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
# fair-ca-visualization
|
|
37
|
+
|
|
38
|
+
[](https://github.com/matthiaskoenig/fair-ca-visualization/actions/workflows/ci-cd.yml) [](https://matthiaskoenig.github.io/fair-ca-visualization) [](https://pypi.org/project/fair-ca-visualization/) [](https://pypi.org/project/fair-ca-visualization/) [](https://opensource.org/licenses/MIT) [](https://doi.org/10.5281/zenodo.13755820)
|
|
39
|
+
|
|
40
|
+
Computational models are essential tools for studying complex systems which, particularly in clinical settings, need to be quality-approved and transparent. A community-driven approach to enhance the transparency and communication of model features is adherence to the principles of Findability, Accessibility, Interoperability and Reusability (FAIR). The FAIR COMBINE Archive Indicators (FAIR-CA) adapt the FAIR indicators published by the Research Data Alliance to assess the FAIRness of models encoded in domain-specific standards, such as those established by [COMBINE](https://co.mbine.org).
|
|
41
|
+
|
|
42
|
+
The FAIR COMBINE Archive Indicators project is available from [https://github.com/FAIR-CA-indicators/FAIR-CA-indicators.github.io](https://github.com/FAIR-CA-indicators/FAIR-CA-indicators.github.io).
|
|
43
|
+
|
|
44
|
+
`fair-ca-visualization` visualizes FAIR-CA assessments interactively. The app is available from https://faircombine.streamlit.app, the documentation from https://matthiaskoenig.github.io/fair-ca-visualization.
|
|
45
|
+
|
|
46
|
+
[](https://faircombine.streamlit.app)
|
|
47
|
+
|
|
48
|
+
# Installation
|
|
49
|
+
|
|
50
|
+
To run the app locally, install the package from [PyPI](https://pypi.org/project/fair-ca-visualization/) (python 3.14) and start it with `fairvis`:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv tool install fair-ca-visualization
|
|
54
|
+
fairvis
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
See the [installation guide](https://matthiaskoenig.github.io/fair-ca-visualization/installation/) for pip and for running from a checkout, and [contributing](https://matthiaskoenig.github.io/fair-ca-visualization/development/) for the development setup.
|
|
58
|
+
|
|
59
|
+
# How to cite
|
|
60
|
+
|
|
61
|
+
[](https://doi.org/10.5281/zenodo.13755820)
|
|
62
|
+
|
|
63
|
+
> König M. fair-ca-visualization: interactive visualization of the FAIR COMBINE Archive Indicators. Zenodo. [doi:10.5281/zenodo.13755820](https://doi.org/10.5281/zenodo.13755820)
|
|
64
|
+
|
|
65
|
+
```bibtex
|
|
66
|
+
@software{fair_ca_visualization,
|
|
67
|
+
author = {König, Matthias},
|
|
68
|
+
title = {fair-ca-visualization: interactive visualization of the FAIR COMBINE Archive Indicators},
|
|
69
|
+
publisher = {Zenodo},
|
|
70
|
+
doi = {10.5281/zenodo.13755820},
|
|
71
|
+
url = {https://doi.org/10.5281/zenodo.13755820}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
# License
|
|
76
|
+
|
|
77
|
+
- Source Code: [MIT](https://opensource.org/license/MIT)
|
|
78
|
+
- Documentation: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
|
79
|
+
|
|
80
|
+
# Funding
|
|
81
|
+
|
|
82
|
+
Matthias König (MK) was supported by the Federal Ministry of Education and Research (BMBF, Germany) within the research network Systems Medicine of the Liver (LiSyM, grant number 031L0054). MK is supported by the Federal Ministry of Education and Research (BMBF, Germany) within ATLAS by grant number 031L0304B and by the German Research Foundation (DFG) within the Research Unit Program FOR 5151 QuaLiPerF (Quantifying Liver Perfusion-Function Relationship in Complex Resection - A Systems Medicine Approach) by grant number 436883643 and by grant number 465194077 (Priority Programme SPP 2311, Subproject SimLivA).
|
|
83
|
+
|
|
84
|
+
© 2024 - 2026 Matthias König
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
fairvis/__init__.py,sha256=QKec5mel6WuhYiJfip0L7zUSPROakL6gNrvqSFNK7W0,135
|
|
2
|
+
fairvis/__main__.py,sha256=nko_6M0jQuqHg4XRfaPcqSYzo7urXqA_yCIjQQwq8j8,115
|
|
3
|
+
fairvis/app.py,sha256=dv0NPANrVbnzun-yvRSfcfG1AW9bIlyCcmNhIEkHEu4,7942
|
|
4
|
+
fairvis/cli.py,sha256=_fp--nPtPuEF4ZudbhulCafQkDmLci3wqviJJtaDXy0,1517
|
|
5
|
+
fairvis/console.py,sha256=GaohtlJZurR4ExXRtL7GIHrj67_6-jFYTw2ai6QYVnQ,331
|
|
6
|
+
fairvis/data_io.py,sha256=nTCKUTvUai-XdxEx8ZYBpiZwaVD7X87GYJyz2nYXWdE,5208
|
|
7
|
+
fairvis/example.py,sha256=XPfjXfkMHlbPyaadSjhVHqpkBqr_lbZZqoUoswNLT_Q,417
|
|
8
|
+
fairvis/settings.py,sha256=yoajWwJ9SkNG8F4cwbCFqotx9uF7TpYH5jljYtKEf0s,589
|
|
9
|
+
fairvis/visualization.py,sha256=pPFi55209bd76TUsTN0XgnNUZ42fNTf42degrCkyJvA,3118
|
|
10
|
+
fairvis/data/FAIR_assessment_template.xlsx,sha256=-VzXMptkrXSgVktUEPN17898o8Coe5TgEsydDnHcU54,17011
|
|
11
|
+
fairvis/data/assessments/Alejandro2026_empagliflozin.xlsx,sha256=IXrMmyHaKuQ4uaf59_B1br5Swc-NZSFYE8q45jT-c9Y,67728
|
|
12
|
+
fairvis/data/assessments/BioModels_C19_curated.xlsx,sha256=meLgXWwnvH0q4rntdmtw2zZCSjfZuoStUpeHU8PVb5Y,18859
|
|
13
|
+
fairvis/data/assessments/BioModels_curated.xlsx,sha256=gcj8prxjigVI0Q21gh-3wprbFd3oEUpJJrGd0yel-pc,17585
|
|
14
|
+
fairvis/data/assessments/BioModels_non_curated.xlsx,sha256=o-H948t6T1GJcN0o_QRvel-Piz4fvFZdOqIrxNkEWGA,18896
|
|
15
|
+
fairvis/data/assessments/Casabianca2026_rivaroxaban.xlsx,sha256=9Rq9fz1RqFIthmo1E79lCRN-djiC8qPtBDzuQ5F0vzk,67726
|
|
16
|
+
fairvis/data/assessments/Elias2025_glimepiride.xlsx,sha256=8ghUfq29mvFjoqoPKM_td9I3GuzwgPnHLn25NRNU1EU,67724
|
|
17
|
+
fairvis/data/assessments/Nemitz2026_dapagliflozin.xlsx,sha256=2cawChl4OOQ7Q-uKPGrJ4Vvnjna62pYRXz64rKefr_Y,67191
|
|
18
|
+
fairvis/data/assessments/PMR_curated.xlsx,sha256=jv5nNGNwMw1Ygyja-WoNRDSfvs9tGcRG0vxMBWOrel0,18063
|
|
19
|
+
fairvis/data/assessments/PMR_non_curated.xlsx,sha256=AVRNTnu6-kVkNEYZ2EjRaY3d2PXnuRa5KEk5RSRd_LU,18385
|
|
20
|
+
fairvis/data/assessments/Tensil2026_losartan.xlsx,sha256=XqXSsu4lOZp2LvrPQOD6jxMQBXJHcFhHEC7iqF6RCHw,67724
|
|
21
|
+
fairvis/data/assessments/Tereshchuk2026_canagliflozin.xlsx,sha256=oaHDUthxVrrsFP4jubURX2-8ytiLnJQzWjwaAvw7ukY,67728
|
|
22
|
+
fair_ca_visualization-0.4.0.dist-info/METADATA,sha256=i5sRmKRKPi06te8EhL8OuGoVWrwC-BBUFYPtEzKBTNA,5673
|
|
23
|
+
fair_ca_visualization-0.4.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
24
|
+
fair_ca_visualization-0.4.0.dist-info/entry_points.txt,sha256=TGDUiYZ6BRCWrAu3umTEc4QuVQzNLyxYxKUBdoBv3eg,45
|
|
25
|
+
fair_ca_visualization-0.4.0.dist-info/licenses/LICENSE,sha256=-nDb-yPz_IzNCxwvh--aoEgFTIBZBpvjwVSsxPAgRUM,1064
|
|
26
|
+
fair_ca_visualization-0.4.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2025-2026 Matthias König
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
fairvis/__init__.py
ADDED
fairvis/__main__.py
ADDED
fairvis/app.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Streamlit app of the FAIR COMBINE Archive Indicators.
|
|
2
|
+
|
|
3
|
+
Run with `fairvis`, or from a checkout with `streamlit run src/fairvis/app.py`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import streamlit as st
|
|
8
|
+
|
|
9
|
+
from fairvis.data_io import load_assessment, load_indicators, load_model_assessments
|
|
10
|
+
from fairvis.settings import TEMPLATE_PATH
|
|
11
|
+
from fairvis.visualization import visualize_barplot, visualize_polar_barplots
|
|
12
|
+
|
|
13
|
+
st.set_page_config(
|
|
14
|
+
page_title="FAIR-CA-Indicators",
|
|
15
|
+
page_icon="🧊",
|
|
16
|
+
layout="wide",
|
|
17
|
+
menu_items={
|
|
18
|
+
"Get help": "mailto:konigmatt@googlemail.com",
|
|
19
|
+
"Report a bug": "https://github.com/matthiaskoenig/fair-ca-visualization/issues/new",
|
|
20
|
+
"About": """
|
|
21
|
+
FAIR-CA-Indicators application.
|
|
22
|
+
""",
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
st.markdown(
|
|
26
|
+
"""
|
|
27
|
+
<style>
|
|
28
|
+
.block-container {
|
|
29
|
+
padding-top: 2rem;
|
|
30
|
+
padding-bottom: 1rem;
|
|
31
|
+
padding-left: 5rem;
|
|
32
|
+
padding-right: 5rem;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
""",
|
|
36
|
+
unsafe_allow_html=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@st.cache_data
|
|
41
|
+
def load_data() -> tuple[pd.DataFrame, dict[str, pd.DataFrame]]:
|
|
42
|
+
"""Load the indicators and the example assessments.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
indicators with the counts of their assessments, and the assessments by
|
|
46
|
+
model id.
|
|
47
|
+
"""
|
|
48
|
+
df_models = load_model_assessments()
|
|
49
|
+
df_indicators = load_indicators(df_models=df_models)
|
|
50
|
+
|
|
51
|
+
return df_indicators, df_models
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
df_indicators, models = load_data()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- Main ----------------------------------------------------------------------------
|
|
58
|
+
st.markdown(
|
|
59
|
+
"""
|
|
60
|
+
# Visualize FAIR COMBINE Archive Indicators
|
|
61
|
+
"""
|
|
62
|
+
)
|
|
63
|
+
tab_about, tab_assessment, tab_models, tab_indicators = st.tabs(
|
|
64
|
+
[
|
|
65
|
+
"About",
|
|
66
|
+
"Assess Model",
|
|
67
|
+
"Example Assessments",
|
|
68
|
+
"Indicators",
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
with tab_about:
|
|
73
|
+
st.markdown(
|
|
74
|
+
"""
|
|
75
|
+
## Motivation
|
|
76
|
+
Computational models are essential tools for studying complex biological systems. In biomedical and clinical contexts in particular, models must be transparent, well-documented, and **reproducible** to ensure reliability and reuse.
|
|
77
|
+
|
|
78
|
+
A community-driven approach to improving transparency and communication of model characteristics is the adoption of the **FAIR principles**: *Findability, Accessibility, Interoperability, and Reusability*. Building on the FAIR indicators developed by the **Research Data Alliance (RDA)**, we propose an adaptation specifically designed for computational models encoded in domain standards developed within the **[COMBINE](https://co.mbine.org)** community.
|
|
79
|
+
|
|
80
|
+
The **FAIR COMBINE Archive Indicators** enable systematic assessment of the FAIRness of models packaged as COMBINE archives and support improved documentation, sharing, and reuse of computational models in the life sciences. More information is available from:
|
|
81
|
+
|
|
82
|
+
>*Balaur I., Nickerson D.P., Welter D., Wodke J.A.H., Ancien F., Gebhardt T., Grouès V., Hermjakob H., König M., Radde N., Rougny A., Schneider R., Malik-Sheriff R.S., Shiferaw K.B., Stefan M., Satagopam V., Waltemath D. (2025).*
|
|
83
|
+
>**FAIRification of computational models in biology.**
|
|
84
|
+
>bioRxiv 2025.03.21.644517 (preprint). doi: [10.1101/2025.03.21.644517](https://doi.org/10.1101/2025.03.21.644517)
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
This webpage provides an interactive visualization of the **FAIR COMBINE Archive Indicators**,
|
|
88
|
+
|
|
89
|
+
- Use the **Assess Model** tab to assess your own model (a template for the indicators is provided).
|
|
90
|
+
- Use the **Example Assessments** tab to explore FAIR indicator assessments for example models.
|
|
91
|
+
- Use the **Indicators** tab to browse the individual FAIR indicators.
|
|
92
|
+
|
|
93
|
+
## How to cite the visualization tool
|
|
94
|
+
[](https://doi.org/10.5281/zenodo.13755820)
|
|
95
|
+
|
|
96
|
+
## Funding
|
|
97
|
+
Matthias König was supported by the **German Federal Ministry of Education and Research (BMBF)** within the ATLAS project (grant number 031L0304B) and by the **German Research Foundation (DFG)** within the Research Unit Program **FOR 5151 QuaLiPerF** (grant number 436883643) and the **Priority Programme SPP 2311 (Subproject SimLivA)** (grant number 465194077).
|
|
98
|
+
|
|
99
|
+
## Example visualization
|
|
100
|
+
The example visualization demonstrates the **Model** and **Model Metadata** FAIR indicators using the *BioModels_C19_curated* example.
|
|
101
|
+
"""
|
|
102
|
+
)
|
|
103
|
+
figs_example = visualize_polar_barplots(df_data=models["BioModels_C19_curated"])
|
|
104
|
+
col1a, col2a = st.columns(2)
|
|
105
|
+
with col1a:
|
|
106
|
+
st.plotly_chart(figs_example[0], key="pchart_example1")
|
|
107
|
+
with col2a:
|
|
108
|
+
st.plotly_chart(figs_example[1], key="pchart_example2")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
with tab_indicators:
|
|
112
|
+
st.dataframe(
|
|
113
|
+
data=df_indicators,
|
|
114
|
+
width="stretch",
|
|
115
|
+
column_config={
|
|
116
|
+
"Assessment": st.column_config.BarChartColumn(
|
|
117
|
+
"FAIR Assessment",
|
|
118
|
+
help="Assessment of all models (NA, 0.0, 0.5, 1.0)",
|
|
119
|
+
),
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
with tab_models:
|
|
125
|
+
col_select, col_fair, _ = st.columns(3)
|
|
126
|
+
with col_select:
|
|
127
|
+
model_id = st.selectbox(
|
|
128
|
+
"Select model",
|
|
129
|
+
index=0,
|
|
130
|
+
options=list(models.keys()),
|
|
131
|
+
)
|
|
132
|
+
df_model = models[model_id]
|
|
133
|
+
|
|
134
|
+
with col_fair:
|
|
135
|
+
fig_bar = visualize_barplot(df_data=df_model)
|
|
136
|
+
st.plotly_chart(fig_bar, width="content", key="pchart_models_bar1")
|
|
137
|
+
|
|
138
|
+
# plotly plot
|
|
139
|
+
figs = visualize_polar_barplots(df_data=df_model)
|
|
140
|
+
col1, col2 = st.columns(2)
|
|
141
|
+
with col1:
|
|
142
|
+
st.plotly_chart(figs[0], key="pchart_models_col1")
|
|
143
|
+
with col2:
|
|
144
|
+
st.plotly_chart(figs[1], key="pchart_models_col2")
|
|
145
|
+
col3, col4 = st.columns(2)
|
|
146
|
+
with col3:
|
|
147
|
+
st.plotly_chart(figs[2], key="pchart_models_col3")
|
|
148
|
+
with col4:
|
|
149
|
+
st.plotly_chart(figs[3], key="pchart_models_col4")
|
|
150
|
+
|
|
151
|
+
# show dataframe
|
|
152
|
+
st.dataframe(df_model, width="stretch")
|
|
153
|
+
|
|
154
|
+
with tab_assessment:
|
|
155
|
+
col_upload, col_template, col_fair_upload = st.columns(3, gap="medium")
|
|
156
|
+
with col_upload:
|
|
157
|
+
uploaded_xlsx = st.file_uploader(
|
|
158
|
+
"Upload FAIR model assessment",
|
|
159
|
+
type="xlsx",
|
|
160
|
+
accept_multiple_files=False,
|
|
161
|
+
)
|
|
162
|
+
df_model_upload: pd.DataFrame | None = None
|
|
163
|
+
if uploaded_xlsx is not None:
|
|
164
|
+
try:
|
|
165
|
+
df_model_upload = load_assessment(uploaded_xlsx)
|
|
166
|
+
except ValueError as err:
|
|
167
|
+
st.error(f"The uploaded file is no valid FAIR assessment: {err}")
|
|
168
|
+
|
|
169
|
+
with col_template:
|
|
170
|
+
st.html(
|
|
171
|
+
"""
|
|
172
|
+
The template for assessment is available here:
|
|
173
|
+
"""
|
|
174
|
+
)
|
|
175
|
+
st.download_button(
|
|
176
|
+
"Download FAIR Template",
|
|
177
|
+
TEMPLATE_PATH.read_bytes(),
|
|
178
|
+
file_name="FAIR_assessment_template.xlsx",
|
|
179
|
+
help="FAIR assessment template. Fill out and reupload for evaluation.",
|
|
180
|
+
)
|
|
181
|
+
with col_fair_upload:
|
|
182
|
+
if df_model_upload is not None:
|
|
183
|
+
fig_bar = visualize_barplot(df_data=df_model_upload)
|
|
184
|
+
st.plotly_chart(fig_bar, width="content")
|
|
185
|
+
|
|
186
|
+
if df_model_upload is not None:
|
|
187
|
+
# plotly plot
|
|
188
|
+
figs = visualize_polar_barplots(df_data=df_model_upload)
|
|
189
|
+
col1, col2 = st.columns(2)
|
|
190
|
+
with col1:
|
|
191
|
+
st.plotly_chart(figs[0], key="pchart_assessment_col1")
|
|
192
|
+
with col2:
|
|
193
|
+
st.plotly_chart(figs[1], key="pchart_assessment_col2")
|
|
194
|
+
col3, col4 = st.columns(2)
|
|
195
|
+
with col3:
|
|
196
|
+
st.plotly_chart(figs[2], key="pchart_assessment_col3")
|
|
197
|
+
with col4:
|
|
198
|
+
st.plotly_chart(figs[3], key="pchart_assessment_col4")
|
|
199
|
+
|
|
200
|
+
# show dataframe
|
|
201
|
+
st.dataframe(df_model_upload, width="stretch")
|
|
202
|
+
|
|
203
|
+
st.divider()
|
|
204
|
+
st.markdown(
|
|
205
|
+
"""
|
|
206
|
+
© 2024-2026 [Matthias König](https://livermetabolism.com) [](https://doi.org/10.5281/zenodo.13755820)
|
|
207
|
+
"""
|
|
208
|
+
)
|
fairvis/cli.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Console script `fairvis`, starts the app with streamlit.
|
|
2
|
+
|
|
3
|
+
The options of `.streamlit/config.toml` of the repository are not read when the
|
|
4
|
+
app runs from an installed package, so they are passed on the command line.
|
|
5
|
+
Further arguments are passed to `streamlit run`, e.g. `fairvis --server.port 8502`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import io
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from streamlit.web import cli as stcli
|
|
12
|
+
|
|
13
|
+
from fairvis.settings import APP_PATH
|
|
14
|
+
|
|
15
|
+
STREAMLIT_OPTIONS: tuple[str, ...] = (
|
|
16
|
+
"--browser.gatherUsageStats=false",
|
|
17
|
+
"--theme.base=light",
|
|
18
|
+
)
|
|
19
|
+
"""Default options of `streamlit run`, the same as in `.streamlit/config.toml`."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def streamlit_argv(args: list[str]) -> list[str]:
|
|
23
|
+
"""Command line of `streamlit run` for the app.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
args: further arguments for `streamlit run`; they follow the defaults,
|
|
27
|
+
so they override them.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
the arguments, starting with the program name `streamlit`.
|
|
31
|
+
"""
|
|
32
|
+
return ["streamlit", "run", str(APP_PATH), *STREAMLIT_OPTIONS, *args]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main() -> None:
|
|
36
|
+
"""Start the FAIR-CA visualization app.
|
|
37
|
+
|
|
38
|
+
The output is written as UTF-8: the messages of streamlit contain non ascii
|
|
39
|
+
characters, which the ansi code page of a redirected stdout on windows, e.g.
|
|
40
|
+
cp1252, cannot encode (UTF-8 is the default from python 3.15 on, PEP 686).
|
|
41
|
+
"""
|
|
42
|
+
for stream in (sys.stdout, sys.stderr):
|
|
43
|
+
if isinstance(stream, io.TextIOWrapper):
|
|
44
|
+
stream.reconfigure(encoding="utf-8")
|
|
45
|
+
sys.argv = streamlit_argv(sys.argv[1:])
|
|
46
|
+
sys.exit(stcli.main())
|
fairvis/console.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Rich console for logging."""
|
|
2
|
+
|
|
3
|
+
from rich import pretty
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.theme import Theme
|
|
6
|
+
|
|
7
|
+
pretty.install()
|
|
8
|
+
custom_theme = Theme(
|
|
9
|
+
{
|
|
10
|
+
"success": "green",
|
|
11
|
+
"info": "blue",
|
|
12
|
+
"warning": "orange3",
|
|
13
|
+
"error": "red",
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
console = Console(record=True, theme=custom_theme)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
fairvis/data_io.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Loading and validation of FAIR assessments and the indicators."""
|
|
2
|
+
|
|
3
|
+
from numbers import Real
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import BinaryIO
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from fairvis.console import console
|
|
11
|
+
from fairvis.settings import ASSESSMENTS_PATH, TEMPLATE_PATH
|
|
12
|
+
|
|
13
|
+
REQUIRED_COLUMNS: tuple[str, ...] = (
|
|
14
|
+
"ID",
|
|
15
|
+
"Short",
|
|
16
|
+
"Type",
|
|
17
|
+
"Category",
|
|
18
|
+
"Priority",
|
|
19
|
+
"Indicator",
|
|
20
|
+
"Description",
|
|
21
|
+
"Assessment details",
|
|
22
|
+
"Assessment",
|
|
23
|
+
)
|
|
24
|
+
"""Columns of the template an assessment needs."""
|
|
25
|
+
|
|
26
|
+
ASSESSMENT_VALUES: tuple[float, ...] = (0.0, 0.5, 1.0)
|
|
27
|
+
"""Allowed assessment values besides NA (not applicable, an empty cell)."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def assessment_class(value: object) -> int:
|
|
31
|
+
"""Class of an assessment value.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
value: assessment of an indicator, NaN for NA.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
0 for NA, 1 for 0.0, 2 for 0.5 and 3 for 1.0.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ValueError: if the value is none of NA, 0, 0.5 and 1.
|
|
41
|
+
"""
|
|
42
|
+
if isinstance(value, Real) and not isinstance(value, bool):
|
|
43
|
+
number = float(value)
|
|
44
|
+
if np.isnan(number):
|
|
45
|
+
return 0
|
|
46
|
+
for k, reference in enumerate(ASSESSMENT_VALUES, start=1):
|
|
47
|
+
if np.isclose(number, reference):
|
|
48
|
+
return k
|
|
49
|
+
if isinstance(value, str) and _is_number(value):
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"assessment value {value!r} is stored as text, enter it as a number"
|
|
52
|
+
)
|
|
53
|
+
raise ValueError(f"assessment value {value!r} is none of NA, 0, 0.5, 1")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _is_number(text: str) -> bool:
|
|
57
|
+
try:
|
|
58
|
+
float(text)
|
|
59
|
+
except ValueError:
|
|
60
|
+
return False
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate_assessment(df: pd.DataFrame) -> None:
|
|
65
|
+
"""Validate the structure and the values of a raw assessment.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
df: assessment as read from the xlsx file, before the `ID` index is set.
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: if a column of the template is missing or an assessment value
|
|
72
|
+
is none of NA, 0, 0.5 and 1.
|
|
73
|
+
"""
|
|
74
|
+
missing = [column for column in REQUIRED_COLUMNS if column not in df.columns]
|
|
75
|
+
if missing:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"The assessment misses the columns {', '.join(missing)}. "
|
|
78
|
+
"Fill out the FAIR assessment template."
|
|
79
|
+
)
|
|
80
|
+
for indicator_id, value in zip(df["ID"], df["Assessment"], strict=True):
|
|
81
|
+
try:
|
|
82
|
+
assessment_class(value)
|
|
83
|
+
except ValueError as err:
|
|
84
|
+
raise ValueError(f"Indicator '{indicator_id}': {err}.") from err
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def load_assessment(source: Path | BinaryIO) -> pd.DataFrame:
|
|
88
|
+
"""Load an assessment from an xlsx file.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
source: path of the xlsx file or an uploaded file.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
assessment indexed by the indicator `ID`, without the description columns
|
|
95
|
+
and without columns lacking a header (named `Unnamed: <n>` by pandas).
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
ValueError: if the file is no readable xlsx file or no valid assessment.
|
|
99
|
+
"""
|
|
100
|
+
try:
|
|
101
|
+
df = pd.read_excel(source, sheet_name=0)
|
|
102
|
+
except Exception as err:
|
|
103
|
+
raise ValueError(f"The file is no readable xlsx file: {err}") from err
|
|
104
|
+
validate_assessment(df)
|
|
105
|
+
unnamed = [column for column in df.columns if str(column).startswith("Unnamed")]
|
|
106
|
+
return df.set_index("ID").drop(
|
|
107
|
+
columns=["Description", "Assessment details", *unnamed]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def load_model_assessments(
|
|
112
|
+
assessment_dir: Path = ASSESSMENTS_PATH,
|
|
113
|
+
) -> dict[str, pd.DataFrame]:
|
|
114
|
+
"""Load all model assessments of a directory.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
assessment_dir: directory with one xlsx file per model.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
assessments by model id (the file name without suffix), sorted by model id.
|
|
121
|
+
"""
|
|
122
|
+
return {
|
|
123
|
+
path.stem: load_assessment(path)
|
|
124
|
+
for path in sorted(assessment_dir.glob("*.xlsx"))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def load_indicators(df_models: dict[str, pd.DataFrame]) -> pd.DataFrame:
|
|
129
|
+
"""Load the indicators and count their assessments over all models.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
df_models: assessments by model id, see `load_model_assessments`.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
indicators indexed by `ID`; the column `Assessment` holds the number of
|
|
136
|
+
models with NA, 0, 0.5 and 1 for the indicator.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ValueError: if an assessment has other indicators than the template.
|
|
140
|
+
"""
|
|
141
|
+
df_indicators = (
|
|
142
|
+
pd.read_excel(TEMPLATE_PATH, sheet_name=0)
|
|
143
|
+
.set_index("ID")
|
|
144
|
+
.drop(columns=["Description", "Assessment details", "Assessment", "Comment"])
|
|
145
|
+
)
|
|
146
|
+
assessments = [[0.0, 0.0, 0.0, 0.0] for _ in range(len(df_indicators))]
|
|
147
|
+
for model_id, df_model in df_models.items():
|
|
148
|
+
if not df_model.index.sort_values().equals(df_indicators.index.sort_values()):
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"The assessment '{model_id}' has other indicators than the template."
|
|
151
|
+
)
|
|
152
|
+
values = df_model["Assessment"].reindex(df_indicators.index).to_numpy()
|
|
153
|
+
for k, value in enumerate(values):
|
|
154
|
+
assessments[k][assessment_class(value)] += 1.0
|
|
155
|
+
|
|
156
|
+
df_indicators["Assessment"] = assessments
|
|
157
|
+
return df_indicators
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
df_indicators = load_indicators(load_model_assessments())
|
|
162
|
+
console.rule("Indicators", align="left", style="red")
|
|
163
|
+
console.print(df_indicators)
|
fairvis/example.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Example showing the figures of a bundled assessment in the browser."""
|
|
2
|
+
|
|
3
|
+
from fairvis.data_io import load_assessment
|
|
4
|
+
from fairvis.settings import ASSESSMENTS_PATH
|
|
5
|
+
from fairvis.visualization import visualize_barplot, visualize_polar_barplots
|
|
6
|
+
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
df = load_assessment(ASSESSMENTS_PATH / "BioModels_curated.xlsx")
|
|
9
|
+
visualize_barplot(df).show()
|
|
10
|
+
visualize_polar_barplots(df)[0].show()
|
fairvis/settings.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Paths of the app and of the data bundled with the package."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
PACKAGE_PATH: Path = Path(__file__).parent
|
|
6
|
+
"""Directory of the installed `fairvis` package."""
|
|
7
|
+
|
|
8
|
+
APP_PATH: Path = PACKAGE_PATH / "app.py"
|
|
9
|
+
"""Streamlit script of the app."""
|
|
10
|
+
|
|
11
|
+
DATA_PATH: Path = PACKAGE_PATH / "data"
|
|
12
|
+
"""Data bundled with the package."""
|
|
13
|
+
|
|
14
|
+
ASSESSMENTS_PATH: Path = DATA_PATH / "assessments"
|
|
15
|
+
"""Example assessments, one xlsx file per model."""
|
|
16
|
+
|
|
17
|
+
TEMPLATE_PATH: Path = DATA_PATH / "FAIR_assessment_template.xlsx"
|
|
18
|
+
"""Template of a FAIR assessment, also the definition of the indicators."""
|
fairvis/visualization.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Plotly figures of a FAIR assessment."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import plotly.express as px
|
|
6
|
+
import plotly.graph_objects as go
|
|
7
|
+
|
|
8
|
+
SUBSET_KEYS: tuple[str, ...] = (
|
|
9
|
+
"Model",
|
|
10
|
+
"Model metadata",
|
|
11
|
+
"Archive",
|
|
12
|
+
"Archive metadata",
|
|
13
|
+
)
|
|
14
|
+
"""Types of indicators, one polar figure each."""
|
|
15
|
+
|
|
16
|
+
COLOR_DISCRETE_SEQUENCE: tuple[str, ...] = (
|
|
17
|
+
"#1f77b4", # muted blue
|
|
18
|
+
"#ff7f0e", # safety orange
|
|
19
|
+
"#2ca02c", # cooked asparagus green
|
|
20
|
+
"#d62728", # brick red
|
|
21
|
+
"#9467bd", # muted purple
|
|
22
|
+
"#8c564b", # chestnut brown
|
|
23
|
+
"#e377c2", # raspberry yogurt pink
|
|
24
|
+
"#7f7f7f", # middle gray
|
|
25
|
+
"#bcbd22", # curry yellow-green
|
|
26
|
+
"#17becf", # blue-teal
|
|
27
|
+
)
|
|
28
|
+
"""Colors of the categories."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def visualize_barplot(df_data: pd.DataFrame) -> go.Figure:
|
|
32
|
+
"""Bar plot of the fraction of the indicators fulfilled per category.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
df_data: assessment of a model, see `fairvis.data_io.load_assessment`.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
bar plot with the FAIR percentage per category.
|
|
39
|
+
"""
|
|
40
|
+
# Count the fraction of indicators fullfilled per category
|
|
41
|
+
categories = df_data["Category"].unique()
|
|
42
|
+
|
|
43
|
+
values = dict.fromkeys(categories, 0.0)
|
|
44
|
+
totals = dict.fromkeys(categories, 0.0)
|
|
45
|
+
for _, row in df_data.iterrows():
|
|
46
|
+
category = row["Category"]
|
|
47
|
+
assessment = row["Assessment"]
|
|
48
|
+
totals[category] += 1.0
|
|
49
|
+
if not np.isnan(assessment):
|
|
50
|
+
values[category] += assessment
|
|
51
|
+
|
|
52
|
+
items = []
|
|
53
|
+
for category in categories:
|
|
54
|
+
items.append(
|
|
55
|
+
{
|
|
56
|
+
"category": category,
|
|
57
|
+
"value": values[category],
|
|
58
|
+
"total": totals[category],
|
|
59
|
+
"percentage": 100 * values[category] / totals[category],
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
df = pd.DataFrame(items)
|
|
64
|
+
|
|
65
|
+
# barplot
|
|
66
|
+
fig = px.bar(
|
|
67
|
+
df,
|
|
68
|
+
x="category",
|
|
69
|
+
y="percentage",
|
|
70
|
+
color="category", # title="FAIR assessment",
|
|
71
|
+
color_discrete_sequence=list(COLOR_DISCRETE_SEQUENCE),
|
|
72
|
+
labels={"category": "Category", "percentage": "FAIR [%]"},
|
|
73
|
+
)
|
|
74
|
+
fig.update_layout(
|
|
75
|
+
title="FAIR assessment",
|
|
76
|
+
yaxis_range=[0, 100],
|
|
77
|
+
showlegend=False,
|
|
78
|
+
height=250,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return fig
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def visualize_polar_barplots(df_data: pd.DataFrame) -> list[go.Figure]:
|
|
85
|
+
"""Polar bar plots of the indicators of a model, one per type of indicator.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
df_data: assessment of a model, see `fairvis.data_io.load_assessment`.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
one polar bar plot per entry of `SUBSET_KEYS`, in that order.
|
|
92
|
+
"""
|
|
93
|
+
figs = []
|
|
94
|
+
for key in SUBSET_KEYS:
|
|
95
|
+
df = df_data[df_data["Type"] == key]
|
|
96
|
+
fig = px.bar_polar(
|
|
97
|
+
df,
|
|
98
|
+
r="Assessment",
|
|
99
|
+
theta="Short",
|
|
100
|
+
color="Category",
|
|
101
|
+
hover_name="Indicator",
|
|
102
|
+
hover_data=["Indicator", "Category", "Priority"],
|
|
103
|
+
color_discrete_sequence=list(COLOR_DISCRETE_SEQUENCE),
|
|
104
|
+
)
|
|
105
|
+
fig.update_layout(title=key, showlegend=True)
|
|
106
|
+
fig.update_polars(radialaxis_tickvals=[0, 0.5, 1], angularaxis_showgrid=True)
|
|
107
|
+
figs.append(fig)
|
|
108
|
+
|
|
109
|
+
return figs
|