Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import numpy as np 

2from scipy.special import factorial 

3 

4from scipy._lib._util import _asarray_validated 

5 

6 

7__all__ = ["KroghInterpolator", "krogh_interpolate", "BarycentricInterpolator", 

8 "barycentric_interpolate", "approximate_taylor_polynomial"] 

9 

10 

11def _isscalar(x): 

12 """Check whether x is if a scalar type, or 0-dim""" 

13 return np.isscalar(x) or hasattr(x, 'shape') and x.shape == () 

14 

15 

16class _Interpolator1D(object): 

17 """ 

18 Common features in univariate interpolation 

19 

20 Deal with input data type and interpolation axis rolling. The 

21 actual interpolator can assume the y-data is of shape (n, r) where 

22 `n` is the number of x-points, and `r` the number of variables, 

23 and use self.dtype as the y-data type. 

24 

25 Attributes 

26 ---------- 

27 _y_axis 

28 Axis along which the interpolation goes in the original array 

29 _y_extra_shape 

30 Additional trailing shape of the input arrays, excluding 

31 the interpolation axis. 

32 dtype 

33 Dtype of the y-data arrays. Can be set via _set_dtype, which 

34 forces it to be float or complex. 

35 

36 Methods 

37 ------- 

38 __call__ 

39 _prepare_x 

40 _finish_y 

41 _reshape_yi 

42 _set_yi 

43 _set_dtype 

44 _evaluate 

45 

46 """ 

47 

48 __slots__ = ('_y_axis', '_y_extra_shape', 'dtype') 

49 

50 def __init__(self, xi=None, yi=None, axis=None): 

51 self._y_axis = axis 

52 self._y_extra_shape = None 

53 self.dtype = None 

54 if yi is not None: 

55 self._set_yi(yi, xi=xi, axis=axis) 

56 

57 def __call__(self, x): 

58 """ 

59 Evaluate the interpolant 

60 

61 Parameters 

62 ---------- 

63 x : array_like 

64 Points to evaluate the interpolant at. 

65 

66 Returns 

67 ------- 

68 y : array_like 

69 Interpolated values. Shape is determined by replacing 

70 the interpolation axis in the original array with the shape of x. 

71 

72 """ 

73 x, x_shape = self._prepare_x(x) 

74 y = self._evaluate(x) 

75 return self._finish_y(y, x_shape) 

76 

77 def _evaluate(self, x): 

78 """ 

79 Actually evaluate the value of the interpolator. 

80 """ 

81 raise NotImplementedError() 

82 

83 def _prepare_x(self, x): 

84 """Reshape input x array to 1-D""" 

85 x = _asarray_validated(x, check_finite=False, as_inexact=True) 

86 x_shape = x.shape 

87 return x.ravel(), x_shape 

88 

89 def _finish_y(self, y, x_shape): 

90 """Reshape interpolated y back to an N-D array similar to initial y""" 

91 y = y.reshape(x_shape + self._y_extra_shape) 

92 if self._y_axis != 0 and x_shape != (): 

93 nx = len(x_shape) 

94 ny = len(self._y_extra_shape) 

95 s = (list(range(nx, nx + self._y_axis)) 

96 + list(range(nx)) + list(range(nx+self._y_axis, nx+ny))) 

97 y = y.transpose(s) 

98 return y 

99 

100 def _reshape_yi(self, yi, check=False): 

101 yi = np.rollaxis(np.asarray(yi), self._y_axis) 

102 if check and yi.shape[1:] != self._y_extra_shape: 

103 ok_shape = "%r + (N,) + %r" % (self._y_extra_shape[-self._y_axis:], 

104 self._y_extra_shape[:-self._y_axis]) 

105 raise ValueError("Data must be of shape %s" % ok_shape) 

106 return yi.reshape((yi.shape[0], -1)) 

107 

108 def _set_yi(self, yi, xi=None, axis=None): 

109 if axis is None: 

