Example #1
0
def decayfit(t, y, p=(0.05, 0.9), prepend_zero=False, rse=False, lm=False):
    """
    Fit exponential decay, y(t) = w exp(-t/tau), to latter part of trajectory.
    
    :param t: time
    :param y: variable, e.g. an ion current
    :param p: proportion of return to initial value, 
        as for action potential duration
    :param bool prepend_zero: add a 0 at the start of y (hack for use with 
        ion currents)
    :param bool rse: return relative standard error of slope (not of *tau*!).
    :param bool lm: Return R object for fitted linear model.
    :return tau: -1/slope for estimated slope of log(y) vs t
    
    tau has dimension 'time' and is analogous to half-life: '1/e'-life = 0.37-life.
    
    For use with results from :meth:`Clampable.vecvclamp`, see :func:`decayfits`.
    
    Trivial example.
    
    >>> t = np.arange(10)
    >>> y = np.exp(-t)
    >>> y[0] = 0
    >>> decayfit(t, y)
    1.0
    
    Relative standard error when adding noise.
    
    >>> np.random.seed(0)
    >>> noisy = y + 0.05 * np.random.random(size=y.shape)
    >>> decayfit(t, noisy, rse=True, p=[0.5, 0.99])
    (1.292..., 0.045...)
    """
    assert len(p) == 2
    if prepend_zero:
        t = np.r_[0, t]
        y = np.r_[0, y]
    stats = apd(t, y, p)
    _ip, i0, i1 = stats["i"]
    # Avoid "Warning message: In is.na(rows) : 
    #        is.na() applied to non-(list or vector) of type 'NULL'"
    if i1 <= i0:
        tau = rse_slope = np.nan
        rlm = None
    else:
        with roptions(show_error_messages=False, deparse_max_lines=0):
            try:
                rlm = r.lm("log(y)~t", data=dict(t=t[i0:i1], 
                                                 y=y[i0:i1].squeeze()))
                coef = r2rec(r.as_data_frame(r.coef(r.summary(rlm))))
                _intercept, slope = coef.Estimate
                _rse_intercept, rse_slope = (coef["Std. Error"] / 
                                             abs(coef.Estimate))
                tau = - 1.0 / slope
            except RRuntimeError:
                tau = rse_slope = np.nan
                rlm = None
    result = (tau,)
    if rse:
        result += (rse_slope,)
    if lm:
        result += (rlm,)
    return result if len(result) > 1 else result[0]
Example #2
0
def markovplot(t, y, a=None, names=None, model=None, comp=None, col="bgrcmyk", 
    plotpy=False, plotr=True, newfig=True, **legend_kwargs):
    """
    Plot markov state distribution for ion channel.
    
    :param array_like t: time
    :param recarray y: state
    :param recarray a: algebraics
    :param names: sequence of variable names to include
    :param Cellmlmodel model: Cellmlmodel object
    :param str comp: (partial) name of component
    :param col: sequence of fill colours for stacked area chart
    :param bool plotpy: plot using Python (matplotlib)?
    :param bool plotr: plot using R (ggplot2)?
    :param bool newfig: create a new figure? (if using matplotlib)
    :param ``**legend_kwargs``: passed to matplotlib legend()
    
    If a is None, it will be computed using 
    ``model.rates_and_algebraic(t, y)``.
    If names is ``None``, include all "dimensionless" variables whose 
    component name contains *comp*.
    
    .. ggplot::
       
       from cgp.virtexp.elphys.examples import Bond
       from cgp.virtexp.elphys.clampable import markovplot
       
       bond = Bond()
       t, y, stats = bond.ap()
       p = markovplot(t, y, model=bond, comp="fast_sodium")
    """
    t, i = np.unique(t, return_index=True)
    y = y[i]
    if a is None:
        _dy, a = model.rates_and_algebraic(t, y)
    else:
        a = a[i]
    if names is None:
        # Often a closed state is defined algebraically as 1 minus the others.
        # This puts it apart from other closed states in the list of names 
        # that we generate below. To remedy this, we sort it.
        names = sorted([n for i in "y", "a" 
                        for n, c, u in zip(*model.legend[i]) 
                        if comp in c and u == "dimensionless"])
        # Distribution only makes sense for at least two states
        if len(names) < 2:
            return None
        # Now put any open state(s) first.
        o = [i for i in names if i.startswith("O")]
        no = [i for i in names if not i.startswith("O")]
        names = o + no
    x = np.rec.fromarrays(
        [y[k] if k in y.dtype.names else a[k] for k in names], names=names)
    xc = x.view(float).cumsum(axis=1).view(x.dtype).squeeze()
    if plotr:
        from ...utils.rec2dict import rec2dict
        r["df"] = r.cbind({"t": t}, r.as_data_frame(rec2dict(x)))
        # r["df"] = r.data_frame(t=t, **rec2dict(xc))
        # r["df"] = r("df[c('" + "','".join(["t"] + names) + "')]")
        r.library("ggplot2")
        r.library("jov")
        # r.plot(r.df)
        r["xm"] = r.melt(r.df, id_var="t")
        cmd = ("qplot(t, value, fill=variable, geom='area', position='stack', "
            "data=xm) + scale_fill_brewer('State', palette='Set3') + "
            "theme_bw() + blankopt")
        if comp:
            cmd += "+ opts(title='%s')" % comp
        return r(cmd)
    if plotpy:
        from pylab import figure, fill_between, legend, axis, Rectangle
        if newfig:
            figure()
        prev = 0
        col = [col[i % len(col)] for i in range(len(names))]
        # Workaround for fill_between not being compatible with legend():
        # http://matplotlib.sourceforge.net/users/legend_guide.html
        # #plotting-guide-legend
        symbols = []
        labels = []
        for i, k in enumerate(x.dtype.names):
            kwargs = dict(label=k, facecolor=col[i], edgecolor="none")
            fill_between(t, xc[k], prev, **kwargs)
            prev = xc[k]
            symbols.append(Rectangle((0, 0), 1, 1, **kwargs))
            labels.append(k)
        axis("tight")
        # Reverse to match vertical order
        legend(reversed(symbols), reversed(labels), labelspacing=0, 
               handlelength=1, handletextpad=0.5, borderaxespad=0, 
               **legend_kwargs)

