أساس أي مشروع ناجح في علوم البيانات هو بيانات نظيفة ومتسقة. ومع ذلك، في وتيرة التحليل السريعة، غالبًا ما يتعثر مستخدمو بايثون المتمرسون في أخطاء معالجة البيانات قبل الإعداد التي يمكن تجنّبها. سواء كنت تُشكّل البيانات لنموذج تعلم آلي أو تحضرها لتصوّر، فإن معرفة الأخطاء التي يجب مراقبتها قد توفر عليك ساعات من الإحباط وتؤدي إلى نتائج أكثر قوة.
لنحلل معًا خمسة من أكثر أخطاء تنظيف البيانات شيوعًا (وعادة ما تكون قاتلة في أثرها) في بايثون، مع نصائح عملية وأمثلة توضّحية حتى تكون أنظمة عملك صلبة وكفؤة.
أحد أبرز القضايا التي ستواجهها في أي مجموعة بيانات واقعية هو وجود بيانات مفقودة. في بايثون، وخصوصًا مع أدوات مثل pandas، فإن استبدال القيم الناقصة أو إزالتها سهل: df.dropna() أو df.fillna(0) يفعلان ذلك في سطر واحد. لكن السهولة لا تعني الصحة.
إسقاط الصفوف التي تحتوي على قيم مفقودة تلقائيًا يمكن أن يقلّص مجموعة البيانات بشكل كبير أو—في الحالات التي تكون فيها القيم المفقودة مرتبطة بعنصر ما—يضيف تحيّزًا. تعبئة القيم بالمتوسط أو بالصفر قد تشوّش التوزيعات، خاصة في الأعمدة غير الرقمية أو تلك التي تحتوي على قيم شاذة.
انظر إلى هذا المقتطف:
# Too hasty with missing value treatment
import pandas as pd
df = pd.read_csv('survey.csv')
df = df.dropna() # Danger: goodbye valuable data!
إذا كانت 30% من الصفوف مفقودة في حقل اختياري واحد فقط—مثل العمر—فستفقد 30% من بياناتك. إذا كانت القيم المفقودة في الأعمار تتركز أساسًا في فئة ديموغرافية محددة، فسيكون الناتج مجموعة بيانات لا تمثل السكان بشكل دقيق.
df.isnull().sum() أو df.info() لرؤية أنماط وجود القيم المفقودة.sklearn.impute.SimpleImputer للملء المدروس، أو استخدم منطقًا يخص المجال.
البيانات المجمَّعة من مصادر متعددة غالبًا لا تتوافق تمامًا مع صيغة واحدة. التواريخ، والفئات، وترميزات السلاسل النصية معرضة لأخطاء دقيقة وخفيّة.
dtype: object)، مما يعطّل العمليات الرقميةمشكلة بايثون الكلاسيكية:
# Date times imported as strings, causing issues
import pandas as pd
df = pd.read_csv('sales.csv')
df['created_at'].min() # Only finds the minimum string, not chronological min
df.dtypes يكشف بسرعة عن الأعمدة التي يجب أن تكون رقمية لكنها ليست كذلك.pd.to_datetime() وpd.to_numeric() وتحويلات الفئات بمجرد الاستيراد..str.lower().str.strip() لأعمدة التصنيف؛ استبدل المرادفات أو الأخطاء الإملائية بقيمة موحّدة.encoding='utf-8' أو encoding='cp1252').df['created_at'] = pd.to_datetime(df['created_at'], errors='coerce')
df = df.dropna(subset=['created_at']) # Remove rows where dates couldn't parse
جارِ الانتباه هنا يمنع ساعات من تصحيح التحليلات الغريبة لاحقًا.
القيم الشاذة هي "الورقة المتأرجحة" في تنظيف البيانات—أحيانًا تشير إلى أخطاء إدخال البيانات؛ وأحيانًا تكون هي الأحداث التي تستحق الدراسة فعلاً!
أتمتة البرامج التي تقضي على القيم خارج نطاق معين دون اعتبار للسياق قد تقضي على البيانات من كل من الأخطاء والإشارات الهامة.
لدى مجموعة بيانات صحية عمود لضغط الدم. بعض القيم مُسجَّلة كـ 400، وهو احتمال وجود خطأ في البيانات (الوحدات أو إدخال البيانات). قد تكون قيم أخرى حالات طرفية، مثل حالات طارئة بسبب فرط الضغط الدموي.
# Don't just drop anything >200 without context
bp_outliers = df[df['blood_pressure'] > 200]
print(bp_outliers) # Investigate: are these errors or medically relevant cases?
df.describe() وتصورات مثل مخطط صندوق ومخططات التوزيع لكشف تفاصيل التوزيع ورصد القيم الشاذة.عندما تتبين أن القيم الشاذة حقيقية، يمكن أن تعيد تشكيل الرؤى التجارية المعتمدة على البيانات.
البيانات المكررة منتشرة—أخطاء الإدخال، سحب البيانات من الويب، أو أعطال النظام جميعها قد تُدخلها. بينما تتيح لك بايثون df.drop_duplicates() بسرعة، الخطر الحقيقي يكمن في فهم مصدر التكرارات أو في الطريقة الأنسب لحلها.
قد يحتوي قاعدة بيانات تجارة تجزئة على عدة صفوف لنفس أمر العميل بسبب إرسال النظام عدة مرات. إسقاط كل شيء ما عدا صفاً واحداً يعمل فقط إذا تmatched كل عمود؛ وإلا قد تُفقد معلومات.
# Problematic: Dropping all duplicates based only on 'order_id'
df = df.drop_duplicates(subset=['order_id']) # Could lose different addresses or notes attached to split-row orders
إذا اختلفت أعمدة مثل 'delivery_notes' بين الصفوف، فإسقاط التكرارات بشكل أعمى إما يفقد البيانات أو لا يُوفق بين المعلومات المتعارضة.
df.duplicated(subset=key_cols, keep=False) للإشارة إلى التكرارات الحقيقية.is_duplicate للتحليل لاحقاً بدلاً من الإزالة الفورية.إليك مثالاً على تجميع حقول مفيدة قبل إزالة التكرارات:
def collapse_order_notes(notes):
return '; '.join(sorted(set(x for x in notes if pd.notnull(x))))
rollup = df.groupby('order_id').agg({
'customer_id': 'first',
'total_amount': 'sum',
'delivery_notes': collapse_order_notes
}).reset_index()
هذا يحافظ على البيانات المساعدة الهامة.
تتطلب العديد من الخوارزميات القوية مدخلات عددية، وليست مجرد تسميات نصية أو فئات. يعتبر ترميز الأعمدة التصنيفية خطوة حاسمة، لكن التسرع أو اختيار الطريقة الخاطئة قد يعيق أداء النموذج ويولد أخطاء.
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
# Label encoding ignores category meaning
le = LabelEncoder()
df['city_code'] = le.fit_transform(df['city']) # Problem: Model interprets numbers mathematically
pd.get_dummies(df, drop_first=True, dummy_na=False)، أو للخصائص ذات عدد فئات عالي، اعتبر التقطيع عبر الهاش أو الترميز المستهدف.fit/transform من sklearn) حتى تحصل عمليات نشر النموذج على خرائط متطابقة—عقار قديم حين تظهر فئات جديدة وغير مرئية في المدخلات الواقعية.city_counts = df['city'].value_counts()
# Only encode cities appearing at least 10 times
common_cities = city_counts[city_counts > 10].index
df['city'] = df['city'].apply(lambda x: x if x in common_cities else 'Other')
df = pd.get_dummies(df, columns=['city'], drop_first=True)
هذا يحافظ على حجم الميزات عمليًا وتكون النماذج أكثر ثباتًا.
تنظيف البيانات في بايثون يتطلب احترام التفاصيل الدقيقة إضافة إلى السرعة. عبر تجنّب التنظيف الآلي الخالي من السياق، ترتقي أعمالك في علوم البيانات والتحليلات إلى مستوى أعلى من المتوسط. راجع القيم المفقودة بنية مقصودة، واثبت الاتساق في الصيغ، واعتبر القيم الشاذة إشارات وليست مجرد ضجيج، وتدقيق التكرارات، وتبنَّ أسلوبًا تكتيكيًا في ترميز الفئات.
مع هذه الدروس وبعيون حادّة لبياناتك، ستقضي وقتًا أقل في العودة للخلف، وتقلل من الأخطاء المحرجة في الإنتاج، وتبني سمعة لخطوط أنابيب البيانات التي يثق بها المحللون. وفي مجال علم البيانات الذي يتسع باستمرار، أن تصبح الشخص الذي تكون بياناته جاهزة فعلاً للرؤية هو قوة خارقة حقيقية.