كيفية استخدام Is Not Null في PySpark (مع أمثلة)

دليل أكاديمي شامل يشرح كيفية استخدام شرط Is Not Null وتصفية القيم المفقودة في PySpark باستخدام أساليب برمجية متعددة مع أمثلة تطبيقية مفصلة.

Mohammed looti أكاديمي وباحث متخصص في علم النفس
تاريخ النشر
تمت المراجعة العلمية · د. مروة عبد العظيم · 24 أكتوبر، 2026
مراجعة وتدقيق علمي معتمد تاريخ التدقيق: 24 أكتوبر، 2026
د. مروة عبد العظيم دكتوراه في علم النفس
عضو هيئة التدريس • جامعة كربلاء
معايير التدقيق والاعتماد السريري

يخضع هذا المحتوى لمعايير ضبط الجودة والتدقيق العلمي والأكاديمي الصارمة في شبكة علم النفس العربي، لضمان صحة المعلومات ودقتها السريرية ومطابقتها لأحدث الأدلة والبراهين الصادرة عن الجمعيات النفسية والطبية المعتمدة (APA / WHO).

تتبوأ هندسة البيانات الضخمة مكانة مركزية في البنية التحتية للمؤسسات المعاصرة، حيث لم تعد إدارة البيانات مقتصرة على مجرد تخزين السجلات واسترجاعها، بل امتدت لتشمل معالجة تدفقات هائلة وغير متجانسة من المعلومات الرقمية في بيئات موزعة وعالية التعقيد. في هذا السياق، يبرز محرك أباتشي سبارك (Apache Spark) كأحد أقوى الحلول التقنية لمعالجة البيانات الضخمة، بفضل قدرته الفائقة على الحوسبة في الذاكرة وتوزيع الأحمال عبر العقد الحاسوبية بكفاءة استثنائية. ومع ذلك، تصطدم هذه القدرات الحسابية الهائلة بحقيقة حتمية في علم البيانات، وهي ظاهرة “البيانات المفقودة” أو “القيم الخالية” (Missing/Null Values)، والتي تنشأ نتيجة أخطاء الإدخال، أو انقطاع أجهزة الاستشعار، أو التباين في مخططات قواعد البيانات التكاملية.

تعد معالجة السجلات غير المكتملة ركيزة لا غنى عنها لضمان موثوقية النماذج التحليلية وسلامة المؤشرات الإحصائية؛ إذ إن تسرب قيمة خالية واحدة إلى مسار تدفق البيانات قد يؤدي إلى نتائج حسابية مضللة، أو يسبب فشلاً ذريعاً أثناء تدريب خوارزميات التعلم الآلي، أو يفرض أعباءً إضافية على موارد الشبكة والذاكرة. هنا تبرز أهمية الدالة الشرطية isNotNull() في واجهة بايسپارك (PySpark)، بوصفها أداة حاسمة تتيح لمهندسي ومحللي البيانات تصفية وتنقية أطر البيانات (DataFrames) بدقة رياضية وتنفيذ أمثل على مستوى العتاد الموزع.

يقدم هذا المقال دراسة استقصائية متكاملة حول كيفية توظيف التعبير الشرطي isNotNull() واستراتيجيات استبعاد وتصفية القيم الخالية في PySpark. يتناول المقال الأبعاد المعمارية والمنطقية الكامنة خلف المنطق ثلاثي القيم (Three-Valued Logic)، ويوضح الفروق الدقيقة بين مختلف صيغ التصفية، فضلاً عن تقديم تطبيقات عملية متسلسلة مدعومة بأكواد برمجية نموذجية، وتحليلات لأداء محرك التحسين الشجري كاتاليست (Catalyst Optimizer)، وصولاً إلى صياغة أفضل الممارسات المنهجية لحوكمة خطوط معالجة البيانات الضخمة في البيئات الإنتاجية.

1. مقدمة شاملة حول معالجة القيم المفقودة في بيئة PySpark

1.1 مفهوم القيم الخالية وتأثيرها على معالجة البيانات الضخمة

في بيئات الحوسبة الموزعة وقواعد البيانات العلائقية، لا تعبر القيمة الخالية (NULL) عن الصفر العددي أو السلسلة النصية الفارغة، بل تمثل غياباً مطلقاً للمعلومة أو حالة عدم تعيين (Unknown State). تستند هذه الدلالة إلى نموذج المنطق ثلاثي القيم (Three-Valued Logic – 3VL) المنبثق عن معايير لغة الاستعلامات البنيوية (ANSI SQL)، حيث تحتمل التعبيرات المنطقية إحدى ثلاث حالات: الصواب (True)، أو الخطأ (False)، أو المجهول (Unknown/Null). يترتب على هذا التمايز الرياضي أن أية عملية مقارنة مباشرة بين قيمة مجهولة وأية قيمة أخرى—حتى لو كانت قيمة خالية مماثلة—تؤول حتماً إلى نتيجة مجهولة (Null)، وليست صواباً أو خطأ، مما يعطل محددات التصفية المنطقية التقليدية إذا لم تتم إدارتها بدقة.

يتضاعف التأثير السلبي للبيانات المفقودة عند الانتقال إلى التحليل الإحصائي والعمليات الحسابية المجمعة. فعند احتساب المتوسطات الحسابية أو الانحرافات المعيارية دون معالجة مسبقة للمتغيرات الناقصة، تتباين المخرجات استناداً إلى آلية تعامل المحرك مع تلك الفراغات؛ إذ قد يؤدي إسقاطها العشوائي إلى تحيز إحصائي غير مقصود (Selection Bias) يشوه معالم العينة الكلية. علاوة على ذلك، في المعالجات الحسابية التي تتضمن ضرب المصفوفات أو تقدير معاملات الانحدار، يتسبب وجود عنصر فارغ واحد في إفساد نتائج السطر بأكمله، مما يولد سلسلة من القيم غير المعرفة عبر مراحل التحويل المتتابعة.

من الناحية المعمارية للأنظمة الموزعة مثل Apache Spark، يفرض انتشار القيم الخالية تحديات تشغيلية ترتبط بكفاءة التخزين ونقل البيانات عبر الشبكة. تخزن البيانات في سبارك بصيغ أعمدة متقدمة مثل Parquet أو ORC، والتي تعتمد على مخططات تبيّن قابلية المتغيرات للاحتواء على فراغات (Nullability). إن سوء إدارة السجلات الخالية عبر وحدات التخزين قد يؤدي إلى تضخم مساحات الفهارس النقطية، ويحدث خللاً في ضغط البيانات وتوزيعها داخل الكتل التخزينية (Partitions)، مما ينعكس سلباً على كفاءة استرجاع السجلات عبر العقد الحاسوبية.

1.2 المقارنة المنهجية بين القيم الخالية في Python ومحرك Spark SQL

يتطلب الفهم العميق لبيئة PySpark تمييزاً دقيقاً بين كائن None المعتمد في لغة بايثون الأصلية، وقيمة NULL التي يتعامل معها محرك Spark SQL المكتوب بلغة سكالا والمستند إلى آلة جافا الافتراضية (JVM). يمثل None في بايثون كائناً منفرداً من نوع NoneType يدل على انعدام القيمة في الذاكرة المحلية للبرنامج. بالمقابل، تخضع قيمة NULL في سبارك إلى محرك التخزين الداخلي المنظم “Tungsten”، والذي لا يتعامل مع كائنات جافا أو بايثون الفردية لتفادي أعباء إدارة الذاكرة واستدعاء جامع المهملات (Garbage Collector)، بل يستخدم مصفوفات بتية (Bitmaps) لتمثيل وجود القيمة من عدمها داخل الصفوف الثنائية المتراصة.

يتجلى تعقيد إضافي عند إدخال القيم الرقمية غير المعرفة NaN (Not a Number)، الشائعة في مكتبات الحوسبة العلمية مثل NumPy وPandas. في محرك سبارك، تُعامل NaN كقيمة عائمة خاصة تخضع لمعايير IEEE 754، وتختلف دلالتها ومعالجتها المنطقية عن NULL؛ حيث تُعد NaN مساوية لنفسها في بعض سياقات الترتيب الدلالي في سبارك (لتسهيل عمليات الفرز والتجميع)، بينما لا تكافئ NULL أي شيء مطلقاً. يوضح هذا التباين أهمية الانتباه إلى نوع البيانات التحتية؛ إذ إن فحص القيم غير الخالية عبر الدالة isNotNull() يستبعد قيم NULL حصراً، ولكنه قد يبقي على قيم NaN كبيانات صالحة ما لم تُدمج بشروط نفي إضافية خاصة بالأرقام العائمة.

يفرض مخطط البيانات الصارم (Strict Schema) في Spark SQL قواعد تحويل تلقائي محددة عند بناء أطر البيانات انطلاقاً من مجموعات بيانات بايثون المحلية. عند تمرير قائمة تحتوي على قيم None إلى محرك سبارك، يقوم المترجم الداخلي بتعيين خاصية nullable = true في حقل StructField المرتبط بذلك العمود. إذا فُرض مخطط بيانات يمنع الفراغات (nullable = false) ووردت قيمة مفقودة، يتم إطلاق استثناءات برمجية أثناء مرحلة المعالجة والتحويل البرمجي، مما يبرز دور التخطيط الهيكلي الواعي في تفادي توقف مسارات المعالجة المفاجئ.

1.3 أهمية استبعاد البيانات المفقودة لضمان سلامة النماذج التحليلية

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

إلى جانب خوارزميات النمذجة، يسهم الاستبعاد الممنهج للبيانات المفقودة في صيانة موثوقية المؤشرات الحسابية للأعمال (KPIs). في التطبيقات التحليلية الكبرى، مثل حساب معدلات الاحتفاظ بالعملاء أو قياس مؤشرات التفاعل في المنصات الرقمية، قد تتسبب السجلات المشوهة في تضخيم النتائج أو تقليصها زيفاً. فعلى سبيل المثال، يؤدي احتساب متوسط زمن الجلسة دون تصفية الجلسات التي تفتقر إلى طابع زمني دقيق للانتهاء إلى توليد مؤشرات مضللة لمتخذي القرار المؤسسي.

علاوة على ذلك، يقلل استبعاد البيانات الناقصة من احتمالية حدوث الأخطاء الاستثنائية أثناء تنفيذ التحويلات المعقدة، مثل عمليات الضم بين الجداول (Joins) أو دوال النوافذ (Window Functions). ففي عمليات الربط، قد يقود انتشار القيم الخالية في حقول المفاتيح الأساسية أو الخارجية إلى تكوين تجزئات بيانية غير متوازنة، مما يؤدي إلى تباطؤ خطوط المعالجة الموزعة وتعريض مساحات الذاكرة العشوائية لخطر الفيض، وهو ما ستتم معالجته تفصيلياً في الفصول المتقدمة من هذا المقال.

2. إعداد بيئة الحوسبة وبناء إطار البيانات التجريبي

2.1 تهيئة جلسة العمل وضبط إعدادات الذاكرة والمحرك

تبدأ أية معالجة برمجية في إطار PySpark بتهيئة نقطة الدخول المركزية المتمثلة في كائن جلسة المعالجة الرئيسية SparkSession، والتي تم تقديمها كمعيار موحد منذ إطلاق الإصدار Spark 2.0، مدمجةً السياقات السابقة مثل SparkContext وSQLContext. يتم بناء الجلسة بالاعتماد على نمط التصميم المعتمد “المُنشئ” (Builder Pattern)، مما يتيح تخصيص معايير الأداء والموارد الحاسوبية بدقة قبل الشروع في إرسال المهام إلى العقد التنفيذية (Executors).

يتطلب الاستخدام الأمثل لموارد الحاسوب، لا سيما في البيئات المحلية أو بيئات الاختبار، تحديد عدد النوى الحاسوبية المخصصة للمحرك، وتعيين سقف استهلاك الذاكرة التشغيلية لتفادي مشكلات تجاوز الحدود المسموحة للذاكرة (Out-Of-Memory Errors). يتم ذلك عبر وسائط الضبط المضمنة داخل الكود البرمجي التالي، والذي يؤسس بيئة عمل تجريبية مهيأة لتطبيق التصفية المنطقية على أطر البيانات:

from pyspark.sql import SparkSession

# تهيئة جلسة العمل وضبط التوزيع الحاسوبي
spark = SparkSession.builder 
    .appName("PySpark_IsNotNull_Tutorial") 
    .master("local[*]") 
    .config("spark.driver.memory", "2g") 
    .config("spark.sql.shuffle.partitions", "4") 
    .getOrCreate()

من خلال السطر .master("local[*]")، يتم توجيه المحرك لاستغلال كافة النوى المنطقية المتاحة في الجهاز المحلي، مما يحاكي بيئة الحوسبة المتوازية المصغرة. كما جرى تقليص عدد تقسيمات الخلط (Shuffle Partitions) إلى أربعة عبر الإعداد spark.sql.shuffle.partitions لملاءمة حجم البيانات التجريبية المتواضعة ومنع التوزيع المفرط الذي قد يبطئ زمن التنفيذ بدون داعٍ.

2.2 بناء مصفوفة البيانات المرجعية وتحديد المخطط الهيكلي

لإجراء تجارب التصفية والتحقق المنطقي بصورة موضوعية، يتعين بناء مصفوفة بيانات نموذجية تحتوي على مزيج متوازن من البيانات السليمة، والسجلات الناقصة كلياً أو جزئياً، والقيم الحدية كالأصفار. تم اختيار سياق تحليلي رياضي واقعي يمثل أداء فرق كرة السلة عبر متغيرات محددة: الفريق (Team)، والمؤتمر الجغرافي (Conference)، ومعدل النقاط (Points)، ومعدل التمريرات الحاسمة (Assists).

