0Pricing
Pandas & NumPy Academy · درس

الاستيفاء والتعويض المتقدم

استخدم interpolate() للملء السلس المستند إلى الوقت، وتعرّف على الحالات المناسبة للتعويض بالمتوسط مقابل الوسيط.

الاستيفاء والتعويض المتقدم درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

متى يتفوق الاستيفاء على fillna()

ينقل الملء إلى الأمام والخلف أقرب قيمة معروفة دون مراعاة اتجاه البيانات. أما الاستيفاء فيقدّر القيم المفقودة بافتراض انتقال سلس بين القيم المعروفة؛ فمثلًا، إذا كانت درجة الحرارة 20°م يوم الاثنين و30°م يوم الجمعة، يقدّر الاستيفاء قيمتها يوم الأربعاء بـ25°م. وينتج ذلك قيمًا مسندة أكثر واقعية للإشارات التي تتغير بسلاسة، مثل بيانات المستشعرات أو الأسعار أو أعداد السكان.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'day': [1, 2, 3, 4, 5],
    'temp': [20.0, np.nan, np.nan, np.nan, 30.0]
})

# Linear interpolation fills gaps smoothly
df['temp_interp'] = df['temp'].interpolate(method='linear')
print(df)
#    day  temp  temp_interp
# 0    1  20.0         20.0
# 1    2   NaN         22.5
# 2    3   NaN         25.0
# 3    4   NaN         27.5
# 4    5  30.0         30.0

أساليب interpolate()

تدعم interpolate() في Pandas عدة أساليب. وأكثرها شيوعًا 'linear' (توزيع متساوٍ بين القيم المعروفة)، و'time' (يراعي الفواصل الزمنية غير المتساوية عند تعيين DatetimeIndex)، و'polynomial' (يلائم منحنى متعدد الحدود ويتطلب وسيطة order)، و'spline' (متعدد حدود قطعي أملس). يُعدّ الاستيفاء الخطي الخيار الافتراضي الأكثر أمانًا، أما الأساليب ذات الرتب الأعلى فقد تفرط في ملاءمة الفجوات الصغيرة.

import pandas as pd
import numpy as np

s = pd.Series([0, np.nan, np.nan, 8.0])

print('linear:', s.interpolate('linear').tolist())
# [0.0, 2.6666..., 5.3333..., 8.0]

print('polynomial(2):', s.interpolate('polynomial', order=2).tolist())
# [0.0, 2.222..., 5.111..., 8.0]

الاستيفاء الزمني باستخدام DatetimeIndex

عندما يكون لدى Series لديك DatetimeIndex بفواصل زمنية غير متساوية (مثل أيام الأسبوع فقط مع فقدان عطلات نهاية الأسبوع)، يستوفي method='time' القيم بما يتناسب مع الزمن المنقضي الفعلي، لا مع الموضع فحسب. وهذا أدق من الاستيفاء الخطي الذي يعامل كل صف على أنه متباعد بالتساوي بغض النظر عن الفجوة التقويمية.

import pandas as pd
import numpy as np

# Unequal gaps: Jan 1, Jan 3, Jan 10
idx = pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-10'])
s = pd.Series([100.0, np.nan, 170.0], index=idx)

# linear treats gaps as equal (position-based)
print(s.interpolate('linear').tolist())  # [100.0, 135.0, 170.0]

# time accounts for actual day count
# Jan 1 to Jan 3 = 2 days, Jan 1 to Jan 10 = 9 days
# fraction: 2/9 of the way from 100 to 170
print(s.interpolate('time').tolist())   # [100.0, 115.55..., 170.0]

تحديد نطاق الاستيفاء

مثل ffill()، تقبل interpolate() معاملًا باسم limit لتحديد عدد مواضع NaN المتتالية التي تُملأ. وتبقى قيم NaN التي تتجاوز الحد مفقودة. ويمنع ذلك الاستيفاء من الامتداد عبر فجوات طويلة جدًا قد تكون القيمة الحقيقية فيها أي شيء؛ إذ ينبغي وضع علامة على الفجوات الطويلة لمراجعتها يدويًا.

import pandas as pd
import numpy as np

s = pd.Series([1.0, np.nan, np.nan, np.nan, np.nan, 10.0])

# Only fill the first 2 NaN positions
filled = s.interpolate('linear', limit=2)
print(filled.tolist())
# [1.0, 2.8, 4.6, NaN, NaN, 10.0]

اختيار الإسناد بالمتوسط أم بالوسيط

