Skip to content

Commit 018265b

Browse files
0.29.5
randorient fixed
1 parent 0f0b554 commit 018265b

5 files changed

Lines changed: 312 additions & 60 deletions

File tree

notebooks/00_spotPython_tests.ipynb

Lines changed: 41 additions & 12 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "spotpython"
10-
version = "0.29.4"
10+
version = "0.29.5"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotpython/utils/effects.py

Lines changed: 185 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,64 @@
66
from sklearn.model_selection import train_test_split
77

88

9-
def randorient(k, p, xi):
9+
def randorient(k, p, xi, seed=None) -> np.ndarray:
10+
"""Generates a random orientation of a sampling matrix.
11+
This function creates a random sampling matrix for a given number of
12+
dimensions (k), number of levels (p), and step length (xi). The
13+
resulting matrix is used for screening designs in the context of
14+
experimental design.
15+
16+
Args:
17+
k (int): Number of dimensions.
18+
p (int): Number of levels.
19+
xi (float): Step length.
20+
seed (int, optional): Seed for the random number generator.
21+
Defaults to None.
22+
23+
Returns:
24+
np.ndarray: A random sampling matrix of shape (k+1, k).
25+
26+
Example:
27+
>>> randorient(k=2, p=3, xi=0.5)
28+
array([[0. , 0. ],
29+
[0.5, 0.5],
30+
[1. , 1. ]])
31+
"""
32+
# Initialize random number generator with the provided seed
33+
if seed is not None:
34+
rng = np.random.default_rng(seed)
35+
else:
36+
rng = np.random.default_rng()
37+
1038
# Step length
1139
Delta = xi / (p - 1)
1240

1341
m = k + 1
1442

1543
# A truncated p-level grid in one dimension
16-
xs = np.arange(0, 1, Delta)
44+
xs = np.arange(0, 1 - Delta, 1 / (p - 1))
1745
xsl = len(xs)
46+
if xsl < 1:
47+
print(f"xi = {xi}.")
48+
print(f"p = {p}.")
49+
print(f"Delta = {Delta}.")
50+
print(f"p - 1 = {p - 1}.")
51+
raise ValueError(f"The number of levels xsl is {xsl}, but it must be greater than 0.")
1852

1953
# Basic sampling matrix
2054
B = np.vstack((np.zeros((1, k)), np.tril(np.ones((k, k)))))
2155

2256
# Randomization
2357

2458
# Matrix with +1s and -1s on the diagonal with equal probability
25-
Dstar = np.diag(2 * np.round(np.random.rand(k)) - 1)
59+
Dstar = np.diag(2 * rng.integers(0, 2, size=k) - 1)
2660

2761
# Random base value
28-
xstar = xs[(np.random.rand(k) * xsl).astype(int)]
62+
xstar = xs[rng.integers(0, xsl, size=k)]
2963

3064
# Permutation matrix
3165
Pstar = np.zeros((k, k))
32-
rp = np.random.permutation(k)
66+
rp = rng.permutation(k)
3367
for i in range(k):
3468
Pstar[i, rp[i]] = 1
3569

@@ -52,12 +86,8 @@ def screeningplan(k, p, xi, r):
5286
return X
5387

5488

55-
def screening(X, fun, xi, p, labels, bounds=None, print=False) -> pd.DataFrame:
56-
"""Generates a DataFrame with elementary effect screening metrics.
57-
58-
This function calculates the mean and standard deviation of the
59-
elementary effects for a given set of design variables and returns
60-
the results as a Pandas DataFrame.
89+
def _screening(X, fun, xi, p, labels, bounds=None) -> tuple:
90+
"""Helper function to calculate elementary effects for a screening design.
6191
6292
Args:
6393
X (np.ndarray): The screening plan matrix, typically structured
@@ -73,13 +103,10 @@ def screening(X, fun, xi, p, labels, bounds=None, print=False) -> pd.DataFrame:
73103
each variable.
74104
75105
Returns:
76-
pd.DataFrame: A DataFrame containing three columns:
77-
- 'varname': The name of each variable.
78-
- 'mean': The mean of the elementary effects for each variable.
79-
- 'sd': The standard deviation of the elementary effects for
106+
tuple: A tuple containing two arrays:
107+
- sm: The mean of the elementary effects for each variable.
108+
- ssd: The standard deviation of the elementary effects for
80109
each variable.
81-
or None: If print is set to False, a plot of the results is
82-
generated instead of returning a DataFrame.
83110
84111
Examples:
85112
>>> import numpy as np
@@ -131,23 +158,148 @@ def screening(X, fun, xi, p, labels, bounds=None, print=False) -> pd.DataFrame:
131158
# Statistical measures (divide by n)
132159
ssd = np.std(F, axis=1, ddof=0)
133160
sm = np.mean(F, axis=1)
161+
return sm, ssd
134162