110 axis = self._y_axis 

111 if axis is None: 

112 raise ValueError("no interpolation axis specified") 

113 

114 yi = np.asarray(yi) 

115 

116 shape = yi.shape 

117 if shape == (): 

118 shape = (1,) 

119 if xi is not None and shape[axis] != len(xi): 

120 raise ValueError("x and y arrays must be equal in length along " 

121 "interpolation axis.") 

122 

123 self._y_axis = (axis % yi.ndim) 

124 self._y_extra_shape = yi.shape[:self._y_axis]+yi.shape[self._y_axis+1:] 

125 self.dtype = None 

126 self._set_dtype(yi.dtype) 

127 

128 def _set_dtype(self, dtype, union=False): 

129 if np.issubdtype(dtype, np.complexfloating) \ 

130 or np.issubdtype(self.dtype, np.complexfloating): 

131 self.dtype = np.complex_ 

132 else: 

133 if not union or self.dtype != np.complex_: 

134 self.dtype = np.float_ 

135 

136 

137class _Interpolator1DWithDerivatives(_Interpolator1D): 

138 def derivatives(self, x, der=None): 

139 """ 

140 Evaluate many derivatives of the polynomial at the point x 

141 

142 Produce an array of all derivative values at the point x. 

143 

144 Parameters 

145 ---------- 

146 x : array_like 

147 Point or points at which to evaluate the derivatives 

148 der : int or None, optional 

149 How many derivatives to extract; None for all potentially 

150 nonzero derivatives (that is a number equal to the number 

151 of points). This number includes the function value as 0th 

152 derivative. 

153 

154 Returns 

155 ------- 

156 d : ndarray 

157 Array with derivatives; d[j] contains the jth derivative. 

158 Shape of d[j] is determined by replacing the interpolation 

159 axis in the original array with the shape of x. 

160 

161 Examples 

162 -------- 

163 >>> from scipy.interpolate import KroghInterpolator 

164 >>> KroghInterpolator([0,0,0],[1,2,3]).derivatives(0) 

165 array([1.0,2.0,3.0]) 

166 >>> KroghInterpolator([0,0,0],[1,2,3]).derivatives([0,0]) 

167 array([[1.0,1.0], 

168 [2.0,2.0], 

169 [3.0,3.0]]) 

170 

171 """ 

172 x, x_shape = self._prepare_x(x) 

173 y = self._evaluate_derivatives(x, der) 

174 

175 y = y.reshape((y.shape[0],) + x_shape + self._y_extra_shape) 

176 if self._y_axis != 0 and x_shape != (): 

177 nx = len(x_shape) 

178 ny = len(self._y_extra_shape) 

179 s = ([0] + list(range(nx+1, nx + self._y_axis+1)) 

180 + list(range(1, nx+1)) + 

181 list(range(nx+1+self._y_axis, nx+ny+1))) 

182 y = y.transpose(s) 

183 return y 

184 

185 def derivative(self, x, der=1): 

186 """ 

187 Evaluate one derivative of the polynomial at the point x 

188 

189 Parameters 

190 ---------- 

191 x : array_like 

192 Point or points at which to evaluate the derivatives 

193 

194 der : integer, optional 

195 Which derivative to extract. This number includes the 

196 function value as 0th derivative. 

197 

198 Returns 

199 ------- 

200 d : ndarray 

201 Derivative interpolated at the x-points. Shape of d is 

202 determined by replacing the interpolation axis in the 

203 original array with the shape of x. 

204 

205 Notes 

206 ----- 

207 This is computed by evaluating all derivatives up to the desired 

208 one (using self.derivatives()) and then discarding the rest. 

209 

210 """ 

211 x, x_shape = self._prepare_x(x) 

212 y = self._evaluate_derivatives(x, der+1) 

213 return self._finish_y(y[der], x_shape) 

214 

215 

216class KroghInterpolator(_Interpolator1DWithDerivatives): 

