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

1"""ARMA process and estimation with scipy.signal.lfilter 

2 

3Notes 

4----- 

5* written without textbook, works but not sure about everything 

6 briefly checked and it looks to be standard least squares, see below 

7 

8* theoretical autocorrelation function of general ARMA 

9 Done, relatively easy to guess solution, time consuming to get 

10 theoretical test cases, example file contains explicit formulas for 

11 acovf of MA(1), MA(2) and ARMA(1,1) 

12 

13Properties: 

14Judge, ... (1985): The Theory and Practise of Econometrics 

15 

16Author: josefpktd 

17License: BSD 

18""" 

19from statsmodels.compat.pandas import deprecate_kwarg 

20 

21import numpy as np 

22from scipy import signal, optimize, linalg 

23 

24from statsmodels.compat.pandas import Appender 

25from statsmodels.tools.docstring import remove_parameters, Docstring 

26from statsmodels.tools.validation import array_like 

27 

28__all__ = ['arma_acf', 'arma_acovf', 'arma_generate_sample', 

29 'arma_impulse_response', 'arma2ar', 'arma2ma', 'deconvolve', 

30 'lpol2index', 'index2lpol'] 

31 

32 

33# Remove after 0.11 

34@deprecate_kwarg('sigma', 'scale') 

35def arma_generate_sample(ar, ma, nsample, scale=1, distrvs=None, 

36 axis=0, burnin=0): 

37 """ 

38 Simulate data from an ARMA. 

39 

40 Parameters 

41 ---------- 

42 ar : array_like 

43 The coefficient for autoregressive lag polynomial, including zero lag. 

44 ma : array_like 

45 The coefficient for moving-average lag polynomial, including zero lag. 

46 nsample : int or tuple of ints 

47 If nsample is an integer, then this creates a 1d timeseries of 

48 length size. If nsample is a tuple, creates a len(nsample) 

49 dimensional time series where time is indexed along the input 

50 variable ``axis``. All series are unless ``distrvs`` generates 

51 dependent data. 

52 scale : float 

53 The standard deviation of noise. 

54 distrvs : function, random number generator 

55 A function that generates the random numbers, and takes sample size 

56 as argument. The default is np.random.randn. 

57 axis : int 

58 See nsample for details. 

59 burnin : int 

60 Number of observation at the beginning of the sample to drop. 

61 Used to reduce dependence on initial values. 

62 

63 Returns 

64 ------- 

65 ndarray 

66 Random sample(s) from an ARMA process. 

67 

68 Notes 

69 ----- 

70 As mentioned above, both the AR and MA components should include the 

71 coefficient on the zero-lag. This is typically 1. Further, due to the 

72 conventions used in signal processing used in signal.lfilter vs. 

73 conventions in statistics for ARMA processes, the AR parameters should 

74 have the opposite sign of what you might expect. See the examples below. 

75 

76 Examples 

77 -------- 

78 >>> import numpy as np 

79 >>> np.random.seed(12345) 

80 >>> arparams = np.array([.75, -.25]) 

81 >>> maparams = np.array([.65, .35]) 

82 >>> ar = np.r_[1, -arparams] # add zero-lag and negate 

83 >>> ma = np.r_[1, maparams] # add zero-lag 

84 >>> y = sm.tsa.arma_generate_sample(ar, ma, 250) 

85 >>> model = sm.tsa.ARMA(y, (2, 2)).fit(trend='nc', disp=0) 

86 >>> model.params 

87 array([ 0.79044189, -0.23140636, 0.70072904, 0.40608028]) 

88 """ 

89 distrvs = np.random.normal if distrvs is None else distrvs 

90 if np.ndim(nsample) == 0: 

91 nsample = [nsample] 

92 if burnin: 

93 # handle burin time for nd arrays 

94 # maybe there is a better trick in scipy.fft code 

95 newsize = list(nsample) 

96 newsize[axis] += burnin 

97 newsize = tuple(newsize) 

98 fslice = [slice(None)] * len(newsize) 

99 fslice[axis] = slice(burnin, None, None) 

100 fslice = tuple(fslice) 

101 else: 

102 newsize = tuple(nsample) 

103 fslice = tuple([slice(None)] * np.ndim(newsize)) 

104 eta = scale * distrvs(size=newsize) 

105 return signal.lfilter(ma, ar, eta, axis=axis)[fslice] 

