معالجة بيانات السلاسل الزمنية لدى الوكلاء
النوافذ المتحركة، والتجميع، واكتشاف الشذوذ في بيانات المستشعرات المتدفقة.
معالجة بيانات السلاسل الزمنية لدى الوكلاء درس مجاني في AI Agents على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Agents، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Agents 4 دروس في المجموع.
السلاسل الزمنية في وكلاء إنترنت الأشياء
تصل بيانات المستشعر على شكل سلسلة زمنية: وهي تسلسل من أزواج (طابع زمني، قيمة). وتحتوي قراءات المستشعر الأولية على ضوضاء وفجوات وقفزات عرضية. وغالبًا ما يطلق الوكلاء الذين يتصرفون بناءً على البيانات الأولية دون معالجتها إنذارات كاذبة أو يفوّتون أحداثًا حقيقية.
تحوّل معالجة السلاسل الزمنية الإشارات الأولية إلى رؤى قابلة للتنفيذ.
بناء مخزن مؤقت بنافذة متحركة
تحتفظ النافذة المتحركة بآخر N قراءات فقط في الذاكرة. وعندما تمتلئ النافذة، تُحذف أقدم قراءة عند وصول الأحدث. ويشكّل ذلك الأساس لجميع تحليلات السلاسل الزمنية في الوكلاء.
from collections import deque
from datetime import datetime
class SensorBuffer:
def __init__(self, window_size: int = 60):
self.window_size = window_size
self._data = deque(maxlen=window_size)
def add(self, value: float, timestamp: datetime = None):
ts = timestamp or datetime.utcnow()
self._data.append({'ts': ts, 'value': value})
def values(self) -> list:
return [d['value'] for d in self._data]
def timestamps(self) -> list:
return [d['ts'] for d in self._data]
def is_full(self) -> bool:
return len(self._data) == self.window_size
buf = SensorBuffer(window_size=60)
buf.add(22.5)
buf.add(22.8)
buf.add(23.1)
print(f'Buffer: {len(buf._data)} readings, values: {buf.values()}')المتوسط المتحرك
يعمل المتوسط المتحرك البسيط (SMA) على تنعيم الضوضاء من خلال حساب متوسط آخر N من القيم. ويقلل تأثير الأعطال الفردية في المستشعر ويكشف الاتجاه الأساسي. استخدمه كخط أساس لاكتشاف الحالات الشاذة.
import statistics
def simple_moving_average(values: list, window: int = 10) -> list:
if len(values) < window:
return []
return [
statistics.mean(values[i - window:i])
for i in range(window, len(values) + 1)
]
def exponential_moving_average(values: list, alpha: float = 0.2) -> list:
"""EMA weights recent values more heavily."""
if not values:
return []
ema = [values[0]]
for v in values[1:]:
ema.append(alpha * v + (1 - alpha) * ema[-1])
return ema
readings = [22.1, 22.3, 22.0, 35.0, 22.2, 22.4, 22.1, 22.3, 22.5, 22.2, 22.4]
sma = simple_moving_average(readings, window=5)
ema = exponential_moving_average(readings, alpha=0.2)
print(f'SMA (last 3): {[round(v,2) for v in sma[-3:]]}')
print(f'EMA (last 3): {[round(v,2) for v in ema[-3:]]}')اكتشاف القفزات
القفزة هي قراءة تنحرف عن الاتجاه الأخير بأكثر من N من الانحرافات المعيارية (باستخدام طريقة درجة z). وهذا هو الأسلوب الأكثر شيوعًا لاكتشاف الحالات الشاذة في بيانات المستشعرات.
import statistics
def detect_spikes(
values: list,
window: int = 20,
z_threshold: float = 3.0
) -> list:
"""Returns list of (index, value, z_score) for detected spikes."""
if len(values) < window:
return []
spikes = []
for i in range(window, len(values)):
window_vals = values[i - window:i]
mean = statistics.mean(window_vals)
stdev = statistics.stdev(window_vals)
if stdev == 0:
continue
z_score = abs(values[i] - mean) / stdev
if z_score > z_threshold:
spikes.append({
'index': i,
'value': values[i],
'z_score': round(z_score, 2),
'mean': round(mean, 2)
})
return spikes
data = [22.1, 22.3, 22.0, 22.2, 22.4] * 5 + [55.0] + [22.2, 22.3] * 3
spikes = detect_spikes(data, window=10, z_threshold=3.0)
print('Spikes detected:', spikes)Pandas لتحليل السلاسل الزمنية
لإجراء تحليل أكثر تطورًا، حمّل بيانات المستشعر إلى DataFrame من Pandas باستخدام DatetimeIndex. توفر Pandas عمليات مضمّنة للنوافذ المتحركة وإعادة أخذ العينات والاستيفاء، وهي أسرع بكثير في الكتابة من الحلقات اليدوية.
import pandas as pd
from datetime import datetime, timedelta
# Create a sample time series DataFrame
base_time = datetime(2024, 1, 1, 12, 0, 0)
times = [base_time + timedelta(seconds=i*10) for i in range(20)]
values = [22.1, 22.3, None, 22.0, 22.4, 22.2, 35.0, 22.1,
22.3, 22.2, 22.5, 22.1, None, 22.4, 22.2, 22.3,
22.0, 22.1, 22.4, 22.2]
df = pd.DataFrame({'value': values}, index=pd.DatetimeIndex(times))
df.index.name = 'timestamp'
print('Shape:', df.shape)
print('Missing values:', df['value'].isna().sum())
print(df.head())التعامل مع الطوابع الزمنية المفقودة
غالبًا ما تفوّت شبكات المستشعرات قراءات بسبب مشكلات الاتصال. تستطيع Pandas اكتشاف الفجوات وملأها: تنشئ resample شبكة منتظمة، بينما تملأ interpolate القيم المفقودة خطيًا أو باستخدام الملء بالقيمة السابقة. سجّل دائمًا عدد القيم التي جرى تعويضها.
import pandas as pd
def fill_missing_readings(df: pd.DataFrame, freq: str = '10S') -> pd.DataFrame:
"""
df: DataFrame with DatetimeIndex and 'value' column
freq: expected sampling frequency ('10S' = 10 seconds, '1T' = 1 minute)
"""
original_count = df['value'].notna().sum()
# Resample to regular grid (introduces NaN for missing periods)
df_regular = df.resample(freq).mean()
missing_count = df_regular['value'].isna().sum()
print(f'Missing readings before fill: {missing_count}')
# Forward fill then linear interpolate
df_regular['value'] = df_regular['value'].interpolate(
method='linear', limit=5 # don't fill gaps longer than 5 periods
)
filled_count = df_regular['value'].notna().sum()
print(f'Filled {filled_count - original_count} missing values')
return df_regularإعادة أخذ العينات: التجميع من دقيقة واحدة إلى خمس دقائق
تعمل إعادة أخذ العينات على تقليل البيانات عالية التردد إلى دقة أكثر خشونة. ويقلل ذلك الضوضاء ومتطلبات التخزين. استخدم resample('5T').agg() لحساب الحد الأدنى والحد الأقصى والمتوسط والانحراف المعياري لكل نافذة مدتها خمس دقائق.
import pandas as pd
def resample_to_5min(df: pd.DataFrame) -> pd.DataFrame:
return df.resample('5min').agg({
'value': ['mean', 'min', 'max', 'std', 'count']
}).round(3)
# Example with 1-minute data:
times = pd.date_range('2024-01-01 12:00', periods=30, freq='1min')
import random
random.seed(42)
vals = [22.0 + random.gauss(0, 0.5) for _ in range(30)]
df_1min = pd.DataFrame({'value': vals}, index=times)
df_5min = resample_to_5min(df_1min)
print(df_5min)اكتشاف الاتجاه باستخدام الانحدار الخطي
هل ترتفع درجة الحرارة باطراد أم أنها تتأثر بالضوضاء فحسب؟ طبّق انحدارًا خطيًا على النافذة المتحركة. يشير الميل الموجب إلى اتجاه تصاعدي، كما يؤدي تجاوز الميل لعتبة معينة إلى تشغيل تنبيه قبل بلوغ العتبة نفسها.
def detect_trend(
values: list,
slope_threshold: float = 0.1 # units per second
) -> dict:
import statistics
n = len(values)
if n < 2:
return {'trend': 'insufficient_data'}
x = list(range(n))
x_mean = statistics.mean(x)
y_mean = statistics.mean(values)
numerator = sum((xi - x_mean) * (yi - y_mean) for xi, yi in zip(x, values))
denominator = sum((xi - x_mean) ** 2 for xi in x)
slope = numerator / denominator if denominator != 0 else 0.0
return {
'slope': round(slope, 4),
'trend': 'rising' if slope > slope_threshold
else 'falling' if slope < -slope_threshold
else 'stable',
'alert': abs(slope) > slope_threshold * 2
}
readings = [22.0, 22.5, 23.0, 23.5, 24.0, 24.5, 25.0]
print(detect_trend(readings, slope_threshold=0.3))اتخاذ الوكيل للقرار استنادًا إلى السلاسل الزمنية
اجمع بين اكتشاف القفزات واكتشاف الاتجاه والمتوسط المتحرك لاتخاذ قرار مركب. ويستخدم الوكيل تسلسلًا هرميًا من القواعد: فالقفزات تشغّل إجراءات فورية، والاتجاهات تشغّل تحذيرات، بينما يتولى LLM الحالات الملتبسة.
def analyze_sensor_window(
values: list,
topic: str
) -> dict:
if len(values) < 10:
return {'action': 'collecting_data'}
spikes = detect_spikes(values, window=10, z_threshold=3.0)
trend_info = detect_trend(values[-20:], slope_threshold=0.2)
avg = sum(values[-10:]) / 10
if spikes:
return {
'action': 'IMMEDIATE_ALERT',
'reason': f'Spike detected: {spikes[-1]["value"]} (z={spikes[-1]["z_score"]})',
'severity': 'high'
}
if trend_info['alert']:
return {
'action': 'TREND_WARNING',
'reason': f'Rapid {trend_info["trend"]} trend: slope={trend_info["slope"]}',
'severity': 'medium'
}
return {
'action': 'NORMAL',
'avg_last_10': round(avg, 2),
'trend': trend_info['trend']
}
result = analyze_sensor_window([22.0]*15 + [55.0], 'sensors/temp')
print(result)حفظ السلاسل الزمنية في قاعدة بيانات
لإجراء تحليل طويل الأمد، احفظ قراءات المستشعر في قاعدة بيانات للسلاسل الزمنية. تُعد TimescaleDB (إضافة PostgreSQL) وInfluxDB خيارين شائعين. يتيح استخدام psycopg2 مع TimescaleDB الاستعلام عن البيانات باستخدام SQL القياسي، إلى جانب الدوال الخاصة بالوقت.
import psycopg2
from datetime import datetime
# TimescaleDB connection (standard PostgreSQL connection)
conn = psycopg2.connect(
host='localhost', port=5432, dbname='iot',
user='agent', password='YOUR_DB_PASSWORD'
)
def insert_reading(topic: str, value: float, ts: datetime = None):
ts = ts or datetime.utcnow()
with conn.cursor() as cur:
cur.execute(
'INSERT INTO sensor_readings (time, topic, value) VALUES (%s, %s, %s)',
(ts, topic, value)
)
conn.commit()
def query_last_hour(topic: str) -> list:
with conn.cursor() as cur:
cur.execute(
'SELECT time, value FROM sensor_readings '
'WHERE topic=%s AND time > NOW() - INTERVAL \'1 hour\' '
'ORDER BY time ASC',
(topic,)
)
return cur.fetchall()فترة تهدئة التنبيهات
من دون فترة تهدئة، تؤدي حالة شاذة مستمرة إلى إطلاق مئات التنبيهات في الدقيقة. طبّق فترة تهدئة لكل موضوع: بعد إرسال تنبيه لموضوع ما، امنع إرسال تنبيهات إضافية لهذا الموضوع لمدة N من الثواني.
from datetime import datetime, timedelta
class AlertCooldownManager:
def __init__(self, cooldown_seconds: int = 300):
self.cooldown_seconds = cooldown_seconds
self._last_alert: dict = {} # topic -> last alert datetime
def should_alert(self, topic: str) -> bool:
last = self._last_alert.get(topic)
if last is None:
return True
return (datetime.utcnow() - last).seconds >= self.cooldown_seconds
def mark_alerted(self, topic: str):
self._last_alert[topic] = datetime.utcnow()
def cooldown_remaining(self, topic: str) -> int:
last = self._last_alert.get(topic)
if last is None:
return 0
elapsed = (datetime.utcnow() - last).seconds
return max(0, self.cooldown_seconds - elapsed)
cooldown = AlertCooldownManager(cooldown_seconds=300)
if cooldown.should_alert('sensors/temperature'):
print('Sending alert')
cooldown.mark_alerted('sensors/temperature')
else:
print(f'Cooldown: {cooldown.cooldown_remaining("sensors/temperature")}s remaining')اختبار المعرفة
إلى ماذا تشير درجة z التي تزيد على 3.0 عند استخدامها لاكتشاف القفزات؟
مراجعة: معالجة بيانات السلاسل الزمنية
لقد غطيت مجموعة الأدوات الكاملة لمعالجة السلاسل الزمنية في الوكلاء:
- مخزن مؤقت بنافذة متحركة: استخدام deque مع maxlen لبث البيانات بكفاءة في استهلاك الذاكرة
- المتوسط المتحرك: استخدام SMA وEMA لتقليل الضوضاء
- اكتشاف القفزات: استخدام طريقة درجة z بالاستناد إلى إحصاءات النافذة المتحركة
- Pandas: إعادة أخذ العينات والاستيفاء والتجميع باستخدام DatetimeIndex
- اكتشاف الاتجاه: استخدام ميل الانحدار الخطي للإنذار المبكر
- فترة تهدئة التنبيهات: منع تكرار التنبيهات في الحالات الشاذة المستمرة
التالي: الاستجابات الآلية للوكلاء تجاه أحداث المستشعرات — قوائم انتظار الإجراءات، وإزالة التكرار، وسياسات الإجراءات.
الأسئلة الشائعة
هل درس «معالجة بيانات السلاسل الزمنية لدى الوكلاء» مجاني؟
نعم — نص درس «معالجة بيانات السلاسل الزمنية لدى الوكلاء» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Agents، انتقل إلى CoddyKit PRO. تتضمن دورة AI Agents 4 دروس في المجموع.
ماذا ستتعلم في «معالجة بيانات السلاسل الزمنية لدى الوكلاء»؟
النوافذ المتحركة، والتجميع، واكتشاف الشذوذ في بيانات المستشعرات المتدفقة. تتمرن على AI Agents مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Agents؟
لا تُشترط خبرة سابقة. AI Agents على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «معالجة بيانات السلاسل الزمنية لدى الوكلاء»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Agents هذا؟
نعم. كل درس في AI Agents يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- بروتوكول MQTT لتكامل الوكلاء
- معالجة بيانات السلاسل الزمنية لدى الوكلاء
- الاستجابة الآلية لأحداث المستشعرات
- نشر الوكلاء خفيفي الوزن على الحافة