تحليل السلاسل الزمنية: فهم اتجاهات الآلة عبر الزمن
ما هي السلاسل الزمنية؟
السلسلة الزمنية هي سلسلة من القياسات المأخوذة على فترات منتظمة عبر الزمن. في الصناعة، كل مستشعر ينتج سلسلة زمنية:
- قراءات الاهتزاز كل ثانية
- درجة حرارة المحرك كل دقيقة
- معدل الإنتاج كل ساعة
خصائص السلاسل الزمنية الصناعية
- الاتجاه (Trend): تغير طويل المدى (تآكل تدريجي)
- الموسمية (Seasonality): أنماط متكررة (دورة تشغيل يومية)
- الضوضاء (Noise): تقلبات عشوائية
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# محاكاة بيانات اهتزاز محرك لمدة 6 أشهر
np.random.seed(42)
n_days = 180
n_readings = n_days * 24 # قراءة كل ساعة
timestamps = pd.date_range('2025-01-01', periods=n_readings, freq='h')
# بناء المكونات
trend = np.linspace(2.0, 3.5, n_readings) # تآكل تدريجي
seasonality = 0.3 * np.sin(2 * np.pi * np.arange(n_readings) / 24) # دورة يومية
noise = np.random.normal(0, 0.15, n_readings)
vibration = trend + seasonality + noise
ts = pd.DataFrame({'timestamp': timestamps, 'vibration': vibration})
ts.set_index('timestamp', inplace=True)
print(f"الفترة: {ts.index[0].date()} إلى {ts.index[-1].date()}")
print(f"عدد القراءات: {len(ts)}")
التحليل الموسمي: الاتجاه والموسمية والضوضاء
تحليل المكونات يكشف الأنماط المخفية في البيانات:
from statsmodels.tsa.seasonal import seasonal_decompose
# تحليل موسمي (دورة يومية = 24 ساعة)
decomposition = seasonal_decompose(ts['vibration'], model='additive', period=24)
fig, axes = plt.subplots(4, 1, figsize=(12, 10))
axes[0].plot(decomposition.observed[:720], linewidth=0.5)
axes[0].set_title('البيانات الأصلية (أول 30 يوم)')
axes[0].set_ylabel('mm/s')
axes[1].plot(decomposition.trend[:720], linewidth=1, color='red')
axes[1].set_title('الاتجاه (Trend)')
axes[1].set_ylabel('mm/s')
axes[2].plot(decomposition.seasonal[:168], linewidth=1, color='green')
axes[2].set_title('الموسمية (أسبوع واحد)')
axes[2].set_ylabel('mm/s')
axes[3].plot(decomposition.resid[:720], linewidth=0.5, color='gray')
axes[3].set_title('البقايا/الضوضاء')
axes[3].set_ylabel('mm/s')
plt.tight_layout()
plt.savefig('seasonal_decomposition.png', dpi=150)
plt.show()
المتوسط المتحرك والتنعيم الأسي
أدوات بسيطة وفعالة لتنعيم البيانات وكشف الاتجاه:
# المتوسط المتحرك بنوافذ مختلفة
ts['ma_24h'] = ts['vibration'].rolling(window=24).mean() # يوم
ts['ma_168h'] = ts['vibration'].rolling(window=168).mean() # أسبوع
# التنعيم الأسي: يعطي وزناً أكبر للقراءات الحديثة
ts['ema_24h'] = ts['vibration'].ewm(span=24).mean()
plt.figure(figsize=(12, 5))
plt.plot(ts.index[:720], ts['vibration'][:720], alpha=0.3, label='بيانات خام')
plt.plot(ts.index[:720], ts['ma_24h'][:720], label='متوسط متحرك 24h', linewidth=2)
plt.plot(ts.index[:720], ts['ema_24h'][:720], label='تنعيم أسي 24h', linewidth=2)
plt.xlabel('الزمن')
plt.ylabel('الاهتزاز (mm/s)')
plt.title('تنعيم بيانات الاهتزاز')
plt.legend()
plt.tight_layout()
plt.savefig('smoothing.png', dpi=150)
plt.show()
# كشف تجاوز حد الإنذار بعد التنعيم
alert_threshold = 3.0
alerts = ts[ts['ma_24h'] > alert_threshold]
if len(alerts) > 0:
print(f"تجاوز حد الإنذار ({alert_threshold} mm/s) بدءاً من: {alerts.index[0]}")
نموذج ARIMA: التنبؤ الكلاسيكي
ARIMA هو النموذج الكلاسيكي للتنبؤ بالسلاسل الزمنية:
from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings('ignore')
# استخدام بيانات يومية (متوسط يومي) للتبسيط
daily = ts['vibration'].resample('D').mean()
# تقسيم: آخر 30 يوم للاختبار
train = daily[:-30]
test = daily[-30:]
# بناء نموذج ARIMA
model = ARIMA(train, order=(2, 1, 2))
fitted = model.fit()
# التنبؤ بـ 30 يوم
forecast = fitted.forecast(steps=30)
# تقييم الدقة
from sklearn.metrics import mean_absolute_error
mae = mean_absolute_error(test, forecast)
print(f"متوسط الخطأ المطلق: {mae:.4f} mm/s")
# رسم التنبؤ
plt.figure(figsize=(12, 5))
plt.plot(train.index[-60:], train[-60:], label='بيانات التدريب')
plt.plot(test.index, test, label='بيانات فعلية', color='green')
plt.plot(test.index, forecast, label='التنبؤ', color='red', linestyle='--')
plt.xlabel('التاريخ')
plt.ylabel('الاهتزاز (mm/s)')
plt.title('تنبؤ ARIMA بمستوى الاهتزاز')
plt.legend()
plt.tight_layout()
plt.savefig('arima_forecast.png', dpi=150)
plt.show()
Prophet: تنبؤ بسيط وقوي
Prophet من Meta مصمم للتنبؤ العملي بأقل إعداد:
# Prophet يتطلب أعمدة بأسماء محددة: ds (التاريخ) و y (القيمة)
prophet_df = daily.reset_index()
prophet_df.columns = ['ds', 'y']
# تقسيم البيانات
train_prophet = prophet_df[:-30]
test_prophet = prophet_df[-30:]
# ملاحظة: يتطلب تثبيت prophet
# pip install prophet
try:
from prophet import Prophet
model_prophet = Prophet(daily_seasonality=False, yearly_seasonality=False)
model_prophet.fit(train_prophet)
future = model_prophet.make_future_dataframe(periods=30)
forecast_prophet = model_prophet.predict(future)
# عرض النتائج
forecast_30 = forecast_prophet.tail(30)['yhat'].values
mae_prophet = mean_absolute_error(test_prophet['y'], forecast_30)
print(f"متوسط الخطأ المطلق (Prophet): {mae_prophet:.4f} mm/s")
except ImportError:
print("مكتبة Prophet غير مثبتة. استخدم: pip install prophet")
print("يمكن الاكتفاء بـ ARIMA كبديل فعال")
مثال عملي: توقع موعد صيانة محرك من بيانات الاهتزاز
سنبني نظاماً يتنبأ بموعد وصول الاهتزاز لحد الخطر:
import pandas as pd
import numpy as np
from statsmodels.tsa.arima.model import ARIMA
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
# بيانات اهتزاز محرك: تآكل تدريجي مع ضوضاء
np.random.seed(42)
n_days = 120
dates = pd.date_range('2025-01-01', periods=n_days, freq='D')
# اهتزاز يزداد تدريجياً (محاكاة تآكل المحامل)
base = 2.0
growth_rate = 0.015
vibration_daily = base + growth_rate * np.arange(n_days) + np.random.normal(0, 0.1, n_days)
motor_ts = pd.Series(vibration_daily, index=dates)
# حد الصيانة المطلوبة
maintenance_threshold = 4.5
# تدريب ARIMA على البيانات المتاحة
model = ARIMA(motor_ts, order=(1, 1, 1))
fitted = model.fit()
# التنبؤ بـ 90 يوم مستقبلي
forecast_days = 90
future_forecast = fitted.forecast(steps=forecast_days)
future_dates = pd.date_range(motor_ts.index[-1] + pd.Timedelta(days=1),
periods=forecast_days, freq='D')
# متى سيصل الاهتزاز لحد الصيانة؟
exceeds = future_forecast[future_forecast > maintenance_threshold]
if len(exceeds) > 0:
maintenance_date = future_dates[future_forecast.values > maintenance_threshold][0]
days_remaining = (maintenance_date - motor_ts.index[-1]).days
print(f"حد الصيانة ({maintenance_threshold} mm/s) سيتم تجاوزه في: {maintenance_date.date()}")
print(f"الأيام المتبقية: {days_remaining} يوم")
print(f"التوصية: جدولة الصيانة قبل {maintenance_date.date()}")
else:
print("الاهتزاز لن يتجاوز حد الصيانة خلال 90 يوم القادمة")
# رسم التنبؤ مع حد الصيانة
plt.figure(figsize=(14, 5))
plt.plot(motor_ts.index, motor_ts, label='بيانات فعلية', color='blue')
plt.plot(future_dates, future_forecast, label='تنبؤ', color='red', linestyle='--')
plt.axhline(y=maintenance_threshold, color='orange', linestyle='-.',
label=f'حد الصيانة ({maintenance_threshold} mm/s)')
plt.fill_between(future_dates, future_forecast * 0.95, future_forecast * 1.05,
alpha=0.2, color='red', label='هامش عدم اليقين')
plt.xlabel('التاريخ')
plt.ylabel('الاهتزاز (mm/s)')
plt.title('التنبؤ بموعد الصيانة القادمة')
plt.legend()
plt.tight_layout()
plt.savefig('maintenance_forecast.png', dpi=150)
plt.show()
الخلاصة
السلاسل الزمنية هي لغة البيانات الصناعية. تعلمنا تحليل مكوناتها، وتنعيمها، والتنبؤ بقيمها المستقبلية. الجمع بين التنبؤ وحدود الصيانة يمكّننا من جدولة الصيانة الوقائية بدقة، مما يقلل التوقفات غير المخطط لها. في الدرس القادم سنتعلم كيفية تقييم نماذج التعلم الآلي بشكل شامل.