الإسناد بالمتوسط والوسيط بسيط، لكنه ينطوي على مفاضلات مهمة. فالمتوسط حساس للقيم الشاذة؛ إذ ترفعه بعض القيم الكبيرة جدًا، مما يجعله خيارًا سيئًا للملء في توزيع ملتوي إلى اليمين، مثل الدخل أو أسعار المنازل. أما الوسيط فهو متين أمام القيم الشاذة، ويُعدّ الخيار الأفضل للأعمدة الملتوية. استخدم المتوسط فقط عندما تكون بياناتك متماثلة تقريبًا وخالية من القيم المتطرفة.

import pandas as pd
import numpy as np

# Skewed income data with an outlier
income = pd.Series([30000, 35000, 32000, 1_000_000, np.nan])

print('Mean:  ', income.mean())    # ~274000 — pulled by outlier
print('Median:', income.median())  # ~33500 — robust choice

# Prefer median for skewed data
filled = income.fillna(income.median())
print(filled.tolist())
# [30000, 35000, 32000, 1000000, 33500.0]

مفهوم الإسناد باستخدام KNN

يستبدل الإسناد باستخدام أقرب الجيران K-Nearest Neighbour (KNN) القيمة المفقودة بمتوسط (أو متوسط موزون) صفوف k الأكثر تشابهًا، ويُقاس ذلك بالمسافة عبر الميزات غير المفقودة. وهذه استراتيجية متعددة المتغيرات؛ إذ تستخدم معلومات من أعمدة أخرى لتحديد قيمة الملء، ولذلك تكون أدق من الإسناد بمتوسط عمود واحد عندما تكون الميزات مترابطة.

تتكامل KNNImputer في Scikit-learn مباشرةً مع مصفوفات NumPy وDataFrames في Pandas.

import pandas as pd
import numpy as np
from sklearn.impute import KNNImputer

df = pd.DataFrame({
    'age': [25, 35, np.nan, 45],
    'income': [50000, 70000, 60000, np.nan]
})

imputer = KNNImputer(n_neighbors=2)
df_imputed = pd.DataFrame(
    imputer.fit_transform(df),
    columns=df.columns
)
print(df_imputed.round(1))
#     age   income
# 0  25.0  50000.0
# 1  35.0  70000.0
# 2  30.0  60000.0   <- filled from neighbours
# 3  45.0  65000.0   <- filled from neighbours

الإسناد المتعدد باستخدام IterativeImputer

يُعدّ الإسناد المتعدد المعيار الذهبي للتعامل مع البيانات المفقودة في الأبحاث الإحصائية. تطبّق IterativeImputer في Scikit-learn أسلوب MICE (الإسناد المتعدد بالمعادلات المتسلسلة): إذ تنمذج كل عمود يحتوي على قيم مفقودة بوصفه دالة في الأعمدة الأخرى، وتكرر عملية الإسناد حتى تتقارب القيم. يلتقط هذا الأسلوب العلاقات بين الميزات بصورة أفضل من الاستراتيجيات التي تعتمد على عمود واحد.

import pandas as pd
import numpy as np
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import IterativeImputer

df = pd.DataFrame({
    'x1': [1, 2, np.nan, 4, 5],
    'x2': [2, np.nan, 6, 8, 10],
    'y':  [3, 5, 7, np.nan, 11]
})

imp = IterativeImputer(max_iter=10, random_state=0)
df_filled = pd.DataFrame(imp.fit_transform(df), columns=df.columns)
print(df_filled.round(2))

أعمدة المؤشرات للبيانات المفقودة

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

import pandas as pd
import numpy as np

df = pd.DataFrame({'salary': [50000, np.nan, 70000, np.nan, 90000]})

# Add indicator before filling
df['salary_was_missing'] = df['salary'].isna().astype(int)

# Then fill
df['salary'] = df['salary'].fillna(df['salary'].median())
print(df)
#    salary  salary_was_missing
# 0  50000.0                   0
# 1  70000.0                   1
# 2  70000.0                   0
# 3  70000.0                   1
# 4  90000.0                   0

الإسناد وتسرّب البيانات

تتمثل إحدى القواعد المهمة في خطوط أنابيب تعلم الآلة في حساب إحصاءات الإسناد (المتوسط والوسيط والمنوال) على مجموعة التدريب فقط، ثم تطبيق القيم نفسها على مجموعة الاختبار. يؤدي حساب الإحصاءات على مجموعة البيانات كاملة قبل تقسيمها إلى تسرّب البيانات؛ إذ يرى النموذج بيانات الاختبار بصورة غير مباشرة أثناء التدريب، مما يضخم تقديرات الأداء. درّب أدوات الإسناد دائمًا على بيانات التدريب، ثم حوّل بيانات التدريب والاختبار كلتيهما.

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split

