3

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

import statsmodels.formula.api as smf

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

如果我将此功能提供给用户,然后他/她会尝试定义他/她自己的功能:

def square(x):
    return x**2

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

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

仅供参考:这是针对 Python 3.4.3 的。

4

2 回答 2

2

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

作为参考,请参阅http://patsy.readthedocs.org/en/latest/API-reference.html中的 eval_env 关键字

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

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

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

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

于 2016-04-22T21:29:59.057 回答
1

如果您愿意用来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),这应该做你想。

于 2016-04-22T20:10:19.407 回答