simdec 1.5.0__tar.gz → 1.5.2__tar.gz
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.
- {simdec-1.5.0 → simdec-1.5.2}/.gitignore +8 -0
- {simdec-1.5.0 → simdec-1.5.2}/PKG-INFO +6 -3
- {simdec-1.5.0 → simdec-1.5.2}/README.md +1 -0
- simdec-1.5.2/netlify.toml +24 -0
- simdec-1.5.2/package.json +10 -0
- {simdec-1.5.0 → simdec-1.5.2}/pyproject.toml +6 -3
- simdec-1.5.2/scripts/build_wasm.sh +140 -0
- {simdec-1.5.0 → simdec-1.5.2}/src/simdec/__init__.py +3 -0
- {simdec-1.5.0 → simdec-1.5.2}/src/simdec/decomposition.py +29 -14
- simdec-1.5.2/src/simdec/heterogeneity_indices.py +642 -0
- simdec-1.5.2/src/simdec/sensitivity_indices.py +224 -0
- {simdec-1.5.0 → simdec-1.5.2}/src/simdec/visualization.py +183 -2
- simdec-1.5.0/src/simdec/sensitivity_indices.py +0 -170
- {simdec-1.5.0 → simdec-1.5.2}/CODE_OF_CONDUCT.md +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/CONTRIBUTING.md +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/LICENSE.txt +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/joss/paper.bib +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/joss/paper.md +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/joss/simdec_presentation.png +0 -0
- {simdec-1.5.0 → simdec-1.5.2}/src/simdec/auth.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: simdec
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: Sensitivity analysis using simulation decomposition
|
|
5
5
|
Project-URL: homepage, https://www.simdec.fi/
|
|
6
6
|
Project-URL: documentation, https://simdec.readthedocs.io
|
|
@@ -30,7 +30,7 @@ Requires-Dist: panel>=1.4.5; extra == 'dashboard'
|
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: accessible-pygments; extra == 'dev'
|
|
32
32
|
Requires-Dist: cryptography; extra == 'dev'
|
|
33
|
-
Requires-Dist:
|
|
33
|
+
Requires-Dist: ipython>=9.1; extra == 'dev'
|
|
34
34
|
Requires-Dist: myst-nb; extra == 'dev'
|
|
35
35
|
Requires-Dist: numpydoc; extra == 'dev'
|
|
36
36
|
Requires-Dist: panel>=1.4.5; extra == 'dev'
|
|
@@ -40,6 +40,8 @@ Requires-Dist: pytest; extra == 'dev'
|
|
|
40
40
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
41
41
|
Requires-Dist: sphinx; extra == 'dev'
|
|
42
42
|
Requires-Dist: watchfiles; extra == 'dev'
|
|
43
|
+
Provides-Extra: display
|
|
44
|
+
Requires-Dist: ipython>=9.1; extra == 'display'
|
|
43
45
|
Provides-Extra: doc
|
|
44
46
|
Requires-Dist: accessible-pygments; extra == 'doc'
|
|
45
47
|
Requires-Dist: myst-nb; extra == 'doc'
|
|
@@ -95,5 +97,6 @@ Foundation, and Finnish Foundation for Economic Education.
|
|
|
95
97
|
If you use SimDec in your research we would appreciate a citation to the
|
|
96
98
|
following publications:
|
|
97
99
|
|
|
100
|
+
- Kozlova, M., Ahola, A., Roy, P., & Yeomans, J. S. (2025). Simple Binning Algorithm and SimDec Visualization for Comprehensive Sensitivity Analysis of Complex Computational Models. _Journal of Environmental Informatics Letters_, 13(1), 38-56. [https://arxiv.org/pdf/2310.13446](https://arxiv.org/pdf/2310.13446)
|
|
98
101
|
- Kozlova, M., Moss, R. J., Yeomans, J. S., & Caers, J. (2024). Uncovering Heterogeneous Effects in Computational Models for Sustainable Decision-making. _Environmental Modelling & Software_, 171, 105898. [https://doi.org/10.1016/j.envsoft.2023.105898](https://doi.org/10.1016/j.envsoft.2023.105898)
|
|
99
102
|
- Kozlova, M., Moss, R. J., Roy, P., Alam, A., & Yeomans, J. S. (2024). SimDec algorithm and guidelines for its usage and interpretation. In M. Kozlova & J. S. Yeomans (Eds.), _Sensitivity Analysis for Business, Technology, and Policymaking. Made Easy with Simulation Decomposition_. Routledge. [Available here](https://github.com/Simulation-Decomposition/SimDec-book/blob/74ce72c1d3dda650eba1c59e3b215a4bb35c6be0/chapters/02_SimDec_algorithm_and_instructions/Ch2.pdf).
|
|
@@ -42,5 +42,6 @@ Foundation, and Finnish Foundation for Economic Education.
|
|
|
42
42
|
If you use SimDec in your research we would appreciate a citation to the
|
|
43
43
|
following publications:
|
|
44
44
|
|
|
45
|
+
- Kozlova, M., Ahola, A., Roy, P., & Yeomans, J. S. (2025). Simple Binning Algorithm and SimDec Visualization for Comprehensive Sensitivity Analysis of Complex Computational Models. _Journal of Environmental Informatics Letters_, 13(1), 38-56. [https://arxiv.org/pdf/2310.13446](https://arxiv.org/pdf/2310.13446)
|
|
45
46
|
- Kozlova, M., Moss, R. J., Yeomans, J. S., & Caers, J. (2024). Uncovering Heterogeneous Effects in Computational Models for Sustainable Decision-making. _Environmental Modelling & Software_, 171, 105898. [https://doi.org/10.1016/j.envsoft.2023.105898](https://doi.org/10.1016/j.envsoft.2023.105898)
|
|
46
47
|
- Kozlova, M., Moss, R. J., Roy, P., Alam, A., & Yeomans, J. S. (2024). SimDec algorithm and guidelines for its usage and interpretation. In M. Kozlova & J. S. Yeomans (Eds.), _Sensitivity Analysis for Business, Technology, and Policymaking. Made Easy with Simulation Decomposition_. Routledge. [Available here](https://github.com/Simulation-Decomposition/SimDec-book/blob/74ce72c1d3dda650eba1c59e3b215a4bb35c6be0/chapters/02_SimDec_algorithm_and_instructions/Ch2.pdf).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build]
|
|
2
|
+
command = "npm run build"
|
|
3
|
+
publish = "dist/pyodide"
|
|
4
|
+
|
|
5
|
+
[dev]
|
|
6
|
+
ignore = ["dist", ".wasm-build-venv", "panel/data"]
|
|
7
|
+
port = 8888
|
|
8
|
+
|
|
9
|
+
[build.environment]
|
|
10
|
+
PYTHON_VERSION = "3.11"
|
|
11
|
+
NODE_VERSION = "20"
|
|
12
|
+
CACHE_VERSION = "2"
|
|
13
|
+
|
|
14
|
+
[[headers]]
|
|
15
|
+
for = "/*"
|
|
16
|
+
[headers.values]
|
|
17
|
+
Cross-Origin-Embedder-Policy = "require-corp"
|
|
18
|
+
Cross-Origin-Opener-Policy = "same-origin"
|
|
19
|
+
|
|
20
|
+
[[headers]]
|
|
21
|
+
for = "/*.whl"
|
|
22
|
+
[headers.values]
|
|
23
|
+
Content-Type = "application/zip"
|
|
24
|
+
Access-Control-Allow-Origin = "*"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simdec-wasm-site",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Static Panel WASM build wrapper",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "bash ./scripts/build_wasm.sh",
|
|
8
|
+
"serve": "mkdir -p dist/pyodide && python3 -m http.server 8000 --directory dist/pyodide"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simdec"
|
|
7
|
-
version = "1.5.
|
|
7
|
+
version = "1.5.2"
|
|
8
8
|
description = "Sensitivity analysis using simulation decomposition"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -41,6 +41,10 @@ dashboard = [
|
|
|
41
41
|
"cryptography",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
|
+
display = [
|
|
45
|
+
"ipython>=9.1"
|
|
46
|
+
]
|
|
47
|
+
|
|
44
48
|
test = [
|
|
45
49
|
"pytest",
|
|
46
50
|
"pytest-cov",
|
|
@@ -55,10 +59,9 @@ doc = [
|
|
|
55
59
|
]
|
|
56
60
|
|
|
57
61
|
dev = [
|
|
58
|
-
"simdec[doc,test,dashboard]",
|
|
62
|
+
"simdec[doc,test,dashboard, display]",
|
|
59
63
|
"watchfiles",
|
|
60
64
|
"pre-commit",
|
|
61
|
-
"hatch",
|
|
62
65
|
]
|
|
63
66
|
|
|
64
67
|
[project.urls]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# Logging
|
|
4
|
+
set -x
|
|
5
|
+
|
|
6
|
+
# Setup paths and environment
|
|
7
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
8
|
+
OUT_DIR="${ROOT_DIR}/dist/pyodide"
|
|
9
|
+
|
|
10
|
+
# Wipe old build before starting
|
|
11
|
+
if [ -d "${OUT_DIR}" ]; then
|
|
12
|
+
echo "Cleaning old build..."
|
|
13
|
+
rm -rf "${OUT_DIR}"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
cd "${ROOT_DIR}"
|
|
17
|
+
|
|
18
|
+
# Determine which Python interpreter to use
|
|
19
|
+
HOST_PYTHON="${PYTHON_BIN:-}"
|
|
20
|
+
if [[ -z "${HOST_PYTHON}" ]]; then
|
|
21
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
22
|
+
HOST_PYTHON="python3"
|
|
23
|
+
elif command -v python >/dev/null 2>&1; then
|
|
24
|
+
HOST_PYTHON="python"
|
|
25
|
+
else
|
|
26
|
+
echo "Error: No python interpreter found. Install python3 and retry."
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Create build virtual environment
|
|
32
|
+
VENV_DIR="${ROOT_DIR}/.wasm-build-venv"
|
|
33
|
+
if [[ ! -d "${VENV_DIR}" ]]; then
|
|
34
|
+
echo "Creating build virtual environment at ${VENV_DIR}..."
|
|
35
|
+
"${HOST_PYTHON}" -m venv "${VENV_DIR}"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
PYTHON_BIN="${VENV_DIR}/bin/python"
|
|
39
|
+
|
|
40
|
+
# Install build dependencies and build the wheel
|
|
41
|
+
echo "Installing build tools and generating local wheel..."
|
|
42
|
+
"${PYTHON_BIN}" -m pip install --upgrade pip build \
|
|
43
|
+
"panel==1.8.10" \
|
|
44
|
+
"bokeh==3.8.0" \
|
|
45
|
+
"matplotlib==3.10.8" \
|
|
46
|
+
"seaborn==0.13.2" \
|
|
47
|
+
"scipy==1.17.1" \
|
|
48
|
+
"SALib==1.5.2"
|
|
49
|
+
# Clean old builds to avoid picking up the wrong wheel
|
|
50
|
+
rm -rf dist/*.whl
|
|
51
|
+
"${PYTHON_BIN}" -m build --wheel .
|
|
52
|
+
|
|
53
|
+
# Identify the generated wheel file
|
|
54
|
+
SIMDEC_WHEEL_PATH=$(ls dist/*.whl | head -n 1 || echo "")
|
|
55
|
+
|
|
56
|
+
if [[ -z "${SIMDEC_WHEEL_PATH}" ]]; then
|
|
57
|
+
echo "Error: No wheel file found in dist/. Build failed."
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
WHEEL_FILENAME=$(basename "${SIMDEC_WHEEL_PATH}")
|
|
62
|
+
|
|
63
|
+
# Prepare output directory - USE ABSOLUTE PATH
|
|
64
|
+
OUT_DIR="${ROOT_DIR}/dist/pyodide"
|
|
65
|
+
mkdir -p "${OUT_DIR}/_static"
|
|
66
|
+
mkdir -p "${OUT_DIR}/data"
|
|
67
|
+
|
|
68
|
+
# Copy the wheel into the output directory so it's accessible via HTTP
|
|
69
|
+
cp "${SIMDEC_WHEEL_PATH}" "${OUT_DIR}/"
|
|
70
|
+
|
|
71
|
+
# Copy the assets to where the Python scripts expect them
|
|
72
|
+
mkdir -p panel/_static
|
|
73
|
+
if [ -d "docs/_static" ]; then
|
|
74
|
+
# Use || true to prevent failure if it's empty
|
|
75
|
+
cp -r docs/_static/* panel/_static/ || true
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
echo "Converting Panel apps to Pyodide worker output..."
|
|
79
|
+
export PYTHONPATH="${ROOT_DIR}/src:${PYTHONPATH:-}"
|
|
80
|
+
|
|
81
|
+
# Change directory into 'panel' so relative paths resolve correctly
|
|
82
|
+
cd "${ROOT_DIR}/panel"
|
|
83
|
+
|
|
84
|
+
# Bring the wheel into the current folder so we can pass just the filename
|
|
85
|
+
cp "${ROOT_DIR}/${SIMDEC_WHEEL_PATH}" .
|
|
86
|
+
|
|
87
|
+
# Safe file checking that won't trigger set -e
|
|
88
|
+
echo "Checking necessary files locally before conversion..."
|
|
89
|
+
if [[ ! -f "simdec_app.py" ]]; then echo "Warning: simdec_app.py not found in panel directory!"; fi
|
|
90
|
+
if [[ ! -f "sampling.py" ]]; then echo "Warning: sampling.py not found in panel directory!"; fi
|
|
91
|
+
|
|
92
|
+
# Ensure data/stress.csv exists relative to the current 'panel' dir
|
|
93
|
+
if [[ ! -f "data/stress.csv" ]]; then
|
|
94
|
+
echo "Warning: panel/data/stress.csv missing."
|
|
95
|
+
if [[ -f "../data/stress.csv" ]]; then
|
|
96
|
+
echo "Found stress.csv in root. Copying to panel/data..."
|
|
97
|
+
mkdir -p data
|
|
98
|
+
cp "../data/stress.csv" data/
|
|
99
|
+
else
|
|
100
|
+
echo "Error: stress.csv not found anywhere! Conversion may fail."
|
|
101
|
+
fi
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Copy stress.csv to output directory
|
|
105
|
+
cp "data/stress.csv" "${OUT_DIR}/data/"
|
|
106
|
+
|
|
107
|
+
# Run conversion
|
|
108
|
+
"${PYTHON_BIN}" -m panel convert \
|
|
109
|
+
simdec_app.py \
|
|
110
|
+
sampling.py \
|
|
111
|
+
--to pyodide-worker \
|
|
112
|
+
--out "${OUT_DIR}" \
|
|
113
|
+
--requirements "${WHEEL_FILENAME}" numpy pandas matplotlib seaborn scipy SALib \
|
|
114
|
+
--resources data/stress.csv
|
|
115
|
+
|
|
116
|
+
cp "./${WHEEL_FILENAME}" "${OUT_DIR}/"
|
|
117
|
+
|
|
118
|
+
# Clean up the copied wheel
|
|
119
|
+
rm "${WHEEL_FILENAME}"
|
|
120
|
+
|
|
121
|
+
# Step back out to the root directory for the rest of the script
|
|
122
|
+
cd "${ROOT_DIR}"
|
|
123
|
+
|
|
124
|
+
# Copy custom index page and static assets
|
|
125
|
+
echo "Copying custom index page and static assets..."
|
|
126
|
+
|
|
127
|
+
# Copy images/thumbnails from docs/_static if they exist
|
|
128
|
+
if [ -d "docs/_static" ]; then
|
|
129
|
+
cp -r docs/_static/* "${OUT_DIR}/_static/" || true
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Overwrite default index.html with your custom homepage
|
|
133
|
+
if [ -f "panel/index.html" ]; then
|
|
134
|
+
cp panel/index.html "${OUT_DIR}/index.html"
|
|
135
|
+
else
|
|
136
|
+
echo "Warning: panel/index.html not found. Using default Panel index."
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
echo "---"
|
|
140
|
+
echo "WASM site successfully generated at ${OUT_DIR}"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""SimDec main namespace."""
|
|
2
2
|
from simdec.decomposition import *
|
|
3
|
+
from simdec.heterogeneity_indices import *
|
|
3
4
|
from simdec.sensitivity_indices import *
|
|
4
5
|
from simdec.visualization import *
|
|
5
6
|
|
|
@@ -8,6 +9,8 @@ __all__ = [
|
|
|
8
9
|
"states_expansion",
|
|
9
10
|
"decomposition",
|
|
10
11
|
"visualization",
|
|
12
|
+
"two_output_visualization",
|
|
11
13
|
"tableau",
|
|
12
14
|
"palette",
|
|
15
|
+
"heterogeneity_indices",
|
|
13
16
|
]
|
|
@@ -68,7 +68,7 @@ def decomposition(
|
|
|
68
68
|
output: pd.DataFrame,
|
|
69
69
|
*,
|
|
70
70
|
sensitivity_indices: np.ndarray,
|
|
71
|
-
dec_limit: float =
|
|
71
|
+
dec_limit: float | None = None,
|
|
72
72
|
auto_ordering: bool = True,
|
|
73
73
|
states: list[int] | None = None,
|
|
74
74
|
statistic: Literal["mean", "median"] | None = "mean",
|
|
@@ -79,7 +79,7 @@ def decomposition(
|
|
|
79
79
|
----------
|
|
80
80
|
inputs : DataFrame of shape (n_runs, n_factors)
|
|
81
81
|
Input variables.
|
|
82
|
-
output : DataFrame of shape (n_runs, 1)
|
|
82
|
+
output : DataFrame of shape (n_runs, 1) or (n_runs,)
|
|
83
83
|
Target variable.
|
|
84
84
|
sensitivity_indices : ndarray of shape (n_factors, 1)
|
|
85
85
|
Sensitivity indices, combined effect of each input.
|
|
@@ -116,7 +116,7 @@ def decomposition(
|
|
|
116
116
|
inputs[cat_col] = codes
|
|
117
117
|
|
|
118
118
|
inputs = inputs.to_numpy()
|
|
119
|
-
output = output.to_numpy()
|
|
119
|
+
output = output.to_numpy().flatten()
|
|
120
120
|
|
|
121
121
|
# 1. variables for decomposition
|
|
122
122
|
var_order = np.argsort(sensitivity_indices)[::-1]
|
|
@@ -125,26 +125,41 @@ def decomposition(
|
|
|
125
125
|
sensitivity_indices = sensitivity_indices[var_order]
|
|
126
126
|
|
|
127
127
|
if auto_ordering:
|
|
128
|
-
|
|
128
|
+
# handle edge case where sensitivity indices don't sum exactly to 1.0
|
|
129
|
+
if dec_limit is None:
|
|
130
|
+
dec_limit = 0.8 * np.sum(sensitivity_indices)
|
|
131
|
+
|
|
132
|
+
cumulative_sum = np.cumsum(sensitivity_indices)
|
|
133
|
+
indices_over_limit = np.where(cumulative_sum >= dec_limit)[0]
|
|
134
|
+
|
|
135
|
+
if indices_over_limit.size > 0:
|
|
136
|
+
n_var_dec = indices_over_limit[0] + 1
|
|
137
|
+
else:
|
|
138
|
+
n_var_dec = sensitivity_indices.size
|
|
139
|
+
|
|
129
140
|
n_var_dec = max(1, n_var_dec) # keep at least one variable
|
|
130
|
-
n_var_dec = min(
|
|
141
|
+
n_var_dec = min(4, n_var_dec) # use at most 4 variables
|
|
131
142
|
else:
|
|
132
143
|
n_var_dec = inputs.shape[1]
|
|
133
144
|
|
|
134
|
-
# 2.
|
|
145
|
+
# 2. variable selection and reordering
|
|
146
|
+
if auto_ordering:
|
|
147
|
+
var_names = var_names[var_order[:n_var_dec]].tolist()
|
|
148
|
+
inputs = inputs[:, var_order[:n_var_dec]]
|
|
149
|
+
else:
|
|
150
|
+
var_names = var_names[:n_var_dec].tolist()
|
|
151
|
+
inputs = inputs[:, :n_var_dec]
|
|
152
|
+
|
|
153
|
+
# 3. states formation (after reordering/selection)
|
|
135
154
|
if states is None:
|
|
136
|
-
states = 3 if n_var_dec
|
|
155
|
+
states = 3 if n_var_dec <= 2 else 2
|
|
137
156
|
states = [states] * n_var_dec
|
|
138
157
|
|
|
139
158
|
for i in range(n_var_dec):
|
|
140
159
|
n_unique = np.unique(inputs[:, i]).size
|
|
141
160
|
states[i] = n_unique if n_unique <= 5 else states[i]
|
|
142
161
|
|
|
143
|
-
|
|
144
|
-
var_names = var_names[var_order[:n_var_dec]].tolist()
|
|
145
|
-
inputs = inputs[:, var_order[:n_var_dec]]
|
|
146
|
-
|
|
147
|
-
# 3. decomposition
|
|
162
|
+
# 4. decomposition
|
|
148
163
|
bins = []
|
|
149
164
|
|
|
150
165
|
statistic_methods = {
|
|
@@ -153,8 +168,8 @@ def decomposition(
|
|
|
153
168
|
}
|
|
154
169
|
try:
|
|
155
170
|
statistic_method = statistic_methods[statistic]
|
|
156
|
-
except
|
|
157
|
-
msg = f"'statistic' must be one of {statistic_methods.
|
|
171
|
+
except KeyError:
|
|
172
|
+
msg = f"'statistic' must be one of {statistic_methods.keys()}"
|
|
158
173
|
raise ValueError(msg)
|
|
159
174
|
|
|
160
175
|
def statistic_(inputs):
|