এই পর্বে যা শিখবেন
- Trend, ঋতু, চক্র আর noise চেনা; additive আর multiplicative-এর তফাত; classical আর STL decomposition চালানো
- Differencing আর seasonal differencing করা, ACF/PACF পড়ে model-এর order আন্দাজ করা
- ADF আর KPSS-এর p-value ঠিকভাবে পড়া, আর KPSS-এর টেবিলের সীমা সামলানো
- AR(1) forecast আর Holt-এর একটি update হাতে করা; statsmodels-এ SARIMA আর Holt–Winters চালানো
- Naive আর seasonal naive baseline-এর বিপক্ষে MAE, RMSE, MAPE, sMAPE আর MASE মাপা, prediction interval আর Ljung–Box দিয়ে model যাচাই
সময়ের ডেটার হাতিয়ার আপনি আগেই চেনেন। AI-এর ভিত্তি কোর্সের পর্ব ২৬-এ DatetimeIndex, resample, rolling, shift আর diff দেখেছেন, আর পর্ব ৫৬-এ শিখেছেন, সময়ের ডেটা এলোমেলো করে ভাগ করা যায় না। এই কোর্সের পর্ব ২১-এ block bootstrap, পর্ব ২৪-এ prediction interval আর coverage, আর পর্ব ৪৬-এ periodic kernel দিয়ে সাপ্তাহিক অর্ডারের অনুমান এসেছে। এবার ধ্রুপদি পরিসংখ্যানের পথ: series-কে ভাগ করা, stationary বানানো, তারপর ARIMA আর exponential smoothing দিয়ে ভবিষ্যৎ বলা, আর সরল baseline-এর সামনে তা যাচাই করা। চারটি চালানো যায় এমন ব্লক পাতাতেই চলে (ব্রাউজারের Python, pandas, statsmodels আর matplotlib); প্রথমবার statsmodels নামতে প্রায় দশ সেকেন্ড লাগতে পারে।
১ · একটি series-এর চার উপাদান — trend, ঋতু, চক্র আর noise
পাড়ার চায়ের দোকানের হিসাব খাতা খুলুন। বছর ধরে বিক্রি ধীরে বাড়ছে, কারণ পাশে নতুন অফিস হয়েছে। প্রতি শুক্রবার বিক্রি কম, কারণ অফিস বন্ধ। রোজার মাসে দিনের বেলা প্রায় শূন্য, ইফতারের পরে ভিড়। আর কোনো দিন হঠাৎ বৃষ্টিতে বিক্রি পড়ে যায়। দোকানি এই চারটি জিনিস মাথায় আলাদা রাখেন বলেই কাল কত দুধ আনবেন, তা আন্দাজ করতে পারেন।
একটি time series হলো সময়ের ক্রমে মাপা একটি মানের ধারা: y1, y2, …, yT. সাধারণ tabular ডেটার সঙ্গে বড় পার্থক্য একটাই: সারিগুলো স্বাধীন নয়, আজকের মান গতকালের মানের সঙ্গে বাঁধা। Forecasting মানে এই বাঁধন কাজে লাগিয়ে yT+1, yT+2, …, yT+h অনুমান করা; h-কে বলে horizon. ধ্রুপদি দৃষ্টিতে series চারটি উপাদানে গড়া:
- Trend: দীর্ঘমেয়াদি দিক, যেমন দোকানের ধীরে বাড়া বিক্রি।
- Seasonality: নির্দিষ্ট দৈর্ঘ্যের পুনরাবৃত্তি, যেমন সপ্তাহে (দৈর্ঘ্য m = ৭) বা বছরে। দৈর্ঘ্য জানা এবং স্থির।
- Cycle (চক্র): ওঠানামা, কিন্তু দৈর্ঘ্য অনির্দিষ্ট, যেমন অর্থনীতির মন্দা আর তেজি। দৈনিক ডেটার দুই বছরে সাধারণত trend-এর ভেতরেই মিশে থাকে।
- Remainder বা noise: বাকি সব, যার কিছু অংশ এলোমেলো আর কিছু অংশ (যেমন ঈদের ধাক্কা) আসলে মডেলে না-ধরা ঘটনা।
Additive decomposition: series = trend + ঋতু + বাকি
চার সপ্তাহের কাল্পনিক দৈনিক অর্ডার; প্রতি সপ্তাহের পঞ্চম দিন (শুক্রবার) নিচে নামে
Additive না multiplicative? Additive মানে y = T + S + R: শুক্রবারে সবসময় প্রায় ৬০টি অর্ডার কম। Multiplicative মানে y = T × S × R: শুক্রবারে সবসময় প্রায় ১৫% কম, তাই দোকান বড় হলে খাদও গভীর হয়। ছবিতে ঋতুর ঢেউ যদি trend-এর সঙ্গে চওড়া হতে থাকে, তাহলে multiplicative. সহজ কৌশল: log নিলে log y = log T + log S + log R, অর্থাৎ multiplicative series additive হয়ে যায়। forecast করে শেষে exp নিয়ে ফেরত আসতে হয়, আর তখন গড়ের বদলে median পাওয়া যায়; এই সূক্ষ্ম তফাত বড় ভুলের মাপে গুরুত্বপূর্ণ হতে পারে।
২ · Decomposition — classical moving average আর STL
Classical decomposition (১৯২০-এর দশক থেকে প্রচলিত) তিন ধাপে চলে। (ক) m = ৭ দিনের কেন্দ্রিক চলমান গড় দিয়ে trend; এতে প্রথম আর শেষ তিনটি দিনের trend পাওয়া যায় না। (খ) y − T থেকে প্রতিটি বারের গড় নিয়ে ঋতু; ফলে সাত দিনের ছাঁদ দুই বছর ধরে হুবহু এক ধরা হয়। (গ) বাকিটা R. দুটি দুর্বলতা স্পষ্ট: ঋতু বদলাতে পারে না, আর একটি বড় outlier (ঈদের সপ্তাহ) চলমান গড়কে টেনে দেয়।
STL (Seasonal-Trend decomposition using Loess; Cleveland ও সহকর্মীরা, ১৯৯০) একই ভাগ করে loess নামের স্থানীয় মসৃণকরণ দিয়ে, বারবার পালা করে trend আর ঋতু হালনাগাদ করে। সুবিধা: ঋতু ধীরে বদলাতে পারে, ধারের দিনগুলোও বাদ যায় না, আর robust=True দিলে বড় residual-এর ওজন কমিয়ে outlier-এর টান কমায়। কত শক্ত ঋতু, তা মাপার একটি প্রচলিত সংখ্যা seasonal strength:
FS = max(০, ১ − Var(R) / Var(S + R))
R-এর variance যদি S + R-এর তুলনায় নগণ্য হয়, FS প্রায় ১: ঋতু প্রধান। আর R-ই সব হলে ০. একইভাবে trend strength FT = max(০, ১ − Var(R)/Var(T + R)). নিচের ডেটা বাজারঘরের (কাল্পনিক দোকান) দুই বছরের দৈনিক অর্ডার, seeded কোডে তৈরি: ধীরে বাড়া এলোমেলো স্তর, শুক্রবারে খাদওয়ালা সাপ্তাহিক ছাঁদ যা একটু একটু বদলায়, দুটি কাল্পনিক ঈদ-ধাক্কা (তারিখগুলো আসল ঈদের নয়), আর আগের দিনের সঙ্গে বাঁধা noise. Index-এ freq="D" বসানো আছে; statsmodels এটি না পেলে frequency নিয়ে warning দেয়।
# বাজারঘর (কাল্পনিক দোকান): দুই বছরের দৈনিক অর্ডার, seeded
import numpy as np
import pandas as pd
def daily_orders(seed=51, n=728):
rng = np.random.default_rng(seed)
idx = pd.date_range("2024-01-01", periods=n, freq="D") # freq = D
level = 300 + np.cumsum(0.25 + rng.normal(0, 4, n)) # ধীরে বাড়া, এলোমেলো
week = np.array([10, 15, 25, -5, -60, 5, 10.0]) # সোম..রবি; শুক্রবার কম
dow, season = idx.day_of_week, np.empty(n)
for i in range(n): # সাপ্তাহিক ছাঁদ ধীরে বদলায়
week[dow[i]] += rng.normal(0, 1)
season[i] = week[dow[i]]
eid = np.zeros(n) # কাল্পনিক ঈদ-ধাক্কা, দুবার
for start in (160, 510):
eid[start:start + 10] = 150 * np.hanning(10)
noise = np.zeros(n)
for i in range(1, n): # AR(1) noise, phi = 0.5
noise[i] = 0.5 * noise[i - 1] + rng.normal(0, 15)
return pd.Series(np.round(level + season + eid + noise),
index=idx, name="orders")শেষ ৫৬ দিন (৮ সপ্তাহ) শুরু থেকেই তালাবদ্ধ: decomposition, test আর model বাছাই সবই প্রথম ৬৭২ দিনে। নিচের ব্লক STL চালায়, দুটি strength আর প্রতিটি বারের গড় ঋতু-মান ছাপায়, classical পদ্ধতির trend-এ কয়টি দিন হারায় তা গোনে, আর চারটি অংশের ছবি আঁকে। ছবির লেখা ইংরেজিতে, কারণ পাতার matplotlib বাংলা অক্ষর ঠিকমতো আঁকে না।
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import STL, seasonal_decompose
from bazar_ts import daily_orders
y = daily_orders().iloc[:-56] # শুধু train অংশ
print(len(y), y.index.freqstr, y.index[0].date(), "->", y.index[-1].date())
stl = STL(y, period=7, robust=True).fit()
T, S, R = stl.trend, stl.seasonal, stl.resid
F_s = max(0, 1 - R.var() / (S + R).var()) # seasonal strength
F_t = max(0, 1 - R.var() / (T + R).var()) # trend strength
print(f"seasonal strength = {F_s:.3f}, trend strength = {F_t:.3f}")
prof = S.groupby(S.index.day_name().str[:3]).mean().round(1)
print(prof[["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]].to_dict())
cl = seasonal_decompose(y, model="additive", period=7)
print("classical trend NaN at ends:", int(cl.trend.isna().sum()))
print("biggest STL residuals:")
print(R.abs().nlargest(3).round(1).to_string())
fig, ax = plt.subplots(4, 1, figsize=(9, 7), sharex=True)
fig.subplots_adjust(hspace=0.3)
for a, (s, name) in zip(ax, [(y, "orders"), (T, "trend"),
(S, "weekly seasonal"), (R, "remainder")]):
a.plot(s.index, s.values, lw=0.8)
a.set_ylabel(name)
ax[0].set_title("STL decomposition of daily orders (period = 7)")
plt.show()আসল আউটপুট (ব্রাউজারের Python, statsmodels 0.14.6; ছবিটি ব্লকের নিচে আঁকা হয়):
672 D 2024-01-01 -> 2025-11-02
seasonal strength = 0.694, trend strength = 0.941
{'Mon': 8.2, 'Tue': 21.8, 'Wed': 27.3, 'Thu': -8.4, 'Fri': -53.0, 'Sat': -0.8, 'Sun': 4.8}
classical trend NaN at ends: 6
biggest STL residuals:
2024-06-14 136.4
2025-05-29 126.9
2025-05-30 116.7
Strength: seasonal strength ০.৬৯৪: সাপ্তাহিক ছাঁদ স্পষ্ট, কিন্তু noise-ও কম নয়। Trend strength ০.৯৪১: স্তরের ওঠানামা প্রধান। ঋতু-মান: শুক্রবারে গড়ে প্রায় ৫৩টি কম, বুধবারে প্রায় ২৭টি বেশি; ডেটা বানানোর সময়ের ছাঁদের (−৬০, +২৫) কাছাকাছি, কিন্তু হুবহু নয়, কারণ ছাঁদ নিজেই সময়ের সঙ্গে সরেছে। Classical: চলমান গড়ের জন্য দুই প্রান্তে মোট ৬টি দিনের trend নেই; forecast-এর ঠিক আগের দিনগুলোই হারায়, যা সবচেয়ে দরকারি। Residual: সবচেয়ে বড় তিনটি ২০২৪-০৬-১৪ আর ২০২৫-০৫-২৯/৩০, দুটি কাল্পনিক ঈদ-ধাক্কার ভেতরে। Decomposition এখানে একটি নির্ণয়ের যন্ত্র: “ঈদ” এমন ঘটনা যা trend বা সাপ্তাহিক ঋতু ব্যাখ্যা করে না, তাই আলাদা calendar feature লাগবে (পর্ব ৫২, ML দিয়ে forecasting — lag feature, backtesting আর অনেক series একসঙ্গে)।
৩ · Stationarity, differencing আর ADF/KPSS
ARIMA-জাতীয় model ধরে নেয় series stationary: গড়, variance আর “k দিন আগের সঙ্গে সম্পর্ক” সময়ের সঙ্গে বদলায় না। ওপরে-ওঠা অর্ডার stationary নয়: ২০২৪-এর গড় আর ২০২৫-এর গড় আলাদা। প্রতিকার differencing. Backshift operator B লিখলে Byt = yt−1, তাই
(১ − B)yt = yt − yt−1 আর (১ − B৭)yt = yt − yt−7
প্রথমটি “গতকালের চেয়ে কত বেশি”, যা স্থির ঢাল বা এলোমেলো হাঁটা (random walk) স্তর সরায়। দ্বিতীয়টি seasonal differencing: “গত সপ্তাহের একই বারের চেয়ে কত বেশি”, যা সাপ্তাহিক ছাঁদ সরায়। দুটো একসঙ্গে (১ − B)(১ − B৭)yt. প্রতিটি differencing একটি করে মান খায় (৭-এরটি ৭টি), আর প্রয়োজনের বেশি করলে over-differencing হয়: noise বাড়ে আর ACF-এর lag ১-এ বড় ঋণাত্মক মান আসে।
Differencing লাগবে কি না, তা যাচাইয়ের দুটি প্রচলিত test আছে, আর তাদের null hypothesis উল্টো:
ADF (augmented Dickey–Fuller)
H0: series-এ unit root আছে (random walk-এর মতো, stationary নয়)। ছোট p মানে H0 বাতিল: stationary-র পক্ষে প্রমাণ।
Dickey ও Fuller ১৯৭৯; augmented রূপ Said ও Dickey ১৯৮৪KPSS
H0: series (একটি স্থির গড়ের চারপাশে) stationary. ছোট p মানে H0 বাতিল: differencing লাগবে।
Kwiatkowski, Phillips, Schmidt ও Shin ১৯৯২দুটি একসঙ্গে চালানোর যুক্তি: ADF বলে “unit root বাতিল করতে পারিনি” মানে unit root প্রমাণিত নয়; test-টির power কম হতে পারে। KPSS উল্টো দিক থেকে প্রশ্ন করে। দুটি একমত হলে আত্মবিশ্বাস বাড়ে। একটি ব্যবহারিক ফাঁদ: statsmodels-এর kpss p-value আনে একটি ছোট টেবিল থেকে, যা শুধু ০.০১ থেকে ০.১০ পর্যন্ত। আসল p এর বাইরে হলে এটি সীমার মানটি ফেরত দেয় আর একটি InterpolationWarning দেয়। সঠিক সামলানো মানে warning চাপা দিয়ে সংখ্যাটিকে হুবহু মান না ভাবা, বরং “p > ০.১০” বা “p < ০.০১” হিসেবে পড়া। নিচের ব্লক ঠিক তা করে: শুধু ওই একটি warning-শ্রেণি একটি সীমিত context-এ বন্ধ রাখে আর সীমা চিহ্নসহ ছাপায়।
import warnings
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller, kpss, acf, pacf
from statsmodels.tools.sm_exceptions import InterpolationWarning
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from bazar_ts import daily_orders
y = daily_orders().iloc[:-56] # শেষ ৫৬ দিন তালাবদ্ধ
series = {"raw": y, "(1-B)": y.diff().dropna(),
"(1-B^7)": y.diff(7).dropna(), "both": y.diff(7).diff().dropna()}
def kpss_p(s): # টেবিল শুধু 0.01 থেকে 0.10
with warnings.catch_warnings():
warnings.simplefilter("ignore", InterpolationWarning)
stat, p, *_ = kpss(s, regression="c", nlags="auto")
sign = ">" if p >= 0.10 else ("<" if p <= 0.01 else "=")
return f"KPSS stat {stat:5.2f}, p {sign} {p:.2f}"
for name, s in series.items():
print(f"{name:8s} n={len(s)} ADF p = {adfuller(s, autolag='AIC')[1]:.3f} {kpss_p(s)}")
np.set_printoptions(precision=2, suppress=True)
d = series["both"]
for name, s in [("raw", y), ("both", d)]:
print(f"{name:5s} ACF 1-8:", acf(s, nlags=8)[1:])
print(f"{name:5s} PACF 1-8:", pacf(s, nlags=8)[1:])
fig, ax = plt.subplots(1, 2, figsize=(9, 3))
fig.subplots_adjust(bottom=0.18, wspace=0.25)
plot_acf(d, lags=21, ax=ax[0], title="ACF of (1-B)(1-B^7) y")
plot_pacf(d, lags=21, ax=ax[1], title="PACF of (1-B)(1-B^7) y")
plt.show()আসল আউটপুট (ব্রাউজারের Python, statsmodels 0.14.6):
raw n=672 ADF p = 0.921 KPSS stat 3.47, p < 0.01
(1-B) n=671 ADF p = 0.000 KPSS stat 0.06, p > 0.10
(1-B^7) n=665 ADF p = 0.000 KPSS stat 0.03, p > 0.10
both n=664 ADF p = 0.000 KPSS stat 0.01, p > 0.10
raw ACF 1-8: [0.88 0.79 0.78 0.77 0.75 0.82 0.89 0.81]
raw PACF 1-8: [ 0.88 0.07 0.3 0.1 0.11 0.49 0.44 -0.45]
both ACF 1-8: [-0.25 0.05 -0.08 0.03 -0.16 0.17 -0.44 0.07]
both PACF 1-8: [-0.25 -0.02 -0.08 -0.01 -0.16 0.09 -0.42 -0.18]
কাঁচা series: ADF p = ০.৯২১ (unit root বাতিল করা যায়নি), KPSS p < ০.০১ (stationarity বাতিল)। দুটি একমত: differencing লাগবে। Differencing-এর পরে: তিনটি রূপেই ADF p প্রায় ০ আর KPSS p > ০.১০; আবারও একমত। কী বলে না: p > ০.১০ মানে “stationary প্রমাণিত” নয়, শুধু এই test এই ডেটায় অমিল খুঁজে পায়নি। ADF-এর ০.৯২১ মানে “unit root থাকার সম্ভাবনা ৯২%” নয়; p-value কোনো hypothesis-এর সম্ভাবনা নয় (AI-এর ভিত্তি কোর্সের পর্ব ৩৭)। আর test সাপ্তাহিক ছাঁদ নিয়ে সরাসরি কিছু বলে না: (১ − B) একাই test পাস করে, অথচ তার ভেতরে সাপ্তাহিক ঢেউ রয়ে গেছে: একই ডেটায় আলাদা একটি desktop run-এ (১ − B)-এর ACF lag ৭-এ ০.৭১ আর lag ১৪-এ ০.৬৭ পাওয়া গেছে (পরের অংশে ACF পড়া)। কাঁচা series-এর ACF lag ৭-এ ০.৮৯, আশপাশের lag-এর চেয়ে উঁচু: সাপ্তাহিক ঋতুর চিহ্ন।
৪ · ACF আর PACF পড়া — AR, MA আর তাদের ছাপ
ACF (autocorrelation function) lag k-তে yt আর yt−k-এর correlation দেয়, অর্থাৎ shift(k) করা কলামের সঙ্গে autocorrelation. PACF (partial ACF) একই প্রশ্ন করে, কিন্তু মাঝের ১ থেকে k − ১ lag-এর প্রভাব সরিয়ে: “গতকাল জানার পরেও পরশু কি নতুন কিছু বলে?” ছবিতে নীল ছায়া মোটামুটি ±১.৯৬/√n-এর সীমা; তার বাইরের দাগ “শূন্য নয়” বলে সন্দেহজনক। দুটি মৌলিক model:
- AR(p) (autoregressive): yt = c + φ1yt−1 + … + φpyt−p + εt. আজকের মান আগের p দিনের মানের ওজনদার যোগফল আর নতুন ধাক্কা εt, যা white noise (গড় ০, স্থির variance, পরস্পর সম্পর্কহীন)।
- MA(q) (moving average model): yt = c + εt + θ1εt−1 + … + θqεt−q. আজকের মান আগের q দিনের ধাক্কার ওপর নির্ভর করে। নামে মিল থাকলেও এটি চলমান গড় (rolling mean) নয়।
| Model | ACF | PACF |
|---|---|---|
| AR(p) | ধীরে ক্ষয় (জ্যামিতিক বা ঢেউয়ের মতো) | lag p-এর পরে হঠাৎ প্রায় শূন্য |
| MA(q) | lag q-এর পরে হঠাৎ প্রায় শূন্য | ধীরে ক্ষয় |
| ARMA(p, q) | ধীরে ক্ষয় | ধীরে ক্ষয়; order পড়া কঠিন, AIC দিয়ে বাছাই |
| Seasonal (m = ৭) | lag ৭, ১৪, …-এ দাগ | lag ৭, ১৪, …-এ দাগ |
| Differencing দরকার | খুব ধীরে, প্রায় সরলরেখায় ক্ষয় | lag ১-এ প্রায় ১ |
ব্লক ২-এর কাঁচা series-এ ACF ০.৮৮, ০.৭৯, … খুব ধীরে নামে: differencing দরকারের ক্লাসিক ছাপ। দুবার differencing-এর পরে ACF lag ১-এ −০.২৫ আর lag ৭-এ −০.৪৪, বাকিগুলো ছোট; PACF lag ৭, ৮-এ −০.৪২, −০.১৮ নিয়ে ধীরে ক্ষয়। টেবিল মেলালে: lag ১-এ একটি non-seasonal MA আর lag ৭-এ একটি seasonal MA. এটি বিখ্যাত “airline model” (০,১,১)(০,১,১)৭-এর ছাপ। আমরা পরের অংশে তার চেয়ে একটু বড় (১,১,১)(১,১,১)৭ চালাব আর দেখব বাড়তি AR পদ কিছু যোগ করে কি না।
এবার AR(1) দিয়ে forecast হাতে করুন। Mean-কেন্দ্রিক রূপে yt − μ = φ(yt−1 − μ) + εt, |φ| < ১ হলে stationary. ভবিষ্যতের ধাক্কার গড় ০, তাই h ধাপ পরের অনুমান ŷT+h = μ + φh(yT − μ): বিচ্যুতি প্রতি ধাপে φ গুণ ছোট হয়ে গড়ে ফেরে। ভুলের variance σ²(১ + φ² + … + φ২(h−১)), যা বাড়তে বাড়তে σ²/(১ − φ²)-এ থামে।
| হিসাব | |
| অনুমান | |
| ভুলের SD | |
| 95% পরিসর |
- শুরুর অবস্থা। দীর্ঘমেয়াদি গড় ৪০০, শেষ দিন ৪৫০: গড়ের চেয়ে ৫০ বেশি। φ = ০.৬ বলে আজকের বিচ্যুতির ৬০% কাল পর্যন্ত টিকে থাকে।
- এক ধাপ। ŷ = ৪০০ + ০.৬ × ৫০ = ৪৩০. অনিশ্চয়তা শুধু একটি নতুন ধাক্কার, SD ২০; ৯৫% পরিসর ৪৩০ ± ১.৯৬ × ২০ = [৩৯০.৮, ৪৬৯.২].
- দুই ধাপ। বিচ্যুতি ০.৬² × ৫০ = ১৮, ŷ = ৪১৮. এখন দুটি ধাক্কা জমে: variance ২০² × (১ + ০.৩৬), SD ২৩.৩২, পরিসর [৩৭২.৩, ৪৬৩.৭].
- তিন ধাপ। বিচ্যুতি ০.২১৬ × ৫০ = ১০.৮, ŷ = ৪১০.৮. SD ২৪.৪১, পরিসর [৩৬৩.০, ৪৫৮.৬]. আরও এগোলে অনুমান ৪০০-এ আর SD ২০/√০.৬৪ = ২৫-এ থামবে: দূর ভবিষ্যতে AR(1) শুধু “গড়ের কাছে, ±৪৯” বলে।
৫ · ARIMA আর SARIMA — Box–Jenkins-এর পথ
ARIMA(p, d, q) মানে: d বার differencing করার পরে series-টি ARMA(p, q). SARIMA (p, d, q)(P, D, Q)m তার সঙ্গে ঋতুর স্তরে আরেক প্রস্থ যোগ করে: D বার seasonal differencing আর lag m, ২m, …-এ P টি AR ও Q টি MA পদ। Backshift-এ লিখলে (p = P = q = Q = ১, d = D = ১, m = ৭):
(১ − φB)(১ − ΦB৭)(১ − B)(১ − B৭)yt = (১ + θB)(১ + ΘB৭)εt
φ, Φ হলো non-seasonal আর seasonal AR coefficient, θ, Θ হলো MA coefficient, εt white noise যার variance σ². দুই পাশের গুণফল খুললে আজকের অর্ডার গতকাল, গত সপ্তাহ আর গত সপ্তাহের আগের দিনের মান ও ধাক্কার একটি রৈখিক যোগফল হয়। Fit হয় maximum likelihood দিয়ে (পর্ব ১৮): statsmodels-এর SARIMAX model-টিকে একটি state space রূপে লেখে, আর Kalman filter দিয়ে প্রতিটি দিনের এক-ধাপ অনুমানের ভুল থেকে likelihood গোনে। X অক্ষরটি exogenous (বাইরের) চলকের জন্য, যেমন ঈদের দিন চিহ্নিত একটি কলাম।
George Box আর Gwilym Jenkins-এর ১৯৭০ সালের বই Time Series Analysis: Forecasting and Control একটি চক্র প্রচলিত করে, যা আজও মূল কাঠামো: চেনা (plot, differencing, ACF/PACF) → অনুমান (MLE) → যাচাই (residual white noise কি না) → দরকারে আবার চেনা। কয়েকটি প্রার্থী model-এর মধ্যে AIC দিয়ে বাছাই করা যায়, তবে শুধু একই d আর D-এর model-এর মধ্যে, কারণ differencing বদলালে likelihood ভিন্ন ডেটার ওপর গোনা হয়। চূড়ান্ত বিচার holdout-এ (অংশ ৮)।
MA coefficient −১-এর খুব কাছে (যেমন θ ≈ −০.৯৫) হলে সতর্ক হোন। (১ − B) আর (১ + θB) প্রায় কেটে যায়, অর্থাৎ ডেটা হয়তো differencing চায়নি (over-differencing), বা trend প্রায় স্থির। তখন model invertible-এর সীমায় থাকে, আর prediction interval সংকীর্ণ বা অদ্ভুত হতে পারে। বিকল্প চেষ্টা করুন: differencing কমিয়ে একটি স্থির ঢাল (drift) রাখা।
৬ · Exponential smoothing — simple, Holt আর Holt–Winters
আবহাওয়ার পূর্বাভাস দেখে প্রতিদিন ছাতা নেবেন কি না ঠিক করেন। গতকালের অভিজ্ঞতা বেশি গুরুত্ব পায়, গত সপ্তাহের কম, গত মাসের প্রায় কিছুই না। Exponential smoothing ঠিক এই নিয়মে চলে: পুরোনো পর্যবেক্ষণের ওজন জ্যামিতিক হারে কমে।
Simple exponential smoothing (Robert Brown-এর ১৯৫০-এর দশকের কাজে প্রচলিত) একটি স্তর ℓ রাখে: ℓt = αyt + (১ − α)ℓt−1, ০ < α ≤ ১. খুললে ওজন α, α(১ − α), α(১ − α)², …; α বড় মানে নতুন ডেটায় দ্রুত সাড়া। সব horizon-এর অনুমান একই ℓT. Holt-এর পদ্ধতি (Charles Holt, ১৯৫৭) একটি ঢাল b যোগ করে:
ℓt = αyt + (১ − α)(ℓt−1 + bt−1)
bt = β(ℓt − ℓt−1) + (১ − β)bt−1
ŷt+h = ℓt + h·bt
নতুন স্তর হলো “আজ যা দেখলাম” আর “কাল যা ভেবেছিলাম” (ℓt−1 + bt−1)-এর ওজনদার গড়। নতুন ঢাল হলো “স্তর আজ কতটা সরল” আর পুরোনো ঢালের গড়। Holt–Winters (Peter Winters, ১৯৬০) তৃতীয় সমীকরণে ঋতু-মান s যোগ করে, γ ওজন দিয়ে; additive রূপে স্তরের সমীকরণে yt-এর জায়গায় yt − st−m বসে।
| ধাপ | |
| হিসাব | |
| সমতুল্য রূপ | |
| কালকের অনুমান |
- আগের অনুমান। গতকালের স্তর ৪০০ আর ঢাল দিনে ২, তাই আজকের জন্য অনুমান ছিল ৪০২.
- ভুল। আসল অর্ডার ৪১০: অনুমানের চেয়ে ৮ বেশি। Holt-এর পুরো update এই একটি সংখ্যা দিয়ে লেখা যায় (error-correction রূপ)।
- স্তর। α = ০.৫, তাই নতুন স্তর ৪১০ আর ৪০২-এর ঠিক মাঝে, ৪০৬. সমতুল্যভাবে, অনুমান + α × ভুল = ৪০২ + ৪.
- ঢাল আর সামনে। স্তর আজ ৬ সরেছে; β = ০.২ তার ২০% নেয় আর পুরোনো ঢালের ৮০%: নতুন ঢাল ২.৮. কালকের অনুমান ৪০৮.৮, তিন দিন পরের ৪১৪.৪. একটি ধনাত্মক চমক স্তর আর ঢাল দুটোকেই একটু ওপরে ঠেলেছে।
ETS আর পরিসর। মূল Holt–Winters শুধু বিন্দু-অনুমান দেয়। Rob Hyndman ও সহকর্মীরা (২০০২; বই ২০০৮) একে ETS (Error, Trend, Seasonal) state space model হিসেবে লেখেন, যাতে likelihood, AIC আর prediction interval পাওয়া যায়। statsmodels-এ দুটি পথ: ExponentialSmoothing (ধ্রুপদি, parameter বাছে বর্গ-ভুল কমিয়ে) আর ETSModel (state space, likelihood দিয়ে)। আমরা প্রথমটি চালিয়ে তার simulate দিয়ে ১০০০টি সম্ভাব্য ভবিষ্যৎ টেনে পরিসর বের করব। একটি সুন্দর সম্পর্ক: simple exponential smoothing হুবহু ARIMA(০,১,১)-এর forecast দেয়, θ = α − ১ হলে; additive Holt একটি ARIMA(০,২,২)-এর সমান। দুই পরিবার আলাদা নয়, আংশিকভাবে একই model-এর দুই রূপ।
৭ · Baseline আর metric — হারানোর মতো কিছু চাই
একটি forecast ভালো কি না, তার একমাত্র সৎ প্রশ্ন: “সরল নিয়মের চেয়ে ভালো কি না?” দুটি baseline প্রায় বিনা খরচে পাওয়া যায়। Naive: সব ভবিষ্যৎ দিন = শেষ দেখা মান। Seasonal naive: প্রতিটি দিন = শেষ সপ্তাহের একই বার। যাচাই হয় সময় ধরে: শেষ অংশ holdout, আর model শুধু আগের অংশ দেখে (time-based split). একাধিক কাটার বিন্দুতে বারবার এই কাজ (rolling origin backtest) পর্ব ৫২-এর বিষয়।
ভুল et = yt − ŷt ধরে চারটি মাপ। MAE = গড় |e|, অর্ডারের এককে। RMSE = √(গড় e²), বড় ভুলে বেশি শাস্তি। MAPE = গড় |e/y| × ১০০% (AI-এর ভিত্তি কোর্সের পর্ব ৬০): এককহীন, কিন্তু y শূন্যের কাছে হলে বিস্ফোরিত হয়, আর বেশি-অনুমান আর কম-অনুমানকে সমান শাস্তি দেয় না। sMAPE = গড় ২|e|/(|y| + |ŷ|) × ১০০% সেই সমস্যা কমাতে আনা, কিন্তু নামের “symmetric” পুরোপুরি সত্য নয়। Rob Hyndman আর Anne Koehler ২০০৬ সালে MASE প্রস্তাব করেন:
MASE = MAEholdout / ( (১/(T − m)) ∑t=m+1..T |yt − yt−m| )
হর হলো training ডেটায় এক-ধাপ (seasonal) naive-এর গড় পরম ভুল; m = ১ হলে সাধারণ naive. তাই MASE < ১ মানে আপনার forecast গড়ে training-এর ওই naive ভুলের চেয়ে ছোট ভুল করছে। এটি এককহীন, শূন্য-অর্ডারের দিনেও চলে, আর ভিন্ন series-এর মধ্যে তুলনা করা যায়। লেখকেরা একে সাধারণ ব্যবহারের জন্য সুপারিশ করেছিলেন; তবু কোনো একটি metric সব ব্যবসার প্রশ্নের উত্তর নয়, যেমন স্টক ফুরিয়ে যাওয়ার খরচ বেশি হলে quantile loss (পর্ব ২৪) বেশি মানানসই।
| ধাপ | |
| হিসাব |
- হর। Training-এর পরপর দুই দিনের পার্থক্য: ৪, −৬, ৮, ৪, −২; পরম মানের গড় ৪.৮. মানে “গতকালের মান দিয়ে আজ বললে” training-এ গড়ে ৪.৮ ভুল হতো।
- Model-এর ভুল। পাঁচ দিনে ভুল ২, −৩, ৩, −৩, ৪. চিহ্ন বদলায়, তাই পদ্ধতিগত পক্ষপাত কম মনে হয়।
- MASE. MAE ৩.০, ভাগ ৪.৮: MASE ০.৬২৫. একই ডেটায় MAPE প্রায় ২.৬৫%, যা ছোট দেখায়, কিন্তু তুলনার মাপকাঠি বলে না।
- Naive-এর সঙ্গে। শেষ মান ১০৮ সব দিনে বললে MAE ৫.০, MASE ১.০৪. Holdout-এ naive নিজের training-এর ভুলের চেয়েও একটু খারাপ করল, কারণ series ওপরে উঠছে। Model এই baseline-কে স্পষ্টভাবে হারিয়েছে।
৮ · মুখোমুখি — SARIMA বনাম Holt–Winters বনাম baseline
এবার পুরো পরীক্ষা। প্রথম ৬৭২ দিনে SARIMA(১,১,১)(১,১,১)৭ আর additive Holt–Winters (trend আর ঋতু দুটোই additive) fit করা হয়, তারপর ৫৬ দিন সামনে অনুমান, প্রতিটির সঙ্গে ৯৫% prediction interval. SARIMA-র পরিসর আসে model-এর সূত্র থেকে; Holt–Winters-এর পরিসর ১০০০টি simulated ভবিষ্যতের ২.৫ আর ৯৭.৫ percentile থেকে। চারটি metric নিজে লেখা, কোনো library ছাড়া। পরের ব্লক একই namespace-এ চলে (data-shared), তাই model আবার fit হয় না।
import numpy as np
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from bazar_ts import daily_orders
y = daily_orders()
train, test = y.iloc[:-56], y.iloc[-56:] # সময় ধরে ভাগ, শেষ ৫৬ দিন
h, m, a = len(test), 7, test.values
sar = SARIMAX(train, order=(1, 1, 1), seasonal_order=(1, 1, 1, 7)).fit(disp=False)
sar_fc = sar.get_forecast(h)
sar_lo, sar_hi = sar_fc.conf_int(alpha=0.05).values.T
hw = ExponentialSmoothing(train, trend="add", seasonal="add",
seasonal_periods=m).fit()
sims = hw.simulate(h, repetitions=1000, error="add", anchor="end",
random_state=51) # ১০০০টি সম্ভাব্য ভবিষ্যৎ
hw_lo, hw_hi = sims.quantile([0.025, 0.975], axis=1).values
print({k: round(float(v), 3) for k, v in sar.params.items()})
print({k: round(float(v), 3) for k, v in hw.params.items() if "smoothing" in k})
preds = {"naive": np.repeat(train.iloc[-1], h),
"seasonal naive": np.tile(train.values[-m:], h // m),
"SARIMA": sar_fc.predicted_mean.values, "Holt-Winters": hw.forecast(h).values}
scale = np.mean(np.abs(train.values[m:] - train.values[:-m])) # MASE-এর হর
rows = {}
for k, p in preds.items():
e = a - p
rows[k] = {"MAE": np.mean(np.abs(e)), "RMSE": np.sqrt(np.mean(e ** 2)),
"MAPE%": 100 * np.mean(np.abs(e) / a),
"sMAPE%": 200 * np.mean(np.abs(e) / (np.abs(a) + np.abs(p))),
"MASE": np.mean(np.abs(e)) / scale}
print(f"MASE scale (train seasonal-naive MAE) = {scale:.2f}")
print(pd.DataFrame(rows).T.round(2).to_string())
for k, lo, hi in [("SARIMA", sar_lo, sar_hi), ("HW", hw_lo, hw_hi)]:
print(f"{k}: 95% coverage {np.mean((a >= lo) & (a <= hi)):.2f}, "
f"width day 1 = {hi[0] - lo[0]:.0f}, day 56 = {hi[-1] - lo[-1]:.0f}")আসল আউটপুট (ব্রাউজারের Python, statsmodels 0.14.6; এক run, একটি holdout):
{'ar.L1': 0.612, 'ma.L1': -0.924, 'ar.S.L7': 0.073, 'ma.S.L7': -0.943, 'sigma2': 351.142}
{'smoothing_level': 0.686, 'smoothing_trend': 0.0, 'smoothing_seasonal': 0.0}
MASE scale (train seasonal-naive MAE) = 22.84
MAE RMSE MAPE% sMAPE% MASE
naive 29.62 34.49 5.77 5.86 1.30
seasonal naive 24.52 29.02 4.75 4.84 1.07
SARIMA 13.37 16.35 2.61 2.61 0.59
Holt-Winters 17.54 21.78 3.37 3.45 0.77
SARIMA: 95% coverage 1.00, width day 1 = 73, day 56 = 179
HW: 95% coverage 1.00, width day 1 = 78, day 56 = 406
Parameter: SARIMA-র θ = −০.৯২৪ আর Θ = −০.৯৪৩, দুটিই −১-এর কাছে (অংশ ৫-এর সতর্কতা): স্তর আর ঋতু ধীরে বদলায়, তাই differencing প্রায় “বেশি” হয়ে গেছে। AR পদ φ = ০.৬১২ noise-এর আগের দিনের সঙ্গে বাঁধন ধরেছে; Φ = ০.০৭৩ প্রায় শূন্য, অর্থাৎ seasonal AR প্রায় কিছু যোগ করেনি, যা ACF-এর পড়ার সঙ্গে মেলে। Holt–Winters-এর α = ০.৬৮৬, কিন্তু β আর γ ঠিক ০: optimizer ঢাল আর সাপ্তাহিক ছাঁদকে পুরো স্থির ধরেছে। Metric: naive সবচেয়ে খারাপ (MASE ১.৩০), seasonal naive ১.০৭, Holt–Winters ০.৭৭, SARIMA ০.৫৯. পাঁচটি metric-এই ক্রম এক। SARIMA এগিয়ে, সম্ভবত কারণ তার AR পদ noise-এর বাঁধন ধরে, যা Holt–Winters-এ নেই। পরিসর: দুটির coverage ১.০০, অর্থাৎ ৫৬টি দিনই পরিসরের ভেতরে; কিন্তু Holt–Winters-এর পরিসর ৫৬তম দিনে ৪০৬ চওড়া, SARIMA-র ১৭৯. একটি holdout আর ৫৬টি বিন্দু থেকে “কোনটি সবসময় ভালো” বলা যায় না; কয়েকটি কাটার বিন্দুতে backtest লাগে (পর্ব ৫২)।
ছবিতে পরিসর দেখা আর residual যাচাই। Model ঠিক হলে residual white noise হওয়ার কথা। Ljung–Box test (Greta Ljung আর George Box, ১৯৭৮) প্রথম k টি residual autocorrelation একসঙ্গে পরীক্ষা করে: Q = n(n + ২)∑ rj²/(n − j), H0: সব শূন্য। ARMA-র residual-এ মুক্তির মাত্রা (degrees of freedom) থেকে আনুমানিক parameter-সংখ্যা বাদ যায়, তাই SARIMA-তে model_df=4. SARIMA-র প্রথম ৮টি residual (d + D·m = ১ + ৭) differencing-এর শুরুর অবস্থার কারণে অর্থহীনভাবে বড়, তাই বাদ।
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.stats.diagnostic import acorr_ljungbox
from statsmodels.tsa.stattools import acf
fig, ax = plt.subplots(figsize=(9, 3.6))
fig.subplots_adjust(bottom=0.15)
ax.plot(train.index[-84:], train.values[-84:], color="gray", lw=1, label="train")
ax.plot(test.index, a, color="black", lw=1.3, label="actual (holdout)")
for k, lo, hi, c in [("SARIMA", sar_lo, sar_hi, "C0"),
("Holt-Winters", hw_lo, hw_hi, "C1")]:
ax.plot(test.index, preds[k], color=c, lw=1.3, label=k)
ax.fill_between(test.index, lo, hi, color=c, alpha=0.15)
ax.set_title("56-day holdout: forecasts and 95% prediction intervals")
ax.set_ylabel("orders per day")
ax.legend(loc="upper left", fontsize=8)
plt.show()
r_sar = sar.resid.iloc[8:] # প্রথম d + D*s = 8টি বাদ
for name, r, df in [("SARIMA", r_sar, 4), ("Holt-Winters", hw.resid, 0)]:
p = acorr_ljungbox(r, lags=[7, 14], model_df=df)["lb_pvalue"]
print(f"{name:12s} Ljung-Box p (lag 7, 14) = {p.round(4).tolist()}")
print(f"{'':12s} residual ACF 1-7 = {acf(r, nlags=7)[1:].round(2)}")
big = r_sar.abs().nlargest(4)
print("largest SARIMA residuals:", [f"{d.date()} {v:.0f}" for d, v in big.items()])আসল আউটপুট (ব্রাউজারের Python, statsmodels 0.14.6; ছবিটি ব্লকের নিচে):
SARIMA Ljung-Box p (lag 7, 14) = [0.0019, 0.0473]
residual ACF 1-7 = [-0.05 0.08 -0.01 0.01 -0.11 0.03 0. ]
Holt-Winters Ljung-Box p (lag 7, 14) = [0.0, 0.0]
residual ACF 1-7 = [ 0.04 -0.05 -0.15 -0.13 -0.18 -0.01 0.1 ]
largest SARIMA residuals: ['2025-05-29 78', '2024-06-14 70', '2024-06-11 65', '2024-10-17 62']
ছবি: দুটি অনুমানই শুক্রবারের খাদসহ সাপ্তাহিক ঢেউ সামনে টেনে নেয়, আর দুটি ছায়াই দিন যত দূরে তত চওড়া; Holt–Winters-এর ছায়া অনেক দ্রুত ফাঁপে, কারণ তার simulation-এ α = ০.৬৮৬-এর স্তর প্রতিদিনের ধাক্কা জমাতে থাকে। SARIMA-র residual: lag ৭-এ p = ০.০০১৯, lag ১৪-এ ০.০৪৭৩: white noise-এর H0 বাতিল, অর্থাৎ কিছু গঠন বাকি। কিন্তু autocorrelation-গুলো ছোট (সবচেয়ে বড় lag ৫-এ −০.১১); ৬৬৪টি residual-এ test এমন ছোট গঠনও ধরে ফেলে। Holt–Winters: p প্রায় ০, আর lag ৩–৫-এ −০.১৩ থেকে −০.১৮: noise-এর বাঁধন model ধরেনি। সবচেয়ে বড় residual: চারটির তিনটি কাল্পনিক ঈদ-ধাক্কার ভেতরে। সিদ্ধান্ত: SARIMA ব্যবহারযোগ্য, কিন্তু নিখুঁত নয়; পরের চেষ্টা ঈদের জন্য একটি exogenous কলাম আর অন্য order, প্রতিটি বদল আবার holdout-এ যাচাই করে। Test-এর p-value দেখে বারবার model বদলালে নিজেও একধরনের overfitting হয়, তাই ছোট আর ব্যাখ্যাযোগ্য বদলই ভালো।
আরও গভীরে: Hyndman আর Athanasopoulos-এর বিনামূল্যের অনলাইন বই Forecasting: Principles and Practice (তৃতীয় সংস্করণ R-ভিত্তিক; একটি Python সংস্করণও প্রকাশিত হয়েছে বলে জানা যায়) এই পর্বের প্রায় সব বিষয় গভীরে দেখায়, seasonal strength-সহ। ETS-এর তত্ত্ব Hyndman, Koehler, Ord ও Snyder-এর ২০০৮ সালের বই Forecasting with Exponential Smoothing: The State Space Approach-এ। সরকারি পরিসংখ্যানে ঋতু-সমন্বয়ের মান US Census Bureau-র X-13ARIMA-SEATS, যা statsmodels বাইরের প্রোগ্রাম হিসেবে ডাকতে পারে। Hyndman ও Khandakar-এর ২০০৮ সালের auto.arima algorithm (KPSS দিয়ে d, তারপর AIC দিয়ে order) Python-এ pmdarima আর statsforecast-এ রূপ পেয়েছে।
পরীক্ষা ও ইন্টারভিউতে: (১) ADF আর KPSS-এর null? ADF: unit root আছে; KPSS: stationary. (২) ACF lag q-এর পরে কাটে, PACF ধীরে নামে? MA(q). (৩) AR(1), μ = ৫০, φ = ০.৫, yT = ৭০; দুই ধাপ পরের অনুমান? ৫০ + ০.২৫ × ২০ = ৫৫. (৪) MASE = ১.২ মানে? Holdout-এর MAE training-এর এক-ধাপ (seasonal) naive MAE-এর ১.২ গুণ: সাধারণত baseline হারায়নি। (৫) Ljung–Box-এর ছোট p? Residual-এ autocorrelation বাকি; model-এ আরও গঠন ধরার সুযোগ আছে।
৯ · AI-এর যুগে — forecasting কোড যাচাই
“আমার বিক্রির ডেটায় ARIMA চালিয়ে আগামী মাসের forecast দাও” বললে AI assistant কয়েক সেকেন্ডে কোড আর একটি সুন্দর ছবি দেয়। এই বিষয়ের ভুলগুলো প্রায়ই ছবিতে ধরা পড়ে না।
| AI প্রায়ই যা করে | কেন সমস্যা | কীভাবে ধরবেন |
|---|---|---|
পুরোনো statsmodels.tsa.arima_model.ARIMA ডাকে | এটি ০.১৩ সংস্করণে সরানো হয়েছে; ImportError | statsmodels.tsa.arima.model.ARIMA বা SARIMAX; নিজের সংস্করণের docs দেখুন |
train_test_split(shuffle=True) বা সাধারণ k-fold | ভবিষ্যৎ দেখে অতীত বলা: leakage, ফল অবাস্তব ভালো | সময় ধরে holdout; TimeSeriesSplit |
সব warning চাপা দেয় (warnings.filterwarnings("ignore")) | convergence বা frequency সমস্যা লুকায়; KPSS-এর সীমা-মান আসল p ভাবা হয় | শুধু নির্দিষ্ট warning, কারণ বুঝে; freq বসান |
| শুধু in-sample fit বা AIC দেখিয়ে “model ভালো” বলে | training-এ ভালো মানে ভবিষ্যতে ভালো নয় | holdout-এ MASE, seasonal naive-এর সঙ্গে |
| শূন্য-বিক্রির দিনে MAPE | ভাগ শূন্যে: inf বা বিশাল সংখ্যা | MASE বা MAE; প্রয়োজনে weighted MAPE |
| “p > ০.০৫, তাই stationary প্রমাণিত” লেখে | প্রমাণের অভাব, প্রমাণ নয়; ADF-এর power কম হতে পারে | ADF আর KPSS দুটিই, সঙ্গে ছবি আর ACF |
AI-কে কোডের সঙ্গে যুক্তি আর যাচাইও চাইতে বলুন:
AI-এর দেওয়া forecasting কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Forecasting প্রতিযোগিতায় সরল পদ্ধতি বারবার জটিল পদ্ধতির সঙ্গে পাল্লা দিয়েছে। এর কারণ কী, আর তা থেকে কী শেখার আছে?
১৯৭০-এর দশকে Box–Jenkins পদ্ধতি পরিসংখ্যানের মর্যাদা পায়: তত্ত্বে শক্ত, ধাপগুলো সুনির্দিষ্ট। Spyros Makridakis আর Michèle Hibon ১৯৭৯ সালে Journal of the Royal Statistical Society-তে একটি তুলনা প্রকাশ করেন, যেখানে অনেক বাস্তব series-এ সরল পদ্ধতি (যেমন exponential smoothing) গড়ে ARIMA-র চেয়ে খারাপ করেনি। আলোচনায় তীব্র আপত্তি ওঠে; অনেকে বলেন পদ্ধতিটি ঠিকভাবে প্রয়োগ হয়নি। এর জবাবে শুরু হয় খোলা প্রতিযোগিতা: M-competition (১৯৮২), M3 (ফল ২০০০ সালে প্রকাশিত, ৩০০৩টি series), M4 (২০১৮, এক লাখ series).
মোটামুটি সারকথা, যা প্রতিযোগিতার লেখকেরা নিজেরাই টেনেছেন: পরিসংখ্যানে জটিল model নতুন ডেটায় নিশ্চিতভাবে বেশি নির্ভুল নয়; কয়েকটি পদ্ধতির গড় (combination) প্রায়ই একক সেরা পদ্ধতির চেয়ে ভালো; আর ফল horizon ও metric-এর ওপর নির্ভর করে। M4-এ বিজয়ী হিসেবে প্রায়ই Slawek Smyl-এর exponential smoothing আর neural network-এর মিশ্রণের কথা বলা হয়, যা দেখায় সরল কাঠামো আর শেখা অংশ একসঙ্গে কাজ করতে পারে।
কারণ কী? প্রতিটি parameter অনুমানে ভুল থাকে, আর ছোট series-এ বেশি parameter মানে বেশি variance (bias–variance-এর পুরোনো কথা)। বাস্তব series-এ কাঠামোগত বদল ঘটে (নতুন প্রতিযোগী, মহামারি), যেখানে নিখুঁতভাবে মেলানো model বেশি ভুল করে; সরল model দ্রুত মানিয়ে নেয়। আর in-sample fit দিয়ে model বাছলে overfitting প্রায় অবধারিত।
শিক্ষাটি এই পর্বের অংশ ৭-এ লেখা: baseline সবসময় রাখুন, সময় ধরে holdout-এ মাপুন, আর জটিলতার দাম হিসেবে স্পষ্ট লাভ চান। এই পর্বের ডেটায় SARIMA জিতেছে, কিন্তু একটি series আর একটি holdout থেকে সাধারণ নিয়ম টানা যায় না।
মূল উপলব্ধি: Forecasting-এ জটিলতা নিজে গুণ নয়; প্রতিটি বাড়তি parameter-কে holdout-এ সরল baseline আর combination-এর বিপক্ষে নিজের জায়গা প্রমাণ করতে হয়।
প্র ০২ADF-এর p-value ০.৯২ মানে কী, আর কী নয়? Unit root test-এর ওপর কতটা ভরসা করা যায়?
ADF-এর null hypothesis: series-এ unit root আছে। p = ০.৯২ মানে, unit root সত্য হলে এমন বা আরও চরম test statistic পাওয়া খুবই সাধারণ ঘটনা। তাই H0 বাতিলের কোনো যুক্তি নেই। এর অর্থ এই নয় যে unit root থাকার সম্ভাবনা ৯২%; p-value hypothesis-এর সম্ভাবনা দেয় না। আবার “বাতিল করা গেল না” মানে “সত্য প্রমাণিত” নয়।
ভরসার সীমা কয়েকটি কারণে। প্রথম, power: φ = ০.৯৮-এর মতো প্রায়-unit-root series stationary হলেও কয়েকশো বিন্দুতে ADF তাকে প্রায়ই unit root থেকে আলাদা করতে পারে না। দ্বিতীয়, কাঠামোগত ভাঙন: Pierre Perron ১৯৮৯ সালে Econometrica-তে দেখান, একটি বড় একবারের লাফ (যেমন নীতি বদল) থাকলে stationary series-কেও ADF unit root বলে ভুল করতে পারে। ঈদের মতো ঘটনা ছোট আকারে একই ধরনের গোলমাল আনে। তৃতীয়, specification: test-এ ধ্রুবক আর trend রাখা হবে কি না, কয়টি lag নেওয়া হবে, এসব পছন্দে ফল বদলায়।
এ কারণেই KPSS আনা হয়েছিল, যার null উল্টো। দুটি মিলিয়ে চারটি অবস্থা: দুটি একমত হলে সিদ্ধান্ত সহজ; ADF বাতিল করে আর KPSS-ও বাতিল করে, তাহলে series হয়তো আংশিক স্মৃতির (long memory) বা ভাঙনওয়ালা; কোনোটিই বাতিল না করলে ডেটা যথেষ্ট তথ্য দিচ্ছে না।
ব্যবহারিক দৃষ্টিতে test একটি সহায়ক মাত্র। ছবি, ACF-এর ক্ষয়ের ধরন, ব্যবসার জ্ঞান (“অর্ডার কি সত্যিই একটি স্থির গড়ে ফেরে?”) আর শেষে holdout-এ forecast-এর মান: কোন differencing ভালো, তার আসল উত্তর সেখানেই।
মূল উপলব্ধি: Unit root test প্রমাণ দেয় না, প্রমাণের ওজন দেয়; ADF আর KPSS একসঙ্গে, ছবি আর holdout-এর পাশে রেখে পড়ুন।
প্র ০৩MAPE ব্যবসায় এত জনপ্রিয়, তবু forecasting গবেষকেরা কেন MASE-এর পক্ষে যুক্তি দেন? Metric বাছাই কি শুধু রুচির ব্যাপার?
MAPE সহজে বোঝানো যায়: “গড়ে ৩% ভুল”। কিন্তু Rob Hyndman আর Anne Koehler ২০০৬ সালে International Journal of Forecasting-এ “Another look at measures of forecast accuracy” প্রবন্ধে প্রচলিত metric-গুলোর সমস্যা গুছিয়ে দেখান। MAPE-এ y শূন্য হলে ভাগ অসংজ্ঞায়িত, শূন্যের কাছে হলে বিশাল। কম-বিক্রির পণ্য বা দিন পুরো গড়কে দখল করে। আর এটি অসম: অনুমান কম হলে ভুলের শতাংশ সর্বোচ্চ ১০০%, বেশি হলে সীমাহীন; তাই MAPE কমাতে গিয়ে model কম-অনুমানের দিকে ঝোঁকে। sMAPE এই অসমতা সারাতে আনা হয়েছিল, কিন্তু তার নিজের অসমতা আছে, আর y ও ŷ দুটোই শূন্য হলে আবার অসংজ্ঞায়িত।
MASE হরে একটি এককহীন মাপকাঠি বসায়: training-এ naive পদ্ধতির ভুল। ফলে ভিন্ন এককের series (একটি পণ্যে দিনে ৫টি, আরেকটিতে ৫০০০টি) একসঙ্গে গড় করা যায়, শূন্যের দিনে সমস্যা নেই (যদি পুরো training series স্থির না হয়), আর “১-এর নিচে মানে naive-এর চেয়ে ভালো” এই ব্যাখ্যা পাওয়া যায়। তবে একটি সূক্ষ্মতা: হরটি training-এর এক-ধাপ ভুল, অথচ holdout-এর ভুল বহু ধাপ সামনের। তাই holdout-এ seasonal naive-এরও MASE ১-এর বেশি হতে পারে, যেমন অংশ ৮-এ ১.০৭।
Metric বাছাই রুচি নয়, সিদ্ধান্তের প্রশ্ন। গুদামের স্টক ঠিক করতে কম-অনুমানের খরচ (খালি তাক, হারানো ক্রেতা) বেশি-অনুমানের (পচনশীল পণ্য নষ্ট) চেয়ে আলাদা; তখন quantile loss (পর্ব ২৪) বা সরাসরি টাকার হিসাব বেশি অর্থবহ। আর যে metric বেছে নেবেন, model-ও সেই metric-এর জন্য বাছাই করবেন: MAE কমানো model median-এর দিকে, RMSE কমানো model গড়ের দিকে যায়।
মূল উপলব্ধি: Metric ঠিক করে আপনি কোন ভুলকে কত দামি মনে করেন; MASE তুলনার জন্য নিরাপদ মাপ, কিন্তু চূড়ান্ত বিচার ব্যবসার খরচের ভাষায়।
প্র ০৪Forecast-এর ৯৫% পরিসর বাস্তবে প্রায়ই খুব সরু হয় বলে অভিযোগ আছে। কেন, আর কীভাবে যাচাই ও উন্নত করবেন?
SARIMA-র পরিসর আসে একটি সূত্র থেকে: model সঠিক, parameter ঠিক জানা, আর ভবিষ্যতের ধাক্কা Gaussian ও আগের মতো. বাস্তবে তিনটিই টলতে পারে। Chris Chatfield ১৯৯৩ সালে Journal of Business & Economic Statistics-এ interval forecast নিয়ে একটি পর্যালোচনায় এই সমস্যার কথা জোর দিয়ে বলেন: empirical গবেষণায় পরিসর প্রায়ই নামমাত্র coverage-এর চেয়ে কম ধরে।
কারণগুলো চেনা। Parameter-এর অনিশ্চয়তা সাধারণ সূত্রে ধরা হয় না; ৬৭২ দিনে ছোট, কিন্তু ৬০ দিনের ডেটায় বড়। Model-এর অনিশ্চয়তা আরও বড়: কোন order, কোন differencing, ঈদ ধরা হবে কি না, প্রতিটি পছন্দ আলাদা পরিসর দেয়, আর সবচেয়ে ভালো-দেখানো model বাছলে পরিসর আরও আশাবাদী হয়। Noise-এর বণ্টনও Gaussian না হতে পারে: ঈদ, বন্যা বা হঠাৎ ভাইরাল হওয়া পোস্টের মতো ঘটনা ভারী লেজ বানায়।
এই পর্বের উল্টো ছবিটিও শিক্ষণীয়: দুটি model-এর coverage ১.০০, অথচ Holt–Winters-এর পরিসর ৫৬তম দিনে ৪০০-র বেশি চওড়া, যা ব্যবসার সিদ্ধান্তে প্রায় অকেজো। Coverage আর প্রস্থ একসঙ্গে দেখতে হয়; ১০০% coverage-এর পরিসর সহজেই বানানো যায়, শুধু সীমাহীন চওড়া করলেই। Quantile loss বা interval score এই দুটিকে একসঙ্গে মাপে।
যাচাইয়ের পথ: অনেকগুলো কাটার বিন্দুতে (rolling origin, পর্ব ৫২) প্রতিটি horizon-এ coverage আর গড় প্রস্থ গোনা। উন্নতির পথ: residual bootstrap (পর্ব ২১-এর block ধারণা), simulation, কয়েকটি model-এর পরিসর মেলানো, আর model-নির্ভরতা কম এমন conformal prediction (পর্ব ৫৮, Conformal prediction — নিশ্চয়তাসহ অনুমানের পরিসর), যদিও সময়ের ডেটায় তার বিনিময়যোগ্যতার শর্ত নিয়ে সতর্ক থাকতে হয়।
মূল উপলব্ধি: Prediction interval একটি দাবি, যা model-এর অনুমানের ওপর দাঁড়ায়; বহু কাটার বিন্দুতে coverage আর প্রস্থ মেপে সেই দাবি যাচাই করুন।
প্র ০৫ঈদ চাঁদ দেখে হয়, তাই ইংরেজি ক্যালেন্ডারে প্রতি বছর প্রায় ১১ দিন আগে আসে। সাপ্তাহিক বা বার্ষিক seasonality দিয়ে এটি ধরা যায় না কেন, আর ধ্রুপদি model-এ কীভাবে সামলাবেন?
Seasonality মানে একটি স্থির দৈর্ঘ্যের পুনরাবৃত্তি: সাপ্তাহিক m = ৭, বার্ষিক m ≈ ৩৬৫.২৫. হিজরি বছর চান্দ্র মাসে গোনা, প্রায় ৩৫৪ বা ৩৫৫ দিনের, তাই ঈদ গ্রেগরিয়ান ক্যালেন্ডারে প্রতি বছর প্রায় ১০ থেকে ১২ দিন সরে, আর প্রায় ৩৩ বছরে পুরো বছর ঘুরে আসে। বার্ষিক seasonal differencing (১ − B৩৬৫) তাই গত বছরের ঈদের দিনকে এ বছরের সাধারণ দিনের সঙ্গে তুলনা করে: একটি ধাক্কা দুটি হয়ে যায়, একটি ধনাত্মক আর একটি ঋণাত্মক। এই সমস্যা শুধু বাংলাদেশের নয়: চীনা নববর্ষ, ইস্টার, দিওয়ালিও চলমান ছুটি, আর সরকারি পরিসংখ্যান সংস্থাগুলো বহু বছর ধরে এর জন্য বিশেষ সমন্বয় করে।
ধ্রুপদি সমাধান হলো ছুটিকে আলাদা চলক হিসেবে দেওয়া: SARIMAX-এর X. ঈদের আগের ৭ দিন, ঈদের দিনগুলো আর পরের ৩ দিনের জন্য আলাদা ০/১ কলাম, বা ঈদ থেকে দূরত্বের একটি function. শর্ত একটাই: forecast-এর সময় ভবিষ্যতের এই কলাম জানা থাকতে হবে। ঈদের তারিখ আগে থেকে আনুমানিক জানা যায়, তবে চাঁদ দেখার ওপর এক দিনের অনিশ্চয়তা থাকে, যা পরিসরে প্রতিফলিত হওয়া উচিত। Exponential smoothing-এ সরাসরি বাইরের চলক বসানোর সুযোগ সীমিত; সাধারণ কৌশল হলো আগে regression দিয়ে ছুটির প্রভাব সরিয়ে বাকিটায় smoothing.
অংশ ৮-এর residual এই সমস্যার ছোট রূপ দেখিয়েছে: সবচেয়ে বড় ভুল ঈদের দিনে। দুই বছরে মাত্র দুটি ঈদ থাকলে একটি coefficient-ও অনিশ্চিত, তাই বহু পণ্য বা জেলার ডেটা একসঙ্গে ব্যবহার করে ছুটির প্রভাব শেখা অনেক সময় বেশি টেকসই, যা পর্ব ৫২-এর বিষয়।
মূল উপলব্ধি: স্থির দৈর্ঘ্যের seasonality চলমান ছুটি ধরতে পারে না; সেগুলো আলাদা calendar চলক হিসেবে দিন, ভবিষ্যতে সেই চলক জানা আছে কি না নিশ্চিত হয়ে।
অনুশীলন
হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে পাতার ব্লকগুলোতে সংখ্যা বদলে যাচাই করুন।
-
Numerical: (ক) একটি AR(1)-এ μ = ৫০০, φ = ০.৮, yT = ৫৪০, σ = ১০. এক আর দুই ধাপ পরের অনুমান আর ৯৫% পরিসর বের করুন; দূর ভবিষ্যতে অনুমান আর SD কোথায় থামবে? (খ) Holt-এ ℓ = ২০০, b = ৫, আজকের y = ২১২, α = ০.৩, β = ০.১. নতুন ℓ, b আর কালকের অনুমান বের করুন।
(ক) বিচ্যুতি ৪০. ŷ(১) = ৫০০ + ০.৮ × ৪০ = ৫৩২, SD ১০, পরিসর ৫৩২ ± ১৯.৬ = [৫১২.৪, ৫৫১.৬]. ŷ(২) = ৫০০ + ০.৬৪ × ৪০ = ৫২৫.৬, SD ১০√(১ + ০.৬৪) = ১২.৮১, পরিসর ৫২৫.৬ ± ২৫.১ = [৫০০.৫, ৫৫০.৭]. দূরে অনুমান ৫০০-এ আর SD ১০/√(১ − ০.৬৪) = ১৬.৬৭-এ থামে। (খ) আগের অনুমান ২০৫, ভুল ৭. ℓ = ০.৩ × ২১২ + ০.৭ × ২০৫ = ৬৩.৬ + ১৪৩.৫ = ২০৭.১. b = ০.১ × (২০৭.১ − ২০০) + ০.৯ × ৫ = ০.৭১ + ৪.৫ = ৫.২১. কালকের অনুমান ২০৭.১ + ৫.২১ = ২১২.৩১. সংখ্যাগুলো একটি scratch script-এ যাচাই করা।
-
সমালোচনা ও নকশা: একজন বিশ্লেষক লিখেছেন: “৩০টি জেলার দৈনিক বিক্রি একসঙ্গে মিশিয়ে
train_test_splitদিয়ে ৮০/২০ ভাগ করেছি, তারপর ARIMA(৫,২,৫) fit করেছি; AIC সবচেয়ে কম। MAPE ১.২%! ADF-এর p ০.৩০ ছিল, তাই series stationary. দুটি জেলায় অনেক দিন বিক্রি শূন্য, ওগুলো বাদ দিয়েছি।” কী কী সমস্যা, আর আপনার নকশা কী?সমস্যা: (১) এলোমেলো ভাগ সময়ের ক্রম ভাঙে: test-এর দিনগুলোর আগে-পরে training-এর দিন, তাই MAPE অবাস্তব ভালো (leakage). (২) ৩০টি জেলা মিশিয়ে একটি series নয়; প্রতিটি আলাদা series, বা একটি global model (পর্ব ৫২)। (৩) d = ২ আর (৫, ৫) অনেক বড়; over-differencing আর অস্থির fit-এর ঝুঁকি, AIC শুধু in-sample. (৪) ADF-এর p = ০.৩০ মানে unit root বাতিল হয়নি, stationary প্রমাণ তো নয়ই, উল্টো ইঙ্গিত। (৫) শূন্যের দিন বাদ দেওয়া পক্ষপাত আনে; MAPE শূন্যে চলে না বলে metric বদলানো উচিত, ডেটা নয়। (৬) কোনো baseline নেই। নকশা: প্রতি জেলার জন্য freq="D" index, শেষ ৮ সপ্তাহ holdout; STL আর ADF/KPSS-এর সঙ্গে ছবি; ছোট SARIMA আর Holt–Winters, naive ও seasonal naive-এর বিপক্ষে MASE; residual-এ Ljung–Box; শূন্য-ভারী জেলায় intermittent demand-এর পদ্ধতি (পর্ব ৫২-এ Croston-এর উল্লেখ) আর কয়েকটি কাটার বিন্দুতে backtest.
-
আউটপুট অনুমান করুন: চালানোর আগে তিনটি লাইন লিখুন।
import pandas as pd s = pd.Series([10, 12, 15, 11, 10, 13, 17, 12], index=pd.date_range("2025-03-03", periods=8, freq="D")) print(s.diff().tolist()) print(s.diff(4).dropna().tolist()) print(s.diff(4).diff().dropna().tolist())ডেস্কটপে সত্যিকারের run (Python 3.14, pandas 3.0.2):
[nan, 2.0, 3.0, -4.0, -1.0, 3.0, 4.0, -5.0] [0.0, 1.0, 2.0, 1.0] [1.0, 1.0, -1.0]প্রথম লাইনে প্রথম মান NaN, কারণ তার আগের দিন নেই; বাকিগুলো পরপর পার্থক্য। দ্বিতীয় লাইনে “৪ দিন আগের সঙ্গে” পার্থক্য: ১০ − ১০, ১৩ − ১২, ১৭ − ১৫, ১২ − ১১; প্রথম চারটি NaN বাদ। তৃতীয় লাইন সেই ধারার পরপর পার্থক্য, অর্থাৎ (১ − B)(১ − B৪)s; মোট ৫টি মান খোয়া গেছে (৪ + ১)। এই পর্বের ব্লক ২-এ m = ৭ হলে ঠিক একইভাবে ৮টি যায়।
-
কোডিং: একটি function লিখুন
forecast_scores(train, actual, pred, m=1), যা NumPy দিয়ে MAE, RMSE, MAPE (%) আর MASE ফেরত দেয় (MASE-এর হর training-এর lag-m naive ভুল)। MASE stepper-এর সংখ্যায় (training ১০০, ১০৪, ৯৮, ১০৬, ১১০, ১০৮; আসল ১১২, ১০৯, ১১৫, ১১১, ১১৮; অনুমান ১১০, ১১২, ১১২, ১১৪, ১১৪) চালিয়ে মিলিয়ে দেখুন।import numpy as np def forecast_scores(train, actual, pred, m=1): train, actual, pred = (np.asarray(v, dtype=float) for v in (train, actual, pred)) e = actual - pred scale = np.mean(np.abs(train[m:] - train[:-m])) return {"MAE": np.mean(np.abs(e)), "RMSE": np.sqrt(np.mean(e ** 2)), "MAPE%": 100 * np.mean(np.abs(e) / np.abs(actual)), "MASE": np.mean(np.abs(e)) / scale} r = forecast_scores([100, 104, 98, 106, 110, 108], [112, 109, 115, 111, 118], [110, 112, 112, 114, 114]) print({k: round(float(v), 4) for k, v in r.items()})ডেস্কটপে সত্যিকারের run (Python 3.14, NumPy 2.4.6) ছাপে:
{'MAE': 3.0, 'RMSE': 3.0659, 'MAPE%': 2.6478, 'MASE': 0.625}MAE ৩ আর MASE ০.৬২৫ stepper-এর সঙ্গে মেলে। RMSE ৩.০৬৬, MAE-এর একটু ওপরে, কারণ ভুলগুলোর আকার কাছাকাছি (২ থেকে ৪); একটি বড় ভুল থাকলে দুটির ফাঁক অনেক বাড়ত।
float(v)লেখা হয়েছে, কারণ NumPy 2-এ dict-এর ভেতরে NumPy সংখ্যাnp.float64(3.0)আকারে ছাপে। Training series পুরো স্থির হলে হর শূন্য হয়; বাস্তব কোডে সেই অবস্থা আলাদা করে ধরুন।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি দৈনিক series-এ SARIMA দিয়ে forecast করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- শেষ অংশ সময় ধরে holdout হিসেবে আলাদা রাখা
- ছবি আর STL দিয়ে trend, ঋতু আর অস্বাভাবিক দিন দেখা
- Differencing ঠিক করা, ADF ও KPSS দিয়ে যাচাই
- ACF আর PACF পড়ে প্রার্থী order বাছা
- Maximum likelihood দিয়ে fit আর Ljung–Box দিয়ে residual যাচাই
- Holdout-এ forecast করে seasonal naive-এর সঙ্গে MASE তুলনা
একটি AR(1) model-এ μ = ১০০, φ = ০.৫, শেষ মান yT = ১২০. দুই ধাপ পরের অনুমান কত?
- ১১০
- ১০৫
- ১০০
- ১২০
একটি stationary series-এর ACF lag ১-এ বড় আর তারপর সব lag-এ প্রায় শূন্য; PACF ধীরে ধীরে ক্ষয় হয়। কোন model সবচেয়ে মানানসই?
- AR(1)
- Random walk
- MA(1)
- White noise
একটি differenced series-এ KPSS test চালিয়ে statsmodels p = ০.১ ফেরত দিল, সঙ্গে InterpolationWarning. কোন ব্যাখ্যাটি ঠিক?
- Series stationary হওয়ার সম্ভাবনা ঠিক ১০%
- Series-এ unit root প্রমাণিত হলো
- Test ব্যর্থ হয়েছে, ফল ফেলে দিতে হবে
- আসল p ০.১০-এর বেশি; stationarity বাতিলের প্রমাণ নেই
একটি দৈনিক series-এ (m = ৭) holdout-এ একটি model-এর MASE = ১.২০, যেখানে হর training-এর seasonal naive-এর এক-ধাপ MAE. এর মানে কী?
- Holdout-এ গড় পরম ভুল training-এর seasonal naive ভুলের ১.২ গুণ
- গড়ে ১.২% ভুল
- Model seasonal naive-এর চেয়ে ২০% ভালো
- Model ডেটার ১২০% variance ব্যাখ্যা করে
Holt-এর পদ্ধতিতে গতকালের স্তর ℓ = ৫০, ঢাল b = ১, আজকের আসল মান y = ৫৫, α = ০.৪. নতুন স্তর ℓ কত?
- ৫২.০
- ৫২.৬
- ৫৫.০
- ৫১.০
দুই বছরের দৈনিক অর্ডারে forecasting model যাচাইয়ের জন্য কোন ভাগ সবচেয়ে সঠিক?
- সব দিন এলোমেলো করে ৮০/২০ ভাগ
- Shuffle করে ৫-fold cross-validation
- প্রথম অংশে training, শেষের কয়েক সপ্তাহে holdout
- প্রতি সপ্তাহের শুক্রবারগুলো test, বাকি দিন training
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- ভাগ করুন: y = T + S + R (log নিলে multiplicative-ও additive). STL ঋতু বদলাতে দেয় আর robust; FS = max(০, ১ − Var(R)/Var(S + R)).
- Stationary: (১ − B) trend, (১ − Bm) ঋতু সরায়। ADF-এর H0 unit root, KPSS-এর H0 stationarity; বড় p প্রমাণ নয়, আর KPSS-এর সীমা-মান সীমা হিসেবেই পড়ুন।
- Model: AR(p)-এ PACF কাটে, MA(q)-এ ACF কাটে। AR(1): ŷ(h) = μ + φh(yT − μ)। SARIMA (p,d,q)(P,D,Q)m, MLE আর Kalman filter দিয়ে fit.
- Smoothing: Holt: ℓ = αy + (১ − α)(ℓ + b), b = β(Δℓ) + (১ − β)b; Holt–Winters ঋতু যোগ করে; ETS পরিসর দেয়।
- যাচাই: সময় ধরে holdout; naive আর seasonal naive হারাতে হবে; MASE < ১ লক্ষ্য; Ljung–Box আর coverage দেখুন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৫২ · ML দিয়ে forecasting — lag feature, backtesting আর অনেক series একসঙ্গেপরবর্তী পর্বLag feature, rolling-origin backtest আর অনেক series একসঙ্গে শেখানো।
- মেশিন লার্নিং · কোর্স হোম৭৭টি পর্ব১৪টি মডিউলের পুরো রোডম্যাপ; পর্ব ৫২ (ML দিয়ে forecasting) এখান থেকে খুলবেন।
- পর্ব ২৪ · Robust আর quantile regressionআগের ভিত্তিPrediction interval, coverage আর quantile loss.
- পর্ব ২১ · Bootstrap দিয়ে অনিশ্চয়তাআগের ভিত্তিসময়ের ডেটায় block bootstrap.
- পর্ব ৪৬ · Gaussian process — kernel দিয়ে অনিশ্চয়তাসহ অনুমানপাশের পথPeriodic kernel দিয়ে সাপ্তাহিক অর্ডারের আরেক রকম forecast.
- পর্ব ১৮ · Maximum likelihood আর MAPআগের ভিত্তিSARIMA-র parameter যে নীতিতে অনুমান হয়।
- AI-এর ভিত্তি · পর্ব ২৬ · Time series — তারিখ, resample, rolling আর Asia/Dhakaআগের ভিত্তিDatetimeIndex, frequency, shift আর diff.
- AI-এর ভিত্তি · পর্ব ৫৬ · Train, validation আর test — data leakage-এর ফাঁদআগের ভিত্তিTime-based split আর সময়ের leakage.
- DBMS আর SQLপাশের ট্র্যাকদৈনিক অর্ডারের series ডেটাবেস থেকে GROUP BY দিয়ে বানানো।