يتيح إدراج القيم الفارغة عمداً عبر كائن None محاكاة الاختلالات التي تشهدها خطوط تجميع البيانات الحقيقية. يعرف الكود البرمجي أدناه مصفوفة السجلات المرجعية، متبوعاً بقائمة المسميات المقابلة للأعمدة لإنشاء إطار البيانات المبدئي:

# تعريف مصفوفة السجلات التحليلية متضمنة قيماً فارغة عمداً
data_records = [
    ("Hawks", "East", 112.5, 24.3),
    ("Celtics", "East", 118.2, 26.1),
    ("Lakers", "West", 115.8, None),
    ("Warriors", "West", None, 28.5),
    ("Bulls", None, 105.4, 21.0),
    ("Knicks", "East", None, None),
    ("Heat", "East", 108.9, 23.8),
    ("Spurs", "West", 0.0, 19.4),
    (None, "West", 102.1, 20.2),
    ("Nuggets", None, None, 25.7)
]

# تسمية الأعمدة التحليلية
columns = ["Team", "Conference", "Points", "Assists"]

# توليد إطار البيانات بالاعتماد على الاستدلال التلقائي للمخطط
df_basket = spark.createDataFrame(data=data_records, schema=columns)

يوفر هذا التصميم تنوعاً واسعاً يخدم أهداف الاختبار؛ إذ نجد سجلاً يفتقر إلى اسم الفريق، وآخر يفتقر إلى تصنيف المؤتمر والنقاط معاً، وسجلاً ثالثاً يحتوي على قيمة صفرية صريحة في عمود النقاط لاختبار قدرة التصفية على التفريق بين الصفر الرياضي والقيمة الخالية المطلقة.

2.3 معاينة البنية وتفحص السجلات باستخدام دوال الاستعراض المتاحة

عقب توليد إطار البيانات، تقتضي المنهجية العلمية فحص سلامة المخطط المستنتج (Inferred Schema) ومعاينة توزع السجلات في الذاكرة قبل مباشرة عمليات التعديل والفلترة. يتم ذلك عبر تطبيق دالتي show() وprintSchema()، اللتين تكشفان عن البنية الجدولية والخصائص التيبوغرافية لكل عمود على حدة.

# معاينة البيانات الجدولية وتوزيع السجلات
df_basket.show(truncate=False)

# فحص المخطط الهيكلي وقابلية الأعمدة لاحتواء القيم الفارغة
df_basket.printSchema()

# التحقق من إجمالي عدد السجلات قبل الشروع في التصفية
initial_count = df_basket.count()
print(f"إجمالي عدد السجلات الأولي: {initial_count}")

يظهر استعراض المخطط الهيكلي أن جميع الأعمدة قد وُسمت تلقائياً بالخاصية nullable = true، مما يعني أن محرك كاتاليست مستعد منطقياً للتعامل مع وجود فراغات داخل كل حقل من الحقول. وتوضح المعاينة الجدولية وجود عشرة سجلات موزعة، تشكل الأساس المرجعي لإجراء المقارنات الكمية ودراسة فاعلية دوال التحقق من الوجود المنطقي في الفصول التالية.

3. التصفية المباشرة للقيم غير الخالية في عمود محدد عبر الدالة المخصصة

3.1 التحليل النحوي لاستخدام دالة التصفية الشرطية في PySpark

تعتمد آلية التصفية المباشرة في بايسپارك على دمج دالتي التصفية filter() أو مرادفتها النحوية where() مع تعبير العمود المتخصص isNotNull() المتاح عبر فئة pyspark.sql.Column. ترجع دالة isNotNull() تعبيراً برمجياً من النوع المنطقي (Boolean Expression) يقيّم كل سجل على حدة، بحيث ينتج القيمة True إذا كان الحقل المستهدف يحمل قيمة معرفة، وينتج القيمة False إذا كان الحقل يحتوي على NULL.

تتم التصفية دون تفريغ البيانات إلى ذاكرة بايثون المحلية، بل يتم تفسير التعبير عبر شجرة التعبيرات التحليلية الخاصة بمحرك سبارك الداخلي. يوضح الكود التالي كيفية استيراد الدالة المساعدة col وصياغة التعبير الشرطي الصريح لعزل البيانات غير المكتملة:

from pyspark.sql.functions import col

# صياغة التعبير الشرطي المباشر
filtered_by_points = df_basket.filter(col("Points").isNotNull())

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

3.2 تطبيق عملي لتصفية إطار البيانات استناداً إلى عمود النقاط

عند تنفيذ التصفية على عمود النقاط (Points)، يستهدف التعبير الشرطي استبعاد كافة السجلات التي تعاني من غياب القيمة في هذا المتغير العددي الحرج، مع الإبقاء على كافة السجلات الأخرى دون اعتبار لكونها ناقصة في حقول ثانوية. يتم عرض النتائج واحتساب عدد الصفوف المتبقية لمراقبة التغير الهيكلي في الإطار عبر الكود الآتي:

# تنفيذ التصفية واستعراض النتائج المتبقية
df_points_clean = df_basket.filter(col("Points").isNotNull())
df_points_clean.show(truncate=False)

# مقارنة عدد السجلات قبل التصفية وبعدها
clean_count = df_points_clean.count()
print(f"عدد السجلات بعد استبعاد القيم الخالية من عمود النقاط: {clean_count}")

ينخفض عدد السجلات بمقدار ثلاثة صفوف، حيث تم استبعاد السجلات الخاصة بفرق (Warriors، Knicks، Nuggets) نظراً لاحتوائها على None في خانة النقاط. والأهم من ذلك من الناحية التحليلية، أن السجل التابع لفريق “Spurs” الذي يحمل القيمة العديدة 0.0 قد بقي ضمن الإطار؛ إذ إن الصفر يمثل قيمة عددية صالحة وليس انعداماً للمعلومة، مما يبرهن على سلامة ودقة الفحص المنطقي للدالة.

3.3 المقارنة الاصطلاحية بين الصيغ النحوية المتعددة للإشارة إلى الأعمدة

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

  • استدعاء الكائن المباشر عبر الخاصية (Dot Notation): df_basket.filter(df_basket.Points.isNotNull())، وتعد هذه الطريقة موجزة ومباشرة، ولكنها تصبح غير صالحة للاستخدام إذا احتوى اسم العمود على مسافات، أو رموز خاصة، أو تطابق مع إحدى الدوال المدمجة في إطار البيانات نفسه.
  • الوصول عبر المعرف الفهرسي (Bracket Notation): df_basket.filter(df_basket["Points"].isNotNull())، وتمتاز هذه الصيغة بقدرتها على معالجة أسماء الأعمدة غير القياسية التي تتضمن فراغات أو حركات خاصة بأمان تام دون أخطاء نحوية.
  • استخدام الدالة التجريدية col(): df_basket.filter(col("Points").isNotNull())، وتعتبر المعيار الأرقى في التطوير البرمجي؛ نظراً لكونها تفصل التعبير الشرطي عن كائن إطار البيانات الأصلي، مما يسهل إعادة استخدام الدوال وتمريرها بين تحويلات متعددة.

