Pandas: دوال النوافذ

آخر تحديث: 2026-08-26

الدرس 17 قدّم الدوال rolling/expanding/ewm. يتعمّق هذا الدرس أكثر: التجميع بنوافذ مخصّصة، ونوافذ الوقت المتقدمة، وعلاقات معاملات ewm، وتركيبات groupby مع rolling. الدوال النافذة هي أداة أساسية في التحليل الكمي ومعالجة بيانات الاستشعار — تعلّمها جيداً يعني تعلّم القدرة على "رؤية الاتجاهات من خلال عدسة منزلقة."

⚠️ ملاحظة: يجب تشغيل التعليمات البرمجية أدناه في بيئة بايثون محلية.

1. What You Will Learn


2. تحليل نافذة معدل نبضات قلب كارول أثناء التمرين

(3) المشكلة (1): متوسط النافذة لمدة 30 ثانية ليس كافيًا

تحتاج بيانات معدل نبضات قلب كارول أثناء التمرين إلى إحصائيات نافذة أكثر تفصيلاً — ليس المتوسط فقط، بل أيضًا الحد الأقصى وتنوع معدل ضربات القلب (الانحراف المعياري):

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
hr = pd.DataFrame({
    'heart_rate': np.random.normal(75, 10, 60).round(0)
}, index=pd.date_range('2024-01-15 08:00', periods=60, freq='30s'))
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas.

(4) الحل (2): rolling مع عمليات تجميع متعددة

▶ مثال: إحصائيات متعددة المعايير للدوال المتحركة (الصعوبة ⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
hr = pd.DataFrame({
    'heart_rate': np.random.normal(75, 10, 60).round(0)
}, index=pd.date_range('2024-01-15 08:00', periods=60, freq='30s'))

# الدوال المتحركة مع عمليات تجميع متعددة
stats = hr['heart_rate'].rolling(10).agg(['mean', 'std', 'min', 'max'])
stats.columns = ['hr_ma', 'hr_std', 'hr_min', 'hr_max']
print(stats.dropna().head(5).round(1))
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas.

3. المتقدمة للأسطوانات

(1) المعاملات الرئيسية

▶ مثال

TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

: مرجع معاملات الأسطوانات (الصعوبة ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
s = pd.Series(np.random.randn(20).cumsum() + 100)

# min_periods: allow partial windows
print(s.rolling(5, min_periods=2).mean().head(5))

# center: centered window (for smoothing)
print(s.rolling(5, center=True).mean().head(5))

# win_type: weighted window (Gaussian, triangular, etc.)
print(s.rolling(5, win_type='gaussian').mean(std=1.5).head(8))

# on: use a column as time (when not the index)
df = pd.DataFrame({
    'timestamp': pd.date_range('2024-01', periods=20, freq='6h'),
    'value': np.random.randn(20).cumsum()
})
print(df.rolling('12h', on='timestamp')['value'].mean().head(5))
TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

(2) مرجع سريع لمعاملات الأسطوانات

المعامل الافتراضي الوصف
window مطلوب حجم النافذة (عدد صحيح من الصفوف أو سلسلة زمنية)
min_periods window الحد الأدنى من الملاحظات المطلوبة في النافذة
center False تمركز النافذة
win_type None نوع الترجيح (gaussian/triangular، إلخ)
on None استخدام عمود كمحور زمني (بدلاً من الفهرس)
closed 'right' أي طرف من النافذة يكون متضمنًا

4. تجميع النافذة المخصصة

▶ مثال

TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

: دوال التجميع المخصصة (الصعوبة ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'value': np.random.randint(10, 100, 20)
})

# Custom: range (max - min) in window
def window_range(arr):
    return arr.max() - arr.min()

df['rolling_range'] = df['value'].rolling(5).apply(window_range, raw=True)

# Custom: coefficient of variation (std/mean)
def cv(arr):
    return arr.std() / arr.mean() if arr.mean() != 0 else 0

df['rolling_cv'] = df['value'].rolling(5).apply(cv, raw=True)

# Using rolling.apply with raw=True for performance
# raw=True: receives numpy array (fast)
# raw=False: receives Series (slow but flexible)

print(df.head(10))
TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
💡 نصيحة: استخدام raw=True في الدوال المخصصة أسرع بحوالي 10 مرات (يستقبل مصفوفة NumPy بدلاً من Series)، لكنك تفقد الوصول إلى معلومات الفهرس. استخدم raw=True للحسابات الرقمية البسيطة؛ استخدم raw=False عندما تحتاج إلى الفهرس.


5. التوسع بالتفصيل

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا. يرجى تثبيته محليًا (`pip install pandas`) والمتابعة. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas.

: دوال توسع مخصّصة (الصعوبة ⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'score': np.random.randint(60, 100, 15)
})

# Built-in expanding methods
df['cummean'] = df['score'].expanding().mean()
df['cumstd'] = df['score'].expanding().std()
df['cummax'] = df['score'].expanding().max()

# Expanding with min_periods
df['cummean_5'] = df['score'].expanding(min_periods=5).mean()

# Custom: expanding z-score
def zscore(arr):
    if len(arr) < 2: return np.nan
    return (arr[-1] - arr.mean()) / arr.std()

df['zscore'] = df['score'].expanding().apply(zscore, raw=True)
print(df.round(2).head(10))
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا. يرجى تثبيته محليًا (`pip install pandas`) والمتابعة. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas.

6. توضيحات معاملات ewm

(1) العلاقات بين المعاملات الأربعة

المعامل العلاقة المعنى
span com = span - 1 مشابه لمتوسط SMA لـ N فترة
com α = 1/(1+com) مركز الكتلة
halflife α = 1 - 0.5^(1/halflife) عدد الفترات لتصبح الوزن نصفها
alpha يحدد مباشرة معامل التنعيم 0<α≤1

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python المحلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً. يرجى تثبيته محليًا (`pip install pandas`) واتباع الشروحات. قد تختلف القيم الفعلي قليلاً اعتماداً على إصدار pandas.

: مقارنة معاملات ewm (الصعوبة ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
s = pd.Series(np.random.randn(30).cumsum() + 100)

# هذه جميعها متكافئة (تقريبيًا)
ewm_span5 = s.ewm(span=5).mean()
ewm_com4 = s.ewm(com=4).mean()          # com = span - 1
ewm_hl3 = s.ewm(halflife=3).mean()      # وزن مختلف
ewm_alpha = s.ewm(alpha=0.333).mean()   # alpha = 2/(span+1)

# مقارنة مع SMA
sma5 = s.rolling(5).mean()

print("SMA مقارنة مع EWM (أول 10 قيم):")
print(pd.DataFrame({
    'القيمة': s, 'SMA_5': sma5, 'EWM_span5': ewm_span5
}).head(10).round(2))

# الانحراف المعياري ewm (التقلب)
ewm_std = s.ewm(span=10).std()
print(f"\nآخر انحراف معياري لـ EWM: {ewm_std.iloc[-1]:.2f}")
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python المحلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً. يرجى تثبيته محليًا (`pip install pandas`) واتباع الشروحات. قد تختلف القيم الفعلي قليلاً اعتماداً على إصدار pandas.

7. تجميع مع النوافذ المتحركة (groupby + rolling)

▶ مثال

TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) والعمل مع الشروحات. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار المكتبة.

: النوافذ المتحركة داخل المجموعات (الصعوبة ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# بيانات المستشعرات: مستشعرات متعددة، لكل منها سلسلة زمنية خاصة به
np.random.seed(42)
df = pd.DataFrame({
    'sensor_id': np.repeat(['T01', 'T02', 'T03'], 20),
    'timestamp': list(pd.date_range('2024-01', periods=20, freq='6h')) * 3,
    'temperature': np.random.normal(20, 3, 60).round(1)
})

# التدوير داخل كل مجموعة مستشعرات
df = df.sort_values(['sensor_id', 'timestamp'])
df['temp_ma'] = df.groupby('sensor_id')['temperature'].transform(
    lambda x: x.rolling(4, min_periods=1).mean()
)

# بديل: groupby + rolling (يعيد مؤشرًا متعدد المستويات)
result = df.groupby('sensor_id')['temperature'].rolling(4).mean()
print(result.head(12))

# إعادة الضبط للحصول على خرج نظيف
df['temp_ma2'] = df.groupby('sensor_id')['temperature'].transform(
    lambda x: x.rolling(4).mean().values
)
print(df[df['sensor_id'] == 'T01'].head(6))
TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون المحلية الخاصة بك (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) والعمل مع الشروحات. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار المكتبة.

8. مثال كامل: تحليل بيانات معدل ضربات القلب أثناء التمرين

(5) ▶ مقارنة ثلاثة أنواع من النوافذ

100%
graph LR
    A[Window Functions] --> B[rolling Fixed Window]
    A --> C[expanding Cumulative Window]
    A --> D[ewm Exponential Weighting]
    B --> E[Local Trends / Moving Average]
    C --> F[Cumulative Stats / From Start to Now]
    D --> G[Recent Data Weighted More / Smoothing]
TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

▶ مثال

TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

: خط تحليل النوافذ الكامل (الصعوبة ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# مثال شامل: اللياقة البدنية معدل ضربات القلب
# خط تحليل النوافذ
# ============================================

# 1. إنشاء بيانات تمرين لمدة ساعتين (قراءة واحدة كل 30 ثانية = 240 صفًا)
np.random.seed(42)
timestamps = pd.date_range('2024-01-15 08:00', periods=240, freq='30s')
exercise_type = (['Warmup'] * 30 + ['Cardio'] * 90 + ['Weights'] * 60 + ['Cooldown'] * 60)
hr_base = ([70] * 30 + list(np.linspace(70, 140, 90)) + [120] * 60 + list(np.linspace(120, 70, 60)))
heart_rates = [max(50, h + np.random.randn() * 5) for h in hr_base]

df = pd.DataFrame({
    'timestamp': timestamps,
    'exercise': exercise_type,
    'heart_rate': np.round(heart_rates, 0)
})

# 2. متوسط متحرك متنقل: 1 دقيقة (2 قراءة) و 5 دقائق (10 قراءات)
df['hr_ma_1min'] = df['heart_rate'].rolling(2).mean()
df['hr_ma_5min'] = df['heart_rate'].rolling(10).mean()

# 3. متوسط متحرك متنقل: تباين معدل ضربات القلب (الانحراف المعياري في نافذة 5 دقائق)
df['hrv'] = df['heart_rate'].rolling(10).std()

# 4. متوسط متحرك متوسع: المتوسط التراكمي
df['cum_avg_hr'] = df['heart_rate'].expanding().mean()

# 5. المتوسط المتحرك الأسّي: اتجاه مُنعم
df['hr_ewm'] = df['heart_rate'].ewm(span=10).mean()

# 6. تجميع + متوسط متحرك متنقل: لكل نوع تمرين
df['hr_ma_per_exercise'] = df.groupby('exercise')['heart_rate'].transform(
    lambda x: x.rolling(5, min_periods=1).mean()
)

# 7. ملخص حسب التمرين
print("=== ملخص التمرين ===")
summary = df.groupby('exercise')['heart_rate'].agg(['mean', 'max', 'min', 'std']).round(1)
print(summary)
TEXT 📖 للعرض فقط
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.

❓ أسئلة شائعة

س هل يدعم التدوير نوافذ مبنية على الوقت؟
ج نعم — rolling('7D') يستخدم نافذة تقويمية مدتها 7 أيام، بينما rolling(7) يستخدم نافذة مكونة من 7 صفوف. النوافذ المبنية على الوقت مثالية للبيانات المأخوذة بعينات غير منتظمة (مع تخطي عطل نهاية الأسبوع أو الأيام المفقودة)، بينما النوافذ المبنية على الصفوف تناسب أخذ العينات المنتظمة. تتطلب النوافذ المبنية على الوقت أن يكون الفهرس هو DatetimeIndex، أو يمكنك تحديد عمود الوقت باستخدام معامل on.
س كيف هي أداء الدوال المخصصة؟
ج الدوال المخصصة أبطأ بعشر إلى مئة مرة مقارنة بالجمعات المدمجة — يتم استدعاء دالة Python لكل نافذة. نصائح التحسين: استخدم raw=True لتمرير مصفوفة NumPy (أسرع بـ5-10 مرات)، واستبدل حلقات Python بعمليات NumPy. الدوال المدمجة مُنفّذة بلغة C، بينما الدوال المخصصة تمر عبر مترجم Python. إذا كانت دالة مدمجة تؤدي المهمة، تجنب كتابة دالة مخصصة.
س ما العلاقة بين span/halflife/com في ewm؟
ج الثلاثة متكافئون رياضياً — مجرد طرق مختلفة للتعبير عن نفس الشيء. span=N يتصرف كمتوسط متحرك بسيط لفترة N ولكن مع وزن أكبر للبيانات الحديثة؛ com=span-1 هو مركز الكتلة؛ halflife هو عدد الفترات حتى يتقلص الوزن إلى النصف. span هو الأكثر استخداماً (الأكثر بديهية). α=2/(span+1) هو معامل التنعيم. تحتاج فقط إلى تحديد واحد — لا حاجة لتعيين عدة معاملات في آن واحد.
س ما الذي يجب أن أنتبه إليه عند استخدام groupby+rolling؟
ج نقطتان أساسيتان: ① دائمًا قم بفرز القيم sort_values حسب مفتاح المجموعة + الوقت أولاً، وإلا قد تمتد النافذة عبر المجموعات؛ ② transform(lambda x: x.rolling(N).mean()) يُرجع نتيجة بنفس طول DataFrame الأصلي، بينما استدعاء groupby+rolling مباشرة يُرجع MultiIndex. لمجموعات البيانات الكبيرة، transform أكثر ملاءمة.
س ما خيارات win_type المتاحة؟
ج جميع دوال النوافذ من scipy.signal.windows: gaussian (وزن غاوسي)، triang (مثلثي)، blackman، hamming، kaiser، وغيرها. يتطلب pip install scipy. win_type يطبق أوزاناً على البيانات داخل النافذة (وزن أعلى في الوسط، وأقل على الحواف)، مما ينتج نتائج أكثر سلاسة من نافذة بوزن متساوٍ.
س ما وظيفة معامل closed؟
ج closed يتحكم في أي طرف من النافذة يكون مُشملاً — 'right' (الافتراضي، يشمل الطرف الأيمن = الصف الحالي)، 'left' (يشمل الطرف الأيسر)، 'both' (كلا الطرفين)، 'neither' (لا أحد من الطرفين). في التمويل، يُستخدم 'right' عادةً (بيانات تاريخية فقط)، بينما تتطلب بعض السيناريوهات الإحصائية 'both' أو 'left'.

📖 ملخص


📝 تمارين

  1. أساسي (مستوى الصعوبة ⭐): أنشئ بيانات درجة حرارة لمدة 30 يومًا. استخدم rolling(7, min_periods=3) لحساب المتوسط المتحرك والانحراف المعياري، واستخدم ewm(span=7) لحساب التنعيم الأسي.
  2. متوسط (مستوى الصعوبة ⭐⭐): أنشئ بيانات درجة حرارة متعددة المستشعرات (3 مستشعرات، 20 صفًا لكل منها). استخدم groupby+rolling لحساب المتوسط المتحرك لفترة 5 لكل مستشعر.
  3. تحدي (مستوى الصعوبة ⭐⭐⭐): قم بمحاكاة 240 صفًا من بيانات معدل ضربات القلب أثناء التمارين (مع 4 أنواع تمارين). أكمل خط المعالجة التالي: rolling متعدد المقاييس (المتوسط/الانحراف المعياري/الأقصى) → المتوسط التراكمي المُوسَّع (expanding) → التنعيم الأسي (ewm) → groupby+rolling حسب نوع التمرين → تقرير ملخص.

← Previous Lesson: MultiIndex · Next Lesson: Visualization →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%