Example #1
0
    def test_pb(self):
        """Tests the alias list for an 11 factor Plackett-Burman design."""
        factor_data = [[1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1],
                       [1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1],
                       [1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1],
                       [-1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1],
                       [-1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1],
                       [1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1],
                       [-1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1],
                       [-1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1],
                       [1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1],
                       [1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1],
                       [-1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1],
                       [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]]

        factor_names = design.get_factor_names(len(factor_data[0]))
        factor_data = pd.DataFrame(factor_data, columns=factor_names)

        _, alias_coefs = alias_list("(X1+X2+X3+X4+X5+X6+X7+X8+X9+X10+X11)**2",
                                    factor_data)

        for r in range(alias_coefs.shape[0]):
            for c in range(alias_coefs.shape[1]):
                self.assertTrue(
                    np.allclose(alias_coefs[r, c], 1.0)
                    or np.allclose(alias_coefs[r, c], 0)
                    or np.allclose(abs(alias_coefs[r, c]), 1 / 3),
                    "Expected 1, 0 or 1/3 for plackett-burman "
                    "alias, was {}".format(alias_coefs[r, c]))
Example #2
0
def build_simplex_lattice(factor_count, model_order=ModelOrder.quadratic):
    """Builds a Simplex Lattice mixture design.

    This design can be used for 2 to 30 components. A simplex-lattice mixture
    design of degree m consists of m+1 points of equally spaced values between
    0 and 1 for each component. If m = 2 then possible fractions are 0, 1/2, 1.
    For m = 3 the possible values are 0, 1/3, 2/3, 1. The points include the
    pure components and enough points between them to estimate an equation of
    degree m. This design differs from a simplex-centroid design by having
    enough points to estimate a full cubic model.

    :param factor_count: The number of mixture components to build for.
    :type factor_count: int
    :param model_order: The order to build for. ModelOrder.linear will choose
                        vertices only (pure blends). ModelOrder.quadratice will
                        add binary blends, and ModelOrder.cubic will add blends
                        of three components.

    :type model_order: dexpy.model.ModelOrder
    """
    run_count = factor_count  # pure blends
    if model_order == ModelOrder.quadratic:
        run_count += count_nk(factor_count, 2)  # 1/2 1/2 blends
    elif model_order == ModelOrder.cubic:
        # 2/3 1/3 blends (and vice versa)
        run_count += count_nk(factor_count, 2) * 2
        if factor_count > 2:
            run_count += count_nk(factor_count, 3)  # 1/3 1/3 1/3 blends

    factor_names = design.get_factor_names(factor_count)
    factor_data = pd.DataFrame(0,
                               columns=factor_names,
                               index=np.arange(0, run_count))

    row = 0
    # always do pure blends
    for combo in itertools.combinations(factor_names, 1):
        factor_data.loc[row, combo] = 1.0
        row += 1

    if model_order == ModelOrder.quadratic:
        # 1/2 1/2 binary blends
        for combo in itertools.combinations(factor_names, 2):
            factor_data.loc[row, combo] = 0.5
            row += 1
    elif model_order == ModelOrder.cubic:
        # 2/3 1/3 blends
        for combo in itertools.combinations(factor_names, 2):
            factor_data.loc[row, combo] = [2 / 3, 1 / 3]
            row += 1
            factor_data.loc[row, combo] = [1 / 3, 2 / 3]
            row += 1
        # 1/3 1/3 1/3 triple blend
        if factor_count > 2:
            for combo in itertools.combinations(factor_names, 3):
                factor_data.loc[row, combo] = 1 / 3
                row += 1

    return factor_data
