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]
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)
>>> 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