التنبؤ بالسلاسل الزمنية: ARIMA وETS وVAR مع اختبار رجعي سليم في R وPython
دليل عملي إلى التنبؤ بالسلاسل الزمنية: الاستقرارية والفروق، وقراءة ACF وPACF، ونماذج ARIMA وETS وVAR في R وPython، والاختبار الرجعي بأصل متدحرج، وعرض عدم اليقين لصانع القرار.
في هذه المقالة
- افهم السلسلة قبل أن تختار النموذج
- الاستقرارية والفروق
- قراءة ACF وPACF
- ARIMA وSARIMA وETS في R
- البديل في Python: statsmodels وProphet
- الاختبار الرجعي بأصل متدحرج
- مقاييس الدقة: ماذا يقيس كل مقياس؟
- فترات التنبؤ: الجزء الذي لا يحذف من التقرير
- عدة مؤشرات معًا: نموذج VAR
- كيف تنقل عدم اليقين إلى صانع القرار
- أخطاء شائعة
- أسئلة شائعة
- كم مشاهدة أحتاج لبناء نموذج تنبؤ موثوق؟
- أيهما أفضل: ARIMA أم ETS؟
- هل يتفوق التعلم الآلي على النماذج الإحصائية في التنبؤ؟
- لماذا يتجاوز الواقع فترات التنبؤ أكثر من المتوقع؟
يطلب منك قسم التخطيط رقمًا واحدًا: كم سيبلغ الطلب على الخدمة في الأشهر الاثني عشر القادمة؟ سيدخل هذا الرقم في الموازنة وخطة التوظيف، وربما في تقرير يرفع إلى الإدارة العليا. المشكلة أن الرقم الواحد أضعف ما يمكن تقديمه، وأن نموذجًا يطابق البيانات التاريخية جيدًا قد يخفق في أول سنة حقيقية. التنبؤ بالسلاسل الزمنية عمل منهجي له خطوات واضحة: فهم بنية السلسلة، وتحقيق الاستقرارية، واختيار النموذج، ثم اختبار قدرته على بيانات لم يرها، وأخيرًا نقل عدم اليقين إلى صانع القرار بلغة يفهمها. يمر هذا المقال على هذه الخطوات بكود يعمل في R وPython، ويتوقف عند الأخطاء التي تتكرر في التقارير المؤسسية والرسائل العلمية.
افهم السلسلة قبل أن تختار النموذج
تتكون السلسلة الزمنية عادةً من أربعة مكونات: اتجاه عام (Trend) يصف الحركة طويلة الأمد، وموسمية (Seasonality) تتكرر بدورة ثابتة مثل 12 شهرًا أو أربعة أرباع، ودورات غير منتظمة الطول ترتبط غالبًا بالنشاط الاقتصادي، ثم مكون عشوائي متبق. أول خطوة هي رسم السلسلة وتفكيكها، لأن الرسم يكشف ما لا تكشفه الاختبارات: كسر هيكلي بعد تغيير طريقة القياس، أو قيمة شاذة في شهر بعينه، أو تباين يتسع كلما ارتفع المستوى. الحالة الأخيرة شائعة في البيانات الاقتصادية والإدارية، وعلاجها المعتاد أخذ اللوغاريتم قبل النمذجة، فتتحول الموسمية الضربية إلى موسمية جمعية يسهل نمذجتها.
في R تفكك الدالة STL() من حزمة feasts السلسلة إلى مكوناتها، وتسمح للنمط الموسمي بالتغير البطيء عبر الزمن. وإذا كانت السلسلة مجمعة من سجلات إدارية متعددة، فابدأ بـتنظيف البيانات: توحيد صيغ التواريخ، ومعالجة الفترات المفقودة، وتوثيق أي تغيير في تعريف المؤشر، لأن تغيير التعريف يظهر في النموذج كأنه تغير حقيقي في الظاهرة.
الاستقرارية والفروق
السلسلة المستقرة (Stationary) هي التي لا تتغير خصائصها الإحصائية، كالمتوسط والتباين والارتباط الذاتي، بمرور الزمن. تفترض نماذج ARIMA أن السلسلة تصبح مستقرة بعد عدد مناسب من الفروق (Differencing)، أي طرح كل قيمة من سابقتها، أو من القيمة المقابلة لها في الموسم السابق في حالة الفرق الموسمي.
يعتمد الحكم على اختبارين فرضيتاهما الصفريتان متعاكستان: اختبار ديكي-فولر الموسع (ADF) فرضيته الصفرية وجود جذر وحدة، أي أن السلسلة غير مستقرة، أما اختبار KPSS ففرضيته الصفرية أن السلسلة مستقرة. حين يتفق الاختباران يكون الحكم أوضح، وحين يختلفان تكون السلسلة غالبًا قريبة من الحد، فيصبح الرسم وفهمك للظاهرة جزءًا من القرار. يستخدم الكود الآتي بيانات AirPassengers المدمجة في R، وهي أعداد شهرية لركاب خطوط جوية بين 1949 و1960، وفيها اتجاه وموسمية وتباين يتسع مع المستوى.
library(fpp3)
# AirPassengers: monthly airline passengers, 1949-1960
air <- as_tsibble(AirPassengers) |>
mutate(ly = log(value), # log stabilises the growing variance
dly = difference(ly, 12)) # seasonal difference of the log series
air |> features(ly, unitroot_nsdiffs) # suggested number of seasonal differences
air |> features(dly, unitroot_ndiffs) # extra first differences still needed?
air |> features(dly, unitroot_kpss) # KPSS test, H0: stationary
# ACF and PACF of the seasonally differenced log series
air |> ACF(dly, lag_max = 36) |> autoplot()
air |> PACF(dly, lag_max = 36) |> autoplot()
اقرأ الناتج بالترتيب: عدد الفروق الموسمية أولًا، ثم عدد الفروق العادية المطلوبة بعد الفرق الموسمي. واحذر الإفراط في الفروق، فالفرق الزائد يضيف ارتباطًا ذاتيًا سالبًا مصطنعًا ويرفع تباين السلسلة، ومن علاماته أن تقترب قيمة ACF عند الفجوة الأولى من -0.5.
قراءة ACF وPACF
تقيس دالة الارتباط الذاتي (ACF) ارتباط السلسلة بقيمها المتأخرة عند كل فجوة زمنية (Lag)، أما دالة الارتباط الذاتي الجزئي (PACF) فتقيس الارتباط عند الفجوة k بعد إزالة أثر الفجوات الواقعة بينها. نمط الدالتين معًا يقترح مرشحين للنموذج:
| النمط في ACF | النمط في PACF | النموذج المرجح |
|---|---|---|
| تتناقص تدريجيًا | تنقطع بعد الفجوة p | AR(p) |
| تنقطع بعد الفجوة q | تتناقص تدريجيًا | MA(q) |
| تتناقص تدريجيًا | تتناقص تدريجيًا | ARMA(p,q) |
| قمة عند الفجوة 12 وحدها | تناقص عند 12 و24 و36 | متوسط متحرك موسمي SMA(1) |
| تناقص عند 12 و24 و36 | قمة عند الفجوة 12 وحدها | انحدار ذاتي موسمي SAR(1) |
هذه القراءة تقترح نماذج مرشحة ولا تحسم الاختيار. في التطبيق تجمع بينها وبين الاختيار الآلي بمعيار AICc، مع قيد مهم: لا تقارن AICc بين نماذج تختلف في عدد الفروق أو في تحويل المتغير التابع، لأن البيانات التي يحسب عليها المعيار لم تعد واحدة.
ARIMA وSARIMA وETS في R
يكتب نموذج SARIMA بالصيغة ARIMA(p,d,q)(P,D,Q)[m]: الجزء الأول للبنية غير الموسمية (رتبة الانحدار الذاتي، وعدد الفروق، ورتبة المتوسط المتحرك)، والثاني للبنية الموسمية بطول دورة m. أما نماذج ETS فهي عائلة التمهيد الأسي (Exponential Smoothing) في صيغة فضاء الحالة: تحدد نوع الخطأ والاتجاه والموسمية، جمعيًا أو ضربيًا أو غائبًا، مع إمكان تخميد الاتجاه (Damped Trend). لا تشترط ETS الاستقرارية، وتنتج فترات تنبؤ من نموذج احتمالي كامل.
تقدم حزمة fable ضمن منظومة fpp3 الطريقتين بواجهة واحدة، وتختار ARIMA() الرتب آليًا بخوارزمية هايندمان-خاندكار. نحجز آخر سنتين للاختبار، ونضيف النموذج الساذج الموسمي (Seasonal Naive) معيارًا للمقارنة: النموذج الذي لا يتفوق عليه لا يبرر تعقيده.
# Hold out the last two years as a test set
train <- air |> filter_index(~ "1958 Dec")
fit <- train |>
model(
sarima = ARIMA(log(value)), # automatic order selection by AICc
ets = ETS(log(value)), # automatic error/trend/season choice
snaive = SNAIVE(log(value)) # seasonal naive benchmark
)
fit |> select(sarima) |> report()
# Ljung-Box test on innovation residuals (H0: no autocorrelation left)
fit |> augment() |> features(.innov, ljung_box, lag = 24)
fc <- fit |> forecast(h = 24) # back-transformed to the original scale
fc |> accuracy(air) # MAE, RMSE, MAPE, MASE on the test set
fc |> filter(.model == "sarima") |> autoplot(air, level = c(80, 95))
تتعامل fable مع التحويل اللوغاريتمي تلقائيًا، فتعيد التنبؤات وتوزيعاتها إلى المقياس الأصلي. ومن يفضل حزمة forecast الأقدم يحصل على المنطق نفسه بـauto.arima(AirPassengers, lambda = 0) وets(AirPassengers, lambda = 0). أما اختبار ليونغ-بوكس (Ljung-Box) فيفحص ما إذا بقي ارتباط ذاتي في البواقي: القيمة الدالة تعني أن النموذج ترك نمطًا لم يلتقطه، وقد يبقى التنبؤ النقطي مقبولًا، لكن فترات التنبؤ تصبح أقل موثوقية.
البديل في Python: statsmodels وProphet
في Python توفر مكتبة statsmodels الصنف SARIMAX الذي يقبل الرتب الموسمية ومتغيرات خارجية، ويعيد فترات التنبؤ مباشرةً. يولد المثال الآتي سلسلة شهرية اصطناعية حتى يعمل دون أي ملف خارجي:
import numpy as np
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX
# Synthetic monthly series: trend + annual seasonality + noise
rng = np.random.default_rng(42)
t = np.arange(144)
idx = pd.date_range("2013-01-01", periods=144, freq="MS")
y = pd.Series(100 + 0.8 * t + 10 * np.sin(2 * np.pi * t / 12)
+ rng.normal(0, 3, 144), index=idx)
train, test = y.iloc[:-24], y.iloc[-24:]
res = SARIMAX(train, order=(1, 1, 1),
seasonal_order=(0, 1, 1, 12)).fit(disp=False)
fc = res.get_forecast(steps=24)
pred = fc.predicted_mean
pi95 = fc.conf_int(alpha=0.05) # 95% prediction interval
err = test - pred
print(f"MAE = {np.abs(err).mean():.2f}")
print(f"RMSE = {np.sqrt((err ** 2).mean()):.2f}")
inside = (test >= pi95.iloc[:, 0]) & (test <= pi95.iloc[:, 1])
print(f"Interval coverage = {inside.mean():.0%}")
أما Prophet، الذي طوره فريق في Facebook (Meta حاليًا)، فيناسب البيانات اليومية أو الأسبوعية ذات المواسم المتعددة والعطل؛ إذ يجمع اتجاهًا متقطع الخطية مع موسمية بسلاسل فورييه وأثر للعطل. ميزته في البيئة العربية أنك تستطيع تمرير تواريخ رمضان والأعياد عبر الوسيط holidays بوصفها عطلًا مخصصة، لأنها تنتقل في التقويم الميلادي كل عام. لكنه ليس أدق بالضرورة من ARIMA أو ETS، فاختبره رجعيًا مثل أي نموذج آخر:
from prophet import Prophet
# Reuses `train` from the SARIMAX example; Prophet expects columns ds and y
df = train.rename_axis("ds").reset_index(name="y")
m = Prophet(interval_width=0.95).fit(df)
future = m.make_future_dataframe(periods=24, freq="MS")
out = m.predict(future)[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail(24)
print(out.head())
ولمن يتردد بين اللغتين، يعرض مقال Python أم R نقاط القوة في كل منهما. وفي السلاسل الزمنية تحديدًا تتميز منظومة fpp3 في R بواجهة موحدة للنماذج الكلاسيكية وأدوات التقييم، بينما تسهل Python دمج التنبؤ في أنظمة الإنتاج.
الاختبار الرجعي بأصل متدحرج
التقسيم الواحد إلى تدريب واختبار يعطيك نتيجة تتوقف على فترة واحدة قد تكون استثنائية. الأفضل التقييم بأصل متدحرج (Rolling-Origin Evaluation)، ويسمى أيضًا التحقق المتقاطع للسلاسل الزمنية: تدرب النموذج على أول k مشاهدة، وتتنبأ h خطوة للأمام، ثم توسع نافذة التدريب وتكرر. هكذا تحصل على تنبؤات كثيرة من أصول مختلفة بدل تنبؤ واحد، وتقيس الخطأ حسب أفق التنبؤ. ولا تستخدم التحقق المتقاطع العشوائي (k-fold) مع السلاسل الزمنية، لأنه يدرب النموذج على المستقبل ليتنبأ بالماضي.
# Expanding training windows of 96, 102, ..., 132 months (7 origins)
cv <- air |>
filter_index(~ "1959 Dec") |>
stretch_tsibble(.init = 96, .step = 6)
cv_fc <- cv |>
model(sarima = ARIMA(log(value)),
ets = ETS(log(value)),
snaive = SNAIVE(log(value))) |>
forecast(h = 12)
cv_fc |> accuracy(air) # accuracy averaged over all origins
# Accuracy by forecast horizon (1 to 12 months ahead)
cv_fc |>
group_by(.id, .model) |>
mutate(h = row_number()) |>
ungroup() |>
as_fable(response = "value", distribution = value) |>
accuracy(air, by = c("h", ".model"))
في هذا المثال سبعة أصول، يتنبأ كل منها 12 شهرًا. ويمكنك تقييم فترات التنبؤ نفسها لا التنبؤ النقطي فقط، بتمرير measures = interval_accuracy_measures إلى accuracy() للحصول على درجة Winkler، أو بحساب نسبة القيم الفعلية التي وقعت داخل فترة 95%: إذا جاءت أقل منها بوضوح فالفترات أضيق من الواقع.
مقاييس الدقة: ماذا يقيس كل مقياس؟
| المقياس | الفكرة | نقطة القوة | التحفظ |
|---|---|---|---|
| MAE | متوسط القيمة المطلقة للخطأ | بوحدة البيانات نفسها وسهل الشرح | يعتمد على مقياس السلسلة، فلا يقارن بين سلاسل مختلفة |
| RMSE | الجذر التربيعي لمتوسط مربع الخطأ | يعاقب الأخطاء الكبيرة بشدة | حساس للقيم الشاذة ويعتمد على المقياس |
| MAPE | متوسط الخطأ المطلق نسبة إلى القيمة الفعلية | نسبة مئوية يفهمها غير المتخصص | غير معرف عند الصفر، وغير متماثل، ويتطلب صفرًا ذا معنى |
| MASE | MAE مقسومًا على خطأ النموذج الساذج داخل العينة | مستقل عن المقياس، والقيمة الأقل من 1 تعني تفوقًا على المعيار الساذج داخل العينة | أقل بداهة عند عرضه على الإدارة |
| Winkler وCRPS | تقييم الفترات والتوزيعات التنبؤية | تكافئ الفترة الضيقة التي تغطي الواقع | أقل شيوعًا في التقارير |
لمقياس MAPE ثلاث مشكلات معروفة. الأولى أنه غير معرف عند القيم الصفرية ويتضخم قربها، وهذا شائع في السلاسل المتقطعة مثل الطلب على قطع الغيار أو حالات مرض نادر. الثانية أنه غير متماثل: الخطأ بالنقص لا يتجاوز 100% بينما الخطأ بالزيادة بلا سقف، فاختيار النموذج بأدنى MAPE يميل إلى النماذج التي تتنبأ بأقل من الواقع. الثالثة أنه يفترض صفرًا ذا معنى، فلا يصلح لمتغير مثل درجة الحرارة المئوية. اعرضه إن طلبه الجمهور لسهولة فهمه، لكن اختر النموذج بمقياس مثل MASE أو RMSE.
فترات التنبؤ: الجزء الذي لا يحذف من التقرير
فترة التنبؤ 95% تعني أن القيمة الفعلية يتوقع أن تقع داخلها في 95% من الحالات إذا كان النموذج صحيحًا. الشرط الأخير هو المهم: الفترات المحسوبة من النموذج تعكس عشوائية الأخطاء، ولا تعكس عادةً عدم اليقين في تقدير المعالم، ولا احتمال أن يكون النموذج نفسه خاطئًا، ولا الصدمات غير المسبوقة. لذلك تأتي الفترات في الواقع أضيق مما ينبغي، ويكشف الاختبار الرجعي حجم هذا الفرق.
ثلاث ممارسات تحسن الوضع. إذا لم تكن البواقي طبيعية فاستخدم فترات مبنية على البوتستراب بـforecast(h = 24, bootstrap = TRUE, times = 1000) في fable. واستخرج حدود الفترة صراحة بـhilo(fc, 95) لتضعها في الجداول. واعرض الفترة دائمًا مع التنبؤ النقطي في الرسوم والجداول، لأنها تتسع مع الأفق، وهذا الاتساع نفسه معلومة يحتاجها صانع القرار.
عدة مؤشرات معًا: نموذج VAR
حين تريد التنبؤ بعدة مؤشرات يؤثر بعضها في بعض، كالناتج والاستهلاك والاستثمار، يعامل نموذج متجه الانحدار الذاتي (VAR) كل متغير على أنه داخلي، ويفسر قيمته الحالية بقيمه المتأخرة وقيم المتغيرات الأخرى المتأخرة. يشترط النموذج متغيرات مستقرة، لذلك نستخدم معدلات النمو بدل المستويات. يستخدم المثال بيانات macrodata ربع السنوية المدمجة في statsmodels:
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.api import VAR
# US quarterly macro data shipped with statsmodels (starts 1959Q1)
data = sm.datasets.macrodata.load_pandas().data
data.index = pd.date_range("1959-01-01", periods=len(data), freq="QS")
# Stationary transformation: quarterly log growth rates
X = np.log(data[["realgdp", "realcons", "realinv"]]).diff().dropna()
model = VAR(X)
print(model.select_order(maxlags=8).summary()) # AIC, BIC, HQIC by lag
res = model.fit(maxlags=8, ic="aic")
# Does investment growth help predict GDP growth? (Granger F-test)
print(res.test_causality("realgdp", ["realinv"], kind="f").summary())
# 8-quarter forecast with 95% intervals, then impulse responses
point, lower, upper = res.forecast_interval(X.values[-res.k_ar:], steps=8, alpha=0.05)
res.irf(10).plot(orth=True)
أربع ملاحظات قبل أن تعتمد على النتائج. عدد المعالم يتضاعف سريعًا: مع k متغيرات وp فجوات يقدر النموذج نحو k²p معاملًا، فلا تضع فيه كل ما لديك من مؤشرات. وسببية غرانجر (Granger Causality) تعني أن ماضي متغير يحسن التنبؤ بآخر، لا أنه يسببه بالمعنى الذي يهم صانع السياسات. ودوال الاستجابة النبضية المتعامدة تتأثر بترتيب المتغيرات في النموذج، فاذكر الترتيب ومبرره. وإذا كانت المتغيرات غير مستقرة لكنها متكاملة تكاملًا مشتركًا (Cointegrated)، فنموذج تصحيح الخطأ المتجهي (VECM) أنسب من أخذ الفروق وفقدان العلاقة طويلة الأمد. في R توفر حزمة vars الأدوات نفسها، كما تقدم fable الدالة VAR().
كيف تنقل عدم اليقين إلى صانع القرار
- ابدأ بالنطاق لا بالرقم: «نتوقع أن يتراوح الطلب في الربع القادم بين كذا وكذا باحتمال 80% تقريبًا، وأرجح قيمة هي كذا». فترة 80% أسهل في النقاش الإداري من 95%، بشرط أن تذكر ما تعنيه.
- استخدم مخطط المروحة (Fan Chart): يظهر اتساع عدم اليقين مع الأفق أوضح من أي جدول.
- افصل النموذج عن الافتراضات: ما لا يعرفه النموذج، كقرار سياسي مرتقب أو صدمة خارجية، يعرض في سيناريوهات منفصلة مع افتراضاتها الصريحة.
- اعرض سجل الدقة: نتائج الاختبار الرجعي ومقارنتها بالنموذج الساذج تبني الثقة أكثر من أي وصف تقني للنموذج.
- اسأل عن كلفة الخطأ: إذا كان التنبؤ بأقل من الواقع أكلف من التنبؤ بأكثر منه، كما في مخزون الأدوية، فقد يكون المطلوب قيمة عند مئين مرتفع لا المتوسط.
- تجنب الدقة الزائفة: تنبؤ لسنة قادمة بكسور عشرية يوحي بيقين غير موجود؛ اكتب الأرقام بالتقريب الذي تسمح به الدقة الفعلية.
أخطاء شائعة
- تقييم النموذج على بيانات التدريب نفسها، ثم الإعلان عن دقة لن تتكرر.
- استخدام التحقق المتقاطع العشوائي مع بيانات زمنية.
- الاعتماد على MAPE مع سلاسل فيها أصفار أو قيم صغيرة.
- الإفراط في أخذ الفروق، أو مقارنة AICc بين نماذج بدرجات فروق مختلفة.
- غياب نموذج ساذج للمقارنة، فلا يعرف القارئ إن كان النموذج المعقد أضاف شيئًا.
- تجاهل أثر التقويم: عدد أيام العمل في الشهر، وانتقال رمضان والأعياد نحو 11 يومًا في التقويم الميلادي كل عام، وهو ما لا تلتقطه الموسمية الشهرية الثابتة.
- تفسير سببية غرانجر في VAR على أنها علاقة سببية.
- تقديم تنبؤ نقطي دون فترة، أو بفترة لم تختبر تغطيتها الفعلية.
أسئلة شائعة
كم مشاهدة أحتاج لبناء نموذج تنبؤ موثوق؟
لا يوجد عدد ثابت؛ يتوقف الأمر على مستوى الضوضاء وعدد المعالم المقدرة. النماذج الموسمية تحتاج عدة دورات كاملة لتقدير النمط الموسمي بثبات، ومع السلاسل القصيرة تكون النماذج البسيطة، مثل ETS أو النموذج الساذج الموسمي، أكثر أمانًا من نماذج كثيرة المعالم.
أيهما أفضل: ARIMA أم ETS؟
لا يتفوق أحدهما دائمًا. تتعامل ETS بسهولة مع الموسمية الضربية والاتجاه المخمد، وتمتاز ARIMA بمرونتها مع الارتباط الذاتي وإمكان إضافة متغيرات خارجية في الانحدار الديناميكي. احسم الاختيار بالاختبار الرجعي، وجرب متوسط تنبؤات النموذجين، فدمج التنبؤات كثيرًا ما يعطي أداء أكثر استقرارًا من نموذج واحد.
هل يتفوق التعلم الآلي على النماذج الإحصائية في التنبؤ؟
مع سلسلة واحدة قصيرة يصعب غالبًا التفوق على نموذج إحصائي جيد. تظهر قيمة نماذج مثل التعزيز التدرجي حين تكون لديك سلاسل كثيرة مترابطة ومتغيرات تفسيرية غنية، بشرط الحفاظ على التقييم بأصل متدحرج نفسه. تجد التفصيل في مقال التعلم الآلي والنماذج التنبؤية.
لماذا يتجاوز الواقع فترات التنبؤ أكثر من المتوقع؟
لأن الفترات تفترض أن النموذج صحيح وأن المستقبل يشبه الماضي، ولا تحتسب عادةً عدم اليقين في المعالم ولا الكسور الهيكلية. قس التغطية الفعلية في الاختبار الرجعي، وجرب فترات البوتستراب، واعرض سيناريوهات منفصلة للأحداث التي لا يعرفها النموذج.
التنبؤ الجيد نطاق صادق اختبر على الماضي ويفهمه من سيستخدمه، لا رقم يبدو دقيقًا. يعمل فريق مركز المستشار بلغتي Python وR على بناء نماذج السلاسل الزمنية واختبارها رجعيًا وعرض نتائجها في تقارير ولوحات تفاعلية؛ وإن كان لديك مؤشر تريد التنبؤ به، فاطلع على خدمة تحليل البيانات المتقدم، والاستشارة الأولى مجانية.
