انتقل إلى المحتوى الرئيسي

الوحدة 6 — تنظيف البيانات

أثبتت الوحدة 8 من دورة المقدّمة أنّ تحضير البيانات يستهلك معظم المشروع، وأنّ لا خوارزمية تُصلِح بيانات خاطئة. تحوّل هذه الوحدة ذلك الاستنتاج إلى حركات ملموسة. الخيط الناظم: كلّ قرار تنظيف خيارٌ منهجي يستحقّ سطر تبرير، لا ردّ فعل آلياً.

شخّص أولاً، عالج ثانياً

الخطأ الأشيع هو التنظيف قبل القياس. ثلاثة أوامر ترسم الجرد:

df.isna().mean().sort_values(ascending=False)   # نسبة النواقص لكلّ عمود
df.duplicated().sum() # صفوف متطابقة تماماً
df.dtypes # الأنواع الفعلية مقابل المنتظرة

أكملها بـvalue_counts() على كلّ عمود فئوي (أخطاء إملائية، تفاوت حالة الأحرف) وdescribe() على كلّ رقمي (قيم مستحيلة: أعمار سالبة، مبالغ صفرية، تواريخ مستقبلية).

القيم الناقصة: ثلاثة أسئلة قبل أيّ معالجة

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

كم تنقص؟ عند 2% يصلح كلّ علاج معقول تقريباً. عند 40% يصبح العمود نفسه موضع سؤال. وبينهما تتوقّف الموازنة على أهمّية المتغيّر.

حذف أم تعويض؟

df = df.dropna(subset=["amount"])            # حذف الصفوف — إن كانت نادرة وغير دالّة

df["age"] = df["age"].fillna(df["age"].median()) # تعويض بالوسيط — متين أمام القيم القصوى
df["country"] = df["country"].fillna("غير معروف") # فئة صريحة
df["temperature"] = df["temperature"].interpolate() # سلاسل زمنية
التعويض الذي يسرّب

إن كانت البيانات ستدرّب نموذجاً، فإحصاءات التعويض (الوسيط، المتوسّط) تُحسَب على مجموعة التدريب وحدها ثم تُطبَّق على الاختبار. حسابها على كامل البيانات تسرّبُ بيانات — وقد أظهرت دورة المقدّمة (الوحدة 5) كلفته. هذا بالضبط ما تؤتمته خطوط scikit-learn.

التكرارات: التامّة والتقريبية

df = df.drop_duplicates()                                    # صفوف متطابقة تماماً
df = df.drop_duplicates(subset=["client_id", "date"]) # تكرارات "مهنية"
df = df.sort_values("updated").drop_duplicates(subset=["client_id"], keep="last") # الأحدث يبقى

التكرار التامّ سهل. أمّا التكرار المهني — صفّان مختلفان يصفان الواقع نفسه (العميل نفسه بعنوانين مكتوبين بطريقتين) — فيستلزم تعريف مفتاح التفرّد مع أهل المهنة، وقاعدة البقاء (keep). والسؤال المسبق في كلّ الأحوال: هل التكرار خطأ جمع أم واقع (قد يطلب عميل مرّتين في اليوم نفسه بصورة مشروعة)؟

الأنواع: العمود الذي يكذب في طبيعته

عمود منمّط خطأً يفسد كلّ ما بعده في صمت: المبالغ النصّية لا تُجمَع، والتواريخ النصّية لا تُقارَن، وdescribe() يتجاهلها.

df["amount"] = pd.to_numeric(df["amount"], errors="coerce")   # غير القابل للتحويل ← NaN
df["date"] = pd.to_datetime(df["date"], errors="coerce")
df["zip_code"] = df["zip_code"].astype("string") # الرمز ليس رقماً
df["segment"] = df["segment"].astype("category") # فئوي: ذاكرة ودلالة

errors="coerce" تحوّل غير القابل للتحويل إلى NaN يُعاد عدّها بعد ذلك: إن صارت 3000 قيمة فارغة فالمشكلة في المنبع (فاصل عشري، «N/A» حرفية، وحدات ملتصقة بالأرقام) وتُصلَح عند المصدر، غالباً منذ read_csv (decimal=","، na_values=["N/A", "-"]).

حالة المعرّفات الخاصة: الرموز البريدية وأرقام السجلّات وأرقام الهواتف سلاسل نصّية. تنميطها أعداداً صحيحة يدمّر الأصفار البادئة — ضرر كلاسيكي لا رجعة فيه بعد إعادة كتابة الملفّ.

تضارب الإدخال: المِلحق .str

df["city"] = df["city"].str.strip().str.title()       # الفراغات وحالة الأحرف
df["country"] = df["country"].replace({"KSA": "السعودية", "سعودية": "السعودية"})

منهج العمل: value_counts() قبلُ لرؤية الصيغ، ثم توحيد، ثم value_counts() بعدُ للتحقّق. وللمرجعيات المهمّة (بلدان، مناطق) يتفوّق جدول مطابقة صريح على الإصلاحات المتفرّقة: فهو قابل للمراجعة وإعادة الاستخدام.

توثيق ما فُعل

التنظيف النزيه يترك أثراً. الشكل الأدنى — بضعة أسطر سجلّ في النصّ أو الدفتر:

n0 = len(df)
df = df.dropna(subset=["amount"])
print(f"نواقص المبلغ: حُذف {n0 - len(df)} صفاً ({(n0-len(df))/n0:.1%})")

إن فاجأت نتيجةُ تحليلٍ أحداً، فسيكون السؤال الأول «ماذا فعل التنظيف؟»؛ وهذه الآثار تجيب في ثوانٍ. ودوالّ التنظيف المجموعة في وحدة (cleaning.py، الوحدة 3) تجعل الكلّ قابلاً لإعادة التشغيل على بيانات الشهر القادم — وهذا المعيار الذي يفصل عملية تنظيف عن ترقيعة عابرة.

الخلاصة

  • شخّص قبل المعالجة: نسبة النواقص لكلّ عمود، والتكرارات، والأنواع الفعلية، وvalue_counts للفئويات.
  • النواقص: افهم آلية الغياب قبل الاختيار بين حذف وتعويض وعمود مؤشّر؛ وعوّض من التدريب وحده إن تبع ذلك نموذج.
  • التكرارات المهنية: عرّف مفتاح التفرّد وقاعدة البقاء مع أهل المهنة.
  • to_numeric/to_datetime مع errors="coerce" ثم إعادة عدّ NaN؛ والمعرّفات سلاسل لا أعداداً.
  • كلّ قرار موثَّق بسطر؛ والتنظيف القابل لإعادة التشغيل يساوي عشرة تنظيفات يدوية.

الوحدة التالية: جمع الجداول — الدمج والتجميع والجداول المحورية، الثلاثي الذي يحوّل ملفات متفرّقة إلى إجابات.