Example #3
0
    def test_quadratic_model(cls):
        """Test expanding a quadratic model in a rotatable ccd"""
        axial_pt = math.sqrt(2)
        factor_data = [[-1, -1], [1, -1], [-1, 1], [1, 1], [-axial_pt, 0],
                       [axial_pt, 0], [0, -axial_pt], [0, axial_pt], [0, 0]]

        factor_data = pd.DataFrame(factor_data,
                                   columns=design.get_factor_names(
                                       len(factor_data[0])))
        X = design.create_model_matrix(
            factor_data, "1 + X1 + X2 + X1:X2 + I(X1**2) + I(X2**2)")
        np.testing.assert_almost_equal(
            [1.0, axial_pt, 0.0, -0.0,
             pow(axial_pt, 2), 0.0], X[5])
Example #4
0
def build_box_behnken(factor_count, center_points=5):
    """Builds a Box-Behnken design.create_model_matrix

    Box-Behnken designs are response surface designs, specially made to require
    only 3 levels, coded as -1, 0, and +1. Box-Behnken designs are available
    for 3 to 21 factors. They are formed by combining two-level factorial
    designs with incomplete block designs. This procedure creates designs with
    desirable statistical properties but, most importantly, with only a
    fraction of the experiments required for a three-level factorial. Because
    there are only three levels, the quadratic model is appropriate.

    **Center Points**

    Center points, as implied by the name, are points with all levels set to
    coded level 0 - the midpoint of each factor range: (0, 0)

    Center points are usually repeated 4-6 times to get a good estimate of
    experimental error (pure error).

    **Categorical Factors**

    You may also add categorical factors to this design. This will cause the
    number of runs generated to be multiplied by the number of combinations of
    the categorical factor levels.

    Box, G.E.P., and Behnken, D.W., "Some New Three Level Designs for the Study
    of Quantitative Variables", Technometrics, 2, pp. 455-475, 1960.

    :param factor_count: The number of factors to build for.
    :type factor_count: int
    :param center_points: The number of center points to include in the design.
    :type center_points: integer
    """
    factor_names = design.get_factor_names(factor_count)
    csv_path = os.path.join(os.path.dirname(__file__), "data",
                            "BB_{:02d}.csv".format(factor_count))
    factor_data = pd.read_csv(csv_path, names=factor_names)

    if center_points > 0:
        center_point_df = pd.DataFrame(0,
                                       columns=factor_names,
                                       index=np.arange(
                                           len(factor_data),
                                           len(factor_data) + center_points))
        factor_data = factor_data.append(center_point_df)

    return factor_data
Example #5
0
    def test_two_factors(self):
        """Tests the alias list for two identical factors."""
        factor_data = [[-1, -1], [-1, -1], [1, 1], [1, 1], [0, 0]]

        factor_names = design.get_factor_names(len(factor_data[0]))
        factor_data = pd.DataFrame(factor_data, columns=factor_names)

        aliases, alias_coefs = alias_list("X1+X2", factor_data)
        answer = [[1, 0, 0], [0, 1, 1]]

        np.testing.assert_allclose(alias_coefs,
                                   answer,
                                   rtol=1e-4,
                                   atol=np.finfo(float).eps)

        answer_list = ["X1 = X2"]
        self.assertEqual(answer_list, aliases)
Example #6
0
    def test_quadratic_power(cls):
        """Test power for a quadratic model in a rotatable ccd."""
        axial_pt = math.sqrt(2)
        factor_data = [[-1, -1], [1, -1], [-1, 1], [1, 1], [-axial_pt, 0],
                       [axial_pt, 0], [0, -axial_pt], [0, axial_pt], [0, 0]]

        factor_data = pd.DataFrame(factor_data,
                                   columns=design.get_factor_names(
                                       len(factor_data[0])))
        model = "1 + X1 + X2 + X1:X2 + I(X1**2) + I(X2**2)"
        power_result = power.f_power(model, factor_data, 2, 0.05)

        power_answers = [
            0.2887584, 0.49002743118623, 0.49002743118623, 0.28875325867897,
            0.63145653747073, 0.63145653747073
        ]
        np.testing.assert_allclose(power_result, power_answers, rtol=1e-4)