106 

107 

108def arma_acovf(ar, ma, nobs=10, sigma2=1, dtype=None): 

109 """ 

110 Theoretical autocovariance function of ARMA process. 

111 

112 Parameters 

113 ---------- 

114 ar : array_like, 1d 

115 The coefficients for autoregressive lag polynomial, including zero lag. 

116 ma : array_like, 1d 

117 The coefficients for moving-average lag polynomial, including zero lag. 

118 nobs : int 

119 The number of terms (lags plus zero lag) to include in returned acovf. 

120 sigma2 : float 

121 Variance of the innovation term. 

122 

123 Returns 

124 ------- 

125 ndarray 

126 The autocovariance of ARMA process given by ar, ma. 

127 

128 See Also 

129 -------- 

130 arma_acf : Autocorrelation function for ARMA processes. 

131 acovf : Sample autocovariance estimation. 

132 

133 References 

134 ---------- 

135 .. [*] Brockwell, Peter J., and Richard A. Davis. 2009. Time Series: 

136 Theory and Methods. 2nd ed. 1991. New York, NY: Springer. 

137 """ 

138 if dtype is None: 

139 dtype = np.common_type(np.array(ar), np.array(ma), np.array(sigma2)) 

140 

141 p = len(ar) - 1 

142 q = len(ma) - 1 

143 m = max(p, q) + 1 

144 

145 if sigma2.real < 0: 

146 raise ValueError('Must have positive innovation variance.') 

147 

148 # Short-circuit for trivial corner-case 

149 if p == q == 0: 

150 out = np.zeros(nobs, dtype=dtype) 

151 out[0] = sigma2 

152 return out 

153 

154 # Get the moving average representation coefficients that we need 

155 ma_coeffs = arma2ma(ar, ma, lags=m) 

156 

157 # Solve for the first m autocovariances via the linear system 

158 # described by (BD, eq. 3.3.8) 

159 A = np.zeros((m, m), dtype=dtype) 

160 b = np.zeros((m, 1), dtype=dtype) 

161 # We need a zero-right-padded version of ar params 

162 tmp_ar = np.zeros(m, dtype=dtype) 

163 tmp_ar[:p + 1] = ar 

164 for k in range(m): 

165 A[k, :(k + 1)] = tmp_ar[:(k + 1)][::-1] 

166 A[k, 1:m - k] += tmp_ar[(k + 1):m] 

167 b[k] = sigma2 * np.dot(ma[k:q + 1], ma_coeffs[:max((q + 1 - k), 0)]) 

168 acovf = np.zeros(max(nobs, m), dtype=dtype) 

169 acovf[:m] = np.linalg.solve(A, b)[:, 0] 

170 

171 # Iteratively apply (BD, eq. 3.3.9) to solve for remaining autocovariances 

172 if nobs > m: 

173 zi = signal.lfiltic([1], ar, acovf[:m:][::-1]) 

174 acovf[m:] = signal.lfilter([1], ar, np.zeros(nobs - m, dtype=dtype), 

175 zi=zi)[0] 

176 

177 return acovf[:nobs] 

178 

179 

180# Remove after 0.11 

181@deprecate_kwarg('nobs', 'lags') 

182def arma_acf(ar, ma, lags=10): 

183 """ 

184 Theoretical autocorrelation function of an ARMA process. 

185 

186 Parameters 

187 ---------- 

188 ar : array_like 

189 Coefficients for autoregressive lag polynomial, including zero lag. 

190 ma : array_like 

191 Coefficients for moving-average lag polynomial, including zero lag. 

192 lags : int 

193 The number of terms (lags plus zero lag) to include in returned acf. 

194 

195 Returns 

196 ------- 

197 ndarray 

198 The autocorrelations of ARMA process given by ar and ma. 

199 

200 See Also 

201 -------- 

202 arma_acovf : Autocovariances from ARMA processes. 

203 acf : Sample autocorrelation function estimation. 

204 acovf : Sample autocovariance function estimation. 

205 """ 

206 acovf = arma_acovf(ar, ma, lags) 

207 return acovf / acovf[0] 

208 

209 

210# Remove after 0.11 

211@deprecate_kwarg('nobs', 'lags') 

212def arma_pacf(ar, ma, lags=10): 

213 """ 

214 Theoretical partial autocorrelation function of an ARMA process. 

215 

216 Parameters 

217 ---------- 

218 ar : array_like, 1d 

219 The coefficients for autoregressive lag polynomial, including zero lag. 

220 ma : array_like, 1d 

221 The coefficients for moving-average lag polynomial, including zero lag. 

222 lags : int 

223 The number of terms (lags plus zero lag) to include in returned pacf. 

224 

225 Returns 

226 ------- 

227 ndarrray 

228 The partial autocorrelation of ARMA process given by ar and ma. 

229 

230 Notes 

231 ----- 

232 Solves yule-walker equation for each lag order up to nobs lags. 

233 

234 not tested/checked yet 

235 """ 

236 # TODO: Should use rank 1 inverse update 

237 apacf = np.zeros(lags) 

238 acov = arma_acf(ar, ma, lags=lags + 1) 

239 

240 apacf[0] = 1. 

241 for k in range(2, lags + 1): 

242 r = acov[:k] 

243 apacf[k - 1] = linalg.solve(linalg.toeplitz(r[:-1]), r[1:])[-1] 

244 return apacf 

245 

246 

247def arma_periodogram(ar, ma, worN=None, whole=0): 

248 """ 

249 Periodogram for ARMA process given by lag-polynomials ar and ma. 

250 

251 Parameters 

252 ---------- 

253 ar : array_like 

254 The autoregressive lag-polynomial with leading 1 and lhs sign. 

255 ma : array_like 

256 The moving average lag-polynomial with leading 1. 

257 worN : {None, int}, optional 

258 An option for scipy.signal.freqz (read "w or N"). 

259 If None, then compute at 512 frequencies around the unit circle. 

260 If a single integer, the compute at that many frequencies. 

261 Otherwise, compute the response at frequencies given in worN. 

262 whole : {0,1}, optional 

263 An options for scipy.signal.freqz/ 

264 Normally, frequencies are computed from 0 to pi (upper-half of 

265 unit-circle. If whole is non-zero compute frequencies from 0 to 2*pi. 

266 

267 Returns 

268 ------- 

269 w : ndarray 

270 The frequencies. 

271 sd : ndarray 

272 The periodogram, also known as the spectral density. 

273 

274 Notes 

275 ----- 

276 Normalization ? 

277 

278 This uses signal.freqz, which does not use fft. There is a fft version 

279 somewhere. 

280 """ 

281 w, h = signal.freqz(ma, ar, worN=worN, whole=whole) 

282 sd = np.abs(h) ** 2 / np.sqrt(2 * np.pi) 

283 if np.any(np.isnan(h)): 

284 # this happens with unit root or seasonal unit root' 

285 import warnings 

286 warnings.warn('Warning: nan in frequency response h, maybe a unit ' 

287 'root', RuntimeWarning) 

288 return w, sd 

289 

290 

291# Remove after 0.11 

292@deprecate_kwarg('nobs', 'leads') 

293def arma_impulse_response(ar, ma, leads=100): 

294 """ 

295 Compute the impulse response function (MA representation) for ARMA process. 

296 

297 Parameters 

298 ---------- 

299 ar : array_like, 1d 

300 The auto regressive lag polynomial. 

301 ma : array_like, 1d 

302 The moving average lag polynomial. 

303 leads : int 

304 The number of observations to calculate. 

305 

306 Returns 

307 ------- 

308 ndarray 

309 The impulse response function with nobs elements. 

310 

311 Notes 

312 ----- 

313 This is the same as finding the MA representation of an ARMA(p,q). 

314 By reversing the role of ar and ma in the function arguments, the 

315 returned result is the AR representation of an ARMA(p,q), i.e 

316 

317 ma_representation = arma_impulse_response(ar, ma, leads=100) 

318 ar_representation = arma_impulse_response(ma, ar, leads=100) 

319 

320 Fully tested against matlab 

321 

322 Examples 

323 -------- 

324 AR(1) 

325 

326 >>> arma_impulse_response([1.0, -0.8], [1.], leads=10) 

327 array([ 1. , 0.8 , 0.64 , 0.512 , 0.4096 , 

328 0.32768 , 0.262144 , 0.2097152 , 0.16777216, 0.13421773]) 

329 

330 this is the same as 

331 

332 >>> 0.8**np.arange(10) 

333 array([ 1. , 0.8 , 0.64 , 0.512 , 0.4096 , 

334 0.32768 , 0.262144 , 0.2097152 , 0.16777216, 0.13421773]) 

335 

336 MA(2) 

337 

338 >>> arma_impulse_response([1.0], [1., 0.5, 0.2], leads=10) 

339 array([ 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ]) 

340 

341 ARMA(1,2) 

342 

343 >>> arma_impulse_response([1.0, -0.8], [1., 0.5, 0.2], leads=10) 

344 array([ 1. , 1.3 , 1.24 , 0.992 , 0.7936 , 

345 0.63488 , 0.507904 , 0.4063232 , 0.32505856, 0.26004685]) 

346 """ 

347 impulse = np.zeros(leads) 

348 impulse[0] = 1. 

349 return signal.lfilter(ma, ar, impulse) 

350 

351 

352# Remove after 0.11 

353@deprecate_kwarg('nobs', 'lags') 

354def arma2ma(ar, ma, lags=100): 

355 """ 

356 A finite-lag approximate MA representation of an ARMA process. 

357 

358 Parameters 

359 ---------- 

360 ar : ndarray 

361 The auto regressive lag polynomial. 

362 ma : ndarray 

363 The moving average lag polynomial. 

364 lags : int 

365 The number of coefficients to calculate. 

366 

367 Returns 

368 ------- 

369 ndarray 

370 The coefficients of AR lag polynomial with nobs elements. 

371 

372 Notes 

373 ----- 

374 Equivalent to ``arma_impulse_response(ma, ar, leads=100)`` 

375 """ 

376 return arma_impulse_response(ar, ma, leads=lags) 

377 

378 

379# Remove after 0.11 

380@deprecate_kwarg('nobs', 'lags') 

381def arma2ar(ar, ma, lags=100): 

382 """ 

383 A finite-lag AR approximation of an ARMA process. 

384 

385 Parameters 

386 ---------- 

387 ar : array_like 

388 The auto regressive lag polynomial. 

389 ma : array_like 

390 The moving average lag polynomial. 

391 lags : int 

392 The number of coefficients to calculate. 

393 

394 Returns 

395 ------- 

396 ndarray 

397 The coefficients of AR lag polynomial with nobs elements. 

398 

399 Notes 

400 ----- 

401 Equivalent to ``arma_impulse_response(ma, ar, leads=100)`` 

402 """ 

403 return arma_impulse_response(ma, ar, leads=lags) 

404 

405 

406# moved from sandbox.tsa.try_fi 

407def ar2arma(ar_des, p, q, n=20, mse='ar', start=None): 

408 """ 

409 Find arma approximation to ar process. 

410 

411 This finds the ARMA(p,q) coefficients that minimize the integrated 

412 squared difference between the impulse_response functions (MA 

413 representation) of the AR and the ARMA process. This does not check 

414 whether the MA lag polynomial of the ARMA process is invertible, neither 

415 does it check the roots of the AR lag polynomial. 

416 

417 Parameters 

418 ---------- 

419 ar_des : array_like 

420 The coefficients of original AR lag polynomial, including lag zero. 

421 p : int 

422 The length of desired AR lag polynomials. 

423 q : int 

424 The length of desired MA lag polynomials. 

425 n : int 

426 The number of terms of the impulse_response function to include in the 

427 objective function for the approximation. 

428 mse : str, 'ar' 

429 Not used. 

430 start : ndarray 

431 Initial values to use when finding the approximation. 

432 

433 Returns 

434 ------- 

435 ar_app : ndarray 

436 The coefficients of the AR lag polynomials of the approximation. 

437 ma_app : ndarray 

438 The coefficients of the MA lag polynomials of the approximation. 

439 res : tuple 

440 The result of optimize.leastsq. 

441 

442 Notes 

443 ----- 

444 Extension is possible if we want to match autocovariance instead 

445 of impulse response function. 

446 """ 

447 

448 # TODO: convert MA lag polynomial, ma_app, to be invertible, by mirroring 

449 # TODO: roots outside the unit interval to ones that are inside. How to do 

450 # TODO: this? 

451 

452 # p,q = pq 

453 def msear_err(arma, ar_des): 

454 ar, ma = np.r_[1, arma[:p - 1]], np.r_[1, arma[p - 1:]] 

455 ar_approx = arma_impulse_response(ma, ar, n) 

