'Create a Model from a formula and dataframe.
Parameters
formula : str or generic Formula object
The formula specifying the model
data : array-like
The data for the model. See Notes.
re_formula : string
A one-sided formula defining the variance structure of the
model.  The default gives a random intercept for each
group.
vc_formula : dict-like
Formulas describing variance components.  `vc_formula[vc]` is
the formula for the component with variance parameter named
`vc`.  The formula is processed into a matrix, and the columns
of this matrix are linearly combined with independent random
coefficients having mean zero and a common variance.
subset : array-like
An array-like object of booleans, integers, or index
values that indicate the subset of df to use in the
model. Assumes df is a `pandas.DataFrame`
missing : string
Either \'none\' or \'drop\'
args : extra arguments
These are passed to the model
kwargs : extra keyword arguments
These are passed to the model with one exception. The
``eval_env`` keyword is passed to patsy. It can be either a
:class:`patsy:patsy.EvalEnvironment` object or an integer
indicating the depth of the namespace to use. For example, the
default ``eval_env=0`` uses the calling namespace. If you wish
to use a "clean" environment set ``eval_env=-1``.
Returns
model : Model instance
Notes
`data` must define __getitem__ with the keys in the formula
terms args and kwargs are passed on to the model
instantiation. E.g., a numpy structured or rec array, a
dictionary, or a pandas DataFrame.
If the variance component is intended to produce random
intercepts for disjoint subsets of a group, specified by
string labels or a categorical data value, always use \'0 +\' in
the formula so that no overall intercept is included.
If the variance components specify random slopes and you do
not also want a random group-level intercept in the model,
then use \'0 +\' in the formula to exclude the intercept.
The variance components formulas are processed separately for
each group.  If a variable is categorical the results will not
be affected by whether the group labels are distinct or
re-used over the top-level groups.
Examples
Suppose we have an educational data set with students nested
in classrooms nested in schools.  The students take a test,
and we want to relate the test scores to the students\' ages,
while accounting for the effects of classrooms and schools.
The school will be the top-level group, and the classroom is a
nested group that is specified as a variance component.  Note
that the schools may have different number of classrooms, and
the classroom labels may (but need not be) different across
the schools.
>>> vc = {\'classroom\': \'0 + C(classroom)\'}
>>> MixedLM.from_formula(\'test_score ~ age\', vc_formula=vc,                                   re_formula=\'1\', groups=\'school\', data=data)
Now suppose we also have a previous test score called
\'pretest\'.  If we want the relationship between pretest
scores and the current test to vary by classroom, we can
specify a random slope for the pretest score
>>> vc = {\'classroom\': \'0 + C(classroom)\', \'pretest\': \'0 + pretest\'}
>>> MixedLM.from_formula(\'test_score ~ age + pretest\', vc_formula=vc,                                   re_formula=\'1\', groups=\'school\', data=data)
The following model is almost equivalent to the previous one,
but here the classroom random intercept and pretest slope may
be correlated.
>>> vc = {\'classroom\': \'0 + C(classroom)\'}
>>> MixedLM.from_formula(\'test_score ~ age + pretest\', vc_formula=vc,                                   re_formula=\'1 + pretest\', groups=\'school\',                                   data=data)'

@classmethod
def from_formula(cls, formula, data, re_formula=None, vc_formula=None, subset=None, use_sparse=False, missing='none', *args, **kwargs):

    if ('groups' not in kwargs.keys()):
        raise AttributeError("'groups'    is    a    required    keyword    argument    in    MixedLM.from_formula")
    groups = kwargs['groups']
    group_name = 'Group'
    if isinstance(groups, string_types):
        group_name = groups
        groups = np.asarray(data[groups])
    del kwargs['groups']
    if (missing == 'drop'):
        (data, groups) = _handle_missing(data, groups, formula, re_formula, vc_formula)
        missing = 'none'
    if (re_formula is not None):
        if (re_formula.strip() == '1'):
            exog_re = np.ones((data.shape[0], 1))
            exog_re_names = [group_name]
        else:
            eval_env = kwargs.get('eval_env', None)
            if (eval_env is None):
                eval_env = 1
            elif (eval_env == (-1)):
                from patsy import EvalEnvironment
                eval_env = EvalEnvironment({})
            exog_re = patsy.dmatrix(re_formula, data, eval_env=eval_env)
            exog_re_names = exog_re.design_info.column_names
            exog_re_names = [x.replace('Intercept', group_name) for x in exog_re_names]
            exog_re = np.asarray(exog_re)
        if (exog_re.ndim == 1):
            exog_re = exog_re[:, None]
    else:
        exog_re = None
        if (vc_formula is None):
            exog_re_names = [group_name]
        else:
            exog_re_names = []
    if (vc_formula is not None):
        eval_env = kwargs.get('eval_env', None)
        if (eval_env is None):
            eval_env = 1
        elif (eval_env == (-1)):
            from patsy import EvalEnvironment
            eval_env = EvalEnvironment({})
        exog_vc = {}
        gb = data.groupby(groups)
        kylist = list(gb.groups.keys())
        kylist.sort()
        for vc_name in vc_formula.keys():
            exog_vc[vc_name] = {}
            for (group_ix, group) in enumerate(kylist):
                ii = gb.groups[group]
                vcg = vc_formula[vc_name]
                mat = patsy.dmatrix(vcg, data.loc[ii, :], eval_env=eval_env, return_type='dataframe')
                if use_sparse:
                    exog_vc[vc_name][group] = sparse.csr_matrix(mat)
                else:
                    exog_vc[vc_name][group] = np.asarray(mat)
        exog_vc = exog_vc
    else:
        exog_vc = None
    mod = super(MixedLM, cls).from_formula(formula, data, subset=None, exog_re=exog_re, exog_vc=exog_vc, groups=groups, *args, **kwargs)
    (param_names, exog_re_names, exog_re_names_full) = mod._make_param_names(exog_re_names)
    mod.data.param_names = param_names
    mod.data.exog_re_names = exog_re_names
    mod.data.exog_re_names_full = exog_re_names_full
    mod.data.vcomp_names = mod._vc_names
    return mod