Example #7
0
    def test_large_power(cls):
        """Test power for a 9 factor model."""
        factor_count = 9

        factor_data = []
        # generate a 2^9 factorial
        for run in itertools.product([-1, 1], repeat=factor_count):
            factor_data.append(list(run))
        factor_data = pd.DataFrame(
            factor_data, columns=design.get_factor_names(factor_count))

        model = "(X1+X2+X3+X4+X5+X6+X7+X8+X9)**4"  # will generate a 4fi model

        power_result = power.f_power(model, factor_data, 0.2, 0.05)

        answer = np.ndarray(256)
        answer.fill(0.61574355066172015)
        answer[0] = 0.99459040972676238
        np.testing.assert_allclose(power_result, answer, rtol=1e-4)
Example #8
0
def build_simplex_centroid(factor_count):
    """Builds a Simplex Centroid mixture design.

    This mixture design can be used for 3 to 8 components. A simplex-centroid
    design consists of all points that are equally weighted mixtures of 1 to q
    components. Included are permutations of:

        Pure blends: (1, 0, ...,0)

        Binary blends: (1/2, 1/2, 0, ...,0)

        Tertiary blends: (1/3, 1/3, 1/3, 0, ...,0)

    and so on to the overall centroid: (1/q, 1/q, ..., 1/q).

    This design differs from a simplex-lattice design. It cannot be used to
    estimate the full cubic model, but can be used to estimate a special cubic
    model.

    :param factor_count: The number of mixture components to build for.
    :type factor_count: int
    """
    run_count = 0
    for i in range(1, factor_count+1):
        run_count += count_nk(factor_count, i)

    factor_names = design.get_factor_names(factor_count)
    factor_data = pd.DataFrame(0, columns=factor_names,
                                  index=np.arange(0, run_count))


    row = 0
    for i in range(1, factor_count+1):
        proportion = 1 / i
        for combo in itertools.combinations(factor_names, i):
            factor_data.loc[row, combo] = proportion
            row += 1


    return factor_data
Example #9
0
def build_factorial(factor_count, run_count):
    """Builds a regular two-level design based on a number of factors and runs.

    Full two-level factorial designs may be run for up to 9 factors. These
    designs permit estimation of all main effects and all interaction effects.
    If the number of runs requested is a 2^factor_count, the design will be a
    full factorial.

    If the number of runs is less than 2^factor_count (it still must be a power
    of two) a fractional design will be created. Not all combinations of runs
    and factor counts will result in a design. Use the
    :ref:`alias list<alias-list>` method to see what terms are estimable in
    the resulting design.

    :param factor_count: The number of factors to build for.
    :type factor_count: int
    :param run_count: The number of runs in the resulting design. Must be a power of 2.
    :type run_count: int
    :returns: A pandas.DataFrame object containing the requested design.
    """
    # store minimum aberration generators for factors from 3 to max_factors
    # these are from Design-Expert
    generator_list = {
        3: {
            4: ["C=AB"]
        },
        4: {
            8: ["D=ABC"]
        },
        5: {
            8: ["D=AB", "E=AC"],
            16: ["E=ABCD"]
        },
        6: {
            8: ["D=AB", "E=AC", "F=BC"],
            16: ["E=ABC", "F=BCD"],
            32: ["F=ABCDE"]
        },
        7: {
            8: ["D=AB", "E=AC", "F=BC", "G=ABC"],
            16: ["E=ABC", "F=BCD", "G=ACD"],
            32: ["F=ABCD", "G=ABCE"],
            64: ["G=ABCDEF"]
        },
        8: {
            16: ["E=ABC", "F=BCD", "G=ACD", "H=ABD"],
            32: ["F=ABC", "G=ABD", "H=BCDE"],
            64: ["G=ABCD", "H=ABEF"],
            128: ["H=ABCDEFG"]
        },
        9: {
            16: ["E=ABC", "F=BCD", "G=ACD", "H=ABD", "J=ABCD"],
            32: ["F=ABCD", "G=ABCE", "H=ABDE", "J=ACDE"],
            64: ["G=ABCD", "H=ACEF", "J=CDEF"],
            128: ["H=ABCDE", "J=ABCFG"],
            256: ["J=ABCDEFGH"]
        },  # noqa
        10: {
            16: ["E=ABC", "F=BCD", "G=ACD", "H=ABD", "J=ABCD", "K=AB"],
            32: ["F=ABCD", "G=ABCE", "H=ABDE", "J=ACDE", "K=BCDE"],
            64: ["G=ABCD", "H=ABCE", "J=ADEF", "K=BDEF"],
            128: ["H=ABCG", "J=BCDE", "K=ACDF"],
            256: ["J=ABCDEF", "K=ABCDGH"],
            512: ["K=ABCDEFGHJ"]
        },  # noqa
        11: {
            16: ["E=ABC", "F=BCD", "G=ACD", "H=ABD", "J=ABCD", "K=AB", "L=AC"],
            32: ["F=ABC", "G=BCD", "H=CDE", "J=ACD", "K=ADE", "L=BDE"],
            64: ["G=ABCD", "H=ABCE", "J=ABDE", "K=ACDEF", "L=BCDEF"],
            128: ["H=ABCG", "J=BCDE", "K=ACDF", "L=ABCDEFG"],
            256: ["J=ABCDE", "K=ABCFG", "L=ABDFH"],
            512: ["K=ABCDEF", "L=ABCGHJ"]
        },  # noqa
        12: {
            16: [
                "E=ABC", "F=ABD", "G=ACD", "H=BCD", "J=ABCD", "K=AB", "L=AC",
                "M=AD"
            ],
            32:
            ["F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABE", "L=ACE", "M=ADE"],
            64: ["G=ABC", "H=ABD", "J=ACDE", "K=ACDF", "L=ABEF", "M=BCDEF"],
            128: ["H=ABC", "J=ADEF", "K=BDEG", "L=CDFG", "M=ABCEFG"],
            256: ["J=ABCDE", "K=ABCFG", "L=ABDFH", "M=ACEGH"],
            512: ["K=ABCDEF", "L=ABCDGH", "M=ABEFGJ"]
        },  # noqa
        13: {
            16: [
                "E=ABC", "F=ABD", "G=ACD", "H=BCD", "J=ABCD", "K=AB", "L=AC",
                "M=AD", "N=BC"
            ],
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABE", "L=ACE", "M=ADE",
                "N=BCE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ABE", "K=ACDE", "L=ACF", "M=ADEF",
                "N=ABCDEF"
            ],
            128:
            ["H=ABC", "J=ABDE", "K=ABDF", "L=ACDG", "M=AEFG", "N=ABCDEFG"],
            256: ["J=ABCDE", "K=ABCFG", "L=ABDFH", "M=ACEGH", "N=ADEFGH"],
            512: ["K=ABCDEF", "L=ABCDGH", "M=ABEFGJ", "N=ACEGHJ"]
        },  # noqa
        14: {
            16: [
                "E=ABC", "F=ABD", "G=ACD", "H=BCD", "J=ABCD", "K=AB", "L=AC",
                "M=AD", "N=BC", "O=BD"
            ],
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABE", "L=ACE", "M=ADE",
                "N=BCE", "O=BDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ABE", "K=ACDE", "L=ABF", "M=ACDF",
                "N=ACEF", "O=ADEF"
            ],
            128: [
                "H=ABC", "J=ABDE", "K=ABDF", "L=ACEF", "M=ACDG", "N=ABEFG",
                "O=BCDEFG"
            ],
            256: [
                "J=ABCDE", "K=ABCFG", "L=ABDEFG", "M=ABDFH", "N=ADEGH",
                "O=ACEFGH"
            ],
            512: ["K=ABCDE", "L=ABFGH", "M=CDFGJ", "N=ACEFHJ", "O=BDEGHJ"]
        },  # noqa
        15: {
            16: [
                "E=ABC", "F=ABD", "G=ACD", "H=BCD", "J=ABCD", "K=AB", "L=AC",
                "M=AD", "N=BC", "O=BD", "P=CD"
            ],
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABE", "L=ACE", "M=ADE",
                "N=BCE", "O=BDE", "P=CDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ABE", "K=ACDE", "L=ABF", "M=ACDF",
                "N=ACEF", "O=ADEF", "P=ABCDEF"
            ],
            128: [
                "H=ABC", "J=ADE", "K=BDF", "L=ACEF", "M=CDG", "N=BCEG",
                "O=EFG", "P=ABCDEFG"
            ],
            256: [
                "J=ABCD", "K=ABEF", "L=ACEG", "M=BDFG", "N=ABDEH", "O=ACDFH",
                "P=BEGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACDFJ", "O=AEGHJ",
                "P=ABCDEFGHJ"
            ]
        },  # noqa
        16: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABE", "L=ACE", "M=BCE",
                "N=ADE", "O=BDE", "P=CDE", "Q=ABCDE"
            ],
            64: [
                "G=ABCD", "H=ABCE", "J=ABDE", "K=ACDE", "L=BCDE", "M=ABCF",
                "N=ABDF", "O=ACDF", "P=BCDF", "Q=ABCDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDF", "L=ACEF", "M=ACDG", "N=BCEG",
                "O=ABCFG", "P=ABDEFG", "Q=BCDEFG"
            ],
            256: [
                "J=ABCDE", "K=ABCFG", "L=ABDEFG", "M=ABCDFH", "N=CDEFH",
                "O=ACDEGH", "P=AEFGH", "Q=BCEFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ACDFJ",
                "P=BCEGJ", "Q=ABCEFHJ"
            ]
        },  # noqa
        17: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABCD", "L=ABE", "M=ACE",
                "N=BCE", "O=ADE", "P=BDE", "Q=CDE", "R=ABCDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ACD", "K=BCD", "L=ABE", "M=ACE", "N=ABF",
                "O=ACF", "P=ADEF", "Q=BDEF", "R=CDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDF", "L=ACEF", "M=ADEF", "N=ACDG",
                "O=ABEG", "P=ADEG", "Q=ABFG", "R=BCDEFG"
            ],
            256: [
                "J=ABCD", "K=ABEF", "L=ACEG", "M=BDFG", "N=BCEH", "O=ABDFH",
                "P=ABDEGH", "Q=ACDFGH", "R=ABCEFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ACDFJ",
                "P=BCEGJ", "Q=ABCEFHJ", "R=ABDEGHJ"
            ]
        },  # noqa
        18: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABCD", "L=ABE", "M=ACE",
                "N=BCE", "O=ABCE", "P=ADE", "Q=BDE", "R=CDE", "S=ABCDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ACD", "K=BCD", "L=ABE", "M=ACE", "N=BCE",
                "O=ABF", "P=ACF", "Q=ADEF", "R=BDEF", "S=CDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDF", "L=ACEF", "M=ADEF", "N=ACDG",
                "O=ABEG", "P=ADEG", "Q=ABFG", "R=ACFG", "S=BCDEFG"
            ],
            256: [
                "J=ABCD", "K=ABCE", "L=ADEF", "M=ADEG", "N=ABFG", "O=ACFG",
                "P=BCDEH", "Q=BDFH", "R=CEGH", "S=BCFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ACDFJ",
                "P=BCEGJ", "Q=ABCEFHJ", "R=ABDEGHJ", "S=BCDFGHJ"
            ]
        },  # noqa
        19: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABCD", "L=ABE", "M=ACE",
                "N=BCE", "O=ABCE", "P=ADE", "Q=BDE", "R=ABDE", "S=CDE",
                "T=ABCDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ACD", "K=BCD", "L=ABE", "M=ACE", "N=BCE",
                "O=ABF", "P=ACF", "Q=BCF", "R=ADEF", "S=BDEF", "T=CDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDE", "L=ABCF", "M=ADEF", "N=BDEF",
                "O=ACDG", "P=ACEG", "Q=CDEG", "R=ABFG", "S=ADFG", "T=ABCDEFG"
            ],
            256: [
                "J=ABCDE", "K=ABCDF", "L=ABCEG", "M=ABDEFG", "N=ACDEFG",
                "O=BCEFH", "P=ADEGH", "Q=BCDEGH", "R=ABCFGH", "S=BDFGH",
                "T=CDFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ADEFGH",
                "P=BCEFJ", "Q=ABDEGJ", "R=BDFGJ", "S=ACFHJ", "T=BCEGHJ"
            ]
        },  # noqa
        20: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABCD", "L=ABE", "M=ACE",
                "N=BCE", "O=ABCE", "P=ADE", "Q=BDE", "R=ABDE", "S=CDE",
                "T=ACDE", "U=ABCDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ACD", "K=BCD", "L=ABE", "M=ACE", "N=BCE",
                "O=ABF", "P=ACF", "Q=BCF", "R=ADEF", "S=BDEF", "T=CDEF",
                "U=ABCDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDE", "L=ABCF", "M=ADEF", "N=BDEF",
                "O=ACDG", "P=ACEG", "Q=CDEG", "R=ABFG", "S=ADFG", "T=AEFG",
                "U=ABCDEFG"
            ],
            256: [
                "J=ABCDE", "K=ABCDF", "L=ABCEF", "M=ABCDG", "N=ABEFG",
                "O=ACDEFG", "P=ABDEH", "Q=ACDEFH", "R=ABCEGH", "S=BDEGH",
                "T=ABDFGH", "U=BCDEFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ADEFGH",
                "P=BCEFJ", "Q=ABDEGJ", "R=BDFGJ", "S=ACFHJ", "T=BCEGHJ",
                "U=CDFGHJ"
            ]
        },  # noqa
        21: {
            32: [
                "F=ABC", "G=ABD", "H=ACD", "J=BCD", "K=ABCD", "L=ABE", "M=ACE",
                "N=BCE", "O=ABCE", "P=ADE", "Q=BDE", "R=ABDE", "S=CDE",
                "T=ACDE", "U=BCDE", "V=ABCDE"
            ],
            64: [
                "G=ABC", "H=ABD", "J=ACD", "K=BCD", "L=ABE", "M=ACE", "N=BCE",
                "O=ADE", "P=ABF", "Q=ADF", "R=BDF", "S=AEF", "T=CEF", "U=DEF",
                "V=BCDEF"
            ],
            128: [
                "H=ABCD", "J=ABCE", "K=ABDE", "L=ACDE", "M=ABCF", "N=ABDF",
                "O=ACEF", "P=ADEF", "Q=ACDG", "R=ABEG", "S=BCDEG", "T=CDFG",
                "U=BEFG", "V=ABCDEFG"
            ],
            256: [
                "J=ABCDE", "K=ABCDF", "L=ABCEF", "M=ABDEF", "N=ABCDG",
                "O=ABEFG", "P=ACDEFG", "Q=ACDEFH", "R=BCDEFH", "S=BCEGH",
                "T=ABDEGH", "U=ABCFGH", "V=BDFGH"
            ],
            512: [
                "K=ABCDE", "L=ABCFG", "M=ABDFH", "N=ACEGH", "O=ADEFGH",
                "P=BCEFJ", "Q=ABDEGJ", "R=BDFGJ", "S=ACFHJ", "T=BCEGHJ",
                "U=CDFGHJ", "V=DEHJ"
            ]
        }  # noqa
    }

    if run_count == 2**factor_count:
        generators = []
    else:
        generators = generator_list[factor_count][run_count]
    fractional_factors = len(generators)

    full_factor_count = factor_count - fractional_factors
    full_factor_names = design.get_factor_names(full_factor_count)
    factor_data = pd.DataFrame(build_full_factorial(full_factor_count),
                               columns=full_factor_names)

    if full_factor_count == factor_count:
        return factor_data

    for gen in generators:
        lhs, rhs = gen.split("=")
        lhs = "X" + str(design.get_var_id(lhs) + 1)  # rename to X1/X2/etc
        cols = []
        for var in rhs:
            cols.append(design.get_var_id(var))

        generator_column = factor_data.iloc[:,
                                            cols].product(axis=1).rename(lhs)
        factor_data = factor_data.join(generator_column)

    return factor_data
Example #10
0
def build_ccd(factor_count, alpha="rotatable", center_points=1):
    """Builds a central composite design.

    The most popular response surface method (RSM) design is the central
    composite design (CCD). A CCD has three groups of design points:

    (a) two-level factorial or fractional factorial design points
    (b) axial points (sometimes called "star" points)
    (c) center points

    CCDs are designed to estimate the coefficients of a quadratic model.

    **Factorial Points**

    The two-level factorial part of the design consists of all possible
    combinations of the +1 and -1 levels of the factors. For the two factor case
    there are four design points:

        (-1, -1) (+1, -1) (-1, +1) (+1, +1)

    **Star or Axial Points**

    The star points have all of the factors set to 0, the midpoint, except one
    factor, which has the value +/- Alpha. For a two-factor problem, the star
    points are:

        (-Alpha, 0) (+Alpha, 0) (0, -Alpha) (0, +Alpha)

    The value for Alpha is calculated in each design for both rotatability and
    orthogonality of blocks. The experimenter can choose between these values or
    enter a different one. The default value is set to the rotatable value.

    Another position for the star points is at the face of the cube portion on
    the design. This is commonly referred to as a face-centered central
    composite design. You can create this by setting the alpha distance to one,
    or setting the alpha parameter to "face centered". This design only requires
    three levels for each factor.

    **Center Points**

    Center points, as implied by the name, are points with all levels set to
    coded level 0 - the midpoint of each factor range: (0, 0)

    Center points are usually repeated 4-6 times to get a good estimate of
    experimental error (pure error).

    To summarize, central composite designs require 5 levels of each factor:
    -Alpha, -1, 0, 1, and +Alpha. One of the commendable attributes of the
    central composite design is that its structure lends itself to sequential
    experimentation. Central composite designs can be carried out in blocks.

    **Categorical Factors**

    You may also add categorical factors to this design. This will cause the
    number of runs generated to be multiplied by the number of combinations of
    the categorical factor levels.

    :param factor_count: The number of factors to build for.
    :type factor_count: integer
    :param alpha_type: The alpha to use for the axial points calculate. This can
                       be either a float, in which case the "star" or "axial"
                       points in the design will be placed at that distance. It
                       can also be a string indicating the type of CCD to build
                       e.g. "rotatable".
    :type alpha_type: integer or float
    :param center_points: The number of center points to include in the design.
    :type center_points: integer
    """
    factor_names = design.get_factor_names(factor_count)
    factorial_runs = pd.DataFrame(build_full_factorial(factor_count),
                                  columns=factor_names)

    axial_count = factor_count * 2
    axial_runs = pd.DataFrame(0.0,
                              index=np.arange(0, axial_count),
                              columns=factor_names)

    alpha = alpha_from_type(factor_count, alpha, center_points)
    axial_run = 0
    for f in range(factor_count):
        axial_runs.loc[axial_run][f] = -alpha
        axial_run += 1
        axial_runs.loc[axial_run][f] = alpha
        axial_run += 1
    factor_data = factorial_runs.append(axial_runs)

    center_runs = pd.DataFrame(0.0,
                               index=np.arange(0, center_points),
                               columns=factor_names)
    factor_data = factor_data.append(center_runs)

    return factor_data