_models.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. """ Collection of Model instances for use with the odrpack fitting package.
  2. """
  3. import numpy as np
  4. from scipy.odr._odrpack import Model
  5. __all__ = ['Model', 'exponential', 'multilinear', 'unilinear', 'quadratic',
  6. 'polynomial']
  7. def _lin_fcn(B, x):
  8. a, b = B[0], B[1:]
  9. b = b.reshape((b.shape[0], 1))
  10. return a + (x*b).sum(axis=0)
  11. def _lin_fjb(B, x):
  12. a = np.ones(x.shape[-1], float)
  13. res = np.concatenate((a, x.ravel()))
  14. return res.reshape((B.shape[-1], x.shape[-1]))
  15. def _lin_fjd(B, x):
  16. b = B[1:]
  17. b = np.repeat(b, (x.shape[-1],)*b.shape[-1], axis=0)
  18. return b.reshape(x.shape)
  19. def _lin_est(data):
  20. # Eh. The answer is analytical, so just return all ones.
  21. # Don't return zeros since that will interfere with
  22. # ODRPACK's auto-scaling procedures.
  23. if len(data.x.shape) == 2:
  24. m = data.x.shape[0]
  25. else:
  26. m = 1
  27. return np.ones((m + 1,), float)
  28. def _poly_fcn(B, x, powers):
  29. a, b = B[0], B[1:]
  30. b = b.reshape((b.shape[0], 1))
  31. return a + np.sum(b * np.power(x, powers), axis=0)
  32. def _poly_fjacb(B, x, powers):
  33. res = np.concatenate((np.ones(x.shape[-1], float),
  34. np.power(x, powers).flat))
  35. return res.reshape((B.shape[-1], x.shape[-1]))
  36. def _poly_fjacd(B, x, powers):
  37. b = B[1:]
  38. b = b.reshape((b.shape[0], 1))
  39. b = b * powers
  40. return np.sum(b * np.power(x, powers-1), axis=0)
  41. def _exp_fcn(B, x):
  42. return B[0] + np.exp(B[1] * x)
  43. def _exp_fjd(B, x):
  44. return B[1] * np.exp(B[1] * x)
  45. def _exp_fjb(B, x):
  46. res = np.concatenate((np.ones(x.shape[-1], float), x * np.exp(B[1] * x)))
  47. return res.reshape((2, x.shape[-1]))
  48. def _exp_est(data):
  49. # Eh.
  50. return np.array([1., 1.])
  51. class _MultilinearModel(Model):
  52. r"""
  53. Arbitrary-dimensional linear model
  54. .. deprecated:: 1.17.0
  55. `scipy.odr` is deprecated and will be removed in SciPy 1.19.0. Please use
  56. `pypi.org/project/odrpack/ <https://pypi.org/project/odrpack/>`_
  57. instead.
  58. This model is defined by :math:`y=\beta_0 + \sum_{i=1}^m \beta_i x_i`
  59. Examples
  60. --------
  61. We can calculate orthogonal distance regression with an arbitrary
  62. dimensional linear model:
  63. >>> from scipy import odr
  64. >>> import numpy as np
  65. >>> x = np.linspace(0.0, 5.0)
  66. >>> y = 10.0 + 5.0 * x
  67. >>> data = odr.Data(x, y)
  68. >>> odr_obj = odr.ODR(data, odr.multilinear)
  69. >>> output = odr_obj.run()
  70. >>> print(output.beta)
  71. [10. 5.]
  72. """
  73. def __init__(self):
  74. super().__init__(
  75. _lin_fcn, fjacb=_lin_fjb, fjacd=_lin_fjd, estimate=_lin_est,
  76. meta={'name': 'Arbitrary-dimensional Linear',
  77. 'equ': 'y = B_0 + Sum[i=1..m, B_i * x_i]',
  78. 'TeXequ': r'$y=\beta_0 + \sum_{i=1}^m \beta_i x_i$'})
  79. multilinear = _MultilinearModel()
  80. def polynomial(order):
  81. """
  82. Factory function for a general polynomial model.
  83. .. deprecated:: 1.17.0
  84. `scipy.odr` is deprecated and will be removed in SciPy 1.19.0. Please use
  85. `pypi.org/project/odrpack/ <https://pypi.org/project/odrpack/>`_
  86. instead.
  87. Parameters
  88. ----------
  89. order : int or sequence
  90. If an integer, it becomes the order of the polynomial to fit. If
  91. a sequence of numbers, then these are the explicit powers in the
  92. polynomial.
  93. A constant term (power 0) is always included, so don't include 0.
  94. Thus, polynomial(n) is equivalent to polynomial(range(1, n+1)).
  95. Returns
  96. -------
  97. polynomial : Model instance
  98. Model instance.
  99. Examples
  100. --------
  101. We can fit an input data using orthogonal distance regression (ODR) with
  102. a polynomial model:
  103. >>> import numpy as np
  104. >>> import matplotlib.pyplot as plt
  105. >>> from scipy import odr
  106. >>> x = np.linspace(0.0, 5.0)
  107. >>> y = np.sin(x)
  108. >>> poly_model = odr.polynomial(3) # using third order polynomial model
  109. >>> data = odr.Data(x, y)
  110. >>> odr_obj = odr.ODR(data, poly_model)
  111. >>> output = odr_obj.run() # running ODR fitting
  112. >>> poly = np.poly1d(output.beta[::-1])
  113. >>> poly_y = poly(x)
  114. >>> plt.plot(x, y, label="input data")
  115. >>> plt.plot(x, poly_y, label="polynomial ODR")
  116. >>> plt.legend()
  117. >>> plt.show()
  118. """
  119. powers = np.asarray(order)
  120. if powers.shape == ():
  121. # Scalar.
  122. powers = np.arange(1, powers + 1)
  123. powers = powers.reshape((len(powers), 1))
  124. len_beta = len(powers) + 1
  125. def _poly_est(data, len_beta=len_beta):
  126. # Eh. Ignore data and return all ones.
  127. return np.ones((len_beta,), float)
  128. return Model(_poly_fcn, fjacd=_poly_fjacd, fjacb=_poly_fjacb,
  129. estimate=_poly_est, extra_args=(powers,),
  130. meta={'name': 'Sorta-general Polynomial',
  131. 'equ': 'y = B_0 + Sum[i=1..%s, B_i * (x**i)]' % (len_beta-1),
  132. 'TeXequ': r'$y=\beta_0 + \sum_{i=1}^{%s} \beta_i x^i$' %
  133. (len_beta-1)})
  134. class _ExponentialModel(Model):
  135. r"""
  136. Exponential model
  137. .. deprecated:: 1.17.0
  138. `scipy.odr` is deprecated and will be removed in SciPy 1.19.0. Please use
  139. `pypi.org/project/odrpack/ <https://pypi.org/project/odrpack/>`_
  140. instead.
  141. This model is defined by :math:`y=\beta_0 + e^{\beta_1 x}`
  142. Examples
  143. --------
  144. We can calculate orthogonal distance regression with an exponential model:
  145. >>> from scipy import odr
  146. >>> import numpy as np
  147. >>> x = np.linspace(0.0, 5.0)
  148. >>> y = -10.0 + np.exp(0.5*x)
  149. >>> data = odr.Data(x, y)
  150. >>> odr_obj = odr.ODR(data, odr.exponential)
  151. >>> output = odr_obj.run()
  152. >>> print(output.beta)
  153. [-10. 0.5]
  154. """
  155. def __init__(self):
  156. super().__init__(_exp_fcn, fjacd=_exp_fjd, fjacb=_exp_fjb,
  157. estimate=_exp_est,
  158. meta={'name': 'Exponential',
  159. 'equ': 'y= B_0 + exp(B_1 * x)',
  160. 'TeXequ': r'$y=\beta_0 + e^{\beta_1 x}$'})
  161. exponential = _ExponentialModel()
  162. def _unilin(B, x):
  163. return x*B[0] + B[1]
  164. def _unilin_fjd(B, x):
  165. return np.ones(x.shape, float) * B[0]
  166. def _unilin_fjb(B, x):
  167. _ret = np.concatenate((x, np.ones(x.shape, float)))
  168. return _ret.reshape((2,) + x.shape)
  169. def _unilin_est(data):
  170. return (1., 1.)
  171. def _quadratic(B, x):
  172. return x*(x*B[0] + B[1]) + B[2]
  173. def _quad_fjd(B, x):
  174. return 2*x*B[0] + B[1]
  175. def _quad_fjb(B, x):
  176. _ret = np.concatenate((x*x, x, np.ones(x.shape, float)))
  177. return _ret.reshape((3,) + x.shape)
  178. def _quad_est(data):
  179. return (1.,1.,1.)
  180. class _UnilinearModel(Model):
  181. r"""
  182. Univariate linear model
  183. .. deprecated:: 1.17.0
  184. `scipy.odr` is deprecated and will be removed in SciPy 1.19.0. Please use
  185. `pypi.org/project/odrpack/ <https://pypi.org/project/odrpack/>`_
  186. instead.
  187. This model is defined by :math:`y = \beta_0 x + \beta_1`
  188. Examples
  189. --------
  190. We can calculate orthogonal distance regression with an unilinear model:
  191. >>> from scipy import odr
  192. >>> import numpy as np
  193. >>> x = np.linspace(0.0, 5.0)
  194. >>> y = 1.0 * x + 2.0
  195. >>> data = odr.Data(x, y)
  196. >>> odr_obj = odr.ODR(data, odr.unilinear)
  197. >>> output = odr_obj.run()
  198. >>> print(output.beta)
  199. [1. 2.]
  200. """
  201. def __init__(self):
  202. super().__init__(_unilin, fjacd=_unilin_fjd, fjacb=_unilin_fjb,
  203. estimate=_unilin_est,
  204. meta={'name': 'Univariate Linear',
  205. 'equ': 'y = B_0 * x + B_1',
  206. 'TeXequ': '$y = \\beta_0 x + \\beta_1$'})
  207. unilinear = _UnilinearModel()
  208. class _QuadraticModel(Model):
  209. r"""
  210. Quadratic model
  211. .. deprecated:: 1.17.0
  212. `scipy.odr` is deprecated and will be removed in SciPy 1.19.0. Please use
  213. `pypi.org/project/odrpack/ <https://pypi.org/project/odrpack/>`_
  214. instead.
  215. This model is defined by :math:`y = \beta_0 x^2 + \beta_1 x + \beta_2`
  216. Examples
  217. --------
  218. We can calculate orthogonal distance regression with a quadratic model:
  219. >>> from scipy import odr
  220. >>> import numpy as np
  221. >>> x = np.linspace(0.0, 5.0)
  222. >>> y = 1.0 * x ** 2 + 2.0 * x + 3.0
  223. >>> data = odr.Data(x, y)
  224. >>> odr_obj = odr.ODR(data, odr.quadratic)
  225. >>> output = odr_obj.run()
  226. >>> print(output.beta)
  227. [1. 2. 3.]
  228. """
  229. def __init__(self):
  230. super().__init__(
  231. _quadratic, fjacd=_quad_fjd, fjacb=_quad_fjb, estimate=_quad_est,
  232. meta={'name': 'Quadratic',
  233. 'equ': 'y = B_0*x**2 + B_1*x + B_2',
  234. 'TeXequ': '$y = \\beta_0 x^2 + \\beta_1 x + \\beta_2'})
  235. quadratic = _QuadraticModel()