تتطابق هذه الأنماط كلياً عند مرحلة الترجمة والتجميع الشجري في محرك كاتاليست؛ حيث تُترجم جميعها إلى نفس الكائن الداخلي من النوع UnresolvedAttribute قبل ربطه بمخطط البيانات النهائي، مما يجعل المفاضلة بينها قائمة على اعتبارات جودة التوثيق ومعايير كتابة الشيفرة البرمجية في المؤسسة.

4. الحذف الشامل للسجلات الخالية عبر تطبيق دالة إسقاط القيم

4.1 المبادئ التشغيلية لتقنية إسقاط البيانات الناقصة من الإطار

تقدم واجهة PySpark أسلوباً بديلاً للتعامل مع الفراغات الرقمية يتمثل في فئة المعالجة المخصصة للبيانات الخالية pyspark.sql.DataFrameNaFunctions، والتي يمكن الوصول إليها مباشرة عبر الخاصية df.na أو الدالة المختصرة dropna(). على النقيض من التصفية الانتقائية لعمود محدد، تمارس تقنية الإسقاط مسحاً أفقياً شاملاً لسجلات إطار البيانات، وتقوم بحذف أي سجل يتضمن قيمة مفقودة واحدة في أي من حقوله بحسب الإعدادات الافتراضية.

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

4.2 تخصيص سلوك الإسقاط عبر المعاملات المتقدمة للدالة المتاحة

تتجاوز دالة dropna() حدود الحذف العشوائي بتوفير معاملات تحكم متقدمة تتيح هندسة عملية التطهير بدقة فائقة. تشمل هذه المعاملات خيارات التحديد المنطقي، والعتبات الرقمية، والنطاقات الفرعية، كما يتضح في المعاملات التالية:

  • المعامل how: يقبل قيمتين؛ إما 'any' (وهو الوضع الافتراضي الذي يسقط السجل إذا احتوى على قيمة خالية واحدة على الأقل)، أو 'all' (والذي يشترط أن تكون جميع حقول السجل خالية تماماً لتنفيذ الحذف).
  • المعامل thresh: يحدد الحد الأدنى العددي للقيم الصالحة (Non-Null Values) الواجب توافرها في السجل للبقاء عليه في الإطار، متجاهلاً شرط الحذف الكلي إذا استوفى السجل هذا النصاب.
  • المعامل subset: يسمح بحصر نطاق الفحص في مصفوفة محددة من الأعمدة، مع الإبقاء على الفراغات في بقية الأعمدة خارج نطاق الحذف.

يوضح الكود التالي كيفية تطبيق هذه المعاملات لتنفيذ عمليات تنقية مخصصة تناسب المتطلبات التحليلية المختلفة:

# إسقاط السجلات التي تفتقر تماماً للمعلومات في كل الحقول
df_drop_all = df_basket.dropna(how="all")

# اشتراط وجود 3 قيم صالحة على الأقل للإبقاء على الصف
df_drop_thresh = df_basket.dropna(thresh=3)

# قصر الحذف على الفراغات المتواجدة في عمودي النقاط والتمريرات فقط
df_drop_subset = df_basket.dropna(subset=["Points", "Assists"])

4.3 دراسة مقارنة لنتائج التطبيق العملي على البيانات المرجعية

لتقييم الأثر المباشر لأسلوب الإسقاط مقابل التصفية الانتقائية، يتم تطبيق دالة dropna() بالسلوك الافتراضي (how='any') ومقارنة النتائج كمياً مع ما تم استخلاصه في الفصل الثالث:

# تطبيق الإسقاط الجذري الافتراضي
df_strict_clean = df_basket.dropna()
df_strict_clean.show(truncate=False)

print(f"عدد السجلات بعد الإسقاط الشامل: {df_strict_clean.count()}")

تكشف النتائج عن انخفاض حاد في حجم إطار البيانات؛ إذ لم يتبق سوى أربعة سجلات مكتملة تماماً من أصل عشرة سجلات. لقد أدى الإسقاط الشامل إلى خسارة سجل فريق “Lakers” بسبب غياب التمريرات، وسجل فريق “Bulls” لغياب المؤتمر، وسجل الفريق غير المسمى بالرغم من توفر إحصائياته الرقمية بالكامل. يقود هذا التحليل إلى توصية منهجية مفادها: يجب تفضيل التصفية المستهدفة عبر isNotNull() أو استخدام المعامل subset عندما تقتصر متطلبات الدراسة على متغيرات محددة، وحصر الحذف الجذري في الحالات التي تتطلب اكتمالاً مطلقاً لمدخلات الخوارزميات الحسابية المعقدة.

5. التصفية متعددة الشروط عبر دمج المعاملات المنطقية المتقدمة

5.1 الربط المنطقي التراكمي للتحقق من سلامة أكثر من عمود متزامن

في السيناريوهات التحليلية المركبة، تبرز الحاجة المتكررة للتحقق من اكتمال البيانات في أكثر من حقل في آن واحد قبل إدراج السجل في العمليات الحسابية اللاحقة. تتيح واجهة PySpark تطبيق الربط المنطقي التراكمي باستخدام المعامل المنطقي البتي (Bitwise AND) الممثل بالرمز & لربط تعبيرات isNotNull() المتعددة داخل جملة تصفية واحدة.

يتولى محرك التحسين الداخلي تقييم هذه الشروط بصورة متزامنة وموزعة عبر العقد. يوضح المثال التالي كيفية عزل السجلات التي تتضمن قيماً معرفة في عمودي المؤتمر والنقاط معاً، مما يضمن استبعاد أي سجل يعاني من خلل في أي من هذين البعدين:

# التصفية التراكمية المشتركة على حقلين متزامنين
df_conf_and_points = df_basket.filter(
    col("Conference").isNotNull() & col("Points").isNotNull()
)

df_conf_and_points.show(truncate=False)

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

5.2 الربط المنطقي التخييري للحفاظ على السجلات ذات الكفاية الجزئية

في المقابل، تتطلب بعض التحليلات الاستكشافية الحفاظ على السجلات التي تحتوي على حد أدنى من المعلومات المفيدة، بحيث يكفي توفر قيمة في أحد الحقول المستهدفة للإبقاء على الصف ضمن إطار البيانات. يُوظف في هذا السياق المعامل المنطقي التخييري (Bitwise OR) الممثل بالرمز | للربط بين دوال التحقق من الوجود.

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

# التصفية التخييرية للحفاظ على الكفاية الإحصائية الجزئية
df_points_or_assists = df_basket.filter(
    col("Points").isNotNull() | col("Assists").isNotNull()
)

