Skip to content

Commit bd2dda7

Browse files
Merge pull request #75 from Techtonique/multiouputmts
multioutput time series
2 parents 1859b55 + 200d337 commit bd2dda7

File tree

8 files changed

+379
-847
lines changed

8 files changed

+379
-847
lines changed

examples/multioutputmts.py

Lines changed: 50 additions & 338 deletions
Original file line numberDiff line numberDiff line change
@@ -1,340 +1,52 @@
1-
"""
2-
Example script demonstrating MultiOutputMTS usage
3-
"""
4-
1+
import nnetsauce as ns
52
import numpy as np
63
import pandas as pd
7-
import matplotlib.pyplot as plt
8-
import time
9-
from sklearn.linear_model import Ridge, LinearRegression
10-
from sklearn.ensemble import RandomForestRegressor
11-
from sklearn.multioutput import MultiOutputRegressor
12-
13-
# Import the class (assuming it's in your Python path)
14-
try:
15-
import nnetsauce as ns
16-
print("Successfully imported nnetsauce")
17-
except ImportError:
18-
print("Creating a dummy class for demonstration")
19-
# For demonstration, we'll use the class definition above
20-
# In practice, you would import from nnetsauce
21-
22-
23-
def create_sample_data(n_samples=200, n_series=3, noise=0.1, trend=True):
24-
"""Create correlated multivariate time series."""
25-
np.random.seed(42)
26-
27-
# Base trend
28-
if trend:
29-
base_trend = np.linspace(0, 10, n_samples)
30-
else:
31-
base_trend = np.zeros(n_samples)
32-
33-
# Create series with correlations
34-
series_data = np.zeros((n_samples, n_series))
35-
36-
for i in range(n_series):
37-
# Each series has: base trend + seasonal pattern + noise + series-specific offset
38-
seasonal = 2 * np.sin(2 * np.pi * np.arange(n_samples) / 50 + i * np.pi/3)
39-
noise_component = noise * np.random.randn(n_samples)
40-
series_data[:, i] = base_trend + seasonal + noise_component + i * 5
41-
42-
# Add some cross-correlation
43-
for i in range(1, n_series):
44-
series_data[:, i] += 0.3 * series_data[:, i-1]
45-
46-
# Create DataFrame with dates
47-
dates = pd.date_range('2020-01-01', periods=n_samples, freq='D')
48-
df = pd.DataFrame(series_data,
49-
columns=[f'Series_{i}' for i in range(n_series)],
50-
index=dates)
51-
52-
return df
53-
54-
55-
def example_1_native_multioutput():
56-
"""Example 1: Using Ridge regression (native multioutput support)."""
57-
print("\n" + "="*60)
58-
print("EXAMPLE 1: Ridge Regression (Native Multioutput)")
59-
print("="*60)
60-
61-
# Create sample data
62-
df = create_sample_data(n_samples=150, n_series=3)
63-
print(f"Data shape: {df.shape}")
64-
print(f"Columns: {df.columns.tolist()}")
65-
66-
# Create Ridge regressor
67-
ridge = Ridge(alpha=1.0, random_state=42)
68-
69-
# Create MultiOutputMTS model
70-
model = ns.MultiOutputMTS(
71-
obj=ridge,
72-
lags=15,
73-
n_hidden_features=15,
74-
type_pi='kde',
75-
replications=200,
76-
kernel='gaussian',
77-
seed=42,
78-
verbose=1
79-
)
80-
81-
# Fit the model
82-
print("\nFitting model...")
83-
start_time = time.time()
84-
model.fit(df)
85-
fit_time = time.time() - start_time
86-
print(f"Fitting completed in {fit_time:.2f} seconds")
87-
88-
# Get multioutput info
89-
info = model.get_multioutput_info()
90-
print(f"\nMultioutput info:")
91-
for key, value in info.items():
92-
print(f" {key}: {value}")
93-
94-
# Make predictions
95-
print("\nMaking 30-day forecasts with 95% prediction intervals...")
96-
forecast = model.predict(h=30, level=95)
97-
98-
print(f"\nPoint forecasts shape: {forecast.mean.shape}")
99-
print("\nFirst 5 forecast periods:")
100-
print(forecast.mean.head())
101-
102-
print("\nPrediction intervals (first period):")
103-
for i, series in enumerate(df.columns):
104-
print(f" {series}: {forecast.lower.iloc[0, i]:.2f} < {forecast.mean.iloc[0, i]:.2f} < {forecast.upper.iloc[0, i]:.2f}")
105-
106-
return model, df, forecast
107-
108-
109-
def example_2_wrapped_non_multioutput():
110-
"""Example 2: Using RandomForest with MultiOutputRegressor wrapper."""
111-
print("\n" + "="*60)
112-
print("EXAMPLE 2: RandomForest with MultiOutputRegressor Wrapper")
113-
print("="*60)
114-
115-
df = create_sample_data(n_samples=100, n_series=2)
116-
117-
# Create RandomForest (not natively multioutput)
118-
rf = RandomForestRegressor(n_estimators=50, max_depth=5, random_state=42)
119-
120-
# Wrap it for multioutput support
121-
multi_rf = MultiOutputRegressor(rf)
122-
123-
# Create model
124-
model = ns.MultiOutputMTS(
125-
obj=multi_rf,
126-
lags=15,
127-
n_hidden_features=10,
128-
type_pi='gaussian',
129-
verbose=1
130-
)
131-
132-
print("Fitting model...")
133-
model.fit(df)
134-
135-
# Make predictions
136-
forecast = model.predict(h=20, level=90)
137-
138-
print(f"\nForecast shape: {forecast.mean.shape}")
139-
print("\nModel info:")
140-
info = model.get_multioutput_info()
141-
for key, value in info.items():
142-
print(f" {key}: {value}")
143-
144-
return model, df, forecast
145-
146-
147-
def example_3_performance_comparison():
148-
"""Example 3: Performance comparison between MTS and MultiOutputMTS."""
149-
print("\n" + "="*60)
150-
print("EXAMPLE 3: Performance Comparison")
151-
print("="*60)
152-
153-
# Create larger dataset for performance test
154-
df = create_sample_data(n_samples=500, n_series=5)
155-
156-
# Test with Ridge (native multioutput)
157-
ridge = Ridge(alpha=1.0)
158-
159-
# Regular MTS
160-
print("\nTesting Regular MTS (loop-based)...")
161-
start = time.time()
162-
model_regular = ns.MTS(
163-
obj=ridge,
164-
lags=15,
165-
n_hidden_features=10,
166-
type_pi='gaussian',
167-
verbose=0
168-
)
169-
model_regular.fit(df)
170-
regular_time = time.time() - start
171-
print(f"Regular MTS fitting time: {regular_time:.4f} seconds")
172-
173-
# MultiOutputMTS
174-
print("\nTesting MultiOutputMTS...")
175-
start = time.time()
176-
model_multi = ns.MultiOutputMTS(
177-
obj=ridge,
178-
lags=15,
179-
n_hidden_features=10,
180-
type_pi='gaussian',
181-
verbose=0
182-
)
183-
model_multi.fit(df)
184-
multi_time = time.time() - start
185-
print(f"MultiOutputMTS fitting time: {multi_time:.4f} seconds")
186-
187-
print(f"\nSpeedup: {regular_time/multi_time:.2f}x faster")
188-
189-
# Compare predictions
190-
forecast_regular = model_regular.predict(h=10)
191-
forecast_multi = model_multi.predict(h=10)
192-
193-
print("\nPrediction comparison (RMSE between methods):")
194-
for i, series in enumerate(df.columns):
195-
diff = np.sqrt(np.mean((forecast_regular.mean.iloc[:, i] - forecast_multi.mean.iloc[:, i])**2))
196-
print(f" {series}: {diff:.6f}")
197-
198-
return regular_time, multi_time
199-
200-
201-
def example_4_quantile_forecasts():
202-
"""Example 4: Quantile forecasts."""
203-
print("\n" + "="*60)
204-
print("EXAMPLE 4: Quantile Forecasts")
205-
print("="*60)
206-
207-
df = create_sample_data(n_samples=120, n_series=2)
208-
209-
# For quantile regression, we need a quantile-compatible model
210-
from sklearn.linear_model import LinearRegression
211-
212-
lr = LinearRegression()
213-
214-
model = ns.MultiOutputMTS(
215-
obj=lr,
216-
lags=15,
217-
n_hidden_features=12,
218-
type_pi='kde', # Using KDE for quantile simulation
219-
replications=500,
220-
verbose=1
221-
)
222-
223-
model.fit(df)
224-
225-
# Predict specific quantiles
226-
quantiles = [0.05, 0.25, 0.5, 0.75, 0.95]
227-
print(f"\nPredicting quantiles: {quantiles}")
228-
229-
quantile_forecast = model.predict(h=20, quantiles=quantiles)
230-
231-
print(f"\nQuantile forecast columns: {quantile_forecast.columns.tolist()}")
232-
print("\nFirst forecast period:")
233-
print(quantile_forecast.iloc[0].round(2))
234-
235-
return model, df, quantile_forecast
236-
237-
238-
def example_5_visualization(model, df, forecast):
239-
"""Example 5: Visualization of results."""
240-
print("\n" + "="*60)
241-
print("EXAMPLE 5: Visualization")
242-
print("="*60)
243-
244-
# Plot each series
245-
fig, axes = plt.subplots(len(df.columns), 1, figsize=(12, 4*len(df.columns)))
246-
if len(df.columns) == 1:
247-
axes = [axes]
248-
249-
for idx, (ax, series_name) in enumerate(zip(axes, df.columns)):
250-
# Historical data
251-
ax.plot(df.index, df[series_name], 'b-', label='Historical', linewidth=2)
252-
253-
# Forecast
254-
forecast_dates = forecast.mean.index
255-
ax.plot(forecast_dates, forecast.mean[series_name], 'r-', label='Forecast', linewidth=2)
256-
257-
# Prediction intervals
258-
ax.fill_between(
259-
forecast_dates,
260-
forecast.lower[series_name],
261-
forecast.upper[series_name],
262-
alpha=0.2, color='red', label=f'{model.level_}% Prediction Interval'
263-
)
264-
265-
# Add vertical line at forecast start
266-
ax.axvline(x=df.index[-1], color='gray', linestyle='--', alpha=0.5)
267-
268-
ax.set_title(f'{series_name} - 30-Day Forecast')
269-
ax.set_xlabel('Date')
270-
ax.set_ylabel('Value')
271-
ax.legend()
272-
ax.grid(True, alpha=0.3)
273-
274-
plt.tight_layout()
275-
plt.savefig('multioutput_mts_forecast.png', dpi=150, bbox_inches='tight')
276-
print("Plot saved as 'multioutput_mts_forecast.png'")
277-
plt.show()
278-
279-
# Spaghetti plot for first series if simulations available
280-
if hasattr(forecast, 'sims') and forecast.sims is not None:
281-
fig, ax = plt.subplots(figsize=(12, 6))
282-
283-
# Plot historical
284-
ax.plot(df.index, df.iloc[:, 0], 'b-', label='Historical', linewidth=2)
285-
286-
# Plot a sample of simulations
287-
n_sims_to_plot = min(20, len(forecast.sims))
288-
for i in range(n_sims_to_plot):
289-
ax.plot(forecast.mean.index, forecast.sims[i].iloc[:, 0],
290-
alpha=0.1, color='red')
291-
292-
# Plot mean forecast
293-
ax.plot(forecast.mean.index, forecast.mean.iloc[:, 0],
294-
'r-', label='Mean Forecast', linewidth=2)
295-
296-
ax.axvline(x=df.index[-1], color='gray', linestyle='--', alpha=0.5)
297-
ax.set_title(f'{df.columns[0]} - Spaghetti Plot ({n_sims_to_plot} simulations)')
298-
ax.set_xlabel('Date')
299-
ax.set_ylabel('Value')
300-
ax.legend()
301-
ax.grid(True, alpha=0.3)
302-
303-
plt.tight_layout()
304-
plt.savefig('multioutput_mts_spaghetti.png', dpi=150, bbox_inches='tight')
305-
print("Spaghetti plot saved as 'multioutput_mts_spaghetti.png'")
306-
plt.show()
307-
308-
309-
# ============================================================
310-
# MAIN EXECUTION
311-
# ============================================================
312-
if __name__ == "__main__":
313-
# Run examples and capture returns
314-
print("\nRunning MultiOutputMTS Examples...")
315-
print("="*60)
316-
317-
# Example 1: Ridge with KDE intervals
318-
model1, df1, forecast1 = example_1_native_multioutput()
319-
320-
# Example 2: RandomForest with Gaussian intervals
321-
model2, df2, forecast2 = example_2_wrapped_non_multioutput()
322-
323-
# Example 3: Performance comparison
324-
regular_time, multi_time = example_3_performance_comparison()
325-
326-
# Example 4: Quantile forecasts
327-
model4, df4, quantile_forecast = example_4_quantile_forecasts()
328-
329-
# Example 5: Visualization (using results from Example 1)
330-
example_5_visualization(model1, df1, forecast1)
331-
332-
print("\n" + "="*60)
333-
print("All examples completed successfully!")
334-
print("="*60)
335-
print(f"\nSummary:")
336-
print(f" - Example 1: Ridge regression with KDE intervals")
337-
print(f" - Example 2: RandomForest with Gaussian intervals")
338-
print(f" - Example 3: Speedup = {regular_time/multi_time:.2f}x")
339-
print(f" - Example 4: Quantile forecasts generated")
340-
print(f" - Example 5: Visualizations saved")
4+
from sklearn.linear_model import Ridge
5+
6+
# Create sample multivariate time series (3 series, 100 observations)
7+
np.random.seed(42)
8+
n_obs = 100
9+
data = {
10+
'sales': np.cumsum(np.random.randn(n_obs)) + 100,
11+
'revenue': np.cumsum(np.random.randn(n_obs)) + 500,
12+
'orders': np.cumsum(np.random.randn(n_obs)) + 50
13+
}
14+
df = pd.DataFrame(data)
15+
16+
print("Data shape:", df.shape)
17+
print("\nFirst 5 rows:")
18+
print(df.head())
19+
20+
# Fit MultivariateMTS with vectorized Ridge
21+
model = ns.MultiOutputMTS(
22+
obj=Ridge(alpha=1.0),
23+
lags=3,
24+
n_hidden_features=10,
25+
type_pi='bootstrap',
26+
replications=100,
27+
verbose=1,
28+
show_progress=False
29+
)
30+
31+
# Fit model
32+
model.fit(df)
33+
34+
# Predict 10 steps ahead with 95% prediction intervals
35+
forecast = model.predict(h=10, level=95)
36+
print("forecast:", forecast)
37+
print("\n" + "="*60)
38+
print("FORECAST RESULTS")
39+
print("="*60)
40+
print("\nMean predictions:")
41+
print(forecast.mean)
42+
print("\nLower bounds (95%):")
43+
print(forecast.lower)
44+
print("\nUpper bounds (95%):")
45+
print(forecast.upper)
46+
47+
# Predict specific quantiles
48+
quantile_forecast = model.predict(h=10, quantiles=[0.1, 0.5, 0.9])
49+
print("\n" + "="*60)
50+
print("QUANTILE PREDICTIONS")
51+
print("="*60)
52+
print(quantile_forecast)

0 commit comments

Comments
 (0)