title: SciPy + Numpy: Finding the slope of a sigmoid curve
body: <p>I have some data that follow a sigmoid distribution as you can see in the following image: <img src="https://i.stack.imgur.com/fPTM5.png" alt="Sigmoid data for 2003"></p>
  
  <p>After normalizing and scaling my data, I have adjusted the curve at the bottom using <code>scipy.optimize.curve_fit</code> and some initial parameters:</p>
  
  <pre><code>popt, pcov = curve_fit(sigmoid_function, xdata, ydata, p0 = [0.05, 0.05, 0.05])
  &gt;&gt;&gt; print popt
  [  2.82019932e+02  -1.90996563e-01   5.00000000e-02]
  </code></pre>
  
  <p>So <code>popt</code>, according to <a href="http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.curve_fit.html" rel="nofollow noreferrer">the documentation</a>, returns *"Optimal values for the parameters so that the sum of the squared error of f(xdata, <em>popt) - ydata is minimized"</em>. I understand here that there is no calculation of the slope with <code>curve_fit</code>, because I do <strong>not</strong> think the slope of this gentle curve is 282, neither is negative.</p>
  
  <p>Then I tried with <code>scipy.optimize.leastsq</code>, because the documentation says it returns <em>"The solution (or the result of the last iteration for an unsuccessful call).</em>", so I thought the slope would be returned. Like this:</p>
  
  <pre><code>p, cov, infodict, mesg, ier = leastsq(residuals, p_guess, args = (nxdata, nydata), full_output=True)
  &gt;&gt;&gt; print p
  Param(x0=281.73193626250207, y0=-0.012731420027056234, c=1.0069006606656596, k=0.18836680131910222)
  </code></pre>
  
  <p>But again, I did not get what I expected. <code>curve_fit</code> and <code>leastsq</code> returned almost the same values, with is not surprising I guess, as <code>curve_fit</code> is using an implementation of the least squares method within to find the curve. But no slope back...unless I overlooked something.</p>
  
  <p>So, <strong>how to calculate the slope in a point, say, where X = 285 and Y = 0.5?</strong> </p>
  
  <p>I am trying to avoid manual methods, like <a href="https://www.youtube.com/watch?v=8DV8MJCBzkM" rel="nofollow noreferrer">calculating the derivative</a> in, say, (285.5, 0.55) and (284.5, 0.45) and subtract and divide results and so. I would like to know if there is a more automatic method for this. </p>
  
  <p>Thank you all!</p>
  
  <p><strong>EDIT #1</strong></p>
  
  <p>This is my "sigmoid_function", used by curve_fit and leastsq methods:</p>
  
  <pre><code>def sigmoid_function(xdata, x0, k, p0): # p0 not used anymore, only its components (x0, k)
      # This function is called by two different methods: curve_fit and leastsq,
      # this last one through function "residuals". I don't know if it makes sense
      # to use a single function for two (somewhat similar) methods, but there 
      # it goes.
  
      # p0:
      #   + Is the initial parameter for scipy.optimize.curve_fit. 
      #   + For residuals calculation is left empty
      #   + It is initialized to [0.05, 0.05, 0.05]
      # x0:
      #   + Is the convergence parameter in X-axis and also the shift
      #   + It starts with 0.05 and ends up being around ~282 (days in a year)
      # k:
      #   + Set up either by curve_fit or leastsq
      #   + In least squares it is initially fixed at 0.5 and in curve_fit
      #   + to 0.05. Why? Just did this approach in two different ways and 
      #   + it seems it is working. 
      #   + But honestly, I have no clue on what it represents
      # xdata: 
      #   + Positions in X-axis. In this case from 240 to 365
  
  # Finally I changed those parameters as suggested in the answer. 
  # Sigmoid curve has 2 degrees of freedom, therefore, the initial 
  # guess only needs to be this size. In this case, p0 = [282, 0.5]
  
  
      y = np.exp(-k*(xdata-x0)) / (1 + np.exp(-k*(xdata-x0)))
      return y
  
  def residuals(p_guess, xdata, ydata):
      # For the residuals calculation, there is no need of setting up the initial parameters
      # After fixing the initial guess and sigmoid_function header, remove [] 
      # return ydata - sigmoid_function(xdata, p_guess[0], p_guess[1], [])
      return ydata - sigmoid_function(xdata, p_guess[0], p_guess[1], [])
  </code></pre>
  
  <p>I am sorry if I made mistakes while describing the parameters or confused technical terms. I am very new with numpy and I have not studied maths for years, so I am catching up again.</p>
  
  <p>So, again, what is your advice to <strong>calculate the slope of X = 285, Y = 0.5 (more or less the midpoint)</strong> for this dataset? Thanks!! </p>
  
  <p><strong>EDIT #2</strong></p>
  
  <p>Thanks to Oliver W., I updated my code as he suggested and understood a bit better the problem.</p>
  
  <p>There is a final detail I do not fully get. Apparently, <code>curve_fit</code> returns a <code>popt</code> array (x0, k) with the optimum parameters for the fitting: </p>
  
  <ul>
  <li><code>x0</code> seems to be how shifted is the curve by indicating the central point of the curve</li>
  <li><code>k</code> parameter is the slope when y = 0.5, also in the center of the curve (I think!)</li>
  </ul>
  
  <p>Why if the sigmoid function is a growing one, the derivative/slope in popt is negative? Does it make sense?</p>
  
  <p>I used <code>sigmoid_derivative</code> to calculate the slope and, yes, I obtained the same results that <code>popt</code> but with positive sign. </p>
  
  <pre><code># Year 2003, 2005, 2007. Slope in midpoint.
  k = [-0.1910, -0.2545, -0.2259] # Values coming from popt
  slope = [0.1910, 0.2545, 0.2259] # Values coming from sigmoid_derivative function
  </code></pre>
  
  <p>I know this is being a bit peaky because I could use both. The relevant data is in there but with negative sign, but I was wondering why is this happening.</p>
  
  <p>So, the calculation of the derivative function as you suggested, is only required if I <strong>need to know the slope in other points than y = 0.5</strong>. Only for midpoint, I can use <code>popt</code>.</p>
  
  <p>Thanks for your help, it saved me a lot of time. :-)</p>