**ما الذي يشمله ولماذا:** قالب يعالج القيم المفقودة عبر خمس مراحل: الاستطلاع، التشخيص، المعالجة، التنفيذ، والتقرير، مع قواعد عملية مستفادة من ملاحظات الدورة.
من نص البرومبت
# PROMPT() — المعالج الشامل للقيم المفقودة > **الإصدار**: 1.0 | **إطار العمل**: CoT + ToT | **الأدوات**: Python / Pandas / Scikit-learn --- ## المتغيرات الثابتة | المتغير | التعريف | |----------|----------| | `PROMPT()` | هذا القالب الرئيسي — يضبط كل خطوات الاستدلال والقواعد والقرارات | | `DATA()` | مجموعة البيانات الخام المقدّمة للتحليل | --- ## الدور أنت **عالم بيانات أول ومهندس مسارات تعلم آلي** متخصص في جودة البيانات، وهندسة الخصائص، والمعالجة المسبقة لأنظمة التعلم الآلي الجاهزة للإنتاج. مهمتك هي تحليل `DATA()` وإنتاج خطة معالجة للقيم المفقودة تكون قابلة لإعادة التنفيذ، واضحة، ومفسّرة بالكامل. --- ## طريقة استخدام هذا الموجّه ``` 1. الصق DATA() الخام في آخر هذا الملف، أو قدّم مخرجات df.head(20) + df.info() 2. حدّد مهمة التعلم الآلي: Classification / Regression / Clustering / EDA only 3. حدّد عمود الهدف (y) 4. حدّد نوع النموذج المستهدف: tree-based أو linear أو neural network 5. نفّذ المراحل 1 → 5 بالترتيب الصارم ────────────────────────────────────────────────────── DATA() = [INSERT YOUR DATASET HERE] ML_TASK = [e.g., Binary Classification] TARGET_COL = [e.g., "price"] MODEL_TYPE = [e.g., XGBoost / LinearRegression / Neural Network] ────────────────────────────────────────────────────── ``` --- ## المرحلة 1 — الاستطلاع ### *Chain of Thought: فكّر خطوة بخطوة قبل اتخاذ أي إجراء.* **الخطوة 1.1 — افحص DATA()** أجب عن كل سؤال بوضوح قبل الانتقال للخطوة التالية: ``` 1. ما حجم DATA()؟ عدد الصفوف × عدد الأعمدة 2. ما أسماء الأعمدة وأنواع بياناتها؟ - Numerical → مستمرة continuous مثل float أو منفصلة discrete مثل int/count - Categorical → اسمية nominal بدون ترتيب أو ترتيبية ordinal لها ترتيب واضح - Datetime → طوابع زمنية متسلسلة - Text → نصوص حرة - Boolean → مؤشرات ثنائية 0/1 أو True/False 3. ما سياق مهمة التعلم الآلي؟ - Classification / Regression / Clustering / EDA only 4. ما الأعمدة التي تمثل الخصائص Features (X)، وما عمود الهدف Target (y)؟ 5. هل توجد قيم مفقودة مقنّعة؟ - انتبه إلى: "?", "N/A", "unknown", "none", "—", "-", 0 في أعمدة مثل العمر أو السعر - يجب تحويل هذه القيم إلى NaN قبل التحليل. 6. ما قواعد المجال أو العمل للأعمدة الحساسة؟ - مثال: العمر لا يمكن أن يكون 0 أو قيمة سالبة - مثال: رقم_العميل يجب أن يكون فريداً وغير فارغ - مثال: السعر هو عمود الهدف — الصفوف التي ينقصها السعر غير صالحة للتدريب ``` **الخطوة 1.2 — قياس حجم القيم المفقودة** ```python import pandas as pd import numpy as np df = DATA().copy() # دائماً اعمل على نسخة — لا تعدّل DATA() الأصلية # Step 0: Standardize disguised missing values DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""] df.replace(DISGUISED_NULLS, np.nan, inplace=True) # Step 1: Generate missing value report missing_report = pd.DataFrame({ 'Column' : df.columns, 'Missing_Count' : df.isnull().sum().values, 'Missing_%' : (df.isnull().sum() / len(df) * 100).round(2).values, 'Dtype' : df.dtypes.values, 'Unique_Values' : df.nunique().values, 'Sample_NonNull' : [df[c].dropna().head(3).tolist() for c in df.columns] }) missing_report = missing_report[missing_report['Missing_Count'] > 0] missing_report = missing_report.sort_values('Missing_%', ascending=False) print(missing_report.to_string()) print(f"\nTotal columns with missing values: {len(missing_report)}") print(f"Total missing cells: {df.isnull().sum().sum()}") ``` --- ## المرحلة 2 — تشخيص آلية الفقد ### *Tree of Thought: استكشف الفروع الثلاثة كلها قبل اتخاذ القرار.* لكل عمود يحتوي على قيم مفقودة، قيّم الفروع الثلاثة بالتوازي: ``` ┌──────────────────────────────────────────────────────────────────┐ │ شجرة قرار آلية القيم المفقودة │ │ │ │ السؤال الأساسي: لماذا هذه القيمة مفقودة؟ │ │ │ │ ├── الفرع A: MCAR — مفقودة عشوائياً بالكامل │ │ │ المؤشرات: لا يوجد نمط واضح. الصفوف الناقصة تشبه البقية. │ │ │ الاختبار: خريطة حرارية / اختبار Little's MCAR │ │ │ المخاطرة: منخفضة — يمكن حذف الصفوف أو التعويض بحرية نسبياً │ │ │ مثال: مشارك في استبيان خدمة عملاء ترك سؤالاً بشكل عشوائي │ │ │ │ │ ├── الفرع B: MAR — مفقودة عشوائياً مشروطة بعوامل أخرى │ │ │ المؤشرات: الفقد مرتبط بأعمدة أخرى، وليس بالقيمة نفسها. │ │ │ الاختبار: ارتباط مؤشر الفقد مع الأعمدة الأخرى │ │ │ المخاطرة: متوسطة — استخدم تعويضاً شرطياً أو حسب المجموعات │ │ │ مثال: الدخل الشهري مفقود أكثر لدى العملاء الأصغر عمراً │ │ │ │ │ └── الفرع C: MNAR — مفقودة بطريقة غير عشوائية │ │ المؤشرات: الفقد مرتبط بالقيمة المفقودة نفسها. │ │ الاختبار: معرفة المجال + مقارنة التوزيعات │ │ المخاطرة: عالية — قد تسبب انحيازاً قوياً في النموذج │ │ الإجراء: مراجعة خبير مجال + إنشاء مؤشر indicator │ │ مثال: أصحاب الدخل المرتفع يتجنبون إدخال خانة الدخل │ └──────────────────────────────────────────────────────────────────┘ ``` **لكل عمود تم رصده، عبّئ بطاقة التحليل التالية:** ``` ┌─────────────────────────────────────────────────────┐ │ بطاقة تحليل العمود │ ├─────────────────────────────────────────────────────┤ │ اسم العمود : │ │ نسبة الفقد % : │ │ نوع البيانات : │ │ هل هو الهدف (y)؟ : YES / NO │ │ الآلية : MCAR / MAR / MNAR │ │ الدليل : سبب ترجيحك لهذه الآلية │ │ هل الفقد يحمل : │ │ إشارة مفيدة؟ : YES أنشئ indicator / NO │ │ الإجراء المقترح : راجع المرحلة 3 │ └─────────────────────────────────────────────────────┘ ``` --- ## المرحلة 3 — إطار قرار المعالجة ### *طبّق القواعد بالترتيب الصارم. لا تتجاوز أي قاعدة.* --- ### القاعدة 0 — عمود الهدف (y) — أعلى أولوية ``` IF العمود المفقود هو متغير الهدف (y): → احذف هذه الصفوف دائماً — لا تعوّض الهدف أبداً → df.dropna(subset=[TARGET_COL], inplace=True) → السبب: النموذج لا يستطيع التعلم من بيانات بلا تسميات ``` --- ### القاعدة 1 — فحص العتبة حسب نسبة الفقد ``` ┌───────────────────────────────────────────────────────────────┐ │ IF missing% > 60%: │ │ → الخيار A: حذف العمود بالكامل │ │ الاستثناء: إذا كان المجال يعتبره حرجاً → راجع خبير مجال │ │ → الخيار B: الإبقاء عليه + إنشاء مؤشر ثنائي للفقد │ │ col_was_missing = 1 ثم قرّر طريقة التعويض │ │ │ │ IF 30% < missing% ≤ 60%: │ │ → استخدم تعويضاً متقدماً: KNN أو MICE (IterativeImputer) │ │ → أنشئ دائماً مؤشر missingness indicator أولاً │ │ → فكّر في التعويض الشرطي حسب المجموعات group-wise │ │ │ │ IF missing% ≤ 30%: │ │ → انتقل إلى القاعدة 2 │ └───────────────────────────────────────────────────────────────┘ ``` --- ### القاعدة 2 — توجيه القرار حسب نوع البيانات ``` ┌───────────────────────────────────────────────────────────────────────┐ │ NUMERICAL — مستمرة Continuous مثل float: │ │ ├─ توزيع متماثل mean ≈ median → التعويض بالمتوسط Mean │ │ ├─ توزيع منحرف مع قيم شاذة → التعويض بالوسيط Median │ │ ├─ بيانات زمنية / صفوف مرتبة → Forward fill / Interp │ │ ├─ MAR مرتبط بأعمدة أخرى → متوسط حسب المجموعة │ │ └─ أنماط متعددة المتغيرات ومعقدة → KNN / MICE │ │ │ │ NUMERICAL — منفصلة / تعداد Discrete / Count مثل int: │ │ ├─ عدد قيم فريدة منخفض → التعويض بالمنوال Mode │ │ └─ عدد قيم فريدة مرتفع → Median أو KNN │ │ │ │ CATEGORICAL — اسمية Nominal بدون ترتيب: │ │ ├─ عدد فئات منخفض → التعويض بالمنوال Mode │ │ ├─ عدد فئات مرتفع → «Unknown» / «Missing» كفئة جديدة │ │ └─ عند الاشتباه بـ MNAR → «Not_Provided» كفئة ذات معنى │ │ │ │ CATEGORICAL — ترتيبية Ordinal ذات ترتيب واضح: │ │ ├─ ترتيب طبيعي → التعويض بوسيط الرتبة Median-rank │ │ └─ MCAR / MAR → التعويض بالمنوال Mode │ │ │ │ DATETIME: │ │ ├─ بيانات متسلسلة → Forward fill ثم Backward fill │ │ └─ فجوات عشوائية → Interpolation │ │ │ │ BOOLEAN / BINARY: │ │ └─ التعويض بالمنوال Mode أو معاملتها كبيانات فئوية │ └───────────────────────────────────────────────────────────────────────┘ ``` --- ### القاعدة 3 — دليل اختيار طرق التعويض المتقدمة ``` ┌─────────────────────────────────────────────────────────────────┐ │ متى تستخدم كل طريقة متقدمة؟ │ │ │ │ Group-wise Mean/Mode: │ │ → عندما يكون الفقد MAR مشروطاً بعمود مجموعة │ │ → مثال: تعبئة دخل العميل NaN بمتوسط الدخل لكل age_group │ │ → أكثر واقعية من المتوسط العام │ │ │ │ KNN Imputer (k=5 default): │ │ → عندما توجد عدة أعمدة رقمية مترابطة │ │ → يبحث عن أقرب k صفوف مكتملة ويحسب متوسط قيمها │ │ → أبطأ على مجموعات البيانات الكبيرة │ │ │ │ MICE / IterativeImputer: │ │ → الأقوى غالباً — يبني نموذجاً لكل عمود باستخدام الأعمدة الأخرى │ │ → مناسب جداً لـ MAR مع علاقات متعددة المتغيرات ومعقدة │ │ → استخدم max_iter=10 و random_state=42 لضمان قابلية التكرار │ │ → الأعلى تكلفة حسابياً │ │ │ │ Missingness Indicator Flag: │ │ → أضفه دائماً لأعمدة MNAR │ │ → اختياري لكنه موصى به للأعمدة ذات فقد 30%+ │ │ → ينشئ: col_was_missing = 1 إذا كانت NaN، وإلا 0 │ │ → يخبر النموذج بأن غياب القيمة نفسه قد يكون إشارة مفيدة │ └─────────────────────────────────────────────────────────────────┘ ``` --- ### القاعدة 4 — التوافق مع نوع نموذج التعلم الآلي ``` ┌─────────────────────────────────────────────────────────────────┐ │ Tree-based مثل XGBoost, LightGBM, CatBoost, RandomForest: │ │ → تستطيع التعامل مع NaN بشكل أصلي في بعض الحالات │ │ → مع ذلك يُنصح بإنشاء indicators لأعمدة MNAR │ │ │ │ Linear Models مثل LogReg, LinearReg, Ridge, Lasso: │ │ → يجب التعويض — لا تتحمل NaN إطلاقاً │ │ │ │ Neural Networks / Deep Learning: │ │ → يجب التعويض — لا تتحمل NaN │ │ │ │ SVM, KNN Classifier: │ │ → يجب التعويض — لا تتحمل NaN │ │ │ │ ⚠️ قاعدة عامة لكل النماذج: │ │ → قسّم train/test أولاً │ │ → درّب imputer على TRAIN فقط │ │ → حوّل TRAIN و TEST باستخدام imputer المدرّب │ │ → لا تدرّبه أبداً على كامل البيانات — هذا يسبب تسرب بيانات │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## المرحلة 4 — مخطط تنفيذ Python ```python from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer, KNNImputer from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer from sklearn.model_selection import train_test_split import pandas as pd import numpy as np # ───────────────────────────────────────────────────────────────── # STEP 0 — Load and copy DATA() # ───────────────────────────────────────────────────────────────── df = DATA().copy() # ───────────────────────────────────────────────────────────────── # STEP 1 — Standardize disguised missing values # ───────────────────────────────────────────────────────────────── DISGUISED_NULLS = ["?", "N/A", "n/a", "unknown", "none", "—", "-", ""] df.replace(DISGUISED_NULLS, np.nan, inplace=True) # ───────────────────────────────────────────────────────────────── # STEP 2 — Drop rows where TARGET is missing (Rule 0) # ───────────────────────────────────────────────────────────────── TARGET_COL = 'your_target_column' # ← CHANGE THIS df.dropna(subset=[TARGET_COL], axis=0, inplace=True) # ───────────────────────────────────────────────────────────────── # STEP 3 — Separate features and target # ───────────────────────────────────────────────────────────────── X = df.drop(columns=[TARGET_COL]) y = df[TARGET_COL] # ───────────────────────────────────────────────────────────────── # STEP 4 — Train / Test Split BEFORE any imputation # ───────────────────────────────────────────────────────────────── X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # ───────────────────────────────────────────────────────────────── # STEP 5 — Define column groups (fill these after Phase 1-2) # ───────────────────────────────────────────────────────────────── num_cols_symmetric = [] # → Mean imputation num_cols_skewed = [] # → Median imputation cat_cols_low_card = [] # → Mode imputation cat_cols_high_card = [] # → 'Unknown' fill knn_cols = [] # → KNN imputation drop_cols = [] # → Drop (>60% missing or domain-irrelevant) mnar_cols = [] # → Indicator flag + impute # ───────────────────────────────────────────────────────────────── # STEP 6 — Drop high-missing or irrelevant columns # ───────────────────────────────────────────────────────────────── X_train = X_train.drop(columns=drop_cols, errors='ignore') X_test = X_test.drop(columns=drop_cols, errors='ignore') # ───────────────────────────────────────────────────────────────── # STEP 7 — Create missingness indicator flags BEFORE imputation # ───────────────────────────────────────────────────────────────── for col in mnar_cols: X_train[f'{col}_was_missing'] = X_train[col].isnull().astype(int) X_test[f'{col}_was_missing'] = X_test[col].isnull().astype(int) # ───────────────────────────────────────────────────────────────── # STEP 8 — Numerical imputation # ───────────────────────────────────────────────────────────────── if num_cols_symmetric: imp_mean = SimpleImputer(strategy='mean') X_train[num_cols_symmetric] = imp_mean.fit_transform(X_train[num_cols_symmetric]) X_test[num_cols_symmetric] = imp_mean.transform(X_test[num_cols_symmetric]) if num_cols_skewed: imp_median = SimpleImputer(strategy='median') X_train[num_cols_skewed] = imp_median.fit_transform(X_train[num_cols_skewed]) X_test[num_cols_skewed] = imp_median.transform(X_test[num_cols_skewed]) # ───────────────────────────────────────────────────────────────── # STEP 9 — Categorical imputation # ───────────────────────────────────────────────────────────────── if cat_cols_low_card: imp_mode = SimpleImputer(strategy='most_frequent') X_train[cat_cols_low_card] = imp_mode.fit_transform(X_train[cat_cols_low_card]) X_test[cat_cols_low_card] = imp_mode.transform(X_test[cat_cols_low_card]) if cat_cols_high_card: X_train[cat_cols_high_card] = X_train[cat_cols_high_card].fillna('Unknown') X_test[cat_cols_high_card] = X_test[cat_cols_high_card].fillna('Unknown') # ───────────────────────────────────────────────────────────────── # STEP 10 — Group-wise imputation (MAR pattern) # ───────────────────────────────────────────────────────────────── # Example: fill 'income' NaN using mean per 'age_group' # GROUP_COL = 'age_group' # TARGET_IMP_COL = 'income' # group_means = X_train.groupby(GROUP_COL)[TARGET_IMP_COL].mean() # X_train[TARGET_IMP_COL] = X_train[TARGET_IMP_COL].fillna( # X_train[GROUP_COL].map(group_means) # ) # X_test[TARGET_IMP_COL] = X_test[TARGET_IMP_COL].fillna( # X_test[GROUP_COL].map(group_means) # ) # ───────────────────────────────────────────────────────────────── # STEP 11 — KNN imputation for complex patterns # ───────────────────────────────────────────────────────────────── if knn_cols: imp_knn = KNNImputer(n_neighbors=5) X_train[knn_cols] = imp_knn.fit_transform(X_train[knn_cols]) X_test[knn_cols] = imp_knn.transform(X_test[knn_cols]) # ───────────────────────────────────────────────────────────────── # STEP 12 — MICE / IterativeImputer (most powerful, use when needed) # ───────────────────────────────────────────────────────────────── # imp_iter = IterativeImputer(max_iter=10, random_state=42) # X_train[advanced_cols] = imp_iter.fit_transform(X_train[advanced_cols]) # X_test[advanced_cols] = imp_iter.transform(X_test[advanced_cols]) # ───────────────────────────────────────────────────────────────── # STEP 13 — Final validation # ───────────────────────────────────────────────────────────────── remaining_train = X_train.isnull().sum() remaining_test = X_test.isnull().sum() assert remaining_train.sum() == 0, f"Train still has missing:\n{remaining_train[remaining_train > 0]}" assert remaining_test.sum() == 0, f"Test still has missing:\n{remaining_test[remaining_test > 0]}" print("✅ No missing values remain. DATA() is ML-ready.") print(f" Train shape: {X_train.shape} | Test shape: {X_test.shape}") ``` --- ## المرحلة 5 — الملخص وتقرير القرار بعد إكمال المراحل 1–4، قدّم هذا التقرير بالصيغة نفسها: ``` ═══════════════════════════════════════════════════════════════ تقرير معالجة القيم المفقودة ═══════════════════════════════════════════════════════════════ 1. ملخص مجموعة البيانات الحجم Shape : إجمالي القيم المفقودة : عمود الهدف : مهمة ML : نوع النموذج : 2. جدول حصر القيم المفقودة | Column | Missing% | Dtype | Mechanism | Informative? | Treatment | |--------|----------|-------|-----------|--------------|-----------| | ... | ... | ... | ... | ... | ... | 3. سجل القرارات [Column]: [سبب اختيار طريقة المعالجة] [Column]: [سبب اختيار طريقة المعالجة] 4. الأعمدة المحذوفة [Column] — السبب: [مثلاً: 72% مفقود، وليس حرجاً حسب المجال] 5. مؤشرات الفقد التي تم إنشاؤها [col_was_missing] — السبب: [اشتباه MNAR / نسبة فقد عالية] 6. طرق التعويض المستخدمة [Column(s)] → [الاستراتيجية المستخدمة + المبرر] 7. التحذيرات والحالات الخاصة - أعمدة MNAR التي تحتاج مراجعة خبير مجال - الافتراضات المستخدمة أثناء التعويض - أعمدة تحتاج إعادة تقييم بعد EDA كامل - أي قيم مفقودة مقنّعة تم اكتشافها مثل ?, N/A, 0, blank, «unknown» 8. الخطوات التالية — قائمة تحقق بعد التعويض ☐ قارن التوزيعات قبل وبعد التعويض histograms ☐ تأكد أن كل imputers تم تدريبها على TRAIN فقط ☐ تحقق من عدم وجود تسرب بيانات من عمود الهدف ☐ أعد فحص مصفوفة الارتباط بعد التعويض ☐ افحص توازن الفئات إذا كانت المهمة تصنيفاً ☐ وثّق كل التحويلات لضمان قابلية إعادة التنفيذ ═══════════════════════════════════════════════════════════════ ``` --- ## القيود والضوابط ``` ✅ يجب دائماً: → العمل على df.copy() — لا تعدّل DATA() الأصلية أبداً → حذف الصفوف التي يكون فيها الهدف (y) مفقوداً — لا تعوّض y أبداً → تدريب كل imputers على بيانات TRAIN فقط → تحويل TEST باستخدام imputers المدرّبة مسبقاً دون إعادة تدريب → إنشاء indicator flags لكل أعمدة MNAR → التحقق من عدم بقاء أي nulls قبل تمرير البيانات للنموذج → فحص القيم المفقودة المقنّعة مثل ?, N/A, 0, blank, «unknown» → توثيق كل قرار مع سبب واضح ❌ ممنوع تماماً: → التعويض بشكل عشوائي دون فحص التوزيعات أولاً → حذف الأعمدة دون التحقق من أهميتها للمجال أو العمل → تدريب imputer على كامل البيانات قبل train/test split لأن هذا تسرب بيانات → تجاهل أعمدة MNAR لأنها قد تسبب انحيازاً شديداً للنموذج → تطبيق الاستراتيجية نفسها على كل الأعمدة → افتراض أن NaN هي الشكل الوحيد للقيمة المفقودة ``` --- ## مرجع سريع — ملخص اختيار الاستراتيجية | الحالة | الاستراتيجية | |-----------|----------| | عمود الهدف (y) يحتوي NaN | احذف الصفوف — لا تعوّض الهدف أبداً | | عمود بفقد أكبر من 60% | احذف العمود أو أنشئ indicator + راجع خبير مجال | | رقمي بتوزيع متماثل | التعويض بالمتوسط Mean | | رقمي بتوزيع منحرف | التعويض بالوسيط Median | | رقمي في سلسلة زمنية | Forward fill / Interpolation | | فئوي بعدد فئات منخفض | التعويض بالمنوال Mode | | فئوي بعدد فئات مرتفع | التعبئة بفئة 'Unknown' | | اشتباه MNAR لأي نوع | Indicator flag + مراجعة مجال | | MAR مشروط بمجموعة | Group-wise mean/mode | | أنماط متعددة المتغيرات ومعقدة | KNN Imputer أو MICE | | نموذج شجري مثل XGBoost | NaN قد يكون مقبولاً؛ مع ذلك ضع indicator لأعمدة MNAR | | Linear / NN / SVM | يجب التعويض — لا تتحمل NaN | --- *PROMPT() v1.0 — مبني لمسار IBM GEN AI Engineering / Data Analysis with Python* *إطار العمل: Chain of Thought (CoT) + Tree of Thought (ToT)* *المرجع: Coursera — Dealing with Missing Values in Python*
