easyvvuq.analysis.fd_analysis
Analysis element for polynomial chaos expansion (PCE). We use ChaosPy under the hood for this functionality.
1"""Analysis element for polynomial chaos expansion (PCE). We use ChaosPy 2under the hood for this functionality. 3""" 4import logging 5import chaospy as cp 6import numpy as np 7import numpoly 8import warnings 9from easyvvuq import OutputType 10from .base import BaseAnalysisElement 11from .results import AnalysisResults 12from .qmc_analysis import QMCAnalysisResults 13 14__author__ = 'Jalal Lakhlili' 15__license__ = "LGPL" 16 17logger = logging.getLogger(__name__) 18 19 20class PCEAnalysisResults(QMCAnalysisResults): 21 """Analysis results for the FDAnalysis class. 22 """ 23 24 def _get_derivatives_first(self, qoi, input_): 25 """Returns the first order derivative-based index for a given qoi wrt input variable. 26 27 Parameters 28 ---------- 29 qoi : str 30 Quantity of interest 31 input_ : str 32 Input variable 33 34 Returns 35 ------- 36 float 37 First order derivative-based index. 38 """ 39 40 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['derivatives_first']) 41 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 42 43 def _get_sobols_first(self, qoi, input_): 44 """Returns the first order sobol index for a given qoi wrt input variable. 45 46 Parameters 47 ---------- 48 qoi : str 49 Quantity of interest 50 input_ : str 51 Input variable 52 53 Returns 54 ------- 55 float 56 First order sobol index. 57 """ 58 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_first']) 59 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 60 61 def _get_sobols_second(self, qoi, input_): 62 """Returns the second order sobol index for a given qoi wrt input variable. 63 64 Parameters 65 ---------- 66 qoi : str 67 Quantity of interest 68 input_ : str 69 Input variable 70 71 Returns 72 ------- 73 float 74 Second order sobol index. 75 """ 76 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_second']) 77 return dict([(in_, raw_dict[AnalysisResults._to_tuple(qoi)][input_][i]) 78 for i, in_ in enumerate(self.inputs) if in_ != input_]) 79 80 def _get_sobols_total(self, qoi, input_): 81 """Returns the total order sobol index for a given qoi wrt input variable. 82 83 Parameters 84 ---------- 85 qoi : str 86 Quantity of interest 87 input_ : str 88 Input variable 89 90 Returns 91 ------- 92 float 93 Total order sobol index. 94 """ 95 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_total']) 96 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 97 98 def supported_stats(self): 99 """Types of statistics supported by the describe method. 100 101 Returns 102 ------- 103 list of str 104 """ 105 return ['min', 'max', '10%', '90%', '1%', '99%', 'median', 106 'mean', 'var', 'std'] 107 108 def _describe(self, qoi, statistic): 109 """Returns descriptive statistics, similar to pandas describe. 110 111 Parameters 112 ---------- 113 qoi : str 114 Name of quantity of interest. 115 statistic : str 116 One of 'min', 'max', '10%', '90%', 'median', 'mean', 'var', 'std' 117 118 Returns 119 ------- 120 float 121 Value of the requested statistic. 122 """ 123 if statistic not in self.supported_stats(): 124 raise NotImplementedError 125 if statistic == 'min': 126 return np.array([v.lower[0] for _, v in enumerate( 127 self.raw_data['output_distributions'][qoi])]) 128 elif statistic == 'max': 129 return np.array([v.upper[0] for _, v in enumerate( 130 self.raw_data['output_distributions'][qoi])]) 131 elif statistic == '1%': 132 return self.raw_data['percentiles'][qoi]['p01'] 133 elif statistic == '10%': 134 return self.raw_data['percentiles'][qoi]['p10'] 135 elif statistic == '90%': 136 return self.raw_data['percentiles'][qoi]['p90'] 137 elif statistic == '99%': 138 return self.raw_data['percentiles'][qoi]['p99'] 139 elif statistic == 'median': 140 return self.raw_data['percentiles'][qoi]['p50'] 141 else: 142 try: 143 return self.raw_data['statistical_moments'][qoi][statistic] 144 except KeyError: 145 raise NotImplementedError 146 147 def surrogate(self): 148 """Return a PCE surrogate model. 149 150 Returns 151 ------- 152 A function that takes a dictionary of parameter - value pairs and returns 153 a dictionary with the results (same output as decoder). 154 """ 155 def surrogate_fn(inputs): 156 def swap(x): 157 if len(x) > 1: 158 return list(x) 159 else: 160 return x[0] 161 values = np.array([inputs[key] for key in self.inputs]) 162 results = dict([(qoi, swap((self.raw_data['fit'][qoi](*values)).T)) for qoi in self.qois]) 163 return results 164 return surrogate_fn 165 166 def get_distribution(self, qoi): 167 """Returns a distribution for the given qoi. 168 169 Parameters 170 ---------- 171 qoi: str 172 QoI name 173 174 Returns 175 ------- 176 A ChaosPy PDF 177 """ 178 if qoi not in self.qois: 179 raise RuntimeError('no such quantity of interest - {}'.format(qoi)) 180 return self.raw_data['output_distributions'][qoi] 181 182 183class FDAnalysis(BaseAnalysisElement): 184 185 def __init__(self, sampler=None, qoi_cols=None): 186 """Analysis element for polynomial chaos expansion (PCE). 187 188 Parameters 189 ---------- 190 sampler : PCESampler 191 Sampler used to initiate the PCE analysis. 192 qoi_cols : list or None 193 Column names for quantities of interest (for which analysis is 194 performed). 195 """ 196 197 if sampler is None: 198 msg = 'FD analysis requires a paired sampler to be passed' 199 raise RuntimeError(msg) 200 201 # Flag specifing if we should scale the runs with the nominal base run 202 self.relative_analysis = sampler.relative_analysis 203 204 if qoi_cols is None: 205 raise RuntimeError("Analysis element requires a list of " 206 "quantities of interest (qoi)") 207 208 self.qoi_cols = qoi_cols 209 self.output_type = OutputType.SUMMARY 210 self.sampler = sampler 211 212 def element_name(self): 213 """Name for this element for logging purposes. 214 215 Returns 216 ------- 217 str 218 "FD_Analysis" 219 """ 220 return "FD_Analysis" 221 222 def element_version(self): 223 """Version of this element for logging purposes. 224 225 Returns 226 ------- 227 str 228 Element version. 229 """ 230 return "0.6" 231 232 def analyse(self, data_frame=None): 233 """Perform PCE analysis on input `data_frame`. 234 235 Parameters 236 ---------- 237 data_frame : pandas DataFrame 238 Input data for analysis. 239 240 Returns 241 ------- 242 PCEAnalysisResults 243 Use it to get the sobol indices and other information. 244 """ 245 246 if data_frame is None: 247 raise RuntimeError("Analysis element needs a data frame to " 248 "analyse") 249 elif data_frame.empty: 250 raise RuntimeError( 251 "No data in data frame passed to analyse element") 252 253 qoi_cols = self.qoi_cols 254 T = len(data_frame[qoi_cols[0]].values[-1]) 255 256 results = {'statistical_moments': {k: {'mean':np.zeros(T), 257 'var':np.zeros(T), 258 'std':np.zeros(T)} for k in qoi_cols}, 259 'percentiles': {k: {'p01': np.zeros(T), 260 'p10': np.zeros(T), 261 'p50': np.zeros(T), 262 'p90': np.zeros(T), 263 'p99': np.zeros(T)} for k in qoi_cols}, 264 'sobols_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 265 'sobols_second': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 266 'sobols_total': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 267 'correlation_matrices': {k: {} for k in qoi_cols}, 268 'output_distributions': {k: {} for k in qoi_cols}, 269 'fit': {k: cp.polynomial(np.zeros(T)) for k in qoi_cols}, 270 'Fourier_coefficients': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 271 'derivatives_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 272 } 273 274 # Get sampler informations 275 nodes = self.sampler._nodes 276 perturbations = self.sampler._perturbations 277 if self.sampler._is_dependent: 278 nodes_dep = self.sampler._nodes_dep 279 #perturbations_dep = self.sampler._perturbations_dep 280 281 282 for k in qoi_cols: 283 284 base = data_frame[k].values[0] 285 if self.relative_analysis: 286 if np.all(np.array(base) == 0): 287 warnings.warn(f"Removing QoI {k} from the analysis, contains all zeros", RuntimeWarning) 288 continue 289 if np.any(np.array(base) == 0): 290 warnings.warn(f"Removing QoI {k} from the analysis, contains some zeros", RuntimeWarning) 291 continue 292 293 results['statistical_moments'][k] = {'mean': np.mean(data_frame[k].values, axis=0), 294 'var': np.var(data_frame[k].values, axis=0), 295 'std': np.std(data_frame[k].values, axis=0)} 296 297 # Get the QoI value for the base value of the parameters 298 y_base = data_frame[k].values[0] 299 300 # Compute FD approximation 301 offset = 1 302 for pi, p in enumerate(self.sampler.vary.vary_dict): 303 304 # assumes ordering of the nodes [0, ..., +delta, -delta, ...] 305 y_pos = data_frame[k].values[offset] 306 y_neg = data_frame[k].values[offset+1] 307 308 if self.relative_analysis: 309 d_pos = perturbations[pi][offset] 310 d_neg = perturbations[pi][offset+1] 311 #d_pos = nodes[pi][offset]/nodes[pi][0] - 1 312 #d_neg = nodes[pi][offset+1]/nodes[pi][0] - 1 313 314 results["derivatives_first"][k][p] = 0.5*(y_pos/y_base-1)/(d_pos) + 0.5*(y_neg/y_base - 1)/(d_neg) 315 316 # scale the derivatives to the absolute values 317 x_base = nodes[pi][0] # base value of the parameter 318 scaling_factor = y_base/x_base 319 results["derivatives_first"][k][p] *= scaling_factor 320 else: 321 d_pos = nodes[pi][offset] - nodes[pi][0] 322 d_neg = nodes[pi][offset+1] - nodes[pi][0] 323 324 # norm([dg, 0, 0]) = delta_g 325 results["derivatives_first"][k][p] = 0.5*(y_pos - y_base)/(d_pos) + 0.5*(y_neg - y_base)/(d_neg) 326 327 offset = offset + 2 328 329 return PCEAnalysisResults(raw_data=results, samples=data_frame, 330 qois=self.qoi_cols, inputs=list(self.sampler.vary.get_keys()))
logger =
<Logger easyvvuq.analysis.fd_analysis (DEBUG)>
21class PCEAnalysisResults(QMCAnalysisResults): 22 """Analysis results for the FDAnalysis class. 23 """ 24 25 def _get_derivatives_first(self, qoi, input_): 26 """Returns the first order derivative-based index for a given qoi wrt input variable. 27 28 Parameters 29 ---------- 30 qoi : str 31 Quantity of interest 32 input_ : str 33 Input variable 34 35 Returns 36 ------- 37 float 38 First order derivative-based index. 39 """ 40 41 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['derivatives_first']) 42 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 43 44 def _get_sobols_first(self, qoi, input_): 45 """Returns the first order sobol index for a given qoi wrt input variable. 46 47 Parameters 48 ---------- 49 qoi : str 50 Quantity of interest 51 input_ : str 52 Input variable 53 54 Returns 55 ------- 56 float 57 First order sobol index. 58 """ 59 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_first']) 60 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 61 62 def _get_sobols_second(self, qoi, input_): 63 """Returns the second order sobol index for a given qoi wrt input variable. 64 65 Parameters 66 ---------- 67 qoi : str 68 Quantity of interest 69 input_ : str 70 Input variable 71 72 Returns 73 ------- 74 float 75 Second order sobol index. 76 """ 77 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_second']) 78 return dict([(in_, raw_dict[AnalysisResults._to_tuple(qoi)][input_][i]) 79 for i, in_ in enumerate(self.inputs) if in_ != input_]) 80 81 def _get_sobols_total(self, qoi, input_): 82 """Returns the total order sobol index for a given qoi wrt input variable. 83 84 Parameters 85 ---------- 86 qoi : str 87 Quantity of interest 88 input_ : str 89 Input variable 90 91 Returns 92 ------- 93 float 94 Total order sobol index. 95 """ 96 raw_dict = AnalysisResults._keys_to_tuples(self.raw_data['sobols_total']) 97 return raw_dict[AnalysisResults._to_tuple(qoi)][input_] 98 99 def supported_stats(self): 100 """Types of statistics supported by the describe method. 101 102 Returns 103 ------- 104 list of str 105 """ 106 return ['min', 'max', '10%', '90%', '1%', '99%', 'median', 107 'mean', 'var', 'std'] 108 109 def _describe(self, qoi, statistic): 110 """Returns descriptive statistics, similar to pandas describe. 111 112 Parameters 113 ---------- 114 qoi : str 115 Name of quantity of interest. 116 statistic : str 117 One of 'min', 'max', '10%', '90%', 'median', 'mean', 'var', 'std' 118 119 Returns 120 ------- 121 float 122 Value of the requested statistic. 123 """ 124 if statistic not in self.supported_stats(): 125 raise NotImplementedError 126 if statistic == 'min': 127 return np.array([v.lower[0] for _, v in enumerate( 128 self.raw_data['output_distributions'][qoi])]) 129 elif statistic == 'max': 130 return np.array([v.upper[0] for _, v in enumerate( 131 self.raw_data['output_distributions'][qoi])]) 132 elif statistic == '1%': 133 return self.raw_data['percentiles'][qoi]['p01'] 134 elif statistic == '10%': 135 return self.raw_data['percentiles'][qoi]['p10'] 136 elif statistic == '90%': 137 return self.raw_data['percentiles'][qoi]['p90'] 138 elif statistic == '99%': 139 return self.raw_data['percentiles'][qoi]['p99'] 140 elif statistic == 'median': 141 return self.raw_data['percentiles'][qoi]['p50'] 142 else: 143 try: 144 return self.raw_data['statistical_moments'][qoi][statistic] 145 except KeyError: 146 raise NotImplementedError 147 148 def surrogate(self): 149 """Return a PCE surrogate model. 150 151 Returns 152 ------- 153 A function that takes a dictionary of parameter - value pairs and returns 154 a dictionary with the results (same output as decoder). 155 """ 156 def surrogate_fn(inputs): 157 def swap(x): 158 if len(x) > 1: 159 return list(x) 160 else: 161 return x[0] 162 values = np.array([inputs[key] for key in self.inputs]) 163 results = dict([(qoi, swap((self.raw_data['fit'][qoi](*values)).T)) for qoi in self.qois]) 164 return results 165 return surrogate_fn 166 167 def get_distribution(self, qoi): 168 """Returns a distribution for the given qoi. 169 170 Parameters 171 ---------- 172 qoi: str 173 QoI name 174 175 Returns 176 ------- 177 A ChaosPy PDF 178 """ 179 if qoi not in self.qois: 180 raise RuntimeError('no such quantity of interest - {}'.format(qoi)) 181 return self.raw_data['output_distributions'][qoi]
Analysis results for the FDAnalysis class.
def
supported_stats(self):
99 def supported_stats(self): 100 """Types of statistics supported by the describe method. 101 102 Returns 103 ------- 104 list of str 105 """ 106 return ['min', 'max', '10%', '90%', '1%', '99%', 'median', 107 'mean', 'var', 'std']
Types of statistics supported by the describe method.
Returns
- list of str
def
surrogate(self):
148 def surrogate(self): 149 """Return a PCE surrogate model. 150 151 Returns 152 ------- 153 A function that takes a dictionary of parameter - value pairs and returns 154 a dictionary with the results (same output as decoder). 155 """ 156 def surrogate_fn(inputs): 157 def swap(x): 158 if len(x) > 1: 159 return list(x) 160 else: 161 return x[0] 162 values = np.array([inputs[key] for key in self.inputs]) 163 results = dict([(qoi, swap((self.raw_data['fit'][qoi](*values)).T)) for qoi in self.qois]) 164 return results 165 return surrogate_fn
Return a PCE surrogate model.
Returns
- A function that takes a dictionary of parameter - value pairs and returns
- a dictionary with the results (same output as decoder).
def
get_distribution(self, qoi):
167 def get_distribution(self, qoi): 168 """Returns a distribution for the given qoi. 169 170 Parameters 171 ---------- 172 qoi: str 173 QoI name 174 175 Returns 176 ------- 177 A ChaosPy PDF 178 """ 179 if qoi not in self.qois: 180 raise RuntimeError('no such quantity of interest - {}'.format(qoi)) 181 return self.raw_data['output_distributions'][qoi]
Returns a distribution for the given qoi.
Parameters
- qoi (str): QoI name
Returns
- A ChaosPy PDF
184class FDAnalysis(BaseAnalysisElement): 185 186 def __init__(self, sampler=None, qoi_cols=None): 187 """Analysis element for polynomial chaos expansion (PCE). 188 189 Parameters 190 ---------- 191 sampler : PCESampler 192 Sampler used to initiate the PCE analysis. 193 qoi_cols : list or None 194 Column names for quantities of interest (for which analysis is 195 performed). 196 """ 197 198 if sampler is None: 199 msg = 'FD analysis requires a paired sampler to be passed' 200 raise RuntimeError(msg) 201 202 # Flag specifing if we should scale the runs with the nominal base run 203 self.relative_analysis = sampler.relative_analysis 204 205 if qoi_cols is None: 206 raise RuntimeError("Analysis element requires a list of " 207 "quantities of interest (qoi)") 208 209 self.qoi_cols = qoi_cols 210 self.output_type = OutputType.SUMMARY 211 self.sampler = sampler 212 213 def element_name(self): 214 """Name for this element for logging purposes. 215 216 Returns 217 ------- 218 str 219 "FD_Analysis" 220 """ 221 return "FD_Analysis" 222 223 def element_version(self): 224 """Version of this element for logging purposes. 225 226 Returns 227 ------- 228 str 229 Element version. 230 """ 231 return "0.6" 232 233 def analyse(self, data_frame=None): 234 """Perform PCE analysis on input `data_frame`. 235 236 Parameters 237 ---------- 238 data_frame : pandas DataFrame 239 Input data for analysis. 240 241 Returns 242 ------- 243 PCEAnalysisResults 244 Use it to get the sobol indices and other information. 245 """ 246 247 if data_frame is None: 248 raise RuntimeError("Analysis element needs a data frame to " 249 "analyse") 250 elif data_frame.empty: 251 raise RuntimeError( 252 "No data in data frame passed to analyse element") 253 254 qoi_cols = self.qoi_cols 255 T = len(data_frame[qoi_cols[0]].values[-1]) 256 257 results = {'statistical_moments': {k: {'mean':np.zeros(T), 258 'var':np.zeros(T), 259 'std':np.zeros(T)} for k in qoi_cols}, 260 'percentiles': {k: {'p01': np.zeros(T), 261 'p10': np.zeros(T), 262 'p50': np.zeros(T), 263 'p90': np.zeros(T), 264 'p99': np.zeros(T)} for k in qoi_cols}, 265 'sobols_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 266 'sobols_second': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 267 'sobols_total': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 268 'correlation_matrices': {k: {} for k in qoi_cols}, 269 'output_distributions': {k: {} for k in qoi_cols}, 270 'fit': {k: cp.polynomial(np.zeros(T)) for k in qoi_cols}, 271 'Fourier_coefficients': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 272 'derivatives_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 273 } 274 275 # Get sampler informations 276 nodes = self.sampler._nodes 277 perturbations = self.sampler._perturbations 278 if self.sampler._is_dependent: 279 nodes_dep = self.sampler._nodes_dep 280 #perturbations_dep = self.sampler._perturbations_dep 281 282 283 for k in qoi_cols: 284 285 base = data_frame[k].values[0] 286 if self.relative_analysis: 287 if np.all(np.array(base) == 0): 288 warnings.warn(f"Removing QoI {k} from the analysis, contains all zeros", RuntimeWarning) 289 continue 290 if np.any(np.array(base) == 0): 291 warnings.warn(f"Removing QoI {k} from the analysis, contains some zeros", RuntimeWarning) 292 continue 293 294 results['statistical_moments'][k] = {'mean': np.mean(data_frame[k].values, axis=0), 295 'var': np.var(data_frame[k].values, axis=0), 296 'std': np.std(data_frame[k].values, axis=0)} 297 298 # Get the QoI value for the base value of the parameters 299 y_base = data_frame[k].values[0] 300 301 # Compute FD approximation 302 offset = 1 303 for pi, p in enumerate(self.sampler.vary.vary_dict): 304 305 # assumes ordering of the nodes [0, ..., +delta, -delta, ...] 306 y_pos = data_frame[k].values[offset] 307 y_neg = data_frame[k].values[offset+1] 308 309 if self.relative_analysis: 310 d_pos = perturbations[pi][offset] 311 d_neg = perturbations[pi][offset+1] 312 #d_pos = nodes[pi][offset]/nodes[pi][0] - 1 313 #d_neg = nodes[pi][offset+1]/nodes[pi][0] - 1 314 315 results["derivatives_first"][k][p] = 0.5*(y_pos/y_base-1)/(d_pos) + 0.5*(y_neg/y_base - 1)/(d_neg) 316 317 # scale the derivatives to the absolute values 318 x_base = nodes[pi][0] # base value of the parameter 319 scaling_factor = y_base/x_base 320 results["derivatives_first"][k][p] *= scaling_factor 321 else: 322 d_pos = nodes[pi][offset] - nodes[pi][0] 323 d_neg = nodes[pi][offset+1] - nodes[pi][0] 324 325 # norm([dg, 0, 0]) = delta_g 326 results["derivatives_first"][k][p] = 0.5*(y_pos - y_base)/(d_pos) + 0.5*(y_neg - y_base)/(d_neg) 327 328 offset = offset + 2 329 330 return PCEAnalysisResults(raw_data=results, samples=data_frame, 331 qois=self.qoi_cols, inputs=list(self.sampler.vary.get_keys()))
Base class for all EasyVVUQ analysis elements.
Attributes
FDAnalysis(sampler=None, qoi_cols=None)
186 def __init__(self, sampler=None, qoi_cols=None): 187 """Analysis element for polynomial chaos expansion (PCE). 188 189 Parameters 190 ---------- 191 sampler : PCESampler 192 Sampler used to initiate the PCE analysis. 193 qoi_cols : list or None 194 Column names for quantities of interest (for which analysis is 195 performed). 196 """ 197 198 if sampler is None: 199 msg = 'FD analysis requires a paired sampler to be passed' 200 raise RuntimeError(msg) 201 202 # Flag specifing if we should scale the runs with the nominal base run 203 self.relative_analysis = sampler.relative_analysis 204 205 if qoi_cols is None: 206 raise RuntimeError("Analysis element requires a list of " 207 "quantities of interest (qoi)") 208 209 self.qoi_cols = qoi_cols 210 self.output_type = OutputType.SUMMARY 211 self.sampler = sampler
Analysis element for polynomial chaos expansion (PCE).
Parameters
- sampler (PCESampler): Sampler used to initiate the PCE analysis.
- qoi_cols (list or None): Column names for quantities of interest (for which analysis is performed).
def
element_name(self):
213 def element_name(self): 214 """Name for this element for logging purposes. 215 216 Returns 217 ------- 218 str 219 "FD_Analysis" 220 """ 221 return "FD_Analysis"
Name for this element for logging purposes.
Returns
- str: "FD_Analysis"
def
element_version(self):
223 def element_version(self): 224 """Version of this element for logging purposes. 225 226 Returns 227 ------- 228 str 229 Element version. 230 """ 231 return "0.6"
Version of this element for logging purposes.
Returns
- str: Element version.
def
analyse(self, data_frame=None):
233 def analyse(self, data_frame=None): 234 """Perform PCE analysis on input `data_frame`. 235 236 Parameters 237 ---------- 238 data_frame : pandas DataFrame 239 Input data for analysis. 240 241 Returns 242 ------- 243 PCEAnalysisResults 244 Use it to get the sobol indices and other information. 245 """ 246 247 if data_frame is None: 248 raise RuntimeError("Analysis element needs a data frame to " 249 "analyse") 250 elif data_frame.empty: 251 raise RuntimeError( 252 "No data in data frame passed to analyse element") 253 254 qoi_cols = self.qoi_cols 255 T = len(data_frame[qoi_cols[0]].values[-1]) 256 257 results = {'statistical_moments': {k: {'mean':np.zeros(T), 258 'var':np.zeros(T), 259 'std':np.zeros(T)} for k in qoi_cols}, 260 'percentiles': {k: {'p01': np.zeros(T), 261 'p10': np.zeros(T), 262 'p50': np.zeros(T), 263 'p90': np.zeros(T), 264 'p99': np.zeros(T)} for k in qoi_cols}, 265 'sobols_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 266 'sobols_second': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 267 'sobols_total': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 268 'correlation_matrices': {k: {} for k in qoi_cols}, 269 'output_distributions': {k: {} for k in qoi_cols}, 270 'fit': {k: cp.polynomial(np.zeros(T)) for k in qoi_cols}, 271 'Fourier_coefficients': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 272 'derivatives_first': {k: {p: np.zeros(T) for p in self.sampler.vary.vary_dict} for k in qoi_cols}, 273 } 274 275 # Get sampler informations 276 nodes = self.sampler._nodes 277 perturbations = self.sampler._perturbations 278 if self.sampler._is_dependent: 279 nodes_dep = self.sampler._nodes_dep 280 #perturbations_dep = self.sampler._perturbations_dep 281 282 283 for k in qoi_cols: 284 285 base = data_frame[k].values[0] 286 if self.relative_analysis: 287 if np.all(np.array(base) == 0): 288 warnings.warn(f"Removing QoI {k} from the analysis, contains all zeros", RuntimeWarning) 289 continue 290 if np.any(np.array(base) == 0): 291 warnings.warn(f"Removing QoI {k} from the analysis, contains some zeros", RuntimeWarning) 292 continue 293 294 results['statistical_moments'][k] = {'mean': np.mean(data_frame[k].values, axis=0), 295 'var': np.var(data_frame[k].values, axis=0), 296 'std': np.std(data_frame[k].values, axis=0)} 297 298 # Get the QoI value for the base value of the parameters 299 y_base = data_frame[k].values[0] 300 301 # Compute FD approximation 302 offset = 1 303 for pi, p in enumerate(self.sampler.vary.vary_dict): 304 305 # assumes ordering of the nodes [0, ..., +delta, -delta, ...] 306 y_pos = data_frame[k].values[offset] 307 y_neg = data_frame[k].values[offset+1] 308 309 if self.relative_analysis: 310 d_pos = perturbations[pi][offset] 311 d_neg = perturbations[pi][offset+1] 312 #d_pos = nodes[pi][offset]/nodes[pi][0] - 1 313 #d_neg = nodes[pi][offset+1]/nodes[pi][0] - 1 314 315 results["derivatives_first"][k][p] = 0.5*(y_pos/y_base-1)/(d_pos) + 0.5*(y_neg/y_base - 1)/(d_neg) 316 317 # scale the derivatives to the absolute values 318 x_base = nodes[pi][0] # base value of the parameter 319 scaling_factor = y_base/x_base 320 results["derivatives_first"][k][p] *= scaling_factor 321 else: 322 d_pos = nodes[pi][offset] - nodes[pi][0] 323 d_neg = nodes[pi][offset+1] - nodes[pi][0] 324 325 # norm([dg, 0, 0]) = delta_g 326 results["derivatives_first"][k][p] = 0.5*(y_pos - y_base)/(d_pos) + 0.5*(y_neg - y_base)/(d_neg) 327 328 offset = offset + 2 329 330 return PCEAnalysisResults(raw_data=results, samples=data_frame, 331 qois=self.qoi_cols, inputs=list(self.sampler.vary.get_keys()))
Perform PCE analysis on input data_frame.
Parameters
- data_frame (pandas DataFrame): Input data for analysis.
Returns
- PCEAnalysisResults: Use it to get the sobol indices and other information.