معالجة البيانات المكررة والمشوهة (Duplicates & Outliers) باستخدام بايثون


مرحباً أيها المطورون! اليوم سنتعلم كيفية التعامل مع تحديين أساسيين في معالجة البيانات: البيانات المكررة (Duplicates) والقيم الشاذة (Outliers) باستخدام مكتبات بايثون القوية مثل Pandas وScikit-learn.

سنقوم ببناء pipeline كاملة لاكتشاف وتنظيف هذه المشاكل لتحسين جودة بياناتنا وجعلها جاهزة للتحليل أو بناء النماذج.

الخطوة 1: إعداد البيانات واكتشاف وإزالة البيانات المكررة

نبدا بإنشاء DataFrame يحتوي على بعض البيانات المكررة والقيم الشاذة، ثم نكتشف الصفوف المكررة ونقوم بإزالتها.

ملاحظة تقنية: الصفوف المكررة تماماً يمكن أن تسبب تحيزات في التحليل الإحصائي أو تدريب النماذج، ومن المهم إزالتها لضمان دقة النتائج.

import pandas as pd
import numpy as np

# 1. إعداد بيانات نموذجية تحتوي على قيم مكررة وشاذة
data = {
    'ID': [1, 2, 3, 4, 5, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    'Name': ['أحمد', 'سارة', 'علي', 'ليلى', 'فادي', 'أحمد', 'منى', 'خالد', 'نور', 'يوسف', 'زينب', 'طارق', 'ريم', 'حسام', 'سلمى', 'وليد'],
    'Age': [25, 30, 35, 28, 22, 25, 31, 29, 27, 33, 26, 30, 24, 32, 29, 150], # 150 هو قيمة شاذة
    'Score': [85, 92, 78, 95, 88, 85, 90, 81, 89, 93, 75, 87, 91, 79, 86, 10] # 10 هو قيمة شاذة
}
df = pd.DataFrame(data)

print("البيانات الأصلية:")
print(df)
print("\n")

# 2. اكتشاف البيانات المكررة
# تحديد عدد الصفوف المكررة بالكامل
num_duplicates = df.duplicated().sum()
print(f"عدد الصفوف المكررة المكتشفة: {num_duplicates}")

# عرض الصفوف المكررة (بما في ذلك أول ظهور لها) لتحديدها
print("الصفوف المكررة (مع أول ظهور):")
print(df[df.duplicated(keep=False)])
print("\n")

# 3. إزالة البيانات المكررة
# إزالة الصفوف المكررة والاحتفاظ بالظهور الأول افتراضياً
df_cleaned_duplicates = df.drop_duplicates()
print("البيانات بعد إزالة الصفوف المكررة:")
print(df_cleaned_duplicates)
print(f"عدد الصفوف بعد إزالة المكررات: {len(df_cleaned_duplicates)}")
print("\n")

الخطوة 2: اكتشاف القيم الشاذة باستخدام المدى الربيعي (IQR)

القيم الشاذة (Outliers) هي نقاط بيانات تختلف بشكل كبير عن معظم نقاط البيانات الأخرى. سنستخدم طريقة المدى الربيعي (Interquartile Range - IQR) لاكتشافها، وهي طريقة قوية ومقاومة للقيم الشاذة نفسها.

ملاحظة تقنية: طريقة IQR تحدد القيم الشاذة بأنها أي نقاط بيانات تقع خارج النطاق [Q1 - 1.5*IQR, Q3 + 1.5*IQR]، حيث Q1 هو الربع الأول، Q3 هو الربع الثالث، و IQR هو Q3 - Q1.

# نستخدم DataFrame بعد إزالة المكررات كبيانات أساسية للخطوة التالية
df_temp = df_cleaned_duplicates.copy()

# تحديد الأعمدة الرقمية التي قد تحتوي على قيم شاذة
numerical_cols = ['Age', 'Score']

outlier_indices = {}
for col in numerical_cols:
    # حساب الربيع الأول (Q1) والربيع الثالث (Q3)
    Q1 = df_temp[col].quantile(0.25)
    Q3 = df_temp[col].quantile(0.75)
    IQR = Q3 - Q1 # حساب المدى الربيعي

    # تحديد الحدود العليا والسفلى لاكتشاف القيم الشاذة
    # القيم خارج هذا النطاق تعتبر شاذة
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    # اكتشاف القيم الشاذة في العمود الحالي
    col_outliers = df_temp[(df_temp[col] < lower_bound) | (df_temp[col] > upper_bound)]
    outlier_indices[col] = col_outliers.index.tolist()

    print(f"القيم الشاذة المكتشفة في عمود '{col}':")
    print(col_outliers)
    print(f"عدد القيم الشاذة في عمود '{col}': {len(col_outliers)}")
    print("\n")

# جمع كل مؤشرات الصفوف التي تحتوي على قيم شاذة في أي من الأعمدة
all_outlier_indices = sorted(list(set([idx for sublist in outlier_indices.values() for idx in sublist])))
print(f"مؤشرات الصفوف التي تحتوي على قيم شاذة إجمالاً: {all_outlier_indices}")
print("\n")

الخطوة 3: التعامل مع القيم الشاذة (Capping)

بعد اكتشاف القيم الشاذة، نحتاج إلى اتخاذ قرار بشأن كيفية التعامل معها. أحد الأساليب الشائعة هو التحديد (Capping)، حيث يتم استبدال القيم الشاذة التي تتجاوز الحد الأعلى بالحد الأعلى نفسه، والتي تقل عن الحد الأدنى بالحد الأدنى نفسه.

ملاحظة تقنية: Capping هو طريقة جيدة للحفاظ على عدد الصفوف في مجموعة البيانات مع تقليل تأثير القيم المتطرفة بشكل كبير على التحليل الإحصائي أو النماذج التنبؤية.

# 4. التعامل مع القيم الشاذة: التحديد (Capping)
# نقوم بتطبيق Capping على نفس الأعمدة الرقمية
df_final = df_cleaned_duplicates.copy() # نبدأ من البيانات بعد إزالة المكررات

for col in numerical_cols:
    Q1 = df_final[col].quantile(0.25)
    Q3 = df_final[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    # تطبيق Capping: استبدال القيم الأقل من الحد الأدنى بالحد الأدنى
    df_final[col] = np.where(df_final[col] < lower_bound, lower_bound, df_final[col])
    # تطبيق Capping: استبدال القيم الأعلى من الحد الأعلى بالحد الأعلى
    df_final[col] = np.where(df_final[col] > upper_bound, upper_bound, df_final[col])

print("البيانات بعد معالجة القيم الشاذة (Capping):")
print(df_final)
print("\n")

الكود النهائي الكامل

هنا تجد السكربت كاملاً، يجمع جميع الخطوات التي تعلمناها لمعالجة البيانات المكررة والقيم الشاذة.

import pandas as pd
import numpy as np

# 1. إعداد بيانات نموذجية تحتوي على قيم مكررة وشاذة
data = {
    'ID': [1, 2, 3, 4, 5, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    'Name': ['أحمد', 'سارة', 'علي', 'ليلى', 'فادي', 'أحمد', 'منى', 'خالد', 'نور', 'يوسف', 'زينب', 'طارق', 'ريم', 'حسام', 'سلمى', 'وليد'],
    'Age': [25, 30, 35, 28, 22, 25, 31, 29, 27, 33, 26, 30, 24, 32, 29, 150], # 150 هو قيمة شاذة
    'Score': [85, 92, 78, 95, 88, 85, 90, 81, 89, 93, 75, 87, 91, 79, 86, 10] # 10 هو قيمة شاذة
}
df = pd.DataFrame(data)

print("البيانات الأصلية:")
print(df)
print("\n")

# 2. اكتشاف وإزالة البيانات المكررة
num_duplicates = df.duplicated().sum()
print(f"عدد الصفوف المكررة المكتشفة: {num_duplicates}")

df_cleaned_duplicates = df.drop_duplicates()
print("البيانات بعد إزالة الصفوف المكررة:")
print(df_cleaned_duplicates)
print(f"عدد الصفوف بعد إزالة المكررات: {len(df_cleaned_duplicates)}")
print("\n")

# 3. اكتشاف القيم الشاذة باستخدام IQR والتعامل معها (Capping)

df_final = df_cleaned_duplicates.copy() # نبدأ من البيانات بعد إزالة المكررات
numerical_cols = ['Age', 'Score']

for col in numerical_cols:
    Q1 = df_final[col].quantile(0.25)
    Q3 = df_final[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    # عرض القيم الشاذة قبل المعالجة لكل عمود
    col_outliers = df_final[(df_final[col] < lower_bound) | (df_final[col] > upper_bound)]
    print(f"القيم الشاذة المكتشفة في عمود '{col}' قبل المعالجة:")
    print(col_outliers)
    print(f"عدد القيم الشاذة في عمود '{col}': {len(col_outliers)}")
    print("\n")

    # تطبيق Capping
    df_final[col] = np.where(df_final[col] < lower_bound, lower_bound, df_final[col])
    df_final[col] = np.where(df_final[col] > upper_bound, upper_bound, df_final[col])

print("البيانات النهائية بعد إزالة المكررات ومعالجة القيم الشاذة (Capping):")
print(df_final)
print("\n")

النتيجة المتوقعة

عند تشغيل السكربت، ستشاهد المخرجات التالية على التوالي:

  1. البيانات الأصلية: عرض DataFrame الأصلي الذي يحتوي على الصفوف المكررة والقيم الشاذة.
  2. اكتشاف وإزالة المكررات: سيتم طباعة عدد الصفوف المكررة المكتشفة، ثم عرض البيانات بعد إزالة هذه الصفوف.
  3. اكتشاف القيم الشاذة: لكل عمود رقمي ('Age', 'Score')، سيتم طباعة القيم الشاذة التي تم اكتشافها باستخدام طريقة IQR، وعددها.
  4. البيانات النهائية: سيتم عرض DataFrame بعد تطبيق معالجة القيم الشاذة (Capping)، حيث ستلاحظ أن القيم الشاذة (مثل 150 في Age و 10 في Score) قد تم تعديلها لتصبح ضمن الحدود المقبولة (الحدود العليا أو الدنيا).

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