在函数中调用 patsy 时的命名空间问题

Namespace issues when calling patsy within a function

我正在尝试为 statsmodels 公式编写一个包装器 API(这是一个简化版本,该函数的作用不止于此):

import statsmodels.formula.api as smf

def wrapper(formula, data, **kwargs):
    return smf.logit(formula, data).fit(**kwargs)

如果我将此函数提供给用户,然后用户尝试定义 his/her 自己的函数:

def square(x):
    return x**2

model = wrapper('y ~ x + square(x)', data=df)

他们将收到 NameError,因为 patsy 模块正在 wrapper 的命名空间中查找函数 square。在事先不知道函数名称是什么或需要多少函数的情况下,是否有一种安全的 Pythonic 方法来处理这种情况?

仅供参考:这是针对 Python 3.4.3.

如果您愿意使用 eval 来完成函数的繁重工作,您可以从 wrapper 的参数和外部框架的局部变量构建一个命名空间:

wrapper_code = compile("smf.logit(formula, data).fit(**kwargs)",
                       "<WrapperFunction>","eval")
def wrapper(formula,data,**kwargs):
    outer_frame = sys._getframe(1)
    namespace = dict(outer_frame.f_locals)
    namespace.update(formula=formula, data=data, kwargs=kwargs, smf=smf)
    return eval(wrapper_code,namespace)

我真的不认为这是作弊,因为它似乎是 logit 正在做的事情,它引发了一个 NameError,只要 wrapper_code 没有被修改并且那里没有名称冲突(比如使用名为 data 的东西)这应该做你想做的。

statsmodels 使用 patsy 包解析公式并创建设计矩阵。 patsy 允许用户函数作为公式的一部分,并在用户命名空间或环境中获取或评估用户函数。

作为参考参见 http://patsy.readthedocs.org/en/latest/API-reference.html

中的 eval_env 关键字

from_formula是models的方法,实现patsy的公式接口。它使用 eval_env 向 patsy 提供必要的信息,默认情况下是用户的调用环境。这可以由用户使用相应的关键字参数覆盖。

定义 eval_env 的最简单方法是作为一个整数来指示 patsy 应该使用的堆栈级别。 from_formula 正在增加它以考虑 statsmodels 方法中的附加级别。

根据评论,eval_env = 2 将使用创建模型级别的下一个更高级别,例如model = smf.logit(..., eval_env=2).

这将创建模型、调用 patsy 并创建设计矩阵,model.fit() 将对其进行估计并 returns 结果实例。