df_points_or_assists.show(truncate=False)

يسمح هذا التعبير الشرطي باستعادة سجلات مثل “Lakers” (الذي يفتقر للتمريرات ولكنه يمتلك نقاطاً) و”Warriors” (الذي يفتقر للنقاط ويمتلك تمريرات)، مما يوسع نطاق العينة المسترجعة ويدعم الدراسات التي تركز على المؤشرات الفردية المنفصلة.

5.3 الأسبقية الرياضية وقواعد تنسيق الأقواس في لغة التعبير الشرطي

من الأخطاء البرمجية الشائعة والحرجة في بيئة بايسپارك تجاهل الفروق الدقيقة في قواعد الأسبقية الحسابية والمنطقية بين لغة بايثون الأصلية والتعبيرات المبنية لمحرك سبارك. في لغة بايثون، تحظى المعاملات البتية (& و|) بأسبقية رياضية أعلى من معاملات المقارنة والمساواة، وأعلى من تقييم التعبيرات الوظيفية للكائنات.

بناءً على ذلك، إذا صيغت جملة التصفية دون حصر كل تعبير شرطي بأقواس مستقلة ومحكمة، يقوم مفسر بايثون بمحاولة ربط المعامل البتي بين الكائنات غير المتوافقة قبل تقييم دوال التحقق، مما يؤدي فوراً إلى إطلاق استثناء نحوي (Syntax or TypeError) يوقف المعالجة. تفرض أفضل الممارسات اتباع التنسيق الصارم الموضح أدناه عند بناء الشروط المعقدة والمتداخلة:

# صياغة محكمة تحترم الأسبقية المنطقية عبر الأقواس المستقلة
df_complex_filter = df_basket.filter(
    (col("Team").isNotNull()) &
    (
        (col("Points").isNotNull()) | 
        (col("Assists").isNotNull())
    )
)

يسهم هذا التنسيق الشجري الهيكلي في تجنب الغموض المنطقي، ويسهل مراجعة الكود وتدقيقه وصيانته ضمن مشاريع هندسة البيانات الكبيرة والمتعددة المطورين.

6. توظيف استعلامات التعبير القياسي Spark SQL في التصفية

6.1 بناء التعابير النصية المتوافقة مع معايير SQL التقليدية

توفر منصة Spark بيئة موحدة تجمع بسلاسة بين واجهة برمجة التطبيقات للأعمدة (DataFrame API) والتعابير النصية القياسية للغة SQL. يتيح ذلك لمهندسي البيانات تمرير عبارات شرطية نصية مباشرة كمعاملات لخاصية التصفية filter() دون الحاجة إلى استيراد دوال إضافية أو كتابة تعبيرات برمجية مطولة.

تستخدم التعابير النصية صيغة المعيار القياسي IS NOT NULL بصورة صريحة وغير حساسة لحالة الأحرف، مما يجعل الكود شديد الوضوح والانسيابية للمطورين ومحللي قواعد البيانات المعتادين على بيئات SQL العلائقية التقليدية. يجسد الكود التالي هذا الأسلوب المباشر:

# تمرير الجملة النصية القياسية كمعيار تصفية
df_sql_expression = df_basket.filter("Points IS NOT NULL AND Conference IS NOT NULL")

df_sql_expression.show(truncate=False)

يقوم محرك التحليل اللغوي في سبارك باستلام هذا التعبير النصي وتحويله داخلياً وبشكل تلقائي إلى شجرة تعبيرات منطقية تتطابق وظيفياً مع التعبيرات المبنية عبر كائنات col()، مما يمنح المطور حرية الاختيار بين الأسلوب البرمجي أو الأسلوب التعبيري الصريح.

6.2 إنشاء الجداول المؤقتة وتشغيل الاستعلامات الهيكلية الشاملة

لتوسيع نطاق الاعتماد على استعلامات SQL، يتيح المحرك تسجيل أطر البيانات كواجهات جدولية مؤقتة (Temporary Views) صالحة للاستخدام ضمن نطاق جلسة العمل SparkSession الحالية. يتم ذلك عبر استدعاء دالة createOrReplaceTempView()، والتي تنشئ إسقاطاً بيانياً في الذاكرة الوصفية (Catalog) دون تكرار تخزين البيانات الأساسية.

عقب تسجيل الجدول، يمكن كتابة استعلامات بنيوية كاملة تشمل أوامر الاختيار والتصفية والفرز باستخدام الدالة spark.sql() كما هو موضح أدناه:

# تسجيل إطار البيانات كجدول مؤقت في الذاكرة
df_basket.createOrReplaceTempView("basketball_teams")

# صياغة وتشغيل استعلام استرجاع هيكلي قياسي
sql_query = """
    SELECT 
        Team, 
        Conference, 
        Points, 
        Assists
    FROM 
        basketball_teams
    WHERE 
        Team IS NOT NULL 
        AND Points IS NOT NULL
"""

df_sql_result = spark.sql(sql_query)
df_sql_result.show(truncate=False)

ينتج عن تنفيذ spark.sql() إطار بيانات جديد بالكامل يحمل نفس خصائص ومزايا أطر بيانات PySpark العادية، مما يسمح بمواصلة تطبيق التحويلات والدوال البرمجية عليه بسلاسة متناهية.

6.3 تحليل الأداء المعماري للتعابير النصية مقابل واجهة التطبيقات المباشرة

يبرز تساؤل متكرر في أوساط مطوري الحوسبة الموزعة حول المفاضلة الأدائية بين كتابة الاستعلامات عبر التعابير النصية في SQL، واستخدام الواجهة البرمجية للأعمدة عبر col().isNotNull(). تؤكد الأدلة المعمارية الموثقة لمحرك Spark أن المسارين متكافئان تماماً من حيث الأداء وزمن التنفيذ وكفاءة استهلاك الذاكرة العشوائية.

يرجع هذا التكافؤ إلى دور محرك التحسين الشجري “كاتاليست” (Catalyst Optimizer). عندما يستقبل المحرك استعلاماً نصياً أو تعبيراً برمجياً، فإنه يقوم في الخطوة الأولى بتحويل كلا الشكلين إلى “خطة منطقية مجردة” (Unresolved Logical Plan). تمر هذه الخطة بمراحل التحليل والربط مع الكتالوج، ثم التحسين المنطقي (Logical Optimization)، وأخيراً توليد “الخطة المادية” (Physical Plan) الموجهة للمحرك التنفيذي. وبما أن التعبيرين يُترجمان إلى نفس النواة البرمجية في الخطة المادية، فإن الاختيار بينهما يخضع كلياً لمعايير وضوح الشيفرة وسياق هندسة النظام دون أي تأثير سلبي على الأداء الحاسوبي الموزع.

7. التفاعل بين تصفية القيم الخالية والعمليات التجميعية والإحصائية