135-
if print:
136-
idx = np.argsort(-np.abs(sm))
137-
sorted_labels = [labels[i] for i in idx]
138-
sm = sm[idx]
139-
ssd = ssd[idx]
140-
df = pd.DataFrame({"varname": sorted_labels, "mean": sm, "sd": ssd})
141-
return df
142-
else:
143-
plt.figure()
144-
for i in range(k):
145-
plt.text(sm[i], ssd[i], labels[i], fontsize=10)
146-
plt.axis([min(sm), 1.1 * max(sm), min(ssd), 1.1 * max(ssd)])
147-
plt.xlabel("Sample means")
148-
plt.ylabel("Sample standard deviations")
149-
plt.gca().tick_params(labelsize=10)
150-
plt.grid(True)
163+
164+
def screening_print(X, fun, xi, p, labels, bounds=None) -> pd.DataFrame:
165+
"""Generates a DataFrame with elementary effect screening metrics.
166+
167+
This function calculates the mean and standard deviation of the
168+
elementary effects for a given set of design variables and returns
169+
the results as a Pandas DataFrame.
170+
171+
Args:
172+
X (np.ndarray): The screening plan matrix, typically structured
173+
within a [0,1]^k box.
174+
fun (object): The objective function to evaluate at each
175+
design point in the screening plan.
176+
xi (float): The elementary effect step length factor.
177+
p (int): Number of discrete levels along each dimension.
178+
labels (list of str): A list of variable names corresponding to
179+
the design variables.
180+
bounds (np.ndarray): A 2xk matrix where the first row contains
181+
lower bounds and the second row contains upper bounds for
182+
each variable.
183+
184+
Returns:
185+
pd.DataFrame: A DataFrame containing three columns:
186+
- 'varname': The name of each variable.
187+
- 'mean': The mean of the elementary effects for each variable.
188+
- 'sd': The standard deviation of the elementary effects for
189+
each variable.
190+
or None: If print is set to False, a plot of the results is
191+
generated instead of returning a DataFrame.
192+
193+
Examples:
194+
>>> import numpy as np
195+
from spotpython.utils.effects import screening, screeningplan
196+
from spotpython.fun.objectivefunctions import Analytical
197+
fun = Analytical()
198+
k = 10
199+
p = 10
200+
xi = 1
201+
r = 25
202+
X = screeningplan(k=k, p=p, xi=xi, r=r) # shape (r x (k+1), k)
203+
# Provide real-world bounds from the wing weight docs (2 x 10).
204+
value_range = np.array([
205+
[150, 220, 6, -10, 16, 0.5, 0.08, 2.5, 1700, 0.025],
206+
[200, 300, 10, 10, 45, 1.0, 0.18, 6.0, 2500, 0.08 ],
207+
])
208+
labels = [
209+
"S_W", "W_fw", "A", "Lambda",
210+
"q", "lambda", "tc", "N_z",
211+
"W_dg", "W_p"
212+
]
213+
screening(
214+
X=X,
215+
fun=fun.fun_wingwt,
216+
bounds=value_range,
217+
xi=xi,
218+
p=p,
219+
labels=labels,
220+
print=False,
221+
)
222+
"""
223+
sm, ssd = _screening(X=X, fun=fun, xi=xi, p=p, labels=labels, bounds=bounds)
224+
idx = np.argsort(-np.abs(sm))
225+
sorted_labels = [labels[i] for i in idx]
226+
sm = sm[idx]
227+
ssd = ssd[idx]
228+
df = pd.DataFrame({"varname": sorted_labels, "mean": sm, "sd": ssd})
229+
return df
230+
231+
232+
def screening_plot(X, fun, xi, p, labels, bounds=None, show=True):
233+
"""Generates a plot with elementary effect screening metrics.
234+
235+
This function calculates the mean and standard deviation of the
236+
elementary effects for a given set of design variables and plots
237+
the results.
238+
239+
Args:
240+
X (np.ndarray): The screening plan matrix, typically structured
241+
within a [0,1]^k box.
242+
fun (object): The objective function to evaluate at each
243+
design point in the screening plan.
244+
xi (float): The elementary effect step length factor.
245+
p (int): Number of discrete levels along each dimension.
246+
labels (list of str): A list of variable names corresponding to
247+
the design variables.
248+
bounds (np.ndarray): A 2xk matrix where the first row contains
249+
lower bounds and the second row contains upper bounds for
250+
each variable.
251+
show: (bool): If True, the plot is displayed. Defaults to True.
252+
253+
Returns:
254+
pd.DataFrame: A DataFrame containing three columns:
255+
- 'varname': The name of each variable.
256+
- 'mean': The mean of the elementary effects for each variable.
257+
- 'sd': The standard deviation of the elementary effects for
258+
each variable.
259+
or None: If print is set to False, a plot of the results is
260+
generated instead of returning a DataFrame.
261+
262+
Examples:
263+
>>> import numpy as np
264+
from spotpython.utils.effects import screening, screeningplan
265+
from spotpython.fun.objectivefunctions import Analytical
266+
fun = Analytical()
267+
k = 10
268+
p = 10
269+
xi = 1
270+
r = 25
271+
X = screeningplan(k=k, p=p, xi=xi, r=r) # shape (r x (k+1), k)
272+
# Provide real-world bounds from the wing weight docs (2 x 10).
273+
value_range = np.array([
274+
[150, 220, 6, -10, 16, 0.5, 0.08, 2.5, 1700, 0.025],
275+
[200, 300, 10, 10, 45, 1.0, 0.18, 6.0, 2500, 0.08 ],
276+
])
277+
labels = [
278+
"S_W", "W_fw", "A", "Lambda",
279+
"q", "lambda", "tc", "N_z",
280+
"W_dg", "W_p"
281+
]
282+
screening(
283+
X=X,
284+
fun=fun.fun_wingwt,
285+
bounds=value_range,
286+
xi=xi,
287+
p=p,
288+
labels=labels,
289+
print=False,
290+
)
291+
"""
292+
k = X.shape[1]
293+
sm, ssd = _screening(X=X, fun=fun, xi=xi, p=p, labels=labels, bounds=bounds)
294+
plt.figure()
295+
for i in range(k):
296+
plt.text(sm[i], ssd[i], labels[i], fontsize=10)
297+
plt.axis([min(sm), 1.1 * max(sm), min(ssd), 1.1 * max(ssd)])
298+
plt.xlabel("Sample means")
299+
plt.ylabel("Sample standard deviations")
300+
plt.gca().tick_params(labelsize=10)
301+
plt.grid(True)
302+
if show:
151303
plt.show()
152304

153305

0 commit comments

Comments
 (0)