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

دقائق القراءة: 5

لماذا تمثل البيانات المكررة والمشوهة تحدياً حقيقياً؟

في مشاريع Data Science وBig Data، لا تأتي المشكلة غالباً من نقص البيانات، بل من رداءة جودتها. التكرار يضخم الإحصاءات، والقيم الشاذة قد تضلل النماذج أو تشوه مؤشرات الأداء. لذلك تبدأ أي عملية تحليل جادة بفحص بنية البيانات قبل بناء Pipeline موثوق.

إذا كنت قد قرأت سابقاً تنظيف البيانات (Data Cleaning): اكتشاف ومعالجة القيم المفقودة (Missing Values)، فستلاحظ أن معالجة التكرار والقيم الشاذة هي الامتداد الطبيعي لنفس المنهجية: فهم السلوك الطبيعي للبيانات ثم عزل الانحرافات غير الموثوقة.

التمييز بين التكرار والقيم الشاذة

التكرار Duplicate

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

القيم الشاذة Outliers

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

المنهج العملي في بايثون

أفضل ممارسة هي السير وفق سلسلة واضحة: استكشاف، اكتشاف، قرار، ثم توثيق. هذه السلسلة تمنع الحذف العشوائي وتدعم قابلية التتبع في البيئات المؤسسية.

  • توحيد الأنواع والحقول قبل أي فحص.
  • قياس التكرار على مستوى الصف أو المفتاح التجاري.
  • تحليل التوزيع لاكتشاف القيم الشاذة.
  • اختيار المعالجة: حذف، قص، استبدال، أو وسم.
  • التحقق النهائي من أثر التنظيف على الإحصاءات.

مثال عملي باستخدام Pandas وNumPy

import pandas as pd
import numpy as np

df = pd.read_csv("sales.csv")

# 1) إزالة السجلات المكررة
df = df.drop_duplicates()

# 2) اكتشاف التكرار على مستوى عميل + تاريخ + منتج
duplicate_mask = df.duplicated(subset=["customer_id", "order_date", "product_id"], keep=False)
duplicate_rows = df.loc[duplicate_mask]

# 3) حساب القيم الشاذة بطريقة IQR
numeric_col = "order_value"
q1 = df[numeric_col].quantile(0.25)
q3 = df[numeric_col].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr

outlier_mask = (df[numeric_col] < lower_bound) | (df[numeric_col] > upper_bound)
outliers = df.loc[outlier_mask]

# 4) معالجة القيم الشاذة عبر القص
df[numeric_col] = df[numeric_col].clip(lower_bound, upper_bound)

print("Duplicates:", len(duplicate_rows))
print("Outliers:", len(outliers))

متى نستخدم الحذف ومتى نستخدم القص؟

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

في التحليلات المالية والتشغيلية، حذف القيم الشاذة دون مراجعة قد يخفي أحداثاً مهمة. لذلك يفضل أحياناً وسم السجل بدلاً من حذفه، ثم استخدامه في النمذجة كإشارة خاصة أو Feature إضافية.

عند العمل في بيئات Data Architecture واسعة النطاق، اجعل فحص التكرار جزءاً مبكراً من خط الإدخال وليس خطوة لاحقة في التحليل. هذا يقلل الكلفة ويمنع تضخم التخزين ويثبت جودة النتائج عبر الطبقات التحليلية.

كشف القيم الشاذة بإحصاء متين

يعتمد الكثير من المحللين على الانحراف المعياري، لكنه حساس للقيم المتطرفة نفسها. لهذا يفضل في حالات عديدة استخدام الوسيط وIQR أو درجة Z-score مع الانتباه إلى شكل التوزيع.

  • إذا كان التوزيع شبه طبيعي، فـ Z-score خيار مناسب.
  • إذا كان التوزيع منحرفاً، فـ IQR أكثر استقراراً.
  • في البيانات الزمنية، افحص الشذوذ داخل النافذة الزمنية نفسها.
  • في البيانات متعددة الفئات، طبّق الفحص داخل كل شريحة على حدة.

معالجة متقدمة في Spark

عندما تكبر البيانات، تصبح المعالجة المتجهة داخل Pandas محدودة. هنا تظهر قيمة PySpark في توزيع الحمل عبر العقد، مع الحفاظ على منطق التنظيف نفسه.

from pyspark.sql import SparkSession
from pyspark.sql.functions import col, row_number
from pyspark.sql.window import Window

spark = SparkSession.builder.appName("clean_duplicates_outliers").getOrCreate()

df = spark.read.option("header", True).csv("sales.csv", inferSchema=True)

# إزالة التكرار
df_clean = df.dropDuplicates()

# اكتشاف التكرار المنطقي
window_spec = Window.partitionBy("customer_id", "order_date", "product_id").orderBy(col("order_value").desc())
df_ranked = df.withColumn("rn", row_number().over(window_spec))
df_unique = df_ranked.filter(col("rn") == 1).drop("rn")

# حدود IQR تقريبية عبر جمع الإحصاءات
quantiles = df.approxQuantile("order_value", [0.25, 0.75], 0.01)
q1, q3 = quantiles[0], quantiles[1]
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr

df_bounded = df_unique.withColumn(
    "order_value",
    when(col("order_value") < lower_bound, lower_bound)
    .when(col("order_value") > upper_bound, upper_bound)
    .otherwise(col("order_value"))
)

في خطوط ETL الكبيرة، لا تكتفِ بتنظيف الملف النهائي. الأفضل بناء طبقة تحقق مبكر داخل Pipeline تضع قيوداً على المفاتيح، وتحتفظ بسجل تدقيق، وتفصل السجلات المشكوك فيها في منطقة حجر بيانات مخصصة.

أفضل الممارسات المهنية

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

كما أن توحيد المنهج بين فرق التحليل والـ Machine Learning يضمن أن التدريب والإنتاج لا يختلفان في فهم السجل نفسه. هذا مهم جداً لتجنب انحراف الأداء بين بيئة التجربة وبيئة التشغيل.

خلاصة عملية

البيانات المكررة والمشوهة ليست مجرد مشكلة تنسيق، بل قضية تؤثر في القرار والتحليل والتنبؤ. باستخدام Python وPandas وSpark يمكنك بناء طبقة تنظيف دقيقة تتعامل مع التكرار والقيم الشاذة بطريقة متوازنة، قابلة للتوسع، وملائمة لمتطلبات الجودة الحديثة.

ومع ربط هذه الممارسة بما تعلمته في مكتبة Pandas (2): استكشاف هيكل البيانات وفهم DataFrame و Series، تصبح معالجة الجودة خطوة منهجية وليست إجراءً طارئاً، وهذا هو الفرق بين تحليل سريع وتحليل احترافي.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *