@@ -1538,7 +1538,7 @@ def _validate_forecast_args(
1538
1538
"one or the other to avoid this warning, or pass verbose = False."
1539
1539
)
1540
1540
1541
- def _get_fit_time_index (self ) -> pd .Index :
1541
+ def _get_fit_time_index (self ) -> pd .RangeIndex | pd . DatetimeIndex :
1542
1542
time_index = self ._fit_coords .get (TIME_DIM , None ) if self ._fit_coords is not None else None
1543
1543
if time_index is None :
1544
1544
raise ValueError (
@@ -1547,6 +1547,7 @@ def _get_fit_time_index(self) -> pd.Index:
1547
1547
1548
1548
if isinstance (time_index [0 ], pd .Timestamp ):
1549
1549
time_index = pd .DatetimeIndex (time_index )
1550
+ time_index .freq = time_index .inferred_freq
1550
1551
else :
1551
1552
time_index = np .array (time_index )
1552
1553
@@ -1716,26 +1717,33 @@ def _build_forecast_index(
1716
1717
Index for the forecast results
1717
1718
"""
1718
1719
1719
- def get_or_create_index (x , start = None ):
1720
+ def get_or_create_index (x , time_index , start = None ):
1720
1721
if isinstance (x , pd .DataFrame | pd .Series ):
1721
1722
return x .index
1722
1723
elif isinstance (x , dict ):
1723
- return get_or_create_index (next (iter (x .values ())))
1724
+ return get_or_create_index (next (iter (x .values ())), time_index , start )
1724
1725
elif isinstance (x , np .ndarray | list | tuple ):
1725
1726
if start is None :
1726
1727
raise ValueError (
1727
1728
"Provided scenario has no index and no start date was provided. This combination "
1728
1729
"is ambiguous. Please provide a start date, or add an index to the scenario."
1729
1730
)
1731
+ is_datetime_index = isinstance (time_index , pd .DatetimeIndex )
1730
1732
n = x .shape [0 ] if isinstance (x , np .ndarray ) else len (x )
1733
+
1734
+ if isinstance (start , int ):
1735
+ start = time_index [start ]
1736
+ if is_datetime_index :
1737
+ return pd .date_range (start , periods = n , freq = time_index .freq )
1731
1738
return pd .RangeIndex (start , n + start , step = 1 , dtype = "int" )
1739
+
1732
1740
else :
1733
1741
raise ValueError (f"{ type (x )} is not a valid type for scenario data." )
1734
1742
1735
1743
x0_idx = None
1736
1744
1737
1745
if use_scenario_index :
1738
- forecast_index = get_or_create_index (scenario , start )
1746
+ forecast_index = get_or_create_index (scenario , time_index , start )
1739
1747
is_datetime = isinstance (forecast_index , pd .DatetimeIndex )
1740
1748
1741
1749
# If the user provided an index, we want to take it as-is (without removing the start value). Instead,
@@ -1756,13 +1764,16 @@ def get_or_create_index(x, start=None):
1756
1764
if end is not None :
1757
1765
forecast_index = pd .date_range (start , end = end , freq = freq )
1758
1766
if periods is not None :
1759
- forecast_index = pd .date_range (start , periods = periods , freq = freq )
1767
+ # date_range include both start and end, but we're going to pop off the start later (it will be
1768
+ # interpreted as x0). So we need to add 1 to the periods so the user gets "periods" number of
1769
+ # forecasts back
1770
+ forecast_index = pd .date_range (start , periods = periods + 1 , freq = freq )
1760
1771
1761
1772
else :
1762
1773
if end is not None :
1763
1774
forecast_index = pd .RangeIndex (start , end , step = 1 , dtype = "int" )
1764
1775
if periods is not None :
1765
- forecast_index = pd .RangeIndex (start , start + periods , step = 1 , dtype = "int" )
1776
+ forecast_index = pd .RangeIndex (start , start + periods + 1 , step = 1 , dtype = "int" )
1766
1777
1767
1778
if is_datetime :
1768
1779
if forecast_index .freq != time_index .freq :
@@ -1921,7 +1932,7 @@ def forecast(
1921
1932
)
1922
1933
start = time_index [- 1 ]
1923
1934
1924
- if not isinstance (scenario , dict ):
1935
+ if self . _needs_exog_data and not isinstance (scenario , dict ):
1925
1936
if len (self .data_names ) > 1 :
1926
1937
raise ValueError (
1927
1938
"Model needs more than one exogenous data to do forecasting. In this case, you must "
@@ -1950,7 +1961,6 @@ def forecast(
1950
1961
scenario = scenario ,
1951
1962
use_scenario_index = use_scenario_index ,
1952
1963
)
1953
-
1954
1964
scenario = self ._finalize_scenario_initialization (scenario , forecast_index )
1955
1965
temp_coords = self ._fit_coords .copy ()
1956
1966
@@ -1999,11 +2009,12 @@ def forecast(
1999
2009
x0 ,
2000
2010
P0 ,
2001
2011
* matrices ,
2002
- steps = len (forecast_index [: - 1 ] ),
2012
+ steps = len (forecast_index ),
2003
2013
dims = dims ,
2004
2014
mode = self ._fit_mode ,
2005
2015
sequence_names = self .kalman_filter .seq_names ,
2006
2016
k_endog = self .k_endog ,
2017
+ append_x0 = False ,
2007
2018
)
2008
2019
2009
2020
forecast_model .rvs_to_initial_values = {
0 commit comments