def test_check_length_of_initial_values(self): """ Ensure that a ValueError is raised when one passes an init_vals argument of the wrong length. """ # Create a variable for the standard arguments to the MNL constructor. standard_args = [ self.fake_df, self.alt_id_col, self.obs_id_col, self.choice_col, self.fake_specification ] # Create the mnl model object whose coefficients will be estimated. base_mnl = mnl.MNL(*standard_args) # Create the EstimationObj mapping_res = base_mnl.get_mappings_for_fit() ridge = None zero_vector = np.zeros(1) split_params = mnl.split_param_vec mnl_estimator = mnl.MNLEstimator(base_mnl, mapping_res, ridge, zero_vector, split_params) # Alias the function to be checked func = mnl_estimator.check_length_of_initial_values for i in [2, 3]: init_vals = np.ones(i) self.assertRaises(ValueError, func, init_vals) self.assertIsNone(func(np.ones(1))) return None
def test_ridge_warning_in_fit_mle(self): """ Ensure that a UserWarning is raised when one passes the ridge keyword argument to the `fit_mle` method of an MNL model object. """ # Create a variable for the standard arguments to the MNL constructor. standard_args = [ self.fake_df, self.alt_id_col, self.obs_id_col, self.choice_col, self.fake_specification ] # Create the mnl model object whose coefficients will be estimated. base_mnl = mnl.MNL(*standard_args) # Create a variable for the fit_mle function's kwargs. # The print_res = False arguments are to make sure strings aren't # printed to the console unnecessarily. kwargs = {"ridge": 0.5, "print_res": False} # Test to make sure that the ridge warning message is printed when # using the ridge keyword argument with warnings.catch_warnings(record=True) as w: # Use this filter to always trigger the UserWarnings warnings.simplefilter('always', UserWarning) base_mnl.fit_mle(self.fake_beta, **kwargs) self.assertGreaterEqual(len(w), 1) self.assertIsInstance(w[0].category, type(UserWarning)) self.assertIn(mnl._ridge_warning_msg, str(w[0].message)) return None
def test_outside_intercept_error_in_fit_mle(self): """ Ensures that a ValueError is raised when users try to use any other type of initial value input methods other than the `init_vals` argument of `fit_mle()`. This prevents people from expecting the use of outside intercept or shape parameters to work with the MNL model. """ # Create a variable for the standard arguments to the MNL constructor. standard_args = [ self.fake_df, self.alt_id_col, self.obs_id_col, self.choice_col, self.fake_specification ] # Create the mnl model object whose coefficients will be estimated. base_mnl = mnl.MNL(*standard_args) # Create a variable for the arguments to the fit_mle function. fit_args = [self.fake_beta] # Create variables for the incorrect kwargs. # The print_res = False arguments are to make sure strings aren't # printed to the console unnecessarily. kwarg_map_1 = {"init_shapes": np.array([1, 2]), "print_res": False} kwarg_map_2 = {"init_intercepts": np.array([1]), "print_res": False} kwarg_map_3 = {"init_coefs": np.array([1]), "print_res": False} # Test to ensure that the kwarg ignore message is printed when using # any of these three incorrect kwargs for kwargs in [kwarg_map_1, kwarg_map_2, kwarg_map_3]: self.assertRaises(ValueError, base_mnl.fit_mle, *fit_args, **kwargs) return None
def test_shape_ignore_msg_in_constructor(self): """ Ensures that a UserWarning is raised when the 'shape_ref_pos' or 'shape_names' keyword arguments are passed to the MNL model constructor. This warns people against expecting the MNL to work with shape parameters, and alerts them to the fact they are using an MNL model when they might have been expecting to instantiate a different choice model. """ # Create a variable for the standard arguments to this function. standard_args = [ self.fake_df, self.alt_id_col, self.obs_id_col, self.choice_col, self.fake_specification ] # Create a variable for the kwargs being passed to the constructor kwarg_map_1 = {"shape_ref_pos": 2} kwarg_map_2 = {"shape_names": OrderedDict([("x", ["foo"])])} # Test to ensure that the shape ignore message is printed when using # either of these two kwargs with warnings.catch_warnings(record=True) as context: # Use this filter to always trigger the UserWarnings warnings.simplefilter('always', UserWarning) for pos, bad_kwargs in enumerate([kwarg_map_1, kwarg_map_2]): # Create an MNL model object with the irrelevant kwargs. # This should trigger a UserWarning mnl_obj = mnl.MNL(*standard_args, **bad_kwargs) # Check that the warning has been created. self.assertEqual(len(context), pos + 1) self.assertIsInstance(context[-1].category, type(UserWarning)) self.assertIn(mnl._shape_ignore_msg, str(context[-1].message)) return None
def test_just_point_kwarg(self): # Create a variable for the standard arguments to the MNL constructor. standard_args = [ self.fake_df, self.alt_id_col, self.obs_id_col, self.choice_col, self.fake_specification ] # Create the mnl model object whose coefficients will be estimated. base_mnl = mnl.MNL(*standard_args) # Alias the function being tested func = base_mnl.fit_mle # Get the necessary kwargs kwargs = {"just_point": True} # Get the function results func_result = func(self.fake_beta, **kwargs) # Perform the desired tests to make sure we get back a dictionary with # an "x" key in it and a value that is a ndarray. self.assertIsInstance(func_result, dict) self.assertIn("x", func_result) self.assertIsInstance(func_result["x"], np.ndarray) return None
def make_clog_and_mnl_models(self): # The set up being used is one where there are two choice situations, # The first having three alternatives, and the second having only two # alternatives. There is one generic variable. Two alternative # specific constants and all three shape parameters are used. # Create the betas to be used during the tests fake_betas = np.array([-0.6]) # Create the fake outside intercepts to be used during the tests fake_intercepts = np.array([1, 0.5]) # Create names for the intercept parameters fake_intercept_names = ["ASC 1", "ASC 2"] # Record the position of the intercept that is not being estimated fake_intercept_ref_pos = 2 # Create an array of all model parameters fake_all_params = np.concatenate((fake_intercepts, fake_betas)) # Get the mappping between rows and observations fake_rows_to_obs = csr_matrix( np.array([[1, 0], [1, 0], [1, 0], [0, 1], [0, 1]])) # Create the fake design matrix with columns denoting X # The intercepts are not included because they are kept outside the # index in the scobit model. fake_design = np.array([[1], [2], [3], [1.5], [3.5]]) # Create the index array for this set of choice situations fake_index = fake_design.dot(fake_betas) # Create the needed dataframe for the model constructor fake_df = pd.DataFrame({ "obs_id": [1, 1, 1, 2, 2], "alt_id": [1, 2, 3, 1, 3], "choice": [0, 1, 0, 0, 1], "x": fake_design[:, 0], "intercept": [1 for i in range(5)] }) # Record the various column names alt_id_col = "alt_id" obs_id_col = "obs_id" choice_col = "choice" # Create the index specification and name dictionaryfor the model fake_specification = OrderedDict() fake_names = OrderedDict() fake_specification["x"] = [[1, 2, 3]] fake_names["x"] = ["x (generic coefficient)"] mnl_spec = OrderedDict() mnl_names = OrderedDict() mnl_spec["intercept"] = [1, 2] mnl_names["intercept"] = fake_intercept_names mnl_spec["x"] = fake_specification["x"] mnl_names["x"] = fake_names["x"] # Bundle args and kwargs used to construct the Asymmetric Logit model. clog_args = [ fake_df, alt_id_col, obs_id_col, choice_col, fake_specification ] mnl_args = deepcopy(clog_args) mnl_args[-1] = mnl_spec # Create a variable for the kwargs being passed to the constructor clog_kwargs = { "names": fake_names, "intercept_ref_pos": fake_intercept_ref_pos, "intercept_names": fake_intercept_names } mnl_kwargs = {"names": mnl_names} # Initialize a basic Asymmetric Logit model whose coefficients will be # estimated. clog_obj = clog.MNCL(*clog_args, **clog_kwargs) mnl_obj = mnl.MNL(*mnl_args, **mnl_kwargs) # Create the desired model attributes for the clog log model clog_obj.coefs = pd.Series(fake_betas, index=fake_names["x"]) clog_obj.intercepts =\ pd.Series(fake_intercepts, index=fake_intercept_names) clog_obj.shapes = None clog_obj.nests = None clog_obj.params =\ pd.concat([clog_obj.intercepts, clog_obj.coefs], axis=0, ignore_index=False) mnl_obj.params = clog_obj.params.copy() mnl_obj.coefs = mnl_obj.params.copy() mnl_obj.intercepts = None mnl_obj.shapes = None mnl_obj.nests = None return clog_obj, mnl_obj