Source code for SALib.util.util_funcs
from typing import Dict
import pkgutil
import csv
import numpy as np
[docs]
def avail_approaches(pkg):
"""Create list of available modules.
Parameters
----------
pkg : module
module to inspect
Returns
-------
method : list
A list of available submodules
"""
methods = [
modname
for importer, modname, ispkg in pkgutil.walk_packages(path=pkg.__path__)
if modname not in ["common_args", "directions", "sobol_sequence"]
and "test" not in modname
]
return methods
[docs]
def read_param_file(filename, delimiter=None):
"""Unpacks a parameter file into a dictionary
Reads a parameter file of format::
Param1,0,1,Group1,dist1
Param2,0,1,Group2,dist2
Param3,0,1,Group3,dist3
(Group and Dist columns are optional)
Returns a dictionary containing:
- names - the names of the parameters
- bounds - a list of lists of lower and upper bounds
- num_vars - a scalar indicating the number of variables
(the length of names)
- groups - a list of group names (strings) for each variable
- dists - a list of distributions for the problem,
None if not specified or all uniform
Parameters
----------
filename : str
The path to the parameter file
delimiter : str, default=None
The delimiter used in the file to distinguish between columns
"""
names = []
bounds = []
groups = []
dists = []
num_vars = 0
fieldnames = ["name", "lower_bound", "upper_bound", "group", "dist"]
with open(filename, "r") as csvfile:
dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=delimiter)
csvfile.seek(0)
reader = csv.DictReader(csvfile, fieldnames=fieldnames, dialect=dialect)
for row in reader:
if row["name"].strip().startswith("#"):
pass
else:
num_vars += 1
names.append(row["name"])
bounds.append([float(row["lower_bound"]), float(row["upper_bound"])])
# If the fourth column does not contain a group name, use
# the parameter name
if row["group"] is None:
groups.append(row["name"])
elif row["group"] == "NA":
groups.append(row["name"])
else:
groups.append(row["group"])
# If the fifth column does not contain a distribution
# use uniform
if row["dist"] is None:
dists.append("unif")
else:
dists.append(row["dist"])
if groups == names:
groups = None
elif len(set(groups)) == 1:
raise ValueError(
"""Only one group defined, results will not be
meaningful"""
)
# setting dists to none if all are uniform
# because non-uniform scaling is not needed
if all([d == "unif" for d in dists]):
dists = None
return {
"names": names,
"bounds": bounds,
"num_vars": num_vars,
"groups": groups,
"dists": dists,
}
def _check_groups(problem):
"""Check if there is more than 1 group."""
groups = problem.get("groups")
if not groups:
return False
if groups == problem["names"]:
return False
if len(set(groups)) == 1:
return False
return groups
def _check_bounds(bounds):
"""Check user supplied distribution bounds for validity.
Parameters
----------
problem : dict
The problem definition
Returns
-------
tuple : containing upper and lower bounds
"""
b = np.array(bounds)
lower_bounds = b[:, 0]
upper_bounds = b[:, 1]
if np.any(lower_bounds >= upper_bounds):
raise ValueError("Bounds are not legal")
return lower_bounds, upper_bounds
def _define_problem_with_groups(problem: Dict) -> Dict:
"""
Checks if the user defined the 'groups' key in the problem dictionary.
If not, makes the 'groups' key equal to the variables names. In other
words, the number of groups will be equal to the number of variables, which
is equivalent to no groups.
Parameters
----------
problem : dict
The problem definition
Returns
-------
problem : dict
The problem definition with the 'groups' key, even if the user doesn't
define it
"""
# Checks if there isn't a key 'groups' or if it exists and is set to 'None'
if "groups" not in problem or not problem["groups"]:
problem["groups"] = problem["names"]
elif len(problem["groups"]) != problem["num_vars"]:
raise ValueError(
"Number of entries in 'groups' should be the same " "as in 'names'"
)
return problem