def markovplots(t, y, a=None, model=None):
    """
    Markov plots for all components.
    
    >>> from cgp.virtexp.elphys.examples import Bond
    >>> bond = Bond()
    >>> t, y, stats = bond.ap()
    >>> from cgp.utils.thinrange import thin
    >>> i = thin(len(t), 100)
    
    (Below, the ... ellipsis makes doctest ignore messages that R may print 
    about packages getting loaded. However, doctest output cannot start with 
    ellipsis, so we need to print something else first. Sigh.)
    
    >>> print "Text output ignored:"; L = markovplots(t[i], y[i], model=bond)
    Text output ignored:...
    >>> from rnumpy import r
    >>> r.windows(record=True) # doctest: +SKIP
    >>> print L # doctest: +SKIP    
    """
    comps = np.unique([c for v in model.legend.values() 
                       for _n, c, _u in zip(*v) if c])
    plots = [markovplot(t, y, model=model, comp=comp) for comp in comps]
    return [(c, p) for c, p in zip(comps, plots) if p]

if __name__ == "__main__":
    import doctest
    doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
Example #3
0
    >>> mmfit(x, -y)
    (nan, nan)
    """
    with roptions(show_error_messages=False, deparse_max_lines=0):
        kwargs = dict(formula="y~ymax*x/(x+xhalf)", data=dict(x=x, y=y), 
            start=dict(ymax=max(y), xhalf=np.mean(x)))
        try:
            fit = r.nls(**kwargs)
        except RRuntimeError, exc:
            s = str(exc.exc).split("\n")[1].strip()
            errmsg = "Michaelis-Menten fit failed with message '%s'. " % s
            errmsg += "Arguments to r.nls() were: %s"
            logger.debug(errmsg, kwargs)
            ymax = xhalf = rse_ymax = rse_xhalf = np.nan
        else:
            coef = r2rec(r.as_data_frame(r.coef(r.summary(fit))))
            ymax, xhalf = coef.Estimate
            rse_ymax, rse_xhalf = coef["Std. Error"] / coef.Estimate
            if (ymax < 0) or (xhalf < 0):
                errmsg = ("Michalis-Menten fit gave negative estimate(s): "
                    "ymax=%s, xhalf=%s. Arguments to r.nls() were: %s")
                logger.debug(errmsg, ymax, xhalf, kwargs)
                ymax = xhalf = rse_ymax = rse_xhalf = np.nan
        return (ymax, xhalf, rse_ymax, rse_xhalf) if rse else (ymax, xhalf)

def decayfits(L, i, k, abs_=True):
    """
    Convenience wrapper for applying decayfit() to list returned by vecvclamp().
    
    :param L: list of (proto, traj), where *proto* is a list of 
        (duration, voltage) for each pulse, and *traj* is a list of