df = pd.DataFrame({'feature': [1, 2, np.nan, 4, np.nan, 6, 7, 8]})
train, test = train_test_split(df, test_size=0.25, random_state=0)

# Fit ONLY on training data
imp = SimpleImputer(strategy='mean')
train['feature'] = imp.fit_transform(train[['feature']])

# Transform test using training statistics
test['feature'] = imp.transform(test[['feature']])
print('Train mean used:', imp.statistics_[0])

مقارنة استراتيجيات الإسناد بصريًا

بعد تطبيق عدة أساليب للإسناد، قارن تأثيرها برسم توزيع الأعمدة الأصلية والمسندة جنبًا إلى جنب. ينبغي للإسناد الجيد أن يحافظ على شكل التوزيع الأصلي (المتوسط والتباين والالتواء) بأكبر قدر ممكن. يضيّق الإسناد بالمتوسط التوزيع، بينما تميل KNN وMICE إلى الحفاظ عليه بصورة أفضل.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)
original = np.random.exponential(scale=5, size=200)
with_nan = original.copy()
with_nan[np.random.choice(200, 40, replace=False)] = np.nan

s = pd.Series(with_nan)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
s.dropna().hist(ax=axes[0], bins=20, title='Original (no NaN)')
s.fillna(s.mean()).hist(ax=axes[1], bins=20, title='Mean-imputed')
plt.tight_layout()
plt.savefig('imputation_comparison.png')
print('Saved comparison chart')

اختيار استراتيجية الإسناد المناسبة

لا توجد استراتيجية إسناد واحدة هي الأفضل دائمًا؛ فالاختيار الصحيح يعتمد على السياق. استخدم المتوسط أو الوسيط في الحالات الأحادية البسيطة مع نسبة فقدان منخفضة. واستخدم ffill/bfill للسلاسل الزمنية ذات الاتجاهات المستقرة. واستخدم KNN أو IterativeImputer عندما تكون الميزات مترابطة وتريد الاستفادة من العلاقات بينها. واستخدم ثوابت المجال (مثل 0 للدلالة على الغياب، و-1 للدلالة على عدم المعرفة) عندما تكون للقيمة المفقودة دلالة واضحة في مجال العمل. وثّق اختيارك دائمًا.

# Decision guide (no runnable code)
#
# Missing < 5%  AND data is MCAR?  -> Mean/median fill is fine
# Time series with stable trend?   -> ffill() with limit
# Features are correlated?         -> KNNImputer or IterativeImputer
# Categorical column?              -> Mode fill or 'Unknown' sentinel
# Going into ML model?             -> Add indicator column + fill
# Research / publication quality?  -> Multiple imputation (MICE)
print('Strategy selected based on context')

تحقق سريع

اختبر فهمك للاستيفاء والإسناد المتقدم.

مراجعة الدرس

تعلمت في هذا الدرس أن interpolate() يقدّر القيم المفقودة بافتراض اتجاه سلس بين القيم المعروفة، وأن method='time' يتعامل مع الفواصل غير المتساوية في DatetimeIndex، وأن الاستراتيجيات المتقدمة مثل KNNImputer وIterativeImputer تستخدم أعمدة أخرى لتحديد قيم الملء. أضف دائمًا أعمدة المؤشرات قبل الإسناد، واحسب الإحصاءات على مجموعة التدريب فقط لتجنب تسرّب البيانات. بعد ذلك سنفحص أنواع بيانات أعمدة DataFrame ونحوّلها.

الأسئلة الشائعة

هل درس «الاستيفاء والتعويض المتقدم» مجاني؟

نعم — نص درس «الاستيفاء والتعويض المتقدم» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

ماذا ستتعلم في «الاستيفاء والتعويض المتقدم»؟

استخدم interpolate() للملء السلس المستند إلى الوقت، وتعرّف على الحالات المناسبة للتعويض بالمتوسط مقابل الوسيط. تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟

لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «الاستيفاء والتعويض المتقدم»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟

نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. اكتشاف القيم المفقودة
  2. حذف القيم المفقودة
  3. ملء القيم المفقودة
  4. الاستيفاء والتعويض المتقدم
← العودة إلى Pandas & NumPy Academy