217 """ 

218 Interpolating polynomial for a set of points. 

219 

220 The polynomial passes through all the pairs (xi,yi). One may 

221 additionally specify a number of derivatives at each point xi; 

222 this is done by repeating the value xi and specifying the 

223 derivatives as successive yi values. 

224 

225 Allows evaluation of the polynomial and all its derivatives. 

226 For reasons of numerical stability, this function does not compute 

227 the coefficients of the polynomial, although they can be obtained 

228 by evaluating all the derivatives. 

229 

230 Parameters 

231 ---------- 

232 xi : array_like, length N 

233 Known x-coordinates. Must be sorted in increasing order. 

234 yi : array_like 

235 Known y-coordinates. When an xi occurs two or more times in 

236 a row, the corresponding yi's represent derivative values. 

237 axis : int, optional 

238 Axis in the yi array corresponding to the x-coordinate values. 

239 

240 Notes 

241 ----- 

242 Be aware that the algorithms implemented here are not necessarily 

243 the most numerically stable known. Moreover, even in a world of 

244 exact computation, unless the x coordinates are chosen very 

245 carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - 

246 polynomial interpolation itself is a very ill-conditioned process 

247 due to the Runge phenomenon. In general, even with well-chosen 

248 x values, degrees higher than about thirty cause problems with 

249 numerical instability in this code. 

250 

251 Based on [1]_. 

252 

253 References 

254 ---------- 

255 .. [1] Krogh, "Efficient Algorithms for Polynomial Interpolation 

256 and Numerical Differentiation", 1970. 

257 

258 Examples 

259 -------- 

260 To produce a polynomial that is zero at 0 and 1 and has 

261 derivative 2 at 0, call 

262 

263 >>> from scipy.interpolate import KroghInterpolator 

264 >>> KroghInterpolator([0,0,1],[0,2,0]) 

265 

266 This constructs the quadratic 2*X**2-2*X. The derivative condition 

267 is indicated by the repeated zero in the xi array; the corresponding 

268 yi values are 0, the function value, and 2, the derivative value. 

269 

270 For another example, given xi, yi, and a derivative ypi for each 

271 point, appropriate arrays can be constructed as: 

272 

273 >>> xi = np.linspace(0, 1, 5) 

274 >>> yi, ypi = np.random.rand(2, 5) 

275 >>> xi_k, yi_k = np.repeat(xi, 2), np.ravel(np.dstack((yi,ypi))) 

276 >>> KroghInterpolator(xi_k, yi_k) 

277 

278 To produce a vector-valued polynomial, supply a higher-dimensional 

279 array for yi: 

280 

281 >>> KroghInterpolator([0,1],[[2,3],[4,5]]) 

282 

283 This constructs a linear polynomial giving (2,3) at 0 and (4,5) at 1. 

284 

285 """ 

286 

287 def __init__(self, xi, yi, axis=0): 

288 _Interpolator1DWithDerivatives.__init__(self, xi, yi, axis) 

289 

290 self.xi = np.asarray(xi) 

291 self.yi = self._reshape_yi(yi) 

292 self.n, self.r = self.yi.shape 

293 

294 c = np.zeros((self.n+1, self.r), dtype=self.dtype) 

295 c[0] = self.yi[0] 

296 Vk = np.zeros((self.n, self.r), dtype=self.dtype) 

297 for k in range(1, self.n): 

298 s = 0 

299 while s <= k and xi[k-s] == xi[k]: 

300 s += 1 

301 s -= 1 

302 Vk[0] = self.yi[k]/float(factorial(s)) 

303 for i in range(k-s): 

304 if xi[i] == xi[k]: 

305 raise ValueError("Elements if `xi` can't be equal.") 

306 if s == 0: 

307 Vk[i+1] = (c[i]-Vk[i])/(xi[i]-xi[k]) 

308 else: 

309 Vk[i+1] = (Vk[i+1]-Vk[i])/(xi[i]-xi[k]) 

310 c[k] = Vk[k-s] 

311 self.c = c 

312 

313 def _evaluate(self, x): 

314 pi = 1 

315 p = np.zeros((len(x), self.r), dtype=self.dtype) 

316 p += self.c[0,np.newaxis,:] 

317 for k in range(1, self.n): 

318 w = x - self.xi[k-1] 

319 pi = w*pi 

320 p += pi[:,np.newaxis] * self.c[k] 

321 return p 

322 

323 def _evaluate_derivatives(self, x, der=None): 

324 n = self.n 

325 r = self.r 

326 

327 if der is None: 

328 der = self.n 

329 pi = np.zeros((n, len(x))) 

330 w = np.zeros((n, len(x))) 

331 pi[0] = 1 

332 p = np.zeros((len(x), self.r), dtype=self.dtype) 

333 p += self.c[0, np.newaxis, :] 

334 

335 for k in range(1, n): 

336 w[k-1] = x - self.xi[k-1] 

337 pi[k] = w[k-1] * pi[k-1] 

338 p += pi[k, :, np.newaxis] * self.c[k] 

339 

340 cn = np.zeros((max(der, n+1), len(x), r), dtype=self.dtype) 

341 cn[:n+1, :, :] += self.c[:n+1, np.newaxis, :] 

342 cn[0] = p 

343 for k in range(1, n): 

344 for i in range(1, n-k+1): 

345 pi[i] = w[k+i-1]*pi[i-1] + pi[i] 

346 cn[k] = cn[k] + pi[i, :, np.newaxis]*cn[k+i] 

347 cn[k] *= factorial(k) 

348 

349 cn[n, :, :] = 0 

350 return cn[:der] 

351 

352 

353def krogh_interpolate(xi, yi, x, der=0, axis=0): 

354 """ 

355 Convenience function for polynomial interpolation. 

356 

357 See `KroghInterpolator` for more details. 

358 

359 Parameters 

360 ---------- 

361 xi : array_like 

362 Known x-coordinates. 

363 yi : array_like 

364 Known y-coordinates, of shape ``(xi.size, R)``. Interpreted as 

365 vectors of length R, or scalars if R=1. 

366 x : array_like 

367 Point or points at which to evaluate the derivatives. 

368 der : int or list, optional 

369 How many derivatives to extract; None for all potentially 

370 nonzero derivatives (that is a number equal to the number 

371 of points), or a list of derivatives to extract. This number 

372 includes the function value as 0th derivative. 

373 axis : int, optional 

374 Axis in the yi array corresponding to the x-coordinate values. 

375 

376 Returns 

377 ------- 

378 d : ndarray 

379 If the interpolator's values are R-D then the 

380 returned array will be the number of derivatives by N by R. 

381 If `x` is a scalar, the middle dimension will be dropped; if 

382 the `yi` are scalars then the last dimension will be dropped. 

383 

384 See Also 

385 -------- 

386 KroghInterpolator : Krogh interpolator 

387 

388 Notes 

389 ----- 

390 Construction of the interpolating polynomial is a relatively expensive 

391 process. If you want to evaluate it repeatedly consider using the class 

392 KroghInterpolator (which is what this function uses). 

393 

394 Examples 

395 -------- 

396 We can interpolate 2D observed data using krogh interpolation: 

397 

398 >>> import matplotlib.pyplot as plt 

399 >>> from scipy.interpolate import krogh_interpolate 

400 >>> x_observed = np.linspace(0.0, 10.0, 11) 

401 >>> y_observed = np.sin(x_observed) 

402 >>> x = np.linspace(min(x_observed), max(x_observed), num=100) 

403 >>> y = krogh_interpolate(x_observed, y_observed, x) 

404 >>> plt.plot(x_observed, y_observed, "o", label="observation") 

405 >>> plt.plot(x, y, label="krogh interpolation") 

406 >>> plt.legend() 

407 >>> plt.show() 

408 

409 """ 

