PageRenderTime 58ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/statsmodels/tsa/base/tsa_model.py

http://github.com/statsmodels/statsmodels
Python | 787 lines | 783 code | 4 blank | 0 comment | 0 complexity | 3a6fc557fca4075e32a8f513aa2d1d4d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. from statsmodels.compat.pandas import (
  2. NumericIndex,
  3. is_float_index,
  4. is_int_index,
  5. is_numeric_dtype,
  6. )
  7. import numbers
  8. import warnings
  9. import numpy as np
  10. from pandas import (
  11. DatetimeIndex,
  12. Index,
  13. Period,
  14. PeriodIndex,
  15. RangeIndex,
  16. Series,
  17. Timestamp,
  18. date_range,
  19. period_range,
  20. to_datetime,
  21. )
  22. from pandas.tseries.frequencies import to_offset
  23. from statsmodels.base.data import PandasData
  24. import statsmodels.base.model as base
  25. import statsmodels.base.wrapper as wrap
  26. from statsmodels.tools.sm_exceptions import ValueWarning
  27. _tsa_doc = """
  28. %(model)s
  29. Parameters
  30. ----------
  31. %(params)s
  32. dates : array_like, optional
  33. An array-like object of datetime objects. If a pandas object is given
  34. for endog or exog, it is assumed to have a DateIndex.
  35. freq : str, optional
  36. The frequency of the time-series. A Pandas offset or 'B', 'D', 'W',
  37. 'M', 'A', or 'Q'. This is optional if dates are given.
  38. %(extra_params)s
  39. %(extra_sections)s"""
  40. _model_doc = "Timeseries model base class"
  41. _generic_params = base._model_params_doc
  42. _missing_param_doc = base._missing_param_doc
  43. def get_index_loc(key, index):
  44. """
  45. Get the location of a specific key in an index
  46. Parameters
  47. ----------
  48. key : label
  49. The key for which to find the location if the underlying index is
  50. a DateIndex or a location if the underlying index is a RangeIndex
  51. or a NumericIndex.
  52. index : pd.Index
  53. The index to search.
  54. Returns
  55. -------
  56. loc : int
  57. The location of the key
  58. index : pd.Index
  59. The index including the key; this is a copy of the original index
  60. unless the index had to be expanded to accommodate `key`.
  61. index_was_expanded : bool
  62. Whether or not the index was expanded to accommodate `key`.
  63. Notes
  64. -----
  65. If `key` is past the end of of the given index, and the index is either
  66. a NumericIndex or a date index, this function extends the index up to
  67. and including key, and then returns the location in the new index.
  68. """
  69. base_index = index
  70. index = base_index
  71. date_index = isinstance(base_index, (PeriodIndex, DatetimeIndex))
  72. int_index = is_int_index(base_index)
  73. range_index = isinstance(base_index, RangeIndex)
  74. index_class = type(base_index)
  75. nobs = len(index)
  76. # Special handling for RangeIndex
  77. if range_index and isinstance(key, (int, np.integer)):
  78. # Negative indices (that lie in the Index)
  79. if key < 0 and -key <= nobs:
  80. key = nobs + key
  81. # Out-of-sample (note that we include key itself in the new index)
  82. elif key > nobs - 1:
  83. # See gh5835. Remove the except after pandas 0.25 required.
  84. try:
  85. base_index_start = base_index.start
  86. base_index_step = base_index.step
  87. except AttributeError:
  88. base_index_start = base_index._start
  89. base_index_step = base_index._step
  90. stop = base_index_start + (key + 1) * base_index_step
  91. index = RangeIndex(start=base_index_start,
  92. stop=stop,
  93. step=base_index_step)
  94. # Special handling for NumericIndex
  95. if (not range_index and int_index and not date_index and
  96. isinstance(key, (int, np.integer))):
  97. # Negative indices (that lie in the Index)
  98. if key < 0 and -key <= nobs:
  99. key = nobs + key
  100. # Out-of-sample (note that we include key itself in the new index)
  101. elif key > base_index[-1]:
  102. index = NumericIndex(np.arange(base_index[0], int(key + 1)))
  103. # Special handling for date indexes
  104. if date_index:
  105. # Use index type to choose creation function
  106. if index_class is DatetimeIndex:
  107. index_fn = date_range
  108. else:
  109. index_fn = period_range
  110. # Integer key (i.e. already given a location)
  111. if isinstance(key, (int, np.integer)):
  112. # Negative indices (that lie in the Index)
  113. if key < 0 and -key < nobs:
  114. key = index[nobs + key]
  115. # Out-of-sample (note that we include key itself in the new
  116. # index)
  117. elif key > len(base_index) - 1:
  118. index = index_fn(start=base_index[0],
  119. periods=int(key + 1),
  120. freq=base_index.freq)
  121. key = index[-1]
  122. else:
  123. key = index[key]
  124. # Other key types (i.e. string date or some datetime-like object)
  125. else:
  126. # Covert the key to the appropriate date-like object
  127. if index_class is PeriodIndex:
  128. date_key = Period(key, freq=base_index.freq)
  129. else:
  130. date_key = Timestamp(key)
  131. # Out-of-sample
  132. if date_key > base_index[-1]:
  133. # First create an index that may not always include `key`
  134. index = index_fn(start=base_index[0], end=date_key,
  135. freq=base_index.freq)
  136. # Now make sure we include `key`
  137. if not index[-1] == date_key:
  138. index = index_fn(start=base_index[0],
  139. periods=len(index) + 1,
  140. freq=base_index.freq)
  141. # To avoid possible inconsistencies with `get_loc` below,
  142. # set the key directly equal to the last index location
  143. key = index[-1]
  144. # Get the location
  145. if date_index:
  146. # (note that get_loc will throw a KeyError if key is invalid)
  147. loc = index.get_loc(key)
  148. elif int_index or range_index:
  149. # For NumericIndex and RangeIndex, key is assumed to be the location
  150. # and not an index value (this assumption is required to support
  151. # RangeIndex)
  152. try:
  153. index[key]
  154. # We want to raise a KeyError in this case, to keep the exception
  155. # consistent across index types.
  156. # - Attempting to index with an out-of-bound location (e.g.
  157. # index[10] on an index of length 9) will raise an IndexError
  158. # (as of Pandas 0.22)
  159. # - Attemtping to index with a type that cannot be cast to integer
  160. # (e.g. a non-numeric string) will raise a ValueError if the
  161. # index is RangeIndex (otherwise will raise an IndexError)
  162. # (as of Pandas 0.22)
  163. except (IndexError, ValueError) as e:
  164. raise KeyError(str(e))
  165. loc = key
  166. else:
  167. loc = index.get_loc(key)
  168. # Check if we now have a modified index
  169. index_was_expanded = index is not base_index
  170. # Return the index through the end of the loc / slice
  171. if isinstance(loc, slice):
  172. end = loc.stop - 1
  173. else:
  174. end = loc
  175. return loc, index[:end + 1], index_was_expanded
  176. def get_index_label_loc(key, index, row_labels):
  177. """
  178. Get the location of a specific key in an index or model row labels
  179. Parameters
  180. ----------
  181. key : label
  182. The key for which to find the location if the underlying index is
  183. a DateIndex or is only being used as row labels, or a location if
  184. the underlying index is a RangeIndex or a NumericIndex.
  185. index : pd.Index
  186. The index to search.
  187. row_labels : pd.Index
  188. Row labels to search if key not found in index
  189. Returns
  190. -------
  191. loc : int
  192. The location of the key
  193. index : pd.Index
  194. The index including the key; this is a copy of the original index
  195. unless the index had to be expanded to accommodate `key`.
  196. index_was_expanded : bool
  197. Whether or not the index was expanded to accommodate `key`.
  198. Notes
  199. -----
  200. This function expands on `get_index_loc` by first trying the given
  201. base index (or the model's index if the base index was not given) and
  202. then falling back to try again with the model row labels as the base
  203. index.
  204. """
  205. try:
  206. loc, index, index_was_expanded = get_index_loc(key, index)
  207. except KeyError as e:
  208. try:
  209. if not isinstance(key, (int, np.integer)):
  210. loc = row_labels.get_loc(key)
  211. else:
  212. raise
  213. # Require scalar
  214. # Pandas may return a slice if there are multiple matching
  215. # locations that are monotonic increasing (otherwise it may
  216. # return an array of integer locations, see below).
  217. if isinstance(loc, slice):
  218. loc = loc.start
  219. if isinstance(loc, np.ndarray):
  220. # Pandas may return a mask (boolean array), for e.g.:
  221. # pd.Index(list('abcb')).get_loc('b')
  222. if loc.dtype == bool:
  223. # Return the first True value
  224. # (we know there is at least one True value if we're
  225. # here because otherwise the get_loc call would have
  226. # raised an exception)
  227. loc = np.argmax(loc)
  228. # Finally, Pandas may return an integer array of
  229. # locations that match the given value, for e.g.
  230. # pd.DatetimeIndex(['2001-02', '2001-01']).get_loc('2001')
  231. # (this appears to be slightly undocumented behavior, since
  232. # only int, slice, and mask are mentioned in docs for
  233. # pandas.Index.get_loc as of 0.23.4)
  234. else:
  235. loc = loc[0]
  236. if not isinstance(loc, numbers.Integral):
  237. raise
  238. index = row_labels[:loc + 1]
  239. index_was_expanded = False
  240. except:
  241. raise e
  242. return loc, index, index_was_expanded
  243. def get_prediction_index(start, end, nobs, base_index, index=None, silent=False, index_none=False,
  244. index_generated=None, data=None):
  245. """
  246. Get the location of a specific key in an index or model row labels
  247. Parameters
  248. ----------
  249. start : label
  250. The key at which to start prediction. Depending on the underlying
  251. model's index, may be an integer, a date (string, datetime object,
  252. pd.Timestamp, or pd.Period object), or some other object in the
  253. model's row labels.
  254. end : label
  255. The key at which to end prediction (note that this key will be
  256. *included* in prediction). Depending on the underlying
  257. model's index, may be an integer, a date (string, datetime object,
  258. pd.Timestamp, or pd.Period object), or some other object in the
  259. model's row labels.
  260. nobs : int
  261. base_index : pd.Index
  262. index : pd.Index, optional
  263. Optionally an index to associate the predicted results to. If None,
  264. an attempt is made to create an index for the predicted results
  265. from the model's index or model's row labels.
  266. silent : bool, optional
  267. Argument to silence warnings.
  268. Returns
  269. -------
  270. start : int
  271. The index / observation location at which to begin prediction.
  272. end : int
  273. The index / observation location at which to end in-sample
  274. prediction. The maximum value for this is nobs-1.
  275. out_of_sample : int
  276. The number of observations to forecast after the end of the sample.
  277. prediction_index : pd.Index or None
  278. The index associated with the prediction results. This index covers
  279. the range [start, end + out_of_sample]. If the model has no given
  280. index and no given row labels (i.e. endog/exog is not Pandas), then
  281. this will be None.
  282. Notes
  283. -----
  284. The arguments `start` and `end` behave differently, depending on if
  285. they are integer or not. If either is an integer, then it is assumed
  286. to refer to a *location* in the index, not to an index value. On the
  287. other hand, if it is a date string or some other type of object, then
  288. it is assumed to refer to an index *value*. In all cases, the returned
  289. `start` and `end` values refer to index *locations* (so in the former
  290. case, the given location is validated and returned whereas in the
  291. latter case a location is found that corresponds to the given index
  292. value).
  293. This difference in behavior is necessary to support `RangeIndex`. This
  294. is because integers for a RangeIndex could refer either to index values
  295. or to index locations in an ambiguous way (while for `NumericIndex`,
  296. since we have required them to be full indexes, there is no ambiguity).
  297. """
  298. # Convert index keys (start, end) to index locations and get associated
  299. # indexes.
  300. try:
  301. start, _, start_oos = get_index_label_loc(start, base_index, data.row_labels)
  302. except KeyError:
  303. raise KeyError('The `start` argument could not be matched to a'
  304. ' location related to the index of the data.')
  305. if end is None:
  306. end = max(start, len(base_index) - 1)
  307. try:
  308. end, end_index, end_oos = get_index_label_loc(end, base_index, data.row_labels)
  309. except KeyError:
  310. raise KeyError('The `end` argument could not be matched to a'
  311. ' location related to the index of the data.')
  312. # Handle slices (if the given index keys cover more than one date)
  313. if isinstance(start, slice):
  314. start = start.start
  315. if isinstance(end, slice):
  316. end = end.stop - 1
  317. # Get the actual index for the prediction
  318. prediction_index = end_index[start:]
  319. # Validate prediction options
  320. if end < start:
  321. raise ValueError('Prediction must have `end` after `start`.')
  322. # Handle custom prediction index
  323. # First, if we were given an index, check that it's the right size and
  324. # use it if so
  325. if index is not None:
  326. if not len(prediction_index) == len(index):
  327. raise ValueError('Invalid `index` provided in prediction.'
  328. ' Must have length consistent with `start`'
  329. ' and `end` arguments.')
  330. # But if we weren't given Pandas input, this index will not be
  331. # used because the data will not be wrapped; in that case, issue
  332. # a warning
  333. if not isinstance(data, PandasData) and not silent:
  334. warnings.warn('Because the model data (`endog`, `exog`) were'
  335. ' not given as Pandas objects, the prediction'
  336. ' output will be Numpy arrays, and the given'
  337. ' `index` argument will only be used'
  338. ' internally.', ValueWarning)
  339. prediction_index = Index(index)
  340. # Now, if we *do not* have a supported index, but we were given some
  341. # kind of index...
  342. elif index_generated and not index_none:
  343. # If we are in sample, and have row labels, use them
  344. if data.row_labels is not None and not (start_oos or end_oos):
  345. prediction_index = data.row_labels[start:end + 1]
  346. # Otherwise, warn the user that they will get an NumericIndex
  347. else:
  348. if not silent:
  349. warnings.warn('No supported index is available.'
  350. ' Prediction results will be given with'
  351. ' an integer index beginning at `start`.',
  352. ValueWarning)
  353. warnings.warn('No supported index is available. In the next'
  354. ' version, calling this method in a model'
  355. ' without a supported index will result in an'
  356. ' exception.', DeprecationWarning)
  357. elif index_none:
  358. prediction_index = None
  359. # For backwards compatibility, set `predict_*` values
  360. if prediction_index is not None:
  361. data.predict_start = prediction_index[0]
  362. data.predict_end = prediction_index[-1]
  363. data.predict_dates = prediction_index
  364. else:
  365. data.predict_start = None
  366. data.predict_end = None
  367. data.predict_dates = None
  368. # Compute out-of-sample observations
  369. out_of_sample = max(end - (nobs - 1), 0)
  370. end -= out_of_sample
  371. return start, end, out_of_sample, prediction_index
  372. class TimeSeriesModel(base.LikelihoodModel):
  373. __doc__ = _tsa_doc % {"model": _model_doc, "params": _generic_params,
  374. "extra_params": _missing_param_doc,
  375. "extra_sections": ""}
  376. def __init__(self, endog, exog=None, dates=None, freq=None,
  377. missing='none', **kwargs):
  378. super().__init__(endog, exog, missing=missing, **kwargs)
  379. # Date handling in indexes
  380. self._init_dates(dates, freq)
  381. def _init_dates(self, dates=None, freq=None):
  382. """
  383. Initialize dates
  384. Parameters
  385. ----------
  386. dates : array_like, optional
  387. An array like object containing dates.
  388. freq : str, tuple, datetime.timedelta, DateOffset or None, optional
  389. A frequency specification for either `dates` or the row labels from
  390. the endog / exog data.
  391. Notes
  392. -----
  393. Creates `self._index` and related attributes. `self._index` is always
  394. a Pandas index, and it is always NumericIndex, DatetimeIndex, or
  395. PeriodIndex.
  396. If Pandas objects, endog / exog may have any type of index. If it is
  397. an NumericIndex with values 0, 1, ..., nobs-1 or if it is (coerceable to)
  398. a DatetimeIndex or PeriodIndex *with an associated frequency*, then it
  399. is called a "supported" index. Otherwise it is called an "unsupported"
  400. index.
  401. Supported indexes are standardized (i.e. a list of date strings is
  402. converted to a DatetimeIndex) and the result is put in `self._index`.
  403. Unsupported indexes are ignored, and a supported NumericIndex is
  404. generated and put in `self._index`. Warnings are issued in this case
  405. to alert the user if the returned index from some operation (e.g.
  406. forecasting) is different from the original data's index. However,
  407. whenever possible (e.g. purely in-sample prediction), the original
  408. index is returned.
  409. The benefit of supported indexes is that they allow *forecasting*, i.e.
  410. it is possible to extend them in a reasonable way. Thus every model
  411. must have an underlying supported index, even if it is just a generated
  412. NumericIndex.
  413. """
  414. # Get our index from `dates` if available, otherwise from whatever
  415. # Pandas index we might have retrieved from endog, exog
  416. if dates is not None:
  417. index = dates
  418. else:
  419. index = self.data.row_labels
  420. # Sanity check that we do not have a `freq` without an index
  421. if index is None and freq is not None:
  422. raise ValueError('Frequency provided without associated index.')
  423. # If an index is available, see if it is a date-based index or if it
  424. # can be coerced to one. (If it cannot we'll fall back, below, to an
  425. # internal, 0, 1, ... nobs-1 integer index for modeling purposes)
  426. inferred_freq = False
  427. if index is not None:
  428. # Try to coerce to date-based index
  429. if not isinstance(index, (DatetimeIndex, PeriodIndex)):
  430. try:
  431. # Only try to coerce non-numeric index types (string,
  432. # list of date-times, etc.)
  433. # Note that np.asarray(Float64Index([...])) yields an
  434. # object dtype array in earlier versions of Pandas (and so
  435. # will not have is_numeric_dtype == True), so explicitly
  436. # check for it here. But note also that in very early
  437. # Pandas (~0.12), Float64Index does not exist (and so the
  438. # statsmodels compat makes it an empty tuple, so in that
  439. # case also check if the first element is a float.
  440. _index = np.asarray(index)
  441. if (is_numeric_dtype(_index) or
  442. is_float_index(index) or
  443. (isinstance(_index[0], float))):
  444. raise ValueError('Numeric index given')
  445. # If a non-index Pandas series was given, only keep its
  446. # values (because we must have a pd.Index type, below, and
  447. # pd.to_datetime will return a Series when passed
  448. # non-list-like objects)
  449. if isinstance(index, Series):
  450. index = index.values
  451. # All coercion is done via pd.to_datetime
  452. # Note: date coercion via pd.to_datetime does not handle
  453. # string versions of PeriodIndex objects most of the time.
  454. _index = to_datetime(index)
  455. # Older versions of Pandas can sometimes fail here and
  456. # return a numpy array - check to make sure it's an index
  457. if not isinstance(_index, Index):
  458. raise ValueError('Could not coerce to date index')
  459. index = _index
  460. except:
  461. # Only want to actually raise an exception if `dates` was
  462. # provided but cannot be coerced. If we got the index from
  463. # the row_labels, we'll just ignore it and use the integer
  464. # index below
  465. if dates is not None:
  466. raise ValueError('Non-date index index provided to'
  467. ' `dates` argument.')
  468. # Now, if we were given, or coerced, a date-based index, make sure
  469. # it has an associated frequency
  470. if isinstance(index, (DatetimeIndex, PeriodIndex)):
  471. # If no frequency, try to get an inferred frequency
  472. if freq is None and index.freq is None:
  473. freq = index.inferred_freq
  474. # If we got an inferred frequncy, alert the user
  475. if freq is not None:
  476. inferred_freq = True
  477. if freq is not None:
  478. warnings.warn('No frequency information was'
  479. ' provided, so inferred frequency %s'
  480. ' will be used.'
  481. % freq, ValueWarning)
  482. # Convert the passed freq to a pandas offset object
  483. if freq is not None:
  484. freq = to_offset(freq)
  485. # Now, if no frequency information is available from the index
  486. # itself or from the `freq` argument, raise an exception
  487. if freq is None and index.freq is None:
  488. # But again, only want to raise the exception if `dates`
  489. # was provided.
  490. if dates is not None:
  491. raise ValueError('No frequency information was'
  492. ' provided with date index and no'
  493. ' frequency could be inferred.')
  494. # However, if the index itself has no frequency information but
  495. # the `freq` argument is available (or was inferred), construct
  496. # a new index with an associated frequency
  497. elif freq is not None and index.freq is None:
  498. resampled_index = date_range(
  499. start=index[0], end=index[-1], freq=freq)
  500. if not inferred_freq and not resampled_index.equals(index):
  501. raise ValueError('The given frequency argument could'
  502. ' not be matched to the given index.')
  503. index = resampled_index
  504. # Finally, if the index itself has a frequency and there was
  505. # also a given frequency, raise an exception if they are not
  506. # equal
  507. elif (freq is not None and not inferred_freq and
  508. not (index.freq == freq)):
  509. raise ValueError('The given frequency argument is'
  510. ' incompatible with the given index.')
  511. # Finally, raise an exception if we could not coerce to date-based
  512. # but we were given a frequency argument
  513. elif freq is not None:
  514. raise ValueError('Given index could not be coerced to dates'
  515. ' but `freq` argument was provided.')
  516. # Get attributes of the index
  517. has_index = index is not None
  518. date_index = isinstance(index, (DatetimeIndex, PeriodIndex))
  519. period_index = isinstance(index, PeriodIndex)
  520. int_index = is_int_index(index)
  521. range_index = isinstance(index, RangeIndex)
  522. has_freq = index.freq is not None if date_index else None
  523. increment = Index(range(self.endog.shape[0]))
  524. is_increment = index.equals(increment) if int_index else None
  525. is_monotonic = index.is_monotonic if date_index else None
  526. # Issue warnings for unsupported indexes
  527. if has_index and not (date_index or range_index or is_increment):
  528. warnings.warn('An unsupported index was provided and will be'
  529. ' ignored when e.g. forecasting.', ValueWarning)
  530. if date_index and not has_freq:
  531. warnings.warn('A date index has been provided, but it has no'
  532. ' associated frequency information and so will be'
  533. ' ignored when e.g. forecasting.', ValueWarning)
  534. if date_index and not is_monotonic:
  535. warnings.warn('A date index has been provided, but it is not'
  536. ' monotonic and so will be ignored when e.g.'
  537. ' forecasting.', ValueWarning)
  538. # Construct the internal index
  539. index_generated = False
  540. valid_index = ((date_index and has_freq and is_monotonic) or
  541. (int_index and is_increment) or range_index)
  542. if valid_index:
  543. _index = index
  544. else:
  545. _index = increment
  546. index_generated = True
  547. self._index = _index
  548. self._index_generated = index_generated
  549. self._index_none = index is None
  550. self._index_int64 = int_index and not range_index and not date_index
  551. self._index_dates = date_index and not index_generated
  552. self._index_freq = self._index.freq if self._index_dates else None
  553. self._index_inferred_freq = inferred_freq
  554. # For backwards compatibility, set data.dates, data.freq
  555. self.data.dates = self._index if self._index_dates else None
  556. self.data.freq = self._index.freqstr if self._index_dates else None
  557. def _get_index_loc(self, key, base_index=None):
  558. """
  559. Get the location of a specific key in an index
  560. Parameters
  561. ----------
  562. key : label
  563. The key for which to find the location if the underlying index is
  564. a DateIndex or a location if the underlying index is a RangeIndex
  565. or an NumericIndex.
  566. base_index : pd.Index, optional
  567. Optionally the base index to search. If None, the model's index is
  568. searched.
  569. Returns
  570. -------
  571. loc : int
  572. The location of the key
  573. index : pd.Index
  574. The index including the key; this is a copy of the original index
  575. unless the index had to be expanded to accommodate `key`.
  576. index_was_expanded : bool
  577. Whether or not the index was expanded to accommodate `key`.
  578. Notes
  579. -----
  580. If `key` is past the end of of the given index, and the index is either
  581. an NumericIndex or a date index, this function extends the index up to
  582. and including key, and then returns the location in the new index.
  583. """
  584. if base_index is None:
  585. base_index = self._index
  586. return get_index_loc(key, base_index)
  587. def _get_index_label_loc(self, key, base_index=None):
  588. """
  589. Get the location of a specific key in an index or model row labels
  590. Parameters
  591. ----------
  592. key : label
  593. The key for which to find the location if the underlying index is
  594. a DateIndex or is only being used as row labels, or a location if
  595. the underlying index is a RangeIndex or an NumericIndex.
  596. base_index : pd.Index, optional
  597. Optionally the base index to search. If None, the model's index is
  598. searched.
  599. Returns
  600. -------
  601. loc : int
  602. The location of the key
  603. index : pd.Index
  604. The index including the key; this is a copy of the original index
  605. unless the index had to be expanded to accommodate `key`.
  606. index_was_expanded : bool
  607. Whether or not the index was expanded to accommodate `key`.
  608. Notes
  609. -----
  610. This method expands on `_get_index_loc` by first trying the given
  611. base index (or the model's index if the base index was not given) and
  612. then falling back to try again with the model row labels as the base
  613. index.
  614. """
  615. if base_index is None:
  616. base_index = self._index
  617. return get_index_label_loc(key, base_index, self.data.row_labels)
  618. def _get_prediction_index(self, start, end, index=None, silent=False):
  619. """
  620. Get the location of a specific key in an index or model row labels
  621. Parameters
  622. ----------
  623. start : label
  624. The key at which to start prediction. Depending on the underlying
  625. model's index, may be an integer, a date (string, datetime object,
  626. pd.Timestamp, or pd.Period object), or some other object in the
  627. model's row labels.
  628. end : label
  629. The key at which to end prediction (note that this key will be
  630. *included* in prediction). Depending on the underlying
  631. model's index, may be an integer, a date (string, datetime object,
  632. pd.Timestamp, or pd.Period object), or some other object in the
  633. model's row labels.
  634. index : pd.Index, optional
  635. Optionally an index to associate the predicted results to. If None,
  636. an attempt is made to create an index for the predicted results
  637. from the model's index or model's row labels.
  638. silent : bool, optional
  639. Argument to silence warnings.
  640. Returns
  641. -------
  642. start : int
  643. The index / observation location at which to begin prediction.
  644. end : int
  645. The index / observation location at which to end in-sample
  646. prediction. The maximum value for this is nobs-1.
  647. out_of_sample : int
  648. The number of observations to forecast after the end of the sample.
  649. prediction_index : pd.Index or None
  650. The index associated with the prediction results. This index covers
  651. the range [start, end + out_of_sample]. If the model has no given
  652. index and no given row labels (i.e. endog/exog is not Pandas), then
  653. this will be None.
  654. Notes
  655. -----
  656. The arguments `start` and `end` behave differently, depending on if
  657. they are integer or not. If either is an integer, then it is assumed
  658. to refer to a *location* in the index, not to an index value. On the
  659. other hand, if it is a date string or some other type of object, then
  660. it is assumed to refer to an index *value*. In all cases, the returned
  661. `start` and `end` values refer to index *locations* (so in the former
  662. case, the given location is validated and returned whereas in the
  663. latter case a location is found that corresponds to the given index
  664. value).
  665. This difference in behavior is necessary to support `RangeIndex`. This
  666. is because integers for a RangeIndex could refer either to index values
  667. or to index locations in an ambiguous way (while for `NumericIndex`,
  668. since we have required them to be full indexes, there is no ambiguity).
  669. """
  670. nobs = len(self.endog)
  671. return get_prediction_index(start,
  672. end,
  673. nobs,
  674. base_index=self._index,
  675. index=index,
  676. silent=silent,
  677. index_none=self._index_none,
  678. index_generated=self._index_generated,
  679. data=self.data)
  680. def _get_exog_names(self):
  681. return self.data.xnames
  682. def _set_exog_names(self, vals):
  683. if not isinstance(vals, list):
  684. vals = [vals]
  685. self.data.xnames = vals
  686. # overwrite with writable property for (V)AR models
  687. exog_names = property(_get_exog_names, _set_exog_names, None,
  688. 'The names of the exogenous variables.')
  689. class TimeSeriesModelResults(base.LikelihoodModelResults):
  690. def __init__(self, model, params, normalized_cov_params, scale=1.):
  691. self.data = model.data
  692. super().__init__(model, params, normalized_cov_params, scale)
  693. class TimeSeriesResultsWrapper(wrap.ResultsWrapper):
  694. _attrs = {}
  695. _wrap_attrs = wrap.union_dicts(base.LikelihoodResultsWrapper._wrap_attrs,
  696. _attrs)
  697. _methods = {'predict' : 'dates'}
  698. _wrap_methods = wrap.union_dicts(base.LikelihoodResultsWrapper._wrap_methods,
  699. _methods)
  700. wrap.populate_wrapper(TimeSeriesResultsWrapper, # noqa:E305
  701. TimeSeriesModelResults)