7.1 السلوك الافتراضي للدوال التجميعية عند مواجهة الحقول المفرغة

تتبع الدوال التجميعية المدمجة في محرك Spark SQL—مثل count()، وsum()، وavg()، وmax()—معايير صارمة محددة سلفاً في مواجهة القيم الخالية. عند احتساب المجموع الحسابي أو المتوسط لعمود رقمي يحتوي على فراغات، تتجاهل دوال التجميع قيم NULL تلقائياً وتجري حساباتها اعتماداً على الأرقام الصالحة فقط دون أن تتسبب في انهيار المعالجة.

ومع ذلك، ينشأ التباين الإحصائي الحرج عند استخدام دوال العد التكراري. فالدالة count(col("Points")) تقوم بعدّ السجلات الحاوية على قيم صالحة حصراً، في حين أن الدالة count("*") أو df.count() تقوم باحتساب السطور كلياً متضمنةً الفراغات. يتضح هذا السلوك ومقارنته مع التصفية الصريحة السابقة في الكود التالي:

from pyspark.sql.functions import avg, count, sum

# فحص السلوك الافتراضي للدوال التجميعية على العمود الأصلي
df_basket.select(
    count("*").alias("Total_Rows"),
    count("Points").alias("Valid_Points_Count"),
    sum("Points").alias("Total_Points"),
    avg("Points").alias("Average_Points")
).show()

تؤكد هذه التجربة أن التصفية المسبقة عبر isNotNull() تؤثر بشكل جوهري على دقة قواسم المتوسطات ومؤشرات النسب المئوية، مما يفرض على مهندس البيانات تحديد الهدف التحليلي بدقة: هل المطلوب احتساب المتوسط كنسبة من الحالات النشطة فقط، أم كنسبة من العينة الكلية الشاملة؟

7.2 تحسين عمليات التجميع الفئوي من خلال التنقية المسبقة للمجموعات

تتضاعف أهمية تصفية القيم الخالية عند استخدامها مع عمليات التجميع المقسم فئوياً باستخدام العبارة groupBy(). عند تطبيق التجميع الفئوي على عمود يحتوي على فراغات دون تنقية مسبقة، يقوم المحرك بإنشاء مجموعة مستقلة خاصة بقيمة null كفئة قائمة بذاتها، وهو ما يؤدي في كثير من الحالات إلى نتائج تحليلية غير مرغوبة وتشويش على دقة التقارير.

علاوة على ذلك، يتسبب تجميع المفاتيح الخالية في حدوث انحراف حاد في توزيع البيانات (Data Skew) أثناء مراحل الخلط وإعادة التوزيع (Shuffle Phase)؛ إذ تُوجه كافة السجلات التابعة للقيمة الخالية إلى منفذ حاسوبي فردي (Single Reducer)، مما يخلق اختناقاً معمارياً يعطل إتمام المهمة الكلية. يوضح الكود الآتي أفضلية التطهير المسبق لمفاتيح التجميع:

# التجميع الفئوي النظيف بعد استبعاد المفاتيح المفقودة
df_grouped_clean = df_basket 
    .filter(col("Conference").isNotNull()) 
    .groupBy("Conference") 
    .agg(
        avg("Points").alias("Mean_Points"),
        sum("Assists").alias("Sum_Assists")
    )

df_grouped_clean.show(truncate=False)

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

7.3 إدارة الدوال التحليلية المتقدمة ودوال النوافذ الحسابية

تمثل دوال النوافذ (Window Functions) أحد أكثر المحاور حساسية تجاه القيم المفقودة. عند استخدام دوال الترتيب والترقيم التسلسلي مثل row_number() أو rank() أو dense_rank()، أو دوال الإزاحة مثل lag() وlead()، يؤثر موضع الفراغات تأثيراً مباشراً على منطق الترتيب؛ إذ يضع محرك سبارك القيم الخالية افتراضياً إما في أدنى الترتيب (Nulls First) أو أقصاه (Nulls Last) بناءً على اتجاه الفرز المعتمد.

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

from pyspark.sql.window import Window
from pyspark.sql.functions import row_number

# تعريف نافذة التوزيع والترتيب بعد التحقق من استبعاد الفراغات
window_spec = Window.partitionBy("Conference").orderBy(col("Points").desc())

# تطبيق دالة النافذة الحسابية على بيانات مطهرة مسبقاً
df_ranked = df_basket 
    .filter(col("Conference").isNotNull() & col("Points").isNotNull()) 
    .withColumn("Rank_In_Conf", row_number().over(window_spec))

df_ranked.show(truncate=False)

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

8. تحسين الأداء وإدارة الموارد الحاسوبية في المجموعات الموزعة

8.1 دور التصفية المبكرة في تقليل نقل البيانات عبر الشبكة

يعد نقل البيانات عبر الشبكة التحتية (Data Shuffle) العملية الأكثر استهلاكاً للوقت والموارد في حوسبة أباتشي سبارك الموزعة. تحدث عمليات الخلط عند إعادة تقسيم البيانات بين العقد أثناء تنفيذ عمليات الضم (Joins)، أو التجميع (GroupBy)، أو الترتيب (OrderBy). تلعب التصفية المبكرة باستخدام isNotNull() دوراً استراتيجياً في خفض حجم هذه البيانات قبل وصولها إلى مرحلة الخلط، مما يجنب النظام استهلاك النطاق الترددي للشبكة في نقل سجلات غير صالحة تحليلياً.

تتكامل هذه الممارسة بصورة مثالية مع ميزة “دفع التعبيرات الشرطية لطبقة التخزين” (Predicate Pushdown). عند قراءة ملفات بصيغ أعمدة مهيكلة مثل Apache Parquet، يقوم محرك سبارك بدفع شرط التصفية isNotNull() إلى مستوى القارئ المادي للملفات (File Reader). بفضل الفهارس الإحصائية المضمنة في رؤوس ملفات Parquet التي تسجل الحدود الدنيا والقصوى وعدد القيم الخالية لكل كتلة تخزينية، يستطيع المحرك تخطي قراءة كتل بيانات كاملة إذا كانت خالية، مما يوفر عمليات الإدخال والإخراج في القرص (Disk I/O) ويحمي الذاكرة العشوائية من الفيض.

8.2 معالجة انحراف توزيع البيانات الناتج عن كثافة القيم الخالية

يمثل انحراف توزيع البيانات (Data Skew) أحد أخطر التحديات التي تهدد استقرار مجموعات سبارك المعالجة للبيانات الضخمة. ينشأ هذا الانحراف عندما تتوزع البيانات عبر الكتل (Partitions) بشكل غير متساوٍ، مما يفرض على إحدى العقد التنفيذية معالجة حجم بيانات يفوق بكثير ما تعالجه العقد الأخرى، فتتحول إلى عنق زجاجة تشغيلي يطيل زمن تنفيذ المهمة الإجمالية أو يؤدي إلى فشلها التام.