456 return (ar_des - ar_approx) # ((ar - ar_approx)**2).sum() 

457 

458 if start is None: 

459 arma0 = np.r_[-0.9 * np.ones(p - 1), np.zeros(q - 1)] 

460 else: 

461 arma0 = start 

462 res = optimize.leastsq(msear_err, arma0, ar_des, maxfev=5000) 

463 arma_app = np.atleast_1d(res[0]) 

464 ar_app = np.r_[1, arma_app[:p - 1]], 

465 ma_app = np.r_[1, arma_app[p - 1:]] 

466 return ar_app, ma_app, res 

467 

468 

469_arma_docs = {'ar': arma2ar.__doc__, 

470 'ma': arma2ma.__doc__} 

471 

472 

473def lpol2index(ar): 

474 """ 

475 Remove zeros from lag polynomial 

476 

477 Parameters 

478 ---------- 

479 ar : array_like 

480 coefficients of lag polynomial 

481 

482 Returns 

483 ------- 

484 coeffs : ndarray 

485 non-zero coefficients of lag polynomial 

486 index : ndarray 

487 index (lags) of lag polynomial with non-zero elements 

488 """ 

489 ar = array_like(ar, 'ar') 

490 index = np.nonzero(ar)[0] 

491 coeffs = ar[index] 

492 return coeffs, index 

493 

494 

495def index2lpol(coeffs, index): 

496 """ 

497 Expand coefficients to lag poly 

498 

499 Parameters 

500 ---------- 

501 coeffs : ndarray 

502 non-zero coefficients of lag polynomial 

503 index : ndarray 

504 index (lags) of lag polynomial with non-zero elements 

505 

506 Returns 

507 ------- 

508 ar : array_like 

509 coefficients of lag polynomial 

510 """ 

511 n = max(index) 

512 ar = np.zeros(n + 1) 

513 ar[index] = coeffs 

514 return ar 

515 

516 

517def lpol_fima(d, n=20): 

518 """MA representation of fractional integration 

519 

520 .. math:: (1-L)^{-d} for |d|<0.5 or |d|<1 (?) 

521 

522 Parameters 

523 ---------- 

524 d : float 

525 fractional power 

526 n : int 

527 number of terms to calculate, including lag zero 

528 

529 Returns 

530 ------- 

531 ma : ndarray 

532 coefficients of lag polynomial 

533 """ 

534 # hide import inside function until we use this heavily 

535 from scipy.special import gammaln 

536 j = np.arange(n) 

537 return np.exp(gammaln(d + j) - gammaln(j + 1) - gammaln(d)) 

538 

539 

540# moved from sandbox.tsa.try_fi 

541def lpol_fiar(d, n=20): 

542 """AR representation of fractional integration 

543 

544 .. math:: (1-L)^{d} for |d|<0.5 or |d|<1 (?) 

545 

546 Parameters 

547 ---------- 

548 d : float 

549 fractional power 

550 n : int 

551 number of terms to calculate, including lag zero 

552 

553 Returns 

554 ------- 

555 ar : ndarray 

556 coefficients of lag polynomial 

557 

558 Notes: 

559 first coefficient is 1, negative signs except for first term, 

560 ar(L)*x_t 

561 """ 

562 # hide import inside function until we use this heavily 

563 from scipy.special import gammaln 

564 j = np.arange(n) 

565 ar = - np.exp(gammaln(-d + j) - gammaln(j + 1) - gammaln(-d)) 

566 ar[0] = 1 

567 return ar 

568 

569 

570# moved from sandbox.tsa.try_fi 

571def lpol_sdiff(s): 

572 """return coefficients for seasonal difference (1-L^s) 

573 

574 just a trivial convenience function 

575 

576 Parameters 

577 ---------- 

578 s : int 

579 number of periods in season 

580 

581 Returns 

582 ------- 

583 sdiff : list, length s+1 

584 """ 

585 return [1] + [0] * (s - 1) + [-1] 

586 

587 

588def deconvolve(num, den, n=None): 

