def predict(self,
                data,
                param_list=None,
                return_long_probs=True,
                choice_col=None,
                num_draws=None,
                seed=None):
        """
        Parameters
        ----------
        data : string or pandas dataframe.
            If string, data should be an absolute or relative path to a CSV
            file containing the long format data for this choice model. Note
            long format is has one row per available alternative for each
            observation. If pandas dataframe, the dataframe should be the long
            format data for the choice model. The data should include all of
            the same columns as the original data used to construct the choice
            model, with the sole exception of the "intercept" column. If needed
            the "intercept" column will be dynamically created.
        param_list : list, optional.
            Contains four elements, each being a numpy array or None. Either
            all of the arrays should be 1D or all of the arrays should be 2D.
            If 2D, the arrays should have the same number of columns. Each
            column should be a particular set of parameter values that one
            wants to predict with. The first element in the list should
            contain the index coefficients. The second element should contain
            the 'outside' intercept parameters if there are any, or None
            otherwise. The third element should contain the shape parameters
            if there are any or None otherwise. The fourth element should
            contain the nest coefficients if there are any or None otherwise.
            Default == None.
        return_long_probs : bool, optional.
            Indicates whether or not the long format probabilites (a 1D numpy
            array with one element per observation per available alternative)
            should be returned. Default == True.
        choice_col : str, optional.
            Denotes the column in `data` which contains a one if the
            alternative pertaining to the given row was the observed outcome
            for the observation pertaining to the given row and a zero
            otherwise. Default == None.
        num_draws : int, or None, optional.
            Should be greater than zero. Denotes the number of draws that we
            are making from each normal distribution. This kwarg is only used
            if self.model_type == "Mixed Logit Model". Default == None.
        seed : int, or None, optional.
            If an int is passed, it should be greater than zero. Denotes the
            value to be used in seeding the random generator used to generate
            the draws from the normal distribution. This kwarg is only used if
            self.model_type == "Mixed Logit Model". Default == None.

        Returns
        -------
        numpy array or tuple of two numpy arrays.
            If `choice_col` is passed AND `return_long_probs is True`, then the
            tuple `(chosen_probs, long_probs)` is returned. If
            `return_long_probs is True` and `chosen_row_to_obs is None`, then
            `long_probs` is returned. If `chosen_row_to_obs` is passed and
            `return_long_probs is False` then `chosen_probs` is returned.

            `chosen_probs` is a 1D numpy array of shape (num_observations,).
            Each element is the probability of the corresponding observation
            being associated with its realized outcome.

            `long_probs` is a 1D numpy array with one element per observation
            per available alternative for that observation. Each element is the
            probability of the corresponding observation being associated with
            that rows corresponding alternative.

            It is NOT valid to have `chosen_row_to_obs == None` and
            `return_long_probs == False`.
        """
        # Get the dataframe of observations we'll be predicting on
        dataframe = get_dataframe_from_data(data)

        # Determine the conditions under which we will add an intercept column
        # to our long format dataframe.
        add_intercept_to_dataframe(self.specification, dataframe)

        # Make sure the necessary columns are in the long format dataframe
        for column in [self.alt_id_col,
                       self.obs_id_col,
                       self.mixing_id_col]:
            if column is not None:
                ensure_columns_are_in_dataframe([column], dataframe)

        # If param_list is passed, check the validity of its elements
        self.check_param_list_validity(param_list)

        # Check validity of the return_long_probs and choice_col kwargs
        check_for_choice_col_based_on_return_long_probs(return_long_probs,
                                                        choice_col)

        # Get the new column of alternative IDs and get the new design matrix
        new_alt_IDs = dataframe[self.alt_id_col].values

        new_design_res = create_design_matrix(dataframe,
                                              self.specification,
                                              self.alt_id_col,
                                              names=self.name_spec)

        new_design = new_design_res[0]

        # Get the new mappings between the alternatives and observations
        mapping_res = create_long_form_mappings(dataframe,
                                                self.obs_id_col,
                                                self.alt_id_col,
                                                choice_col=choice_col,
                                                nest_spec=self.nest_spec,
                                                mix_id_col=self.mixing_id_col)

        new_rows_to_obs = mapping_res["rows_to_obs"]
        new_rows_to_alts = mapping_res["rows_to_alts"]
        new_chosen_to_obs = mapping_res["chosen_row_to_obs"]
        new_rows_to_nests = mapping_res["rows_to_nests"]
        new_rows_to_mixers = mapping_res["rows_to_mixers"]

        # Get the parameter arrays to be used in calculating the probabilities
        if param_list is None:
            new_index_coefs = self.coefs.values
            new_intercepts = (self.intercepts.values if self.intercepts
                              is not None else None)
            new_shape_params = (self.shapes.values if self.shapes
                                is not None else None)
            new_nest_coefs = (self.nests.values if self.nests
                              is not None else None)
        else:
            new_index_coefs = param_list[0]
            new_intercepts = param_list[1]
            new_shape_params = param_list[2]
            new_nest_coefs = param_list[3]

        # Get the probability of each observation choosing each available
        # alternative
        if self.model_type == "Nested Logit Model":
            # Get the 'natural' nest coefficients for prediction
            new_natural_nests = naturalize_nest_coefs(new_nest_coefs)
            # Determine the return string for the nested logit model
            if return_long_probs:
                return_string = "long_probs"
            else:
                return_string = "chosen_probs"
            # This condition accounts for the fact that we have a different
            # functional interface for nested vs non-nested models
            return calc_nested_probs(new_natural_nests,
                                     new_index_coefs,
                                     new_design,
                                     new_rows_to_obs,
                                     new_rows_to_nests,
                                     chosen_row_to_obs=new_chosen_to_obs,
                                     return_type=return_string)

        elif self.model_type == "Mixed Logit Model":
            ##########
            # This condition accounts for the fact that Mixed Logit models have
            # a different functional interface than the standard logit-type
            # models.
            ##########
            # Get the draws for each random coefficient
            num_mixing_units = new_rows_to_mixers.shape[1]
            draw_list = mlc.get_normal_draws(num_mixing_units,
                                             num_draws,
                                             len(self.mixing_pos),
                                             seed=seed)
            # Calculate the 3D design matrix for the prediction.
            design_args = (new_design,
                           draw_list,
                           self.mixing_pos,
                           new_rows_to_mixers)
            new_design_3d = mlc.create_expanded_design_for_mixing(*design_args)
            # Calculate the desired probabilities for the mixed logit model.
            prob_args = (new_index_coefs,
                         new_design_3d,
                         new_alt_IDs,
                         new_rows_to_obs,
                         new_rows_to_alts,
                         self.utility_transform)
            prob_kwargs = {"intercept_params": new_intercepts,
                           "shape_params": new_shape_params,
                           "chosen_row_to_obs": new_chosen_to_obs,
                           "return_long_probs": return_long_probs}
            prob_array = calc_probabilities(*prob_args, **prob_kwargs)
            return prob_array.mean(axis=1)

        else:
            return calc_probabilities(new_index_coefs,
                                      new_design,
                                      new_alt_IDs,
                                      new_rows_to_obs,
                                      new_rows_to_alts,
                                      self.utility_transform,
                                      intercept_params=new_intercepts,
                                      shape_params=new_shape_params,
                                      chosen_row_to_obs=new_chosen_to_obs,
                                      return_long_probs=return_long_probs)