في العمليات التي تعتمد على التجزئة باستخدام مفاتيح التوزيع (Hash Partitioning)، كالربط والتجميع، يتم توجيه كافة السجلات ذات القيمة NULL إلى نفس التقسيمة الحاسوبية المادية. فإذا كانت مجموعة البيانات تتضمن ملايين السجلات المفقودة في حقل المفتاح، تتكدس هذه السجلات في عقدة واحدة مسببة انهيارها. يسهم الاستبعاد الاستباقي لهذه الحقول عبر filter(col("Key").isNotNull()) في إعادة التوازن إلى تدفق البيانات عبر المجموعات الحاسوبية، وتأمين تدفق متناغم للأحمال التشغيلية.

8.3 مراقبة خطة التنفيذ ومحرك التحسين الشجري لتحليل الاستعلام

للتحقق العلمي من اندماج شروط التصفية ضمن خطط المعالجة الفيزيائية للمحرك، يوفر بايسپارك الدالة التحليلية explain()، والتي تكشف عن المخططات المنطقية والمادية التفصيلية التي صاغها محرك كاتاليست. يتيح فحص هذه المخرجات للمهندس التأكد من أن التصفية قد تم تطبيقها في أدنى نقطة ممكنة من شجرة الحسابات.

# فحص الخطة التنفيذية المادية والمحسنة للتصفية
df_basket.filter(col("Points").isNotNull()).explain(True)

