def test_blocks(): on_off = [[1,2],[3,4]] tval = np.array([0.4,1.4,2.4,3.4]) b = blocks(on_off) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 1, 0, 1]) b = blocks(on_off, amplitudes=[3,5]) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 3, 0, 5])
def test_step_function(): # test step function # step function is a function of t s = step_function([0,4,5],[2,4,6]) tval = np.array([-0.1,0,3.9,4,4.1,5.1]) lam = lambdify(t, s) yield assert_array_equal(lam(tval), [0, 2, 2, 4, 4, 6]) s = step_function([0,4,5],[4,2,1]) lam = lambdify(t, s) yield assert_array_equal(lam(tval), [0, 4, 4, 2, 2, 1])
def test_1d(): B = gen_BrownianMotion() Bs = implemented_function("B", B) t = sympy.Symbol('t') expr = 3*sympy.exp(Bs(t)) + 4 expected = 3*np.exp(B.y)+4 ee_vec = lambdify(t, expr) yield assert_almost_equal(ee_vec(B.x), expected) # with any arbitrary symbol b = sympy.Symbol('b') expr = 3*sympy.exp(Bs(b)) + 4 ee_vec = lambdify(b, expr) yield assert_almost_equal(ee_vec(B.x), expected)
def test_1d(): B = gen_BrownianMotion() Bs = implemented_function("B", B) t = sympy.Symbol('t') expr = 3 * sympy.exp(Bs(t)) + 4 expected = 3 * np.exp(B.y) + 4 ee_vec = lambdify(t, expr, "numpy") assert_almost_equal(ee_vec(B.x), expected) # with any arbitrary symbol b = sympy.Symbol('b') expr = 3 * sympy.exp(Bs(b)) + 4 ee_vec = lambdify(b, expr, "numpy") assert_almost_equal(ee_vec(B.x), expected)
def test_interp(): times = [0, 4, 5.] values = [2., 4, 6] for int_func in (interp, linear_interp): s = int_func(times, values, np.nan) tval = np.array([-0.1, 0.1, 3.9, 4.1, 5.1]) res = lambdify(t, s)(tval) assert_array_equal(np.isnan(res), [True, False, False, False, True]) assert_array_almost_equal(res[1:-1], [2.05, 3.95, 4.2]) # default is zero fill s = int_func(times, values) res = lambdify(t, s)(tval) assert_array_almost_equal(res, [0, 2.05, 3.95, 4.2, 0]) # Can be some other value s = int_func(times, values, fill=10) res = lambdify(t, s)(tval) assert_array_almost_equal(res, [10, 2.05, 3.95, 4.2, 10]) # If fill is None, raises error on interpolation outside bounds s = int_func(times, values, fill=None) f = lambdify(t, s) assert_array_almost_equal(f(tval[1:-1]), [2.05, 3.95, 4.2]) assert_raises(ValueError, f, tval[:-1]) # specifying kind as linear is OK s = linear_interp(times, values, kind='linear') # bounds_check should match fill int_func(times, values, bounds_error=False) int_func(times, values, fill=None, bounds_error=True) assert_raises(ValueError, int_func, times, values, bounds_error=True) # fill should match fill value int_func(times, values, fill=10, fill_value=10) int_func(times, values, fill_value=0) assert_raises(ValueError, int_func, times, values, fill=10, fill_value=9) int_func(times, values, fill=np.nan, fill_value=np.nan) assert_raises(ValueError, int_func, times, values, fill=10, fill_value=np.nan) assert_raises(ValueError, int_func, times, values, fill=np.nan, fill_value=0)
def test_blocks(): on_off = [[1,2],[3,4]] tval = np.array([0.4,1.4,2.4,3.4]) b = blocks(on_off) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 1, 0, 1]) b = blocks(on_off, amplitudes=[3,5]) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 3, 0, 5]) # Check what happens with names # Default is from step function assert_false(re.match(r'step\d+\(t\)$', str(b)) is None) # Can pass in another b = blocks(on_off, name='funky_chicken') assert_equal(str(b), 'funky_chicken(t)')
def test_step_function(): # test step function # step function is a function of t s = step_function([0,4,5],[2,4,6]) tval = np.array([-0.1,0,3.9,4,4.1,5.1]) lam = lambdify(t, s) assert_array_equal(lam(tval), [0, 2, 2, 4, 4, 6]) s = step_function([0,4,5],[4,2,1]) lam = lambdify(t, s) assert_array_equal(lam(tval), [0, 4, 4, 2, 2, 1]) # Name default assert_false(re.match(r'step\d+\(t\)$', str(s)) is None) # Name reloaded s = step_function([0,4,5],[4,2,1], name='goodie_goodie_yum_yum') assert_equal(str(s), 'goodie_goodie_yum_yum(t)')
def test_step_function(): # test step function # step function is a function of t s = step_function([0, 4, 5], [2, 4, 6]) tval = np.array([-0.1, 0, 3.9, 4, 4.1, 5.1]) lam = lambdify(t, s) assert_array_equal(lam(tval), [0, 2, 2, 4, 4, 6]) s = step_function([0, 4, 5], [4, 2, 1]) lam = lambdify(t, s) assert_array_equal(lam(tval), [0, 4, 4, 2, 2, 1]) # Name default assert_false(re.match(r'step\d+\(t\)$', str(s)) is None) # Name reloaded s = step_function([0, 4, 5], [4, 2, 1], name='goodie_goodie_yum_yum') assert_equal(str(s), 'goodie_goodie_yum_yum(t)')
def test_implemented_function(): # Here we check if the default returned functions are anonymous - in # the sense that we can have more than one function with the same name f = implemented_function('f', lambda x: 2 * x) g = implemented_function('f', lambda x: np.sqrt(x)) l1 = lambdify(x, f(x)) l2 = lambdify(x, g(x)) assert_equal(str(f(x)), str(g(x))) assert_equal(l1(3), 6) assert_equal(l2(3), np.sqrt(3)) # check that we can pass in a sympy function as input func = sympy.Function('myfunc') assert_false(hasattr(func, '_imp_')) f = implemented_function(func, lambda x: 2 * x) assert_true(hasattr(func, '_imp_'))
def test_blocks(): on_off = [[1, 2], [3, 4]] tval = np.array([0.4, 1.4, 2.4, 3.4]) b = blocks(on_off) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 1, 0, 1]) b = blocks(on_off, amplitudes=[3, 5]) lam = lambdify(t, b) assert_array_equal(lam(tval), [0, 3, 0, 5]) # Check what happens with names # Default is from step function assert_false(re.match(r'step\d+\(t\)$', str(b)) is None) # Can pass in another b = blocks(on_off, name='funky_chicken') assert_equal(str(b), 'funky_chicken(t)')
def test_implemented_function(): # Here we check if the default returned functions are anonymous - in # the sense that we can have more than one function with the same name f = implemented_function('f', lambda x: 2*x) g = implemented_function('f', lambda x: np.sqrt(x)) l1 = lambdify(x, f(x)) l2 = lambdify(x, g(x)) yield assert_equal(str(f(x)), str(g(x))) yield assert_equal(l1(3), 6) yield assert_equal(l2(3), np.sqrt(3)) # check that we can pass in a sympy function as input func = sympy.Function('myfunc') yield assert_false(hasattr(func, 'alias')) f = implemented_function(func, lambda x: 2*x) yield assert_true(hasattr(func, 'alias'))
def test_2d(): B1, B2 = [gen_BrownianMotion() for _ in range(2)] B1s = implemented_function("B1", B1) B2s = implemented_function("B2", B2) s, t = sympy.symbols(('s', 't')) e = B1s(s) + B2s(t) ee = lambdify((s, t), e) assert_almost_equal(ee(B1.x, B2.x), B1.y + B2.y)
def test_2d(): B1, B2 = [gen_BrownianMotion() for _ in range(2)] B1s = implemented_function("B1", B1) B2s = implemented_function("B2", B2) s, t = sympy.symbols('s', 't') e = B1s(s)+B2s(t) ee = lambdify((s,t), e) yield assert_almost_equal(ee(B1.x, B2.x), B1.y + B2.y)
def test_interp(): times = [0,4,5.] values = [2.,4,6] for int_func in (interp, linear_interp): s = int_func(times, values, bounds_error=False) tval = np.array([-0.1,0.1,3.9,4.1,5.1]) res = lambdify(t, s)(tval) assert_array_equal(np.isnan(res), [True, False, False, False, True]) assert_array_almost_equal(res[1:-1], [2.05, 3.95, 4.2]) # specifying kind as linear is OK s = linear_interp(times, values, kind='linear')
def lambdify_t(expr): ''' Return sympy function of t `expr` lambdified as function of t Parameters ---------- expr : sympy expr Returns ------- func : callable Numerical implementation of function ''' return lambdify(T, expr, "numpy")
def lambdify_t(expr): """ Return sympy function `expr` lambdified as function of t Parametric ---------- expr : sympy expr Returns ------- func : callable Numerical implementation of function """ return lambdify(T, expr)
def test_interp(): times = [0,4,5.] values = [2.,4,6] for int_func in (interp, linear_interp): s = int_func(times, values, np.nan) tval = np.array([-0.1,0.1,3.9,4.1,5.1]) res = lambdify(t, s)(tval) assert_array_equal(np.isnan(res), [True, False, False, False, True]) assert_array_almost_equal(res[1:-1], [2.05, 3.95, 4.2]) # default is zero fill s = int_func(times, values) res = lambdify(t, s)(tval) assert_array_almost_equal(res, [0, 2.05, 3.95, 4.2, 0]) # Can be some other value s = int_func(times, values, fill=10) res = lambdify(t, s)(tval) assert_array_almost_equal(res, [10, 2.05, 3.95, 4.2, 10]) # If fill is None, raises error on interpolation outside bounds s = int_func(times, values, fill=None) f = lambdify(t, s) assert_array_almost_equal(f(tval[1:-1]), [2.05, 3.95, 4.2]) assert_raises(ValueError, f, tval[:-1]) # specifying kind as linear is OK s = linear_interp(times, values, kind='linear') # bounds_check should match fill int_func(times, values, bounds_error=False) int_func(times, values, fill=None, bounds_error=True) assert_raises(ValueError, int_func, times, values, bounds_error=True) # fill should match fill value int_func(times, values, fill=10, fill_value=10) int_func(times, values, fill_value=0) assert_raises(ValueError, int_func, times, values, fill=10, fill_value=9) int_func(times, values, fill=np.nan, fill_value=np.nan) assert_raises(ValueError, int_func, times, values, fill=10, fill_value=np.nan) assert_raises(ValueError, int_func, times, values, fill=np.nan, fill_value=0)
def define(name, expr): """ Create function of t expression from arbitrary expression `expr` Take an arbitrarily complicated expression `expr` of 't' and make it an expression that is a simple function of t, of form ``'%s(t)' % name`` such that when it evaluates (via ``lambdify``) it has the right values. Parameters ---------- expr : sympy expression with only 't' as a Symbol name : str Returns ------- nexpr: sympy expression Examples -------- >>> t = Term('t') >>> expr = t**2 + 3*t >>> print expr #doctest: +SYMPY_EQUAL 3*t + t**2 >>> newexpr = define('f', expr) >>> print newexpr f(t) >>> f = lambdify_t(newexpr) >>> f(4) 28 >>> 3*4+4**2 28 """ # make numerical implementation of expression v = lambdify(T, expr, "numpy") # convert numerical implementation to sympy function f = implemented_function(name, v) # Return expression that is function of time return f(T)
def test_convolve_functions(): # replicate convolution # This is a square wave on [0,1] f1 = (t > 0) * (t < 1) # ff1 is the numerical implementation of same lam_f1 = lambdify(t, f1) ff1 = lambda x : lam_f1(x).astype(np.int) # The convolution of ``f1`` with itself is a triangular wave on # [0,2], peaking at 1 with height 1 tri = convolve_functions(f1, f1, [0,2], 1.0e-3, name='conv') yield assert_equal(str(tri), 'conv(t)') ftri = lambdify(t, tri) time, value = numerical_convolve(ff1, ff1, [0, 2], 1.0e-3) y = ftri(time) # numerical convolve about the same as ours yield assert_array_almost_equal(value, y) # peak is at 1 yield assert_array_almost_equal(time[np.argmax(y)], 1) # Flip the interval and get the same result tri = convolve_functions(f1, f1, [2, 0], 1.0e-3) ftri = lambdify(t, tri) y = ftri(time) yield assert_array_almost_equal(value, y) # offset square wave by 1 f2 = (t > 1) * (t < 2) tri = convolve_functions(f1, f2, [0,3], 1.0e-3) ftri = lambdify(t, tri) y = ftri(time) yield assert_array_almost_equal(time[np.argmax(y)], 2) # offset both by 1 and start interval at one tri = convolve_functions(f2, f2, [1,3], 1.0e-3) ftri = lambdify(t, tri) # get numerical version lam_f2 = lambdify(t, f2) ff2 = lambda x : lam_f2(x).astype(np.int) time, value = numerical_convolve(ff2, ff2, [1, 3], 1.0e-3) # and our version, compare y = ftri(time) yield assert_array_almost_equal(y, value)
def _setup_design(self): """ Create a callable object to evaluate the design matrix at a given set of parameter values to be specified by a recarray and observed Term values, also specified by a recarray. """ # the design expression is the differentiation of the expression # for the mean. It is a list d = self.design_expr # Before evaluating, we recreate the formula # with numbered terms, and numbered parameters. # This renaming has no impact on the # final design matrix as the # callable, self._f below, is a lambda # that does not care about the names of the terms. # First, find all terms in the mean expression, # and rename them in the form "__t%d__" with a # random offset. # This may cause a possible problem # when there are parameters named something like "__t%d__". # Using the random offset will minimize the possibility # of this happening. # This renaming is here principally because of the intercept. random_offset = np.random.random_integers(low=0, high=2**30) terms = getterms(self.mean) newterms = [] for i, t in enumerate(terms): newt = sympy.Symbol("__t%d__" % (i + random_offset)) for j, _ in enumerate(d): d[j] = d[j].subs(t, newt) newterms.append(newt) # Next, find all parameters that remain in the design expression. # In a standard regression model, there will be no parameters # because they will all be differentiated away in computing # self.design_expr. In nonlinear models, parameters will remain. params = getparams(self.design_expr) newparams = [] for i, p in enumerate(params): newp = make_dummy("__p%d__" % (i + random_offset)) for j, _ in enumerate(d): d[j] = d[j].subs(p, newp) newparams.append(newp) # If there are any aliased functions, these need to be added # to the name space before sympy lambdifies the expression # These "aliased" functions are used for things like # the natural splines, etc. You can represent natural splines # with sympy but the expression is pretty awful. Note that # ``d`` here is list giving the differentiation of the # expression for the mean. self._f(...) therefore also returns # a list self._f = lambdify(newparams + newterms, d, ("numpy")) # The input to self.design will be a recarray of that must # have field names that the Formula will expect to see. # However, if any of self.terms are FactorTerms, then the field # in the recarray will not actually be in the Term. # # For example, if there is a Factor 'f' with levels ['a','b'], # there will be terms 'f_a' and 'f_b', though the input to # design will have a field named 'f'. In this sense, # the recarray used in the call to self.design # is not really made up of terms, but "preterms". # In this case, the callable preterm = [] for t in terms: if not is_factor_term(t): preterm.append(str(t)) else: preterm.append(t.factor_name) preterm = list(set(preterm)) # There is also an argument for parameters that are not # Terms. self._dtypes = { 'param': np.dtype([(str(p), np.float) for p in params]), 'term': np.dtype([(str(t), np.float) for t in terms]), 'preterm': np.dtype([(n, np.float) for n in preterm]) } self.__terms = terms
def test_convolve_functions(): # replicate convolution # This is a square wave on [0,1] f1 = (t > 0) * (t < 1) # ff1 is the numerical implementation of same ff1 = lambdify(t, f1) # Time delta dt = 1e-3 # The convolution of ``f1`` with itself is a triangular wave on # [0, 2], peaking at 1 with height 1 tri = convolve_functions(f1, f1, [0, 2], [0, 2], dt, name='conv') assert_equal(str(tri), 'conv(t)') ftri = lambdify(t, tri) time, value = numerical_convolve(ff1, ff1, [0, 2], dt) y = ftri(time) # numerical convolve about the same as ours assert_array_almost_equal(value, y) # peak is at 1 assert_array_almost_equal(time[np.argmax(y)], 1) # Flip the interval and get the same result for seq1, seq2 in (((0, 2), (2, 0)), ((2, 0), (0, 2)), ((2, 0), (2, 0))): tri = convolve_functions(f1, f1, seq1, seq2, dt) ftri = lambdify(t, tri) y = ftri(time) assert_array_almost_equal(value, y) # offset square wave by 1 - offset triangle by 1 f2 = (t > 1) * (t < 2) tri = convolve_functions(f1, f2, [0, 3], [0, 3], dt) ftri = lambdify(t, tri) o1_time = np.arange(0, 3, dt) z1s = np.zeros((np.round(1./dt))) assert_array_almost_equal(ftri(o1_time), np.r_[z1s, value]) # Same for input function tri = convolve_functions(f2, f1, [0, 3], [0, 3], dt) ftri = lambdify(t, tri) assert_array_almost_equal(ftri(o1_time), np.r_[z1s, value]) # 2 seconds for both tri = convolve_functions(f2, f2, [0, 4], [0, 4], dt) ftri = lambdify(t, tri) o2_time = np.arange(0, 4, dt) assert_array_almost_equal(ftri(o2_time), np.r_[z1s, z1s, value]) # offset by -0.5 - offset triangle by -0.5 f3 = (t > -0.5) * (t < 0.5) tri = convolve_functions(f1, f3, [0, 2], [-0.5, 1.5], dt) ftri = lambdify(t, tri) o1_time = np.arange(-0.5, 1.5, dt) assert_array_almost_equal(ftri(o1_time), value) # Same for input function tri = convolve_functions(f3, f1, [-0.5, 1.5], [0, 2], dt) ftri = lambdify(t, tri) assert_array_almost_equal(ftri(o1_time), value) # -1 second for both tri = convolve_functions(f3, f3, [-0.5, 1.5], [-0.5, 1.5], dt) ftri = lambdify(t, tri) o2_time = np.arange(-1, 1, dt) assert_array_almost_equal(ftri(o2_time), value) # Check it's OK to be off the dt grid tri = convolve_functions(f1, f1, [dt/2, 2 + dt/2], [0, 2], dt, name='conv') ftri = lambdify(t, tri) assert_array_almost_equal(ftri(time), value, 3)
def test_lambdify(): # Test lambdify with implemented functions # first test basic (sympy) lambdify f = sympy.cos assert_equal(lambdify(x, f(x))(0), 1) assert_equal(lambdify(x, 1 + f(x))(0), 2) assert_equal(lambdify((x, y), y + f(x))(0, 1), 2) # make an implemented function and test f = implemented_function("f", lambda x: x + 100) assert_equal(lambdify(x, f(x))(0), 100) assert_equal(lambdify(x, 1 + f(x))(0), 101) assert_equal(lambdify((x, y), y + f(x))(0, 1), 101) # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x: x + 101) assert_raises(ValueError, lambdify, x, f(f2(x))) # our lambdify, like sympy's lambdify, can also handle tuples, # lists, dicts as expressions lam = lambdify(x, (f(x), x)) assert_equal(lam(3), (103, 3)) lam = lambdify(x, [f(x), x]) assert_equal(lam(3), [103, 3]) lam = lambdify(x, [f(x), (f(x), x)]) assert_equal(lam(3), [103, (103, 3)]) lam = lambdify(x, {f(x): x}) assert_equal(lam(3), {103: 3}) lam = lambdify(x, {f(x): x}) assert_equal(lam(3), {103: 3}) lam = lambdify(x, {x: f(x)}) assert_equal(lam(3), {3: 103})
def test_lambdify(): # Test lambdify with implemented functions # first test basic (sympy) lambdify f = sympy.cos yield assert_equal(lambdify(x, f(x))(0), 1) yield assert_equal(lambdify(x, 1 + f(x))(0), 2) yield assert_equal(lambdify((x, y), y + f(x))(0, 1), 2) # make an implemented function and test f = implemented_function("f", lambda x : x+100) yield assert_equal(lambdify(x, f(x))(0), 100) yield assert_equal(lambdify(x, 1 + f(x))(0), 101) yield assert_equal(lambdify((x, y), y + f(x))(0, 1), 101) # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x : x+101) yield assert_raises(ValueError, lambdify, x, f(f2(x))) # our lambdify, like sympy's lambdify, can also handle tuples, # lists, dicts as expressions lam = lambdify(x, (f(x), x)) yield assert_equal(lam(3), (103, 3)) lam = lambdify(x, [f(x), x]) yield assert_equal(lam(3), [103, 3]) lam = lambdify(x, [f(x), (f(x), x)]) yield assert_equal(lam(3), [103, (103, 3)]) lam = lambdify(x, {f(x): x}) yield assert_equal(lam(3), {103: 3}) lam = lambdify(x, {f(x): x}) yield assert_equal(lam(3), {103: 3}) lam = lambdify(x, {x: f(x)}) yield assert_equal(lam(3), {3: 103})
def test_convolve_functions(): # replicate convolution # This is a square wave on [0,1] f1 = (t > 0) * (t < 1) # ff1 is the numerical implementation of same ff1 = lambdify(t, f1) # Time delta dt = 1e-3 # The convolution of ``f1`` with itself is a triangular wave on # [0, 2], peaking at 1 with height 1 tri = convolve_functions(f1, f1, [0, 2], [0, 2], dt, name='conv') assert_equal(str(tri), 'conv(t)') ftri = lambdify(t, tri) time, value = numerical_convolve(ff1, ff1, [0, 2], dt) y = ftri(time) # numerical convolve about the same as ours assert_array_almost_equal(value, y) # peak is at 1 assert_array_almost_equal(time[np.argmax(y)], 1) # Flip the interval and get the same result for seq1, seq2 in (((0, 2), (2, 0)), ((2, 0), (0, 2)), ((2, 0), (2, 0))): tri = convolve_functions(f1, f1, seq1, seq2, dt) ftri = lambdify(t, tri) y = ftri(time) assert_array_almost_equal(value, y) # offset square wave by 1 - offset triangle by 1 f2 = (t > 1) * (t < 2) tri = convolve_functions(f1, f2, [0, 3], [0, 3], dt) ftri = lambdify(t, tri) o1_time = np.arange(0, 3, dt) z1s = np.zeros((np.round(1. / dt))) assert_array_almost_equal(ftri(o1_time), np.r_[z1s, value]) # Same for input function tri = convolve_functions(f2, f1, [0, 3], [0, 3], dt) ftri = lambdify(t, tri) assert_array_almost_equal(ftri(o1_time), np.r_[z1s, value]) # 2 seconds for both tri = convolve_functions(f2, f2, [0, 4], [0, 4], dt) ftri = lambdify(t, tri) o2_time = np.arange(0, 4, dt) assert_array_almost_equal(ftri(o2_time), np.r_[z1s, z1s, value]) # offset by -0.5 - offset triangle by -0.5 f3 = (t > -0.5) * (t < 0.5) tri = convolve_functions(f1, f3, [0, 2], [-0.5, 1.5], dt) ftri = lambdify(t, tri) o1_time = np.arange(-0.5, 1.5, dt) assert_array_almost_equal(ftri(o1_time), value) # Same for input function tri = convolve_functions(f3, f1, [-0.5, 1.5], [0, 2], dt) ftri = lambdify(t, tri) assert_array_almost_equal(ftri(o1_time), value) # -1 second for both tri = convolve_functions(f3, f3, [-0.5, 1.5], [-0.5, 1.5], dt) ftri = lambdify(t, tri) o2_time = np.arange(-1, 1, dt) assert_array_almost_equal(ftri(o2_time), value) # Check it's OK to be off the dt grid tri = convolve_functions(f1, f1, [dt / 2, 2 + dt / 2], [0, 2], dt, name='conv') ftri = lambdify(t, tri) assert_array_almost_equal(ftri(time), value, 3)
def _setup_design(self): """ Create a callable object to evaluate the design matrix at a given set of parameter values to be specified by a recarray and observed Term values, also specified by a recarray. """ # the design expression is the differentiation of the expression # for the mean. It is a list d = self.design_expr # Before evaluating, we recreate the formula # with numbered terms, and numbered parameters. # This renaming has no impact on the # final design matrix as the # callable, self._f below, is a lambda # that does not care about the names of the terms. # First, find all terms in the mean expression, # and rename them in the form "__t%d__" with a # random offset. # This may cause a possible problem # when there are parameters named something like "__t%d__". # Using the random offset will minimize the possibility # of this happening. # This renaming is here principally because of the intercept. random_offset = np.random.random_integers(low=0, high=2**30) terms = getterms(self.mean) newterms = [] for i, t in enumerate(terms): newt = sympy.Symbol("__t%d__" % (i + random_offset)) for j, _ in enumerate(d): d[j] = d[j].subs(t, newt) newterms.append(newt) # Next, find all parameters that remain in the design expression. # In a standard regression model, there will be no parameters # because they will all be differentiated away in computing # self.design_expr. In nonlinear models, parameters will remain. params = getparams(self.design_expr) newparams = [] for i, p in enumerate(params): newp = make_dummy("__p%d__" % (i + random_offset)) for j, _ in enumerate(d): d[j] = d[j].subs(p, newp) newparams.append(newp) # If there are any aliased functions, these need to be added # to the name space before sympy lambdifies the expression # These "aliased" functions are used for things like # the natural splines, etc. You can represent natural splines # with sympy but the expression is pretty awful. Note that # ``d`` here is list giving the differentiation of the # expression for the mean. self._f(...) therefore also returns # a list self._f = lambdify(newparams + newterms, d, ("numpy")) # The input to self.design will be a recarray of that must # have field names that the Formula will expect to see. # However, if any of self.terms are FactorTerms, then the field # in the recarray will not actually be in the Term. # # For example, if there is a Factor 'f' with levels ['a','b'], # there will be terms 'f_a' and 'f_b', though the input to # design will have a field named 'f'. In this sense, # the recarray used in the call to self.design # is not really made up of terms, but "preterms". # In this case, the callable preterm = [] for t in terms: if not is_factor_term(t): preterm.append(str(t)) else: preterm.append(t.factor_name) preterm = list(set(preterm)) # There is also an argument for parameters that are not # Terms. self._dtypes = {'param':np.dtype([(str(p), np.float) for p in params]), 'term':np.dtype([(str(t), np.float) for t in terms]), 'preterm':np.dtype([(n, np.float) for n in preterm])} self.__terms = terms