I will use the discrete logistic function, also called the logistic map.
I will estimate the function parameters.
The equation is: $$ N(t+1) = N(t) \cdot e^{r (1 - N(t)/K)} \Rightarrow \\ N(t) = \frac{K \cdot N(0) e^{rt}}{K + N(0) (e^{rt} - 1)} $$ and $$ \log{\frac{N(t+1)}{N(t)}} = r(1 - N(t)/K) $$
from scipy.stats import linregress
def trans(x):
return log(x[1:]/x[:-1])
def plot_gradient(x,y):
x = array(x)
y = array(y)
fig,ax = subplots(2,2,figsize=(10,10))
ax[0,0].plot(x, y, 'k-');
ax[0,0].set_ylabel('cell density')
ax[1,0].plot(x, gradient(y), 'k-');
ax[1,0].set_xlabel('time (hours)')
ax[1,0].set_ylabel('growth rate')
ax[0,1].plot(y[:-1], trans(y), 'k-')
ax[0,1].set_ylabel('growth rate / cell density')
a, b, _, _, _ = linregress(y[:-1], trans(y))
r = b
K = -b/a
label = 'log N(t+1)/N(t) = %.2g + %.2g N(t)\nr=%.2g, K=%.2g' % (b,a,r,K)
ax[1,1].plot(y, [b + a*z for z in y], 'k-')
ax[1,1].set_xlabel('cell density')
ax[1,1].set_ylabel('growth rate / cell density')
ax[1,1].annotate(label, (0.65, 0.4), xycoords='figure fraction', bbox=dict(boxstyle="round", fc="1"))
return r,K
def logistic(N0, r, K, tmax):
N = array([N0]*tmax)
for t in range(0,tmax-1):
N[t+1] = N[t] * exp(r * (1 - N[t]/K))
return N
r = 0.3
K = 1000000.0
N0 = 10.0
x = arange(0,100)
y = logistic(N0, r, K, len(x))
print "real: : r = %.3f, K=%.2g" % (r,K)
r_sol, K_sol = plot_gradient(x,y)
print "estimated: r = %.3f, K=%.2g" % (r_sol,K_sol)
real: : r = 0.300, K=1e+06 estimated: r = 0.300, K=1e+06
plot(x, y, 'k-', label='real');
plot(x, logistic(y[0], r_sol,K_sol,len(x)), 'ko', label='estimated')
xlabel('time')
ylabel('population')
legend(loc='lower right');