تكشف قراءة مخرجات هذه الدالة عن وجود العملية Filter isnotnull(Points#...) في موقع متقدم يسبق تحويلات البيانات اللاحقة، مما يثبت نجاح المحرك في تحسين مسار المعالجة واختصار زمن التنفيذ المخصص للمهام، وتفادي معالجة السجلات المعطوبة في المراحل التحليلية المتقدمة.

9. معالجة الحالات الاستثنائية وتجنب الأخطاء البرمجية الشائعة

9.1 التمييز بين النصوص الفارغة والمسافات البيضاء والقيم الخالية الحقيقية

من الانزلاقات الشائعة في معالجة البيانات النصية الخلط بين قيمة الانعدام المطلق NULL، والسلسلة النصية ذات الطول الصفري "" (Empty String)، والسلاسل المكونة من مسافات بيضاء فقط " " (Whitespace). لا تعتبر دالة isNotNull() السلاسل النصية الفارغة أو المسافات قيماً خالية، بل تعاملها كبيانات نصية صالحة ومستوفية لشرط الوجود المنطقي.

يتطلب التطهير الدقيق دمج شرط نفي الفراغ المنطقي مع دوال معالجة النصوص، مثل trim() وlength()، لضمان استبعاد كافة أشكال البيانات الميتة في حقل واحد. يعرض الكود التالي أسلوب التنقية المتكامل لتطهير حقل الفرق الرياضية:

from pyspark.sql.functions import length, trim

# التصفية الصارمة لاستبعاد القيم الخالية، والنصوص الفارغة، والمسافات البيضاء
df_strictly_clean_text = df_basket.filter(
    col("Team").isNotNull() & 
    (length(trim(col("Team"))) > 0)
)

df_strictly_clean_text.show(truncate=False)

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

9.2 التعامل مع الهياكل البيانية المعقدة والمصفوفات والخرائط المتداخلة

تتميز هياكل البيانات الحديثة بالاعتماد المتزايد على التنسيقات المركبة مثل الأعمدة المتفرعة (Structs)، والمصفوفات (Arrays)، والخرائط الرابطة (Maps). في مثل هذه البيئات المعقدة، لا يكفي تطبيق isNotNull() على مستوى الجذر؛ إذ قد يكون الحقل الهيكلي الخارجي معرّفاً وسليماً، بينما تحتوي خصائصه الداخلية على عناصر خالية تعيب المعالجة.

للتعامل مع الحقول الهيكلية، يجب توجيه الفحص الشرطي للوصول إلى الحقل الداخلي عبر رمز النقطة (مثال: col("PlayerDetails.Age").isNotNull()). أما في المصفوفات الداخلية، فيتطلب الأمر استخدام دوال متقدمة مستحدثة في إصدارات سبارك الحديثة مثل دالة filter() المخصصة للمصفوفات، أو دالة array_compact() التي تعمل على إزالة كافة العناصر الخالية من داخل المصفوفة دون التأثير على بنيتها، كما يتضح فيما يلي:

from pyspark.sql.functions import array_compact, col

# استعراض تطهير المصفوفات المتداخلة من القيم الخالية
# افتراض وجود إطار بيانات يحوي مصفوفة من الأرقام: df_arrays
# df_cleaned_array = df_arrays.withColumn("Scores", array_compact(col("Scores")))

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

9.3 استراتيجيات تتبع السجلات المستبعدة وعزلها لأغراض التدقيق

في بيئات الإنتاج الحساسة والأنظمة المصرفية، لا يُعد الإسقاط الصامت للسجلات المفقودة سلوكاً مقبولاً؛ إذ تفرض معايير الامتثال والرقابة توثيق وتدقيق أسباب استبعاد أي سجل من مسار المعالجة الأساسي. تقتضي أفضل الممارسات المنهجية شطر تدفق البيانات إلى مسارين متوازيين: مسار البيانات السليمة الموجهة للمعالجة، ومسار البيانات المعيبة الموجهة لصف العزل والتدقيق (Dead-Letter Queue / Quarantine).

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

from pyspark.sql.functions import current_timestamp, lit

# مسار البيانات السليمة
df_valid_stream = df_basket.filter(col("Points").isNotNull())

# مسار العزل والتدقيق للسجلات المعيبة
df_quarantine_stream = df_basket.filter(col("Points").isNull()) 
    .withColumn("rejection_reason", lit("Missing Points Value")) 
    .withColumn("quarantine_timestamp", current_timestamp())

# استعراض سجلات العزل لمراجعتها
df_quarantine_stream.show(truncate=False)

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

10. أفضل الممارسات المنهجية لتضمين التصفية في خطوط المعالجة المستمرة

10.1 بناء دوال التطهير القابلة لإعادة الاستخدام في مسارات البيانات

تقتضي الهندسة البرمجية الرصينة تجنب التكرار النمطي لكود التصفية عبر خطوط المعالجة المختلفة. بدلاً من كتابة تعبيرات filter() يدوياً في كل مرحلة، ينبغي بناء وحدات وظيفية مجردة (Modular Cleansing Functions) تقبل قائمة ديناميكية بالأعمدة الإلزامية وتطبق الشروط بأسلوب متسلسل وآمن.

يعرض المثال التالي تصميماً لدالة تطهير نمطية تستخدم أسلوب البرمجة الوظيفية لتطبيق شروط isNotNull() على كافة الأعمدة المستهدفة دفعة واحدة عبر تكرار تراكمي منضبط:

from typing import List
from pyspark.sql import DataFrame

def enforce_mandatory_columns(df: DataFrame, mandatory_cols: List[str]) -> DataFrame:
    """
    دالة نمطية تضمن خلو كافة الأعمدة المحددة في القائمة من القيم الخالية.
    """
    cleaned_df = df
    for column_name in mandatory_cols:
        cleaned_df = cleaned_df.filter(col(column_name).isNotNull())
    return cleaned_df

# تطبيق الدالة النمطية على إطار البيانات
mandatory_fields = ["Team", "Points"]
df_production_ready = enforce_mandatory_columns(df_basket, mandatory_fields)
df_production_ready.show(truncate=False)

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

10.2 أتمتة اختبارات جودة البيانات والتحقق من صحة المخرجات دورياً

لا تنتهي مسؤولية مهندس البيانات عند كتابة دوال التصفية، بل تمتد لتشمل أتمتة اختبارات الجودة (Data Quality Assertions) المدمجة ضمن مسارات التكامل المستمر (CI/CD). تهدف هذه الاختبارات إلى التحقق القطعي من خلو مجموعات البيانات الخارجة من أية قيم مفقودة قبل دفعها إلى بحيرات البيانات الموحدة أو جداول العرض النهائي.

تستخدم في هذا الصدد إما اختبارات التحقق الصريح عبر استدعاء count() ومقارنته بالصفر، أو دمج أطر فحص الجودة المتقدمة مفتوحة المصدر كأداة Great Expectations أو أداة PyDeequ التابعة لأباتشي سبارك. يقدم الكود التالي نموذجاً مبسطاً لاختبار جودة يطلق استثناءً تحذيرياً إذا تسربت سجلات غير صالحة:

# اختبار تأكيد جودة البيانات قبل التصدير
null_violations = df_production_ready.filter(col("Points").isNull()).count()

if null_violations > 0:
    raise ValueError(f"فشل التحقق من جودة البيانات: تم رصد {null_violations} سجلات خالية!")
else:
    print("اجتازت البيانات فحص الجودة بنجاح: خالية تماماً من الفراغات في الأعمدة الحرجة.")

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

10.3 صياغة التوثيق المؤسسي وحوكمة التعامل مع الفقد في قواعد البيانات

تتجاوز إدارة جودة البيانات الجوانب التقنية لتصل إلى مستوى الحوكمة المؤسسية الشاملة للبيانات (Data Governance). يجب على فرق هندسة وتحليل البيانات صياغة مواثيق واضحة وموثقة تحدد السياسات المعتمدة للتعامل مع الفقد في قواعد البيانات: متى يتم اللجوء إلى استبعاد السجلات كلياً عبر isNotNull()؟ ومتى يتعين تطبيق تقنيات التعويض الإحصائي (Imputation) كاستبدال الفراغات بالمتوسطات أو الوسيط الحسابي؟ ومتى تُترك القيم الخالية كما هي للدلالة على معنى تشغيلي محدد؟

يجب أن يتضمن هذا التوثيق سجلاً تفصيلياً (Data Dictionary / Catalog) يحدد لكل حقل من حقول البيانات درجة قابليته للفراغ، ونسبة التسامح المقبولة للبيانات الناقصة (Thresholds)، والإجراءات التصعيدية المؤتمتة عند تجاوز تلك النسب. إن حوكمة التعامل مع البيانات المفقودة تضمن تجانس المعايير بين مختلف الفرق التقنية، وتبني الثقة في المنظومة الرقمية للمؤسسة كمصدر موحد وحاسم للمعلومات الدقيقة والموثوقة.

خاتمة

استعرض هذا المقال الشامل أبعاد وآليات توظيف الدالة الشرطية isNotNull() في بيئة الحوسبة الموزعة PySpark، مبيناً دورها المحوري كأداة حاسمة في هندسة وتطهير البيانات الضخمة. تم تسليط الضوء على الأساس المنطقي ثلاثي القيم المعتمد في محرك Spark SQL، وتفكيك الفروق الجوهرية بين الفراغات المطلقة، والأصفار الرياضية، والنصوص الفارغة، مع بيان تأثير كل منها على سلامة المعالجات التحليلية والإحصائية.

كما بينت المقارنات التطبيقية التفوق الاستراتيجي للتصفية الانتقائية المستندة إلى الأعمدة مقارنة بالحذف الجذري غير المنضبط، وأوضحت التجارب التنفيذية تكافؤ الأداء الحسابي بين الواجهة البرمجية للأعمدة واستعلامات SQL القياسية بفضل كفاءة محرك كاتاليست. وخَلُص البحث إلى صياغة منظومة من أفضل الممارسات المنهجية التي تجمع بين التصفية المبكرة لدعم كفاءة الشبكة، وعزل السجلات التالفة للتدقيق، وأتمتة اختبارات الجودة، وتأطير الحوكمة المؤسسية للبيانات، بما يضمن بناء خطوط معالجة مستمرة تمتاز بالمتانة، والسرعة، والنزاهة الإحصائية العالية.

المراجع

  • Armbrust, M., Xin, R. X., Lian, C., Huai, Y., Liu, D., Bradley, J. K., Meng, X., Kaftan, T., Franklin, M. J., Ghodsi, A., & Zaharia, M. (2015). Spark SQL: Relational data processing in Spark. In Proceedings of the 2015 ACM SIGMOD International Conference on Management of Data (pp. 1383–1394). Association for Computing Machinery. https://doi.org/10.1145/2723372.2742797
  • Chambers, B., & Zaharia, M. (2018). Spark: The definitive guide: Big data processing made simple. O’Reilly Media.
  • Karau, H., & Warren, R. (2017). High performance Spark: Best practices for scaling and optimizing Apache Spark. O’Reilly Media.
  • Zaharia, M., Chowdhury, M., Franklin, M. J., Shenker, S., & Stoica, I. (2010). Spark: Cluster computing with working sets. In Proceedings of the 2nd USENIX Conference on Hot Topics in Cloud Computing (HotCloud’10) (pp. 10–10). USENIX Association.
★

تقييم هذا المحتوى

5.0 / 5 • 7 تقييمات

اقتباس هذا المقال

looti, M. (2026, أكتوبر 24). كيفية استخدام Is Not Null في PySpark (مع أمثلة). عرب سايكلوجي. https://arabpsychology.com/statistics/how-to-use-is-not-null-in-pyspark-with-examples/
looti, Mohammed. “كيفية استخدام Is Not Null في PySpark (مع أمثلة).” عرب سايكلوجي, 24 أكتوبر 2026, https://arabpsychology.com/statistics/how-to-use-is-not-null-in-pyspark-with-examples/.
looti, Mohammed. “كيفية استخدام Is Not Null في PySpark (مع أمثلة).” عرب سايكلوجي. أكتوبر 24, 2026. https://arabpsychology.com/statistics/how-to-use-is-not-null-in-pyspark-with-examples/.