410 P = KroghInterpolator(xi, yi, axis=axis) 

411 if der == 0: 

412 return P(x) 

413 elif _isscalar(der): 

414 return P.derivative(x,der=der) 

415 else: 

416 return P.derivatives(x,der=np.amax(der)+1)[der] 

417 

418 

419def approximate_taylor_polynomial(f,x,degree,scale,order=None): 

420 """ 

421 Estimate the Taylor polynomial of f at x by polynomial fitting. 

422 

423 Parameters 

424 ---------- 

425 f : callable 

426 The function whose Taylor polynomial is sought. Should accept 

427 a vector of `x` values. 

428 x : scalar 

429 The point at which the polynomial is to be evaluated. 

430 degree : int 

431 The degree of the Taylor polynomial 

432 scale : scalar 

433 The width of the interval to use to evaluate the Taylor polynomial. 

434 Function values spread over a range this wide are used to fit the 

435 polynomial. Must be chosen carefully. 

436 order : int or None, optional 

437 The order of the polynomial to be used in the fitting; `f` will be 

438 evaluated ``order+1`` times. If None, use `degree`. 

439 

440 Returns 

441 ------- 

442 p : poly1d instance 

443 The Taylor polynomial (translated to the origin, so that 

444 for example p(0)=f(x)). 

445 

446 Notes 

447 ----- 

448 The appropriate choice of "scale" is a trade-off; too large and the 

449 function differs from its Taylor polynomial too much to get a good 

450 answer, too small and round-off errors overwhelm the higher-order terms. 

451 The algorithm used becomes numerically unstable around order 30 even 

452 under ideal circumstances. 

453 

454 Choosing order somewhat larger than degree may improve the higher-order 

455 terms. 

456 

457 Examples 

458 -------- 

459 We can calculate Taylor approximation polynomials of sin function with 

460 various degrees: 

461 

462 >>> import matplotlib.pyplot as plt 

463 >>> from scipy.interpolate import approximate_taylor_polynomial 

464 >>> x = np.linspace(-10.0, 10.0, num=100) 

465 >>> plt.plot(x, np.sin(x), label="sin curve") 

466 >>> for degree in np.arange(1, 15, step=2): 

467 ... sin_taylor = approximate_taylor_polynomial(np.sin, 0, degree, 1, 

468 ... order=degree + 2) 

469 ... plt.plot(x, sin_taylor(x), label=f"degree={degree}") 

470 >>> plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', 

471 ... borderaxespad=0.0, shadow=True) 

472 >>> plt.tight_layout() 

473 >>> plt.axis([-10, 10, -10, 10]) 

474 >>> plt.show() 

475 

476 """ 

477 if order is None: 

478 order = degree 

479 

480 n = order+1 

481 # Choose n points that cluster near the endpoints of the interval in 

482 # a way that avoids the Runge phenomenon. Ensure, by including the 

483 # endpoint or not as appropriate, that one point always falls at x 

484 # exactly. 

485 xs = scale*np.cos(np.linspace(0,np.pi,n,endpoint=n % 1)) + x 

486 

487 P = KroghInterpolator(xs, f(xs)) 

488 d = P.derivatives(x,der=degree+1) 

489 

490 return np.poly1d((d/factorial(np.arange(degree+1)))[::-1]) 

491 

492 

493class BarycentricInterpolator(_Interpolator1D): 

494 """The interpolating polynomial for a set of points 

495 

496 Constructs a polynomial that passes through a given set of points. 

497 Allows evaluation of the polynomial, efficient changing of the y 

498 values to be interpolated, and updating by adding more x values. 

499 For reasons of numerical stability, this function does not compute 

500 the coefficients of the polynomial. 

501 

502 The values yi need to be provided before the function is 

503 evaluated, but none of the preprocessing depends on them, so rapid 

504 updates are possible. 

505 

506 Parameters 

507 ---------- 

508 xi : array_like 

509 1-D array of x coordinates of the points the polynomial 

510 should pass through 

511 yi : array_like, optional 

512 The y coordinates of the points the polynomial should pass through. 

513 If None, the y values will be supplied later via the `set_y` method. 

514 axis : int, optional 

515 Axis in the yi array corresponding to the x-coordinate values. 

516 

517 Notes 

518 ----- 

519 This class uses a "barycentric interpolation" method that treats 

520 the problem as a special case of rational function interpolation. 

521 This algorithm is quite stable, numerically, but even in a world of 

522 exact computation, unless the x coordinates are chosen very 

523 carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - 

524 polynomial interpolation itself is a very ill-conditioned process 

525 due to the Runge phenomenon. 

526 

527 Based on Berrut and Trefethen 2004, "Barycentric Lagrange Interpolation". 

528 

529 """ 

530 def __init__(self, xi, yi=None, axis=0): 

531 _Interpolator1D.__init__(self, xi, yi, axis) 

532 

533 self.xi = np.asfarray(xi) 

534 self.set_yi(yi) 

535 self.n = len(self.xi) 

536 

537 self.wi = np.zeros(self.n) 

538 self.wi[0] = 1 

539 for j in range(1, self.n): 

540 self.wi[:j] *= (self.xi[j]-self.xi[:j]) 

541 self.wi[j] = np.multiply.reduce(self.xi[:j]-self.xi[j]) 

542 self.wi **= -1 

543 

544 def set_yi(self, yi, axis=None): 

545 """ 

546 Update the y values to be interpolated 

547 

548 The barycentric interpolation algorithm requires the calculation 

549 of weights, but these depend only on the xi. The yi can be changed 

550 at any time. 

551 

552 Parameters 

553 ---------- 

554 yi : array_like 

555 The y coordinates of the points the polynomial should pass through. 

556 If None, the y values will be supplied later. 

557 axis : int, optional 

558 Axis in the yi array corresponding to the x-coordinate values. 

559 

560 """ 

561 if yi is None: 

562 self.yi = None 

563 return 

564 self._set_yi(yi, xi=self.xi, axis=axis) 

565 self.yi = self._reshape_yi(yi) 

566 self.n, self.r = self.yi.shape 

567 

568 def add_xi(self, xi, yi=None): 

569 """ 

570 Add more x values to the set to be interpolated 

571 

572 The barycentric interpolation algorithm allows easy updating by 

573 adding more points for the polynomial to pass through. 

574 

575 Parameters 

576 ---------- 

577 xi : array_like 

578 The x coordinates of the points that the polynomial should pass 

579 through. 

580 yi : array_like, optional 

581 The y coordinates of the points the polynomial should pass through. 

582 Should have shape ``(xi.size, R)``; if R > 1 then the polynomial is 

583 vector-valued. 

584 If `yi` is not given, the y values will be supplied later. `yi` should 

585 be given if and only if the interpolator has y values specified. 

586 

587 """ 

588 if yi is not None: 

589 if self.yi is None: 

590 raise ValueError("No previous yi value to update!") 

591 yi = self._reshape_yi(yi, check=True) 

592 self.yi = np.vstack((self.yi,yi)) 

593 else: 

594 if self.yi is not None: 

595 raise ValueError("No update to yi provided!") 

596 old_n = self.n 

597 self.xi = np.concatenate((self.xi,xi)) 

598 self.n = len(self.xi) 

599 self.wi **= -1 

600 old_wi = self.wi 

601 self.wi = np.zeros(self.n) 

602 self.wi[:old_n] = old_wi 

603 for j in range(old_n, self.n): 

604 self.wi[:j] *= (self.xi[j]-self.xi[:j]) 

605 self.wi[j] = np.multiply.reduce(self.xi[:j]-self.xi[j]) 

606 self.wi **= -1 

607 

608 def __call__(self, x): 

609 """Evaluate the interpolating polynomial at the points x 

610 

611 Parameters 

612 ---------- 

613 x : array_like 

614 Points to evaluate the interpolant at. 

615 

616 Returns 

617 ------- 

618 y : array_like 

619 Interpolated values. Shape is determined by replacing 

620 the interpolation axis in the original array with the shape of x. 

621 

622 Notes 

623 ----- 

624 Currently the code computes an outer product between x and the 

625 weights, that is, it constructs an intermediate array of size 

626 N by len(x), where N is the degree of the polynomial. 

627 """ 

628 return _Interpolator1D.__call__(self, x) 

629 

630 def _evaluate(self, x): 

631 if x.size == 0: 

632 p = np.zeros((0, self.r), dtype=self.dtype) 

633 else: 

634 c = x[...,np.newaxis]-self.xi 

635 z = c == 0 

636 c[z] = 1 

637 c = self.wi/c 

638 p = np.dot(c,self.yi)/np.sum(c,axis=-1)[...,np.newaxis] 

639 # Now fix where x==some xi 

640 r = np.nonzero(z) 

641 if len(r) == 1: # evaluation at a scalar 

642 if len(r[0]) > 0: # equals one of the points 

643 p = self.yi[r[0][0]] 

644 else: 

645 p[r[:-1]] = self.yi[r[-1]] 

646 return p 

647 

648 

649def barycentric_interpolate(xi, yi, x, axis=0): 

650 """ 

651 Convenience function for polynomial interpolation. 

652 

653 Constructs a polynomial that passes through a given set of points, 

654 then evaluates the polynomial. For reasons of numerical stability, 

655 this function does not compute the coefficients of the polynomial. 

656 

657 This function uses a "barycentric interpolation" method that treats 

658 the problem as a special case of rational function interpolation. 

659 This algorithm is quite stable, numerically, but even in a world of 

660 exact computation, unless the `x` coordinates are chosen very 

661 carefully - Chebyshev zeros (e.g., cos(i*pi/n)) are a good choice - 

662 polynomial interpolation itself is a very ill-conditioned process 

663 due to the Runge phenomenon. 

664 

665 Parameters 

666 ---------- 

667 xi : array_like 

668 1-D array of x coordinates of the points the polynomial should 

669 pass through 

670 yi : array_like 

671 The y coordinates of the points the polynomial should pass through. 

672 x : scalar or array_like 

673 Points to evaluate the interpolator at. 

674 axis : int, optional 

675 Axis in the yi array corresponding to the x-coordinate values. 

676 

677 Returns 

678 ------- 

679 y : scalar or array_like 

680 Interpolated values. Shape is determined by replacing 

681 the interpolation axis in the original array with the shape of x. 

682 

683 See Also 

684 -------- 

685 BarycentricInterpolator : Bary centric interpolator 

686 

687 Notes 

688 ----- 

689 Construction of the interpolation weights is a relatively slow process. 

690 If you want to call this many times with the same xi (but possibly 

691 varying yi or x) you should use the class `BarycentricInterpolator`. 

692 This is what this function uses internally. 

693 

694 Examples 

695 -------- 

696 We can interpolate 2D observed data using barycentric interpolation: 

697 

698 >>> import matplotlib.pyplot as plt 

699 >>> from scipy.interpolate import barycentric_interpolate 

700 >>> x_observed = np.linspace(0.0, 10.0, 11) 

701 >>> y_observed = np.sin(x_observed) 

702 >>> x = np.linspace(min(x_observed), max(x_observed), num=100) 

703 >>> y = barycentric_interpolate(x_observed, y_observed, x) 

704 >>> plt.plot(x_observed, y_observed, "o", label="observation") 

705 >>> plt.plot(x, y, label="barycentric interpolation") 

706 >>> plt.legend() 

707 >>> plt.show() 

708 

709 """ 

710 return BarycentricInterpolator(xi, yi, axis=axis)(x)