Factors and Levels ================== .. class:: sweetpea.Factor(name, levels) A factor for use in an experiment design. By default :class:`.Factor` in SweetPea always creates a :class:`.DiscreteFactor` which contains a finite number of levels (Refer to :class:`.ContinuousFactor` for non-discrete factors). The levels of a factor can be plain :class:`.Level` values, any kind of non-:class:`.Level` value (which is implicitly coerced to a :class:`.Level` value), or :class:`.DerivedLevel` values. In the last case, the result is a *derived factor*. The `levels` list must either contain all derived levels or all values that are not derived levels, and derived levels must all use a compatible derivation as described in :ref:`derivations`. The names of the levels must be distinct; create a level with a weight to get the effect of multiple levels with he same name. :param name: the factor's name :param levels: the factor's levels :type levels: List[Level] .. property:: name The factor's name. :type: str .. method:: get_level(name) Finds a returns a level of the factor with a given name. If the factor has multiple levels with the same name, any one of them might be returned. Indexing a factor with `[]` is the same as calling the `get_level` method. :param name: the level's name :returns: a level with the given name :rtype: Level .. property:: levels Returns the factor's levels. :returns: a list of levels :rtype: List[Level] .. class:: sweetpea.Level(name, weight=1) A level for use in a non-derived factor. A level object can be used for only one factor. If `weight` is provided as a value greater than 1, it affects how the level is used in crossings, causing it to be combined `weight` times with each combination of other factors' levels in a crossing. That's conceptually similar to having multiple levels with the same `name`, but as long as the level's factor is part of a block's crossing, the `weight` crossing occurrences of the level are not considered distinct. Consequently, a sampling strategy without replacement (see :class:`.Gen`) will produce fewer samples than it would for separate levels. Along similar lines, a :class:`.DerivedLevel` can have a weight greater than 1 to affect crossings, but cannot be included in a level multiple times, because each derived level's predicate must match a different set of inputs. For a non-derived level whose factor is not crossed (or, more generally, is not in all crossings in a :class:`.MultiCrossBlock`), a `weight` value has the same effect as duplicating the level's name. That is, the would-be copies are treated as distinct, which means that sampling with replacement is biased toward levels with greater weight. For sampling strategies without replacement, the weight thus increases the number of samples that are considered distinct. :param name: the level's name, which can be any value :param weight: the level's weight :type weight: int :rtype: Level .. property:: name The level's name, which can be any kind of value. .. property:: factor Returns the level's factor. This property exists only for a :class:`.Level` object that is extracted from a :class:`.Factor` object. :returns: a factor :rtype: Factor .. class:: sweetpea.DerivedLevel(name, derivation, weight=1) Creates a derived level, which depends on the levels of other factors in a design. All derived levels for one factor must use compatible derivations as described in :ref:`derivations`. :param name: the level's name, which can be any value :param derivation: a condition on other factors' levels; see :ref:`derivations` :type derivation: Derivation :param weight: the level's weight :type weight: int :returns: a derived level :rtype: Level .. class:: sweetpea.ElseLevel(name, weight=1) Creates a derived level that acts as an “else” case, matching any arguments that other derived levels do not match. An “else” derived level can appear only once among the levels supplied to :class:`.Factor`, and only in combination with other derived levels. It is compatible with any derivation described in :ref:`derivations`. :param name: the level's name, which can be any value :param weight: the level's weight :type weight: int :returns: a derived level :rtype: Level .. class:: sweetpea.ContinuousFactor(name, distribution) Sweetpea also supports a :class:`.ContinuousFactor` for factors without finite levels, which sample continuously at runtime. This is different from :class:`.DiscreteFactor` that requires a finite discrete levels during its initialization. A :class:`.ContinuousFactor` can dynamically generate values at runtime based on the input distribution. To initialize a :class:`.ContinuousFactor`, a `distribution` is required in order to generate values at runtime. The `distribution` must be an instance of a :class:`.Distribution`. Several built-in types are available for :class:`.Distribution`. :class:`.UniformDistribution` Samples values from a uniform distribution within a given range. :class:`.GaussianDistribution` Samples values from a normal distribution with a specified mean and standard deviation. :class:`.ExponentialDistribution` Samples values from an exponential distribution with a given rate parameter. :class:`.LogNormalDistribution` Samples values from a log-normal distribution with a specified mean and standard deviation. :class:`.CustomDistribution` Samples values by calling a user-defined input function. If :class:`.UniformDistribution`, :class:`.GaussianDistribution`, :class:`.LogNormalDistribution`, or :class:`.LogNormalDistribution` is used to initialize the :class:`.ContinuousFactor`, the factor will generate values following the corresponding distribution at runtime through :meth:`.ContinuousFactor.generate`. In this case the factor is always a *non-derived continuous factor*. The user can also use :class:`.CustomDistribution`, in which case the user needs to provide a custom `func(Callable)` to initialize the :class:`.ContinuousFactor`. The factor will then call `func` to generate values at runtime through :meth:`.ContinuousFactor.generate`. In addition, :class:`.CustomDistribution` can also accept an additional argument `dependents` that contains a list of factors that the current :class:`.ContinuousFactor` depends on. The list of factors can be either :class:`.DiscreteFactor` or :class:`.ContinuousFactor` in the design. In such cases, the :class:`ContinuousFactor` initialized is considered a *derived continuous factor* and it also suggests that `func` would require additional values in order to generate values at runtime. For example, when `dependents` constains a :class:`.Factor` `Color` in the design, the `factor_values` for `Color` needs to be passed to :meth:`.ContinuousFactor.generate`. If `distribution` is not set or recognized, an error will be raised. :param name: The name of the continuous factor. :type name: str :param distribution: A distribution used to generate values dynamically. :type distribution: Distribution .. property:: name The continuous factor's name. :type: str .. method:: generate(factor_values=[]) Generate values for the continuous factor based on the input distribution. :param factor_values: optional factor values when generating values with :class:`.CustomDistribution`. The length of `factor_values` needs to be the same as the `dependents` when intializing :class:`.CustomDistribution`. :type factor_values: List[Any] :returns: the value for the factor :rtype: Any .. class:: sweetpea.DiscreteFactor(name, levels) In contrast to :class:`.ContinuousFactor` that generate values dynamically, a :class:`.DiscreteFactor` takes on a finite set of distinct, separate values (or levels) during its initialization. Each level can be represented using the :class:`.Level` class. A :class:`.DiscreteFactor` is initialized using the :class:`.Factor` class.