589 """Deconvolves divisor out of signal, division of polynomials for n terms 

590 

591 calculates den^{-1} * num 

592 

593 Parameters 

594 ---------- 

595 num : array_like 

596 signal or lag polynomial 

597 denom : array_like 

598 coefficients of lag polynomial (linear filter) 

599 n : None or int 

600 number of terms of quotient 

601 

602 Returns 

603 ------- 

604 quot : ndarray 

605 quotient or filtered series 

606 rem : ndarray 

607 remainder 

608 

609 Notes 

610 ----- 

611 If num is a time series, then this applies the linear filter den^{-1}. 

612 If both num and den are both lag polynomials, then this calculates the 

613 quotient polynomial for n terms and also returns the remainder. 

614 

615 This is copied from scipy.signal.signaltools and added n as optional 

616 parameter. 

617 """ 

618 num = np.atleast_1d(num) 

619 den = np.atleast_1d(den) 

620 N = len(num) 

621 D = len(den) 

622 if D > N and n is None: 

623 quot = [] 

624 rem = num 

625 else: 

626 if n is None: 

627 n = N - D + 1 

628 input = np.zeros(n, float) 

629 input[0] = 1 

630 quot = signal.lfilter(num, den, input) 

631 num_approx = signal.convolve(den, quot, mode='full') 

632 if len(num) < len(num_approx): # 1d only ? 

633 num = np.concatenate((num, np.zeros(len(num_approx) - len(num)))) 

634 rem = num - num_approx 

635 return quot, rem 

636 

637 

638_generate_sample_doc = Docstring(arma_generate_sample.__doc__) 

639_generate_sample_doc.remove_parameters(['ar', 'ma']) 

640_generate_sample_doc.replace_block('Notes', []) 

641_generate_sample_doc.replace_block('Examples', []) 

642 

643 

644class ArmaProcess(object): 

645 r""" 

646 Theoretical properties of an ARMA process for specified lag-polynomials. 

647 

648 Parameters 

649 ---------- 

650 ar : array_like 

651 Coefficient for autoregressive lag polynomial, including zero lag. 

652 Must be entered using the signs from the lag polynomial representation. 

653 See the notes for more information about the sign. 

654 ma : array_like 

655 Coefficient for moving-average lag polynomial, including zero lag. 

656 nobs : int, optional 

657 Length of simulated time series. Used, for example, if a sample is 

658 generated. See example. 

659 

660 Notes 

661 ----- 

662 Both the AR and MA components must include the coefficient on the 

663 zero-lag. In almost all cases these values should be 1. Further, due to 

664 using the lag-polynomial representation, the AR parameters should 

665 have the opposite sign of what one would write in the ARMA representation. 

666 See the examples below. 

667 

668 The ARMA(p,q) process is described by 

669 

670 .. math:: 

671 

672 y_{t}=\phi_{1}y_{t-1}+\ldots+\phi_{p}y_{t-p}+\theta_{1}\epsilon_{t-1} 

673 +\ldots+\theta_{q}\epsilon_{t-q}+\epsilon_{t} 

674 

675 and the parameterization used in this function uses the lag-polynomial 

676 representation, 

677 

678 .. math:: 

679 

680 \left(1-\phi_{1}L-\ldots-\phi_{p}L^{p}\right)y_{t} = 

681 \left(1+\theta_{1}L+\ldots+\theta_{q}L^{q}\right)\epsilon_{t} 

682 

683 Examples 

684 -------- 

685 ARMA(2,2) with AR coefficients 0.75 and -0.25, and MA coefficients 0.65 and 0.35 

686 

687 >>> import statsmodels.api as sm 

688 >>> import numpy as np 

689 >>> np.random.seed(12345) 

690 >>> arparams = np.array([.75, -.25]) 

691 >>> maparams = np.array([.65, .35]) 

692 >>> ar = np.r_[1, -arparams] # add zero-lag and negate 

693 >>> ma = np.r_[1, maparams] # add zero-lag 

694 >>> arma_process = sm.tsa.ArmaProcess(ar, ma) 

695 >>> arma_process.isstationary 

696 True 

697 >>> arma_process.isinvertible 

698 True 

699 >>> arma_process.arroots 

700 array([1.5-1.32287566j, 1.5+1.32287566j]) 

701 >>> y = arma_process.generate_sample(250) 

702 >>> model = sm.tsa.ARMA(y, (2, 2)).fit(trend='nc', disp=0) 

703 >>> model.params 

704 array([ 0.79044189, -0.23140636, 0.70072904, 0.40608028]) 

705 

706 The same ARMA(2,2) Using the from_coeffs class method 

707 

708 >>> arma_process = sm.tsa.ArmaProcess.from_coeffs(arparams, maparams) 

709 >>> arma_process.arroots 

710 array([1.5-1.32287566j, 1.5+1.32287566j]) 

711 """ 

712 

713 # TODO: Check unit root behavior 

714 def __init__(self, ar=None, ma=None, nobs=100): 

715 if ar is None: 

716 ar = np.array([1.]) 

717 if ma is None: 

718 ma = np.array([1.]) 

719 self.ar = array_like(ar, 'ar') 

720 self.ma = array_like(ma, 'ma') 

721 self.arcoefs = -self.ar[1:] 

722 self.macoefs = self.ma[1:] 

723 self.arpoly = np.polynomial.Polynomial(self.ar) 

724 self.mapoly = np.polynomial.Polynomial(self.ma) 

725 self.nobs = nobs 

726 

727 @classmethod 

728 def from_coeffs(cls, arcoefs=None, macoefs=None, nobs=100): 

729 """ 

730 Create ArmaProcess from an ARMA representation. 

731 

732 Parameters 

733 ---------- 

734 arcoefs : array_like 

735 Coefficient for autoregressive lag polynomial, not including zero 

736 lag. The sign is inverted to conform to the usual time series 

737 representation of an ARMA process in statistics. See the class 

738 docstring for more information. 

739 macoefs : array_like 

740 Coefficient for moving-average lag polynomial, excluding zero lag. 

741 nobs : int, optional 

742 Length of simulated time series. Used, for example, if a sample 

743 is generated. 

744 

745 Returns 

746 ------- 

747 ArmaProcess 

748 Class instance initialized with arcoefs and macoefs. 

749 

750 Examples 

751 -------- 

752 >>> arparams = [.75, -.25] 

753 >>> maparams = [.65, .35] 

754 >>> arma_process = sm.tsa.ArmaProcess.from_coeffs(ar, ma) 

755 >>> arma_process.isstationary 

756 True 

757 >>> arma_process.isinvertible 

758 True 

759 """ 

760 arcoefs = [] if arcoefs is None else arcoefs 

761 macoefs = [] if macoefs is None else macoefs 

762 return cls(np.r_[1, -np.asarray(arcoefs)], 

763 np.r_[1, np.asarray(macoefs)], 

764 nobs=nobs) 

765 

766 @classmethod 

767 def from_estimation(cls, model_results, nobs=None): 

768 """ 

769 Create an ArmaProcess from the results of an ARMA estimation. 

770 

771 Parameters 

772 ---------- 

773 model_results : ARMAResults instance 

774 A fitted model. 

775 nobs : int, optional 

776 If None, nobs is taken from the results. 

777 

778 Returns 

779 ------- 

780 ArmaProcess 

781 Class instance initialized from model_results. 

782 """ 

783 arcoefs = model_results.arparams 

784 macoefs = model_results.maparams 

785 nobs = nobs or model_results.nobs 

786 return cls(np.r_[1, -arcoefs], np.r_[1, macoefs], nobs=nobs) 

787 

788 def __mul__(self, oth): 

789 if isinstance(oth, self.__class__): 

790 ar = (self.arpoly * oth.arpoly).coef 

791 ma = (self.mapoly * oth.mapoly).coef 

792 else: 

793 try: 

794 aroth, maoth = oth 

795 arpolyoth = np.polynomial.Polynomial(aroth) 

796 mapolyoth = np.polynomial.Polynomial(maoth) 

797 ar = (self.arpoly * arpolyoth).coef 

798 ma = (self.mapoly * mapolyoth).coef 

799 except: 

800 raise TypeError('Other type is not a valid type') 

801 return self.__class__(ar, ma, nobs=self.nobs) 

802 

803 def __repr__(self): 

804 msg = 'ArmaProcess({0}, {1}, nobs={2}) at {3}' 

805 return msg.format(self.ar.tolist(), self.ma.tolist(), 

806 self.nobs, hex(id(self))) 

807 

808 def __str__(self): 

809 return 'ArmaProcess\nAR: {0}\nMA: {1}'.format(self.ar.tolist(), 

810 self.ma.tolist()) 

811 

812 @Appender(remove_parameters(arma_acovf.__doc__, ['ar', 'ma', 'sigma2'])) 

813 def acovf(self, nobs=None): 

814 nobs = nobs or self.nobs 

815 return arma_acovf(self.ar, self.ma, nobs=nobs) 

816 

817 @Appender(remove_parameters(arma_acf.__doc__, ['ar', 'ma'])) 

818 def acf(self, lags=None): 

819 lags = lags or self.nobs 

820 return arma_acf(self.ar, self.ma, lags=lags) 

821 

822 @Appender(remove_parameters(arma_pacf.__doc__, ['ar', 'ma'])) 

823 def pacf(self, lags=None): 

824 lags = lags or self.nobs 

825 return arma_pacf(self.ar, self.ma, lags=lags) 

826 

827 @Appender(remove_parameters(arma_periodogram.__doc__, ['ar', 'ma', 'worN', 

828 'whole'])) 

829 def periodogram(self, nobs=None): 

830 nobs = nobs or self.nobs 

831 return arma_periodogram(self.ar, self.ma, worN=nobs) 

832 

833 @Appender(remove_parameters(arma_impulse_response.__doc__, ['ar', 'ma'])) 

834 def impulse_response(self, leads=None): 

835 leads = leads or self.nobs 

836 return arma_impulse_response(self.ar, self.ma, leads=leads) 

837 

838 @Appender(remove_parameters(arma2ma.__doc__, ['ar', 'ma'])) 

839 def arma2ma(self, lags=None): 

840 lags = lags or self.lags 

841 return arma2ma(self.ar, self.ma, lags=lags) 

842 

843 @Appender(remove_parameters(arma2ar.__doc__, ['ar', 'ma'])) 

844 def arma2ar(self, lags=None): 

845 lags = lags or self.lags 

846 return arma2ar(self.ar, self.ma, lags=lags) 

847 

848 @property 

849 def arroots(self): 

850 """Roots of autoregressive lag-polynomial""" 

851 return self.arpoly.roots() 

852 

853 @property 

854 def maroots(self): 

855 """Roots of moving average lag-polynomial""" 

856 return self.mapoly.roots() 

857 

858 @property 

859 def isstationary(self): 

860 """ 

861 Arma process is stationary if AR roots are outside unit circle. 

862 

863 Returns 

864 ------- 

865 bool 

866 True if autoregressive roots are outside unit circle. 

867 """ 

868 if np.all(np.abs(self.arroots) > 1.0): 

869 return True 

870 else: 

871 return False 

872 

873 @property 

874 def isinvertible(self): 

875 """ 

876 Arma process is invertible if MA roots are outside unit circle. 

877 

878 Returns 

879 ------- 

880 bool 

881 True if moving average roots are outside unit circle. 

882 """ 

883 if np.all(np.abs(self.maroots) > 1): 

884 return True 

885 else: 

886 return False 

887 

888 def invertroots(self, retnew=False): 

889 """ 

890 Make MA polynomial invertible by inverting roots inside unit circle. 

891 

892 Parameters 

893 ---------- 

894 retnew : bool 

895 If False (default), then return the lag-polynomial as array. 

896 If True, then return a new instance with invertible MA-polynomial. 

897 

898 Returns 

899 ------- 

900 manew : ndarray 

901 A new invertible MA lag-polynomial, returned if retnew is false. 

902 wasinvertible : bool 

903 True if the MA lag-polynomial was already invertible, returned if 

904 retnew is false. 

905 armaprocess : new instance of class 

906 If retnew is true, then return a new instance with invertible 

907 MA-polynomial. 

908 """ 

909 # TODO: variable returns like this? 

910 pr = self.maroots 

911 mainv = self.ma 

912 invertible = self.isinvertible 

913 if not invertible: 

914 pr[np.abs(pr) < 1] = 1. / pr[np.abs(pr) < 1] 

915 pnew = np.polynomial.Polynomial.fromroots(pr) 

916 mainv = pnew.coef / pnew.coef[0] 

917 

918 if retnew: 

919 return self.__class__(self.ar, mainv, nobs=self.nobs) 

920 else: 

921 return mainv, invertible 

922 

923 @Appender(str(_generate_sample_doc)) 

924 def generate_sample(self, nsample=100, scale=1., distrvs=None, axis=0, 

925 burnin=0): 

926 return arma_generate_sample(self.ar, self.ma, nsample, scale, distrvs, 

927 axis=axis, burnin=burnin)