现在,您从公式中需要的术语列表开始,然后尝试将它们粘贴到一个复杂的字符串中,patsy 将解析并转换回术语列表。您可以看到 patsy 为这种公式生成的数据结构(ModelDesc.from_formula
是 patsy 的解析器):
In [7]: from patsy import ModelDesc
In [8]: ModelDesc.from_formula("y ~ x1 + x2 + x3")
Out[8]:
ModelDesc(lhs_termlist=[Term([EvalFactor('y')])],
rhs_termlist=[Term([]),
Term([EvalFactor('x1')]),
Term([EvalFactor('x2')]),
Term([EvalFactor('x3')])])
这可能看起来有点吓人,但实际上非常简单——你有一个ModelDesc
,它代表一个公式,它有一个左侧的术语列表和一个右侧的术语列表。每个术语都由一个Term
对象表示,并且每个术语Term
都有一个因素列表。(这里每个术语只有一个因素——如果你有任何交互,那么这些术语将有多个因素。)此外,“空交互”Term([])
是 patsy 表示截距项的方式。
因此,您可以通过直接创建您想要的术语并将它们传递给 patsy 来避免所有这些复杂的引用/解析内容,跳过字符串解析步骤
from patsy import ModelDesc, Term, LookupFactor
response_terms = [Term([LookupFactor(response)])]
# start with intercept...
model_terms = [Term([])]
# ...then add another term for each candidate
model_terms += [Term([LookupFactor(c)]) for c in candidates]
model_desc = ModelDesc(response_terms, model_terms)
现在您可以将该model_desc
对象传递给通常会传递 patsy 公式的任何函数:
ols(model_desc, data).fit().rsquared_adj
这里还有一个技巧:你会注意到第一个例子有EvalFactor
对象,现在我们使用了LookupFactor
对象。不同之处在于它EvalFactor
需要一串任意 Python 代码,如果你想写np.log(x1)
类似weight.in.kg
. LookupFactor
直接获取变量的名称以在您的数据中查找,因此无需进一步引用。
或者,您可以使用一些更高级的 Python 字符串处理来做到这一点,例如:
quoted = ["Q('{}')".format(c) for c in candidates]
formula = "{} ~ {} + 1".format(response, ' + '.join(quoted))
但是,虽然这开始有点简单,但它更脆弱 - 例如,考虑(或尝试)如果您的参数之一包含引号字符会发生什么!您永远不应该在候选名称来自您无法控制的其他地方(例如随机 CSV 文件)的处理管道中编写这样的东西——您可能会执行各种任意代码。上述解决方案避免了所有这些问题。
参考: