Skip to content

Commit 6f7fafa

Browse files
authored
Merge pull request #3382 from adamboche/glm_scope
Allow linear model formula to extract variables from calling scope.
2 parents 8d9d3d8 + e7916de commit 6f7fafa

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

Diff for: RELEASE-NOTES.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### New features
66

77
- `Mixture` now supports mixtures of multidimensional probability distributions, not just lists of 1D distributions.
8+
- `GLM.from_formula` and `LinearComponent.from_formula` can extract variables from the calling scope. Customizable via the new `eval_env` argument. Fixing #3382.
89

910
### Maintenance
1011

@@ -509,4 +510,3 @@ Thus, Thomas, Chris and I are pleased to announce that PyMC3 is now in Beta.
509510
* maahnman <[email protected]>
510511
* paul sorenson <[email protected]>
511512
* zenourn <[email protected]>
512-

Diff for: pymc3/glm/linear.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,22 @@ def __init__(self, x, y, intercept=True, labels=None,
8484

8585
@classmethod
8686
def from_formula(cls, formula, data, priors=None, vars=None,
87-
name='', model=None, offset=0.):
87+
name='', model=None, offset=0., eval_env=0):
88+
"""Creates linear component from `patsy` formula.
89+
90+
Parameters
91+
----------
92+
formula : str - a patsy formula
93+
data : a dict-like object that can be used to look up variables referenced
94+
in `formula`
95+
eval_env : either a `patsy.EvalEnvironment` or else a depth represented as
96+
an integer which will be passed to `patsy.EvalEnvironment.capture()`.
97+
See `patsy.dmatrix` and `patsy.EvalEnvironment` for details.
98+
Other arguments are documented in the constructor.
99+
"""
88100
import patsy
89-
y, x = patsy.dmatrices(formula, data)
101+
eval_env = patsy.EvalEnvironment.capture(eval_env, reference=1)
102+
y, x = patsy.dmatrices(formula, data, eval_env=eval_env)
90103
labels = x.design_info.column_names
91104
return cls(np.asarray(x), np.asarray(y)[:, -1], intercept=False,
92105
labels=labels, priors=priors, vars=vars, name=name,
@@ -140,9 +153,23 @@ def __init__(self, x, y, intercept=True, labels=None,
140153
@classmethod
141154
def from_formula(cls, formula, data, priors=None,
142155
vars=None, family='normal', name='',
143-
model=None, offset=0.):
156+
model=None, offset=0., eval_env=0):
157+
"""
158+
Creates GLM from formula.
159+
160+
Parameters
161+
----------
162+
formula : str - a `patsy` formula
163+
data : a dict-like object that can be used to look up variables referenced
164+
in `formula`
165+
eval_env : either a `patsy.EvalEnvironment` or else a depth represented as
166+
an integer which will be passed to `patsy.EvalEnvironment.capture()`.
167+
See `patsy.dmatrix` and `patsy.EvalEnvironment` for details.
168+
Other arguments are documented in the constructor.
169+
"""
144170
import patsy
145-
y, x = patsy.dmatrices(formula, data)
171+
eval_env = patsy.EvalEnvironment.capture(eval_env, reference=1)
172+
y, x = patsy.dmatrices(formula, data, eval_env=eval_env)
146173
labels = x.design_info.column_names
147174
return cls(np.asarray(x), np.asarray(y)[:, -1], intercept=False,
148175
labels=labels, priors=priors, vars=vars, family=family,

Diff for: pymc3/tests/test_glm.py

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from numpy.testing import assert_equal
33

44
from .helpers import SeededTest
5+
import pymc3
56
from pymc3 import Model, Uniform, Normal, find_MAP, Slice, sample
67
from pymc3 import families, GLM, LinearComponent
78
import pandas as pd
@@ -117,3 +118,15 @@ def test_boolean_y(self):
117118
)
118119
)
119120
assert_equal(model.y.observations, model_bool.y.observations)
121+
122+
def test_glm_formula_from_calling_scope(self):
123+
"""Formula can extract variables from the calling scope."""
124+
z = pd.Series([10, 20, 30])
125+
df = pd.DataFrame({"y": [0, 1, 0], "x": [1.0, 2.0, 3.0]})
126+
GLM.from_formula("y ~ x + z", df, family=pymc3.glm.families.Binomial())
127+
128+
def test_linear_component_formula_from_calling_scope(self):
129+
"""Formula can extract variables from the calling scope."""
130+
z = pd.Series([10, 20, 30])
131+
df = pd.DataFrame({"y": [0, 1, 0], "x": [1.0, 2.0, 3.0]})
132+
LinearComponent.from_formula("y ~ x + z", df)

0 commit comments

Comments
 (0)