66from 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