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]))
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
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])
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
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)
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)
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)
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
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
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