بايثون وبانداس, علم البيانات

بانداس: كيفية استخدام LIKE داخل ()query


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

تقدم مكتبة Pandas، بوصفها حجر الزاوية في منظومة علوم البيانات في بايثون، آليات متعددة للتعامل مع البيانات وجداول البيانات (DataFrames). ومن بين هذه الآليات، تبرز دالة query() كواحدة من أكثر الأدوات أناقة ومرونة، حيث تتيح كتابة شروط التصفية المنطقية في صورة سلاسل نصية قابلة للقراءة دون الحاجة إلى تكرار اسم مصفوفة البيانات كما هو معتاد في الفهرسة التقليدية. غير أن محاكاة سلوك العامل LIKE داخل وسيط التقييم النصي لدالة query() تتطلب فهماً عميقاً لكيفية تكامل وسائط الوصول النصية (.str) والتعبيرات النمطية (Regular Expressions) ضمن بيئة التقييم الديناميكي.

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

1. مقدمة إلى دالة ()query ومفهوم عامل المطابقة النصية LIKE

1.1 التحول من SQL إلى Pandas ومفهوم الاستعلام الديناميكي

شهدت معالجة البيانات عبر العقود الماضية تحولاً جذرياً في بيئات الحوسبة؛ حيث هيمنت لغة الاستعلامات البنيوية (SQL) لفترات طويلة على عمليات استرجاع البيانات وتحليلها في أنظمة إدارة قواعد البيانات العلائقية (RDBMS). تتميز لغة SQL بطبيعتها التقريرية (Declarative Paradigm)، حيث يقوم المطور أو المحلل بتحديد ماهية البيانات المطلوبة (What to extract) ويترك لمحرك قاعدة البيانات، عبر محسن الاستعلامات (Query Optimizer)، مهمة التخطيط لكيفية استخراجها مادياً من وسائط التخزين. ويُعد عامل المطابقة LIKE في SQL أحد أهم الأدوات التقريرية التي تسمح بالبحث المرن عن الأنماط النصية باستخدام الرموز البديلة مثل علامة النسبة المئوية (%) لتمثيل الصفر أو أكثر من المحارف، والشرطة السفلية (_) لتمثيل محرف مفرد.

مع ظهور مكتبة Pandas في بيئة لغة Python، انتقل ثقل تحليل البيانات إلى بيئات الحوسبة الإجرائية والكينونية (Procedural and Object-Oriented Paradigms). في هذا السياق، لم تعد البيانات مخزنة في جداول جامدة يتم استدعاؤها عبر خوادم مركزية فقط، بل أصبحت تُحمل مباشرة في الذاكرة العشوائية ضمن هياكل DataFrame. أوجد هذا الانتقال فجوة إدراكية لدى العديد من المحللين؛ إذ تطلبت الفهرسة المنطقية التقليدية في بايثون كتابة تعليمات برمجية تفصيلية ومكررة، مثل الإشارة الصريحة لاسم إطار البيانات في كل شرط منطقي، مما يقلل من مقروئية الشفرة ويزيد من احتمالية الأخطاء التركيبية.

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

1.2 آلية عمل محرك التقييم داخل دالة ()query

تعتمد دالة query() في بنيتها التحتية على محركات تقييم تعبيرات برمجية أثناء وقت التشغيل (Runtime Evaluation). المحرك الافتراضي المفضل في مكتبة Pandas هو محرك NumExpr، وهو مكتبة متطورة مكتوبة بلغة C تهدف إلى تسريع تقييم العمليات الحسابية والمنطقية المطبقة على مصفوفات NumPy الضخمة عن طريق تجنب إنشاء مصفوفات وسيطة في الذاكرة واستغلال خطوط أنابيب المعالجة المتعددة (Multi-threading) وذاكرة التخزين المؤقت (CPU Cache) بكفاءة قصوى. في حال عدم توفر NumExpr أو عند استدعاء دوال لا يدعمها، ترتد Pandas تلقائياً إلى محرك بايثون القياسي (Python Engine).

عند تمرير سلسلة نصية كمعامل لدالة query()، تقوم Pandas بتحليل هذه السلسلة وتحويلها إلى شجرة بناء نحوي مجردة (Abstract Syntax Tree – AST). يتم فحص الشجرة لتحديد المتغيرات، التي تُربط تلقائياً بأعمدة الـ DataFrame المقابلة، والعمليات الحسابية والمنطقية. يتيح هذا التقييم الديناميكي للمطورين صياغة استعلامات نصية تتغير شروطها برمجياً دون الحاجة إلى إعادة بناء كتل التعليمات البرمجية يدوياً. ومع ذلك، فإن حدود محرك NumExpr تظهر جلياً عند التعامل مع السلاسل النصية؛ حيث إن NumExpr مصمم في الأصل للمتجهات الرقمية والعمليات المنطقية الأساسية، مما يجعل تقييم العمليات النصية المتقدمة يعتمد جزئياً على بيئة بايثون القياسية أو طبقة الربط الخاصة بـ Pandas.

تختلف الفهرسة المنطقية التقليدية (Boolean Indexing) مثل df[df['col'] > 5] عن دالة query() في طريقة التنفيذ؛ فالأولى تُنشئ قناعاً منطقياً (Boolean Mask) في الذاكرة ككائن Series وسيط قبل تمريره إلى مشغل الفهرسة، في حين تسعى query() إلى تحسين تقييم العبارة ككل. على الرغم من أن الفهرسة التقليدية قد تكون أسرع في بعض العمليات البسيطة للغاية نظراً لغياب عبء تحليل السلسلة النصية (Parsing Overhead)، إلا أن query() تتفوق في تعقيد الشروط المركبة، وفي الحفاظ على وضوح الكود (Code Readability)، والحد من استهلاك الذاكرة في المصفوفات الكبيرة.

1.3 الحاجة إلى دوال النصوص (str Accessor) داخل شروط التصفية

تُخزن النصوص داخل مصفوفات Pandas إما ككائنات من نوع object (وهي مؤشرات إلى سلاسل نصوص بايثون القياسية) أو عبر نوع البيانات المحسن StringDtype الذي أُدخل في الإصدارات الحديثة لتعزيز كفاءة الذاكرة والأداء. وبما أن مصفوفات السلاسل النصية تتطلب معالجة خاصة تختلف عن المصفوفات الرقمية المتجانسة، وفرت Pandas وسيط الوصول النصي المتخصص .str (str Accessor). يعمل هذا الوسيط كجسر يتيح تطبيق العمليات النصية على مستوى العناصر الفردية (Element-wise Operations) بطريقة موجهة ومتجهة (Vectorized Manner).

عند الرغبة في محاكاة عامل المطابقة LIKE، لا يمكن الاكتفاء بعوامل المقارنة المنطقية البسيطة (مثل ==) لأنها تقتصر على فحص التطابق الحرفي الكامل. تبرز هنا الحاجة الملحة إلى دوال وسيط الوصول النصي، وتحديداً الدالة str.contains()، والتي تسمح بالتحقق مما إذا كان نمط معين متضمناً داخل كل عنصر في العمود المستهدف. تقوم Pandas بترجمة هذا الاستدعاء الداخلي لتمرير كل نص إلى خوارزمية البحث النمطي المحددة، مع إنتاج مصفوفة منطقية تشير قيمها إلى تحقق الشرط من عدمه لكل صف على حدة.

تتمثل الصعوبة التقنية في استخدام وسيط النصوص داخل query() في ضرورة ضمان توافق الأنواع ومحاذاة البيانات (Data Alignment) بدقة؛ فإذا احتوى العمود على قيم غير نصية أو قيم مفقودة (NaNs)، فإن محاولة تطبيق دوال النصوص قد تؤدي إلى نتائج غير متوقعة أو أخطاء برمجية إن لم يتم ضبط وسائط المعالجة النصية مسبقاً. لذلك، فإن استخدام .str داخل بيئة query() يتطلب مراعاة قواعد الصياغة الدقيقة لتفادي تعارض المفسر وتأمين معالجة متسقة عبر كامل أسطر إطار البيانات.

2. البنية التركيبية الأساسية لاستخدام ()str.contains ضمن ()query

2.1 الصيغة المعيارية وقواعد صياغة الأقواس والاقتباس

تتطلب كتابة استعلام نصي يحاكي سلوك LIKE داخل query() اتباع قواعد صارمة تتعلق بإدارة علامات الاقتباس وبنية العبارة. الصيغة المعيارية الأساسية تعتمد على استدعاء دالة str.contains() مباشرة على اسم العمود المطلوب تصفيته داخل السلسلة النصية للاستعلام، كما في النموذج البرمجي التالي:

import pandas as pd
# الصيغة المعيارية للمطابقة الجزئية
filtered_df = df.query('column_name.str.contains("pattern")')

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

في الحالات التي تحتوي فيها أسماء الأعمدة على مسافات بيضاء أو رموز خاصة غير مقبولة كمعرفات برمجية قياسية (مثل Team Name أو player-id)، يجب إحاطة اسم العمود بعلامات الرمز المائل العكسي (Backticks: `...`). كما تتيح Pandas ميزة استثنائية تتمثل في تمرير المتغيرات المعرفة في النطاق الخارجي لبايثون إلى داخل نص الاستعلام مباشرة عن طريق بادئة الرمز @، مما يمنح الاستعلام مرونة برمجية فائقة:

search_pattern = "Cavs"
# استخدام الرمز المائل العكسي للأعمدة ذات المسافات ورمز @ للمتغيرات الخارجية
filtered_df = df.query('`Team Name`.str.contains(@search_pattern)')

2.2 المعاملات الأساسية لدالة ()str.contains

تمتلك دالة str.contains() مجموعة من المعاملات الصريحة التي تتحكم في كيفية تفسير النصوص وإجراء المقارنات، ويُعد ضبط هذه المعاملات داخل query() أمراً حاسماً للوصول إلى سلوك مطابق تماماً لعامل LIKE في قواعد البيانات:

  • معامل التعبيرات النمطية (regex): يأخذ قيمة منطقية افتراضية هي True، مما يعني أن النمط الممرر سيُعامل كتعبير نمطي (Regular Expression). إذا كان الهدف هو البحث عن نص حرفي مجرد يحتوي على رموز خاصة بالـ Regex (مثل علامات النقطة أو الأقواس)، يجب تعيين regex=False لتحسين الأداء وتجنب أخطاء التفسير.
  • معامل حساسية حالة الأحرف (case): قيمته الافتراضية True، مما يجعل البحث حساساً لحالة الأحرف اللاتينية (Case-Sensitive). لمحاكاة سلوك بعض محركات SQL (مثل سلوك ILIKE في PostgreSQL أو LIKE غير الحساس في MySQL)، يتم تعيين case=False لتجاهل الفروق بين الأحرف الكبيرة والصغيرة.
  • معامل معالجة القيم المفقودة (na): يحدد القيمة المنطقية البديلة التي يجب إرجاعها عند مصادفة قيم مفقودة (NaN أو None) في العمود. افتراضياً، تُرجع الدالة NaN، وهو ما قد يسبب مشكلات أثناء الفلترة المنطقية؛ لذا فإن تعيين na=False يضمن استبعاد القيم المفقودة بأمان واكتمال عملية التصفية دون أخطاء.

2.3 إعداد بيئة البيانات الاختبارية (DataFrame Setup)

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

import pandas as pd
import numpy as np
# إنشاء إطار البيانات النموذجي
data = {
 'team_name': ['Cleveland Cavs', 'cavs Academy', 'Utah Jazz', 'Orlando Magic', 
 'Golden State Warriors', 'LA Clippers', 'Miami Heat', None, 'Brooklyn Nets'],
 'conference': ['East', 'East', 'West', 'East', 'West', 'West', 'East', 'East', 'East'],
 'points_per_game': [112.5, 98.0, 115.2, 104.1, 118.7, 109.3, 110.0, 102.4, 114.8],
 'rebounds': [44.2, 39.5, 46.1, 43.0, 45.8, 42.1, 41.5, 38.0, 40.2],
 'is_active': [True, True, True, True, True, True, True, False, True]
}
df = pd.DataFrame(data)
# معاينة هيكل البيانات والأنواع المرتبطة بها
print(df.info())
print(df)

يحتوي إطار البيانات أعلاه على 9 صفوف و5 أعمدة تمثل أنواع بيانات متعددة: نصوص (object)، أرقام عشرية (float64)، وقيم منطقية (bool)، بالإضافة إلى وجود قيمة مفقودة (None) في عمود team_name. ستتيح لنا هذه المصفوفة اختبار سلوك دوال المطابقة بدقة تحت ظروف تحاكي بيئات العمل الواقعية في هندسة البيانات.

3. تطبيق الطريقة الأولى: استخراج الصفوف المطابقة لنمط نصي أحادي

3.1 التنفيذ العملي للمطابقة الجزئية البسيطة (Sub-string Matching)

في لغة SQL، عندما نريد استخراج كافة السجلات التي تحتوي على مقطع نصي محدد بغض النظر عن موقعه داخل السلسلة، نستخدم التعبير WHERE column LIKE '%pattern%'. في Pandas، تُحاكى هذه العملية مباشرة عبر دالة str.contains() داخل query() دون الحاجة إلى وضع رموز النسبة المئوية، نظراً لأن دالة البحث النمطي تبحث تلقائياً عن وجود النمط الجزئي في أي موضع من السلسلة النصية ما لم يتم تقييدها بمحددات المواقع.

# محاكاة الاستعلام: WHERE team_name LIKE '%Cavs%'
result_single = df.query('team_name.str.contains("Cavs", na=False)')
print(result_single)

عند تنفيذ هذا الاستعلام على إطار البيانات التجريبي، سيقوم محرك التقييم بفحص كل قيمة في عمود team_name. السجل الأول Cleveland Cavs يحتوي على المقطع “Cavs” تماماً، وبالتالي يُرجع قيمة True ويتم تضمينه في الناتج. أما السجل الثاني cavs Academy فسيتم استبعاده لأن حالة الأحرف تبدأ بحرف صغير (c) في حين أن النمط يبدأ بحرف كبير (C)، ما لم نحدد معيار تجاهل حالة الأحرف. تمثل هذه التصفية الجزئية الركيزة الأساسية للبحث داخل الكتل النصية غير المهيكلة كعناوين المنتجات والأسماء المركبة.

3.2 سلوك المطابقة عند تغير موقع النمط النصي داخل السلسلة

تتميز آلية البحث عبر str.contains() بالمرونة في التقاط المقاطع النصية سواء كانت متمركزة في بداية السلسلة النصية، أو في وسطها، أو في نهايتها. هذا السلوك يعادل تماماً السلوك الشامل لعامل LIKE المحاط بعلامتي %...%. على سبيل المثال، إذا كان النمط المستهدف هو Magic، وكان يتواجد في نهاية السلسلة كما في Orlando Magic، فإن خوارزمية البحث تقوم بمسح السلسلة من اليسار إلى اليمين وعند تطابق تسلسل المحارف يتم وسم الصف كصف مطابق فوراً.

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

3.3 التعامل مع النصوص العربية والرموز غير اللاتينية

تعتمد لغة Python 3 ومكتبة Pandas نظام الترميز العالمي Unicode (UTF-8) كمعيار افتراضي لتمثيل السلاسل النصية. يتيح هذا الدعم الكامل إجراء عمليات المطابقة النصية المتقدمة على النصوص المكتوبة بالأبجدية العربية والرموز غير اللاتينية والرموز التعبيرية (Emojis) بنفس السلاسة والكفاءة المتبعة مع النصوص الإنجليزية.

# إطار بيانات يحتوي على نصوص عربية
arabic_data = {
 'city': ['الرياض', 'جدة', 'مكة المكرمة', 'المدينة المنورة', 'القاهرة'],
 'region': ['الوسطى', 'الغربية', 'الغربية', 'الغربية', 'شمال أفريقيا']
}
arabic_df = pd.DataFrame(arabic_data)
# استعلام لمحاكاة LIKE '%المكرمة%'
arabic_result = arabic_df.query('city.str.contains("المكرمة")')
print(arabic_result)

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

4. تطبيق الطريقة الثانية: مطابقة أنماط نصية متعددة باستخدام العامل (|)

4.1 صياغة عامل الاختيار المنطقي (OR Logic) في التعبيرات النمطية

في بيئات SQL التقليدية، يتطلب البحث عن سجلات تطابق واحداً من عدة أنماط نصية تكرار عامل المطابقة مع الروابط المنطقية، مثل: WHERE col LIKE '%pattern1%' OR col LIKE '%pattern2%'. تصبح هذه الصيغة مطولة وغير فعالة برمجياً كلما زاد عدد الأنماط المطلوب مطابقتها. توفر دالة str.contains() داخل query() حلاً متقدماً وموجزاً للغاية عبر الاستفادة من عامل الاختيار المنطقي (Pipe operator |) المدعوم في محرك التعبيرات النمطية القياسي.

يعمل الرمز الأنبوبي | داخل نمط البحث كمعامل “أو” (Logical OR). عند تمرير السلسلة "pattern1|pattern2" إلى str.contains()، يقوم محرك التقييم بمطابقة أي صف يحتوي على pattern1 أو pattern2 أو كليهما، مما يختزل شروط التصفية المعقدة في تعبير نصي واحد بالغ الأناقة والدقة:

# محاكاة: WHERE team_name LIKE '%Cavs%' OR team_name LIKE '%Jazz%'
multi_pattern_df = df.query('team_name.str.contains("Cavs|Jazz", na=False)')
print(multi_pattern_df)

4.2 أمثلة تطبيقية على استخراج أنماط متباينة

عند تطبيق الاستعلام متعدد الأنماط على إطار البيانات النموذجي للبحث عن الفرق التي تحتوي أسماؤها على Cavs أو Jazz أو Warriors، ينفذ المحرك خوارزمية المطابقة بنمط الفحص الموحد (Single-pass Scanning). يتميز هذا النهج بعدم تكرار استرجاع الصفوف وعدم خلق أي ازدواجية في الفهرس، حتى لو افترضنا وجود سجل يحتوي على أكثر من نمط في نفس الوقت.

# استعلام يطابق ثلاثة أنماط متباينة في استدعاء واحد
complex_query = df.query('team_name.str.contains("Cavs|Jazz|Warriors", na=False)')
print(complex_query[['team_name', 'points_per_game']])

يُظهر الناتج استرجاع الصفوف الخاصة بـ Cleveland Cavs و Utah Jazz و Golden State Warriors بكفاءة تامة. يقلل هذا الأسلوب بشكل كبير من استهلاك دورات المعالجة المركزية مقارنة بدمج عدة شروط منطقية منفصلة داخل الاستعلام، نظراً لأن محرك Regex الداخلي يبني آلة حالات محدودة قطعية (Deterministic Finite Automaton – DFA) تقوم بالتحقق من كافة الأنماط بالتوازي أثناء مسح النص مرة واحدة فقط.

4.3 استخدام القوائم والمجموعات الخارجية في الاستعلام

في التطبيقات البرمجية الواقعية، غالباً ما تكون الأنماط المطلوب البحث عنها غير ثابتة (Hardcoded)، بل تأتي ديناميكياً في صورة قائمة بايثون (List) مدخلة من قبل المستخدم أو مسترجعة من واجهة برمجة تطبيقات (API). في هذه الحالة، يمكن دمج عناصر القائمة برمجياً باستخدام دالة str.join() لإنشاء نمط التعبير النمطي، ثم تمريره إلى query() باستخدام بادئة @:

# قائمة خارجية بأنماط البحث المستهدفة
target_teams = ['Cavs', 'Magic', 'Clippers']
# دمج عناصر القائمة لتكوين تعبير نمطي موحد
search_regex = '|'.join(target_teams) # ينتج: 'Cavs|Magic|Clippers'
# تمرير المتغير الديناميكي إلى دالة query
dynamic_result = df.query('team_name.str.contains(@search_regex, na=False)')
print(dynamic_result)

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

5. محاكاة علامات النسبة المئوية (%) وبدايات ونهايات النصوص

5.1 مطابقة بادئات النصوص ومحاكاة النمط ‘Pattern%’

في لغة SQL، يُستخدم النمط WHERE column LIKE 'Pattern%' للدلالة على السلاسل النصية التي تبدأ حصراً بالنمط المحدد وتتبعه أي محارف أخرى. لمحاكاة هذا السلوك بدقة داخل query()، نوظف محدد البداية في التعبيرات النمطية وهو رمز الإقحام (Caret: ^). يوجه هذا الرمز محرك البحث للتحقق من تطابق النمط بدءاً من المحرف الأول في السلسلة النصية فقط، واستبعاد أي تطابق يظهر في مواضع لاحقة.

# محاكاة: WHERE team_name LIKE 'Golden%'
prefix_df = df.query('team_name.str.contains("^Golden", na=False)')
print(prefix_df)

على الرغم من إمكانية استخدام دالة str.startswith("Golden") داخل بيئات الفهرسة المنطقية، إلا أن استخدام str.contains("^Golden") داخل query() يحافظ على نسق موحد لكتابة كافة أنواع الاستعلامات النصية، ويتيح دمج شروط البداية المعقدة والخيارات المتعددة بسلاسة مثل ^Golden|^Cleveland للبحث عن السجلات التي تبدأ بأي من هاتين الكلمتين.

5.2 مطابقة لواحق النصوص ومحاكاة النمط ‘%Pattern’

بالمثل، لمحاكاة النمط اللاحق في SQL والمعرف بـ WHERE column LIKE '%Pattern'، والذي يشترط انتهاء السلسلة النصية بالنمط المستهدف، نستخدم محدد النهاية في التعبيرات النمطية وهو رمز الدولار (Dollar sign: $). يفرض هذا الرمز على المحرك مطابقة النمط مع المحارف الأخيرة من السلسلة النصية حصراً:

# محاكاة: WHERE team_name LIKE '%Heat'
suffix_df = df.query('team_name.str.contains("Heat$", na=False)')
print(suffix_df)

تُعد هذه التقنية أساسية في سيناريوهات تنظيف وهندسة البيانات مثل استخراج الملفات بناءً على امتداداتها (مثل .csv$ أو .parquet$)، أو تصفية أرقام الهواتف بناءً على رموز الدول واللواحق الإقليمية، أو التحقق من صحة المعرفات الأكاديمية والمهنية ذات الهياكل المحددة النهايات.

5.3 المطابقة التامة والمحاكاة الدقيقة لمعامل التساوي

عند دمج رمزي البداية والنهاية معاً في صيغة واحدة ^Pattern$، يتحول سلوك str.contains() من المطابقة الجزئية إلى المطابقة التامة والشاملة لكامل محتوى الخلية النصية. في هذا الوضع، لا يُقبل الصف إلا إذا كان النص مساوياً تماماً للنمط دون أي زيادة أو نقصان في المحارف أو المسافات:

# محاكاة التطابق التام عبر Regex: WHERE team_name = 'Utah Jazz'
exact_regex_df = df.query('team_name.str.contains("^Utah Jazz$", na=False)')
# المقارنة مع عامل المساواة المنطقي المباشر
exact_equal_df = df.query('team_name == "Utah Jazz"')

من الناحية الهندسية والمعمارية، يُفضل دائماً استخدام عامل المساواة المباشر == عند استهداف التطابق الكامل البسيط لكونه يتفادى استدعاء محرك التعبيرات النمطية ويعتمد على المقارنة المباشرة للذاكرة، مما يجعله أسرع في التنفيذ. ومع ذلك، تظل صيغة ^Pattern$ مفيدة عندما يحتوي النمط التام على أجزاء ديناميكية أو فئات محارف متغيرة مثل ^Team_[0-9]{3}$.

6. التحكم في حساسية حالة الأحرف (Case Sensitivity)

6.1 الافتراضات الأساسية للحساسية النصية في Pandas

تتعامل مكتبة Pandas مع السلاسل النصية بافتراض الحساسية لحالة الأحرف (Case-Sensitive) في كافة دوال وسيط النصوص .str، بما يتماشى مع المعايير القياسية للغة بايثون. يعني ذلك برمجياً أن الحرف الكبير 'C' (الذي يمتلك رمز ASCII بقيمة 67) يختلف كلياً عن الحرف الصغير 'c' (الذي يمتلك رمز ASCII بقيمة 99). يؤدي هذا السلوك الافتراضي في كثير من الأحيان إلى استبعاد سجلات صحيحة تحليلياً نتيجة وجود تباينات طفيفة في إدخال البيانات النصية غير النظيفة.

في المقابل، فإن العديد من محركات قواعد البيانات مثل MySQL أو SQL Server تطبق بشكل افتراضي مقارنات نصية غير حساسة لحالة الأحرف (Case-Insensitive Collation). ونتيجة لذلك، يواجه المحللون المنتقلون حديثاً إلى Pandas مفاجأة استبعاد صفوف يفترض منطقياً أن تطابق الاستعلام، كما هو الحال في السجل cavs Academy الذي تم استبعاده سابقاً عند البحث عن Cavs.

6.2 تفعيل معيار case=False داخل تعبير ()query

لإلغاء الحساسية لحالة الأحرف ومحاكاة معاملات SQL مثل ILIKE، توفر دالة str.contains() المعامل case=False. يمكن تمرير هذا المعامل مباشرة داخل نص الاستعلام في query() كما يلي:

# محاكاة: WHERE team_name ILIKE '%cavs%'
case_insensitive_df = df.query('team_name.str.contains("cavs", case=False, na=False)')
print(case_insensitive_df)

عند تنفيذ هذا الكود، سيسترجع الاستعلام كلا الصفين: Cleveland Cavs و cavs Academy بنجاح. يوجه هذا المعامل محرك البحث النمطي إلى تجاهل الفروق بين الحالات الكبيرة والصغيرة عبر تطبيق خوارزميات مطابقة الحالات (Case-Folding) على مستوى كل محرف أثناء المعالجة.

6.3 تأثير التوحيد المسبق للحروف على كفاءة الاستعلام

يطرح مهندسو البيانات تساؤلاً دائماً حول المفاضلة بين استخدام case=False داخل الاستعلام أو إجراء توحيد مسبق لحالة الأحرف للعمود بأكمله باستخدام .str.lower() أو .str.upper() قبل الشروع في عمليات التصفية المتكررة:

# الخيار البديل: التوحيد المسبق للحروف
df['team_lower'] = df['team_name'].str.lower()
result_lowered = df.query('team_lower.str.contains("cavs", na=False)')

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

7. إدارة القيم المفقودة وغير المعرفة (Missing Values and NaNs)

7.1 سلوك str.contains عند مواجهة قيم NaN

تُمثل القيم المفقودة (Missing Values) المعرفة كـ NaN (Not a Number) أو None تحدياً جوهرياً في معالجة البيانات النصية. عندما تطبق Pandas دالة str.contains() على عمود يحتوي على قيم مفقودة، فإن السلوك الافتراضي للدالة هو إرجاع NaN في الموضع المقابل لتلك القيمة، بدلاً من إرجاع قيمة منطقية صريحة (True أو False).

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

7.2 ضبط المعامل na=False لضمان استقرار الاستعلام

لتفادي أي سلوك غير محدد أو أخطاء استثنائية، تتيح دالة str.contains() المعامل na، والذي يحدد القيمة المنطقية التي يجب تعيينها للقيم المفقودة أثناء التقييم:

# الضبط القياسي الآمن: استبعاد الصفوف المفقودة تلقائياً
safe_query_df = df.query('team_name.str.contains("Magic", na=False)')
# تضمين الصفوف المفقودة ضمن النتائج
include_na_df = df.query('team_name.str.contains("Magic", na=True)')

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

7.3 استراتيجيات استباقية لمعالجة الأعمدة المختلطة (Mixed Types)

في بيئات الإنتاج، قد تحتوي الأعمدة ذات النوع object على خليط غير متجانس من السلاسل النصية، والأرقام الصحيحة، والقيم المنطقية، والكائنات الفارغة. يؤدي استدعاء وسيط النصوص .str على أعمدة تحتوي مدخلات غير نصية إلى رفع الخطأ الشهير AttributeError: Can only use .str accessor with string values أو حدوث أخطاء تحويل صامتة.

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

# إنشاء عمود مختلط الأنواع لأغراض الاختبار
df_mixed = pd.DataFrame({'id': [101, 102, 103], 'info': ['Valid String', 12345, np.nan]})
# استراتيجية المعالجة الاستباقية: التحويل الصريح واستبدال القيم الفارغة
df_mixed['info_clean'] = df_mixed['info'].fillna('').astype(str)
# الاستعلام الآمن بعد توحيد النوع
clean_result = df_mixed.query('info_clean.str.contains("123", na=False)')
print(clean_result)

8. توظيف التعبيرات النمطية المتقدمة (Advanced Regex) داخل الاستعلام

8.1 مطابقة فئات الرموز والمحارف الخاصة

يتجاوز استخدام دالة str.contains() داخل query() مجرد محاكاة عامل LIKE البسيط، حيث يفتح الباب واسعاً للاستفادة من القوة التعبيرية الكاملة لمحركات التعبيرات النمطية (Regular Expressions) القياسية. يتيح ذلك صياغة أنماط استعلام بالغة التعقيد لا يمكن تنفيذها في لغة SQL إلا عبر دوال متخصصة مثل REGEXP_LIKE.

يمكن استخدام فئات المحارف المخصصة مثل [0-9] للبحث عن الأرقام، أو [a-z] للبحث عن الحروف اللاتينية الصغيرة، بالإضافة إلى الرموز الخاصة المهربة مثل d للأرقام و s للمسافات البيضاء. يوضح المثال التالي كيفية البحث عن الفرق التي تحتوي أسماؤها على أرقام أو رموز خاصة:

# إطار بيانات يحتوي على أسماء وتنسيقات مختلفة
sku_data = pd.DataFrame({
 'sku_code': ['PROD-1024-X', 'ITEM_9921_Z', 'SAMPLE-001', 'INVALID-CODE'],
 'price': [45.0, 89.9, 12.5, 0.0]
})
# استعلام للبحث عن المنتجات التي تتبع النمط: كلمة تليها أرقام محددة
regex_query_df = sku_data.query('sku_code.str.contains(r"[A-Z]+[-_]d+", na=False)')
print(regex_query_df)

8.2 استخدام مكررات الأنماط ومحددات الكميات (Quantifiers)

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

  • الرمز (+): يطابق ظهور المحرف أو النمط مرة واحدة أو أكثر.
  • الرمز (*): يطابق ظهور النمط صفراً من المرات أو أكثر.
  • الرمز (?): يطابق الظهور الاختياري (مرة واحدة أو صفر).
  • الأقواس المعقوفة ({n,m}): تحدد عدداً دقيقاً لمرات التكرار يتراوح بين الحد الأدنى n والحد الأقصى m.
# استعلام يستخرج المنتجات التي تحتوي على تكرار رقمي دقيق مكون من 4 أرقام
exact_quantifier_df = sku_data.query('sku_code.str.contains(r"d{4}", na=False)')
print(exact_quantifier_df)

8.3 البحث النصي الحرفي مقابل التعبيرات النمطية (regex=False)

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

لحل هذه المشكلة جذرياً وتسريع عملية البحث، يمكن تعطيل محرك التعبيرات النمطية كلياً عبر تمرير regex=False داخل str.contains():

financial_data = pd.DataFrame({
 'transaction': ['Fee: $50.00', 'Discount: 10%', 'Tax:$5.00', 'Total: $55.00'],
 'status': ['Completed', 'Applied', 'Completed', 'Final']
})
# البحث الحرفي الصريح عن رمز الدولار دون تفسيره كنهاية سلسلة
literal_df = financial_data.query('transaction.str.contains("$", regex=False, na=False)')
print(literal_df)

يؤدي تعيين regex=False إلى تفعيل خوارزمية البحث عن النصوص الجزئية في لغة C الأساسية (Substring Search)، وهي خوارزمية فائقة السرعة تتفوق بمراحل على محرك Regex عند البحث عن نصوص ثابتة ومحددة بدقة.

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

9.1 الربط المنطقي المزدوج باستخدام AND و OR و NOT

تتجلى القوة الحقيقية لدالة query() عند بناء استعلامات تصفية هجينة ومركبة تدمج بسلاسة بين مطابقة النصوص والشروط الرقمية والمنطقية التقليدية في سطر برمجي واحد شديد الوضوح. تدعم query() الكلمات المفتاحية الإنجليزية القياسية (and, or, not) بالإضافة إلى الرموز المنطقية المكافئة لها في بايثون (&, |, ~).

# استعلام مركب: البحث عن فرق تحتوي على 'Cavs' ونقاطها أكبر من 100
compound_df = df.query('team_name.str.contains("Cavs", na=False) and points_per_game > 100')
print(compound_df)
# استعلام يحاكي NOT LIKE في SQL باستخدام العامل not
not_like_df = df.query('not team_name.str.contains("Warriors", na=False)')
print(not_like_df)

يُراعى دائماً استخدام الأقواس الدائرية (...) عند بناء شروط منطقية متعددة لتحديد أسبقية العمليات (Operator Precedence) بوضوح ومنع أي لبس في التقييم الرياضي للشروط المركبة.

9.2 التصفية متعددة الأعمدة عبر السلاسل النصية والأرقام

يمكن توسيع نطاق الاستعلام ليفحص شروطاً نصية ورقمية تتوزع عبر عدة أعمدة في آن واحد. يوضح المثال التطبيقي التالي سيناريو استخراج الفرق التابعة للمنطقة الشرقية (East) والتي تحقق معدل متابعات (Rebounds) يفوق 40.0، مع استبعاد أسماء الفرق التي تبدأ بكلمة ‘cavs’ في حالة الأحرف الصغيرة:

multi_col_df = df.query(
 'conference == "East" and '
 'rebounds >= 40.0 and '
 'not team_name.str.contains("^cavs", na=False)'
)
print(multi_col_df[['team_name', 'conference', 'rebounds']])

تتفوق هذه الصيغة الموحدة على أسلوب الفلاتر المتتالية (Chained Filtering) مثل df[df['conf']=='East'][df['reb']>=40] من حيث الأداء واستهلاك الذاكرة، لأنها تُقيم كافة الشروط في مسار منطقي متكامل بدلاً من توليد أطر بيانات وسيطة لكل مرحلة تصفية على حدة.

9.3 التعامل مع التواريخ والأوقات المتداخلة مع النصوص

في مجموعات البيانات الزمنية، تتداخل حقول النصوص غالباً مع حقول التواريخ والأوقات (datetime64). توفر query() إمكانية مقارنة التواريخ باستخدام السلاسل النصية المنسقة وفق المعيار القياسي ISO 8601 بالتزامن مع شروط مطابقة النصوص:

log_data = pd.DataFrame({
 'timestamp': pd.to_datetime(['2023-01-15 10:00:00', '2023-02-20 14:30:00', '2023-03-05 09:15:00']),
 'log_message': ['ERROR: Connection timeout', 'INFO: User login success', 'ERROR: Disk full failure'],
 'server_id': ['srv-01', 'srv-02', 'srv-01']
})
# تصفية السجلات: أخطاء النظام بعد تاريخ محدد
filtered_logs = log_data.query(
 'timestamp >= "2023-02-01" and log_message.str.contains("ERROR", na=False)'
)
print(filtered_logs)

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

10. تحليل الأداء والمقارنة المعيارية: ()query مقابل الفهرسة التقليدية

10.1 قياس زمن التنفيذ واستهلاك الذاكرة (Benchmarking)

لفهم الفروق الجوهرية في الأداء بين استخدام df.query() والفهرسة المنطقية التقليدية df[...]، يجب إجراء اختبارات قياس معيارية دقيقة باستخدام مكتبة timeit على مصفوفات بيانات متفاوتة الأحجام (من 10,000 إلى 1,000,000 صف). يوضح الكود التالي بنية اختبار المقارنة المعيارية:

import timeit
import pandas as pd
import numpy as np
# إنشاء إطار بيانات ضخم للاختبار المعياري (1 مليون صف)
n_rows = 1_000_000
bench_df = pd.DataFrame({
 'text_col': np.random.choice(['Alpha Team', 'Beta Group', 'Gamma Unit', 'Delta Force'], size=n_rows),
 'val_col': np.random.randn(n_rows)
})
# تعريف الدوال المراد قياسها
def benchmark_boolean_indexing():
 return bench_df[bench_df['text_col'].str.contains('Alpha', na=False)]
def benchmark_query():
 return bench_df.query('text_col.str.contains("Alpha", na=False)')
# قياس زمن التنفيذ (متوسط 10 تكرارات)
t_index = timeit.timeit(benchmark_boolean_indexing, number=10) / 10
t_query = timeit.timeit(benchmark_query, number=10) / 10
print(f"زمن الفهرسة التقليدية: {t_index:.4f} ثانية")
print(f"زمن دالة query(): {t_query:.4f} ثانية")

تُظهر النتائج التجريبية عموماً أن الفهرسة التقليدية تكون أسرع بنسبة ضئيلة جداً في مجموعات البيانات الصغيرة إلى المتوسطة (أقل من 100,000 صف)، نظراً للعبء الزمني الطفيف (Parsing Overhead) الذي تستغرقه Pandas في تحليل وتفسير السلسلة النصية داخل query(). ومع ذلك، عند التعامل مع ملايين الصفوف والشروط المنطقية المركبة، تتلاشى هذه الفجوة الزمنية تماماً وتصبح دالة query() مكافئة في السرعة وموفرة للذاكرة التشغيلية.

10.2 مفهوم النسخ السطحي والعميق والتعديل المباشر (In-place Operations)

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

تسهم دالة query() في تفادي هذا التحذير المربك عن طريق إرجاع إطار بيانات جديد بنسخ واضح ومحدد للذاكرة. كما تدعم الدالة المعامل inplace=True، والذي يسمح بتطبيق التصفية مباشرة على نفس كائن إطار البيانات في الذاكرة دون إنشاء كائنات بديلة، مما يوفر استهلاك الذاكرة في الأنظمة المحدودة الموارد:

# التعديل المباشر في الذاكرة لتوفير المساحة
df_inplace = df.copy()
df_inplace.query('team_name.str.contains("Cavs", na=False)', inplace=True)
print(df_inplace)

10.3 تأثير محرك NumExpr على العمليات النصية

من الضروري إدراك الخصائص المعمارية لمحرك NumExpr المدمج في Pandas. صُمم هذا المحرك في الأساس لتسريع العمليات الحسابية العددية (Numeric Arithmetic) والمقارنات المنطقية الثنائية. عند استدعاء دوال وسيط النصوص مثل str.contains() داخل نص الاستعلام، يكتشف محرك query() وجود استدعاءات لكائنات ودوال بايثون غير المدعومة داخل بنية C المباشرة لـ NumExpr.

في هذه الحالة، ترتد Pandas بذكاء وبشكل تلقائي إلى محرك تقييم بايثون (Python Evaluation Engine) لتنفيذ العمليات النصية، بينما تحتفظ بقدرتها على استخدام NumExpr للشروط الرقمية المصاحبة في نفس الاستعلام المركب. ولتحقيق أقصى استفادة حوسبية من المعالجات متعددة الأنوية (Multi-core CPUs)، يفضل الاعتماد على نوع البيانات الجديد pd.StringDtype(storage="pyarrow") المستند إلى مكتبة Apache Arrow، والذي يوفر تنفيذاً فائق السرعة لدوال النصوص المتجهة.

11. الأخطاء الشائعة، استكشاف المشكلات البرمجية، وتصحيحها (Debugging)

11.1 أخطاء الاقتباس والتفسير النحوي (Syntax & Quote Errors)

تُعد أخطاء الاقتباس من أكثر العوائق المتكررة عند كتابة استعلامات النصوص. يوضح الجدول التالي أهم الأخطاء النحوية والحلول البرمجية المقابلة لها:

  • الخطأ: استخدام نفس نوع علامات الاقتباس داخلياً وخارجياً، مثل: df.query('col.str.contains('pattern')').
    الحل: المزاوجة بين الاقتباس المفرد والمزدوج: df.query("col.str.contains('pattern')").
  • الخطأ: ظهور استثناء UndefinedVariableError عند استدعاء اسم عمود يحتوي على مسافات.
    الحل: إحاطة اسم العمود بالرمز المائل العكسي: df.query('`Col Name`.str.contains("pattern")').
  • الخطأ: أخطاء الهروب غير المقصود لمحارف الشرطة المائلة العكسية في التعبيرات النمطية.
    الحل: استخدام السلاسل النصية الخام (Raw Strings) عبر البادئة r: df.query(r'col.str.contains(r"d+")').

11.2 أخطاء توافق الأنواع والتعامل مع الكائنات (TypeError & AttributeError)

يظهر الاستثناء AttributeError: Can only use .str accessor with string values عندما يتم تطبيق الاستعلام على عمود مسجل بنوع object ولكنه يحتوي على كائنات رقمية أو بنى بيانات مخصصة لا تدعم واجهة .str. لتشخيص هذا الخطأ وتصحيحه، يُتبع الأسلوب البرمجي التالي للتحقق من سلامة الأنواع:

# فحص التوزيع الفعلي لأنواع البيانات داخل العمود
print(df['team_name'].apply(type).value_counts())
# التصحيح الإجباري لضمان تجانس البيانات
df['team_name'] = df['team_name'].astype(str)

11.3 منهجية تصحيح أخطاء الاستعلامات المعقدة خطوة بخطوة

عند بناء استعلامات تصفية متقدمة تتضمن شروطاً متعددة ومتداخلة وتفشل في إرجاع النتائج المتوقعة، يوصى باتباع منهجية تفكيك الاستعلام (Query Deconstruction) وفق الخطوات التالية:

  1. عزل الشروط الفردية: فحص كل شرط نصي أو رقمي بصورة مستقلة عبر دالة query() منفصلة للتحقق من صحة القناع المنطقي الناتج.
  2. المقارنة مع الفهرسة الصريحة: تنفيذ الشرط باستخدام الفهرسة المنطقية التقليدية df[df['col'].str.contains(...)] للتأكد من أن الخلل ليس ناتجاً عن قواعد تحليل السلسلة النصية.
  3. بناء اختبارات الوحدة المؤتمتة (Unit Tests): صياغة دوال اختبارية صغيرة عبر مكتبة pytest للتحقق من متانة الاستعلام ضد حالات الحافة (Edge cases) كالقيم الفارغة والرموز الخاصة.

12. أفضل الممارسات البرمجية والتطبيقات المتقدمة في هندسة البيانات

12.1 معايير كتابة استعلامات قابلة للقراءة والصيانة (Clean Code)

يُعد الالتزام بمبادئ الشفرة النظيفة (Clean Code) أمراً حيوياً في هندسة البيانات المؤسسية. عند التعامل مع استعلامات طويلة ومعقدة داخل query()، يُستحسن تجنب كتابة سلاسل أحادية طويلة يصعب تتبعها. بدلاً من ذلك، يجب استخدام السلاسل النصية متعددة الأسطر (Multi-line Strings) وتوثيق التعبيرات النمطية بتعليقات شارحة:

# كتابة استعلام متعدد الأسطر ومنظم هندسياً
clean_query_str = """
 conference == 'East'
 and points_per_game >= 105.0
 and team_name.str.contains('^C|M', case=False, na=False)
"""
final_clean_df = df.query(clean_query_str)

12.2 التكامل مع خطوط معالجة البيانات (Pipelines) وطريقة تسلسل الدوال

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

# خط أنابيب معالجة متكامل بتسلسل الدوال
processed_pipeline_df = (
 df
 .dropna(subset=['conference'])
 .assign(points_per_rebound=lambda x: x['points_per_game'] / x['rebounds'])
 .query('team_name.str.contains("Cavs|Magic", na=False) and points_per_rebound > 2.0')
 .sort_values(by='points_per_game', ascending=False)
 .reset_index(drop=True)
)
print(processed_pipeline_df)

12.3 الخلاصة والتوصيات الهندسية لمحللي ومهندسي البيانات

يلخص الجدول المرجعي التالي الفروق الجوهرية والقرارات الهندسية لاختيار الأداة المناسبة لتصفية السلاسل النصية في مكتبة Pandas:

  • مطابقة جزئية بسيطة (SQL LIKE ‘%abc%’): استخدم df.query('col.str.contains("abc", na=False)').
  • مطابقة بداية السلسلة (SQL LIKE ‘abc%’): استخدم df.query('col.str.contains("^abc", na=False)').
  • مطابقة نهاية السلسلة (SQL LIKE ‘%abc’): استخدم df.query('col.str.contains("abc$", na=False)').
  • تجاهل حالة الأحرف (SQL ILIKE ‘%abc%’): استخدم df.query('col.str.contains("abc", case=False, na=False)').
  • مطابقة خيارات متعددة (SQL LIKE IN): استخدم df.query('col.str.contains("p1|p2|p3", na=False)').
  • تطابق تام ومباشر (SQL = ‘abc’): استخدم df.query('col == "abc"') لتحقيق أقصى سرعة.

خاتمة

تُشكل دالة query() في مكتبة Pandas، عند دمجها مع وسيط العمليات النصية .str.contains()، أداة برمجية فائقة التعبيرية والمرونة تُمكّن محللي ومهندسي البيانات من محاكاة وتجاوز إمكانيات عامل المطابقة النصية LIKE المعروف في قواعد البيانات. يتيح هذا التكامل صياغة شروط تصفية نظيفة وقابلة للصيانة والتطوير، مع الحفاظ على الكفاءة الحوسبية المطلوبة لمعالجة البيانات الضخمة. ومع استمرار تطور منظومة بايثون وتبني محركات خلفية متقدمة مثل Apache Arrow، فإن إتقان هذه الأنماط البرمجية يظل استثماراً استراتيجياً لرفع جودة وكفاءة خطوط معالجة وهندسة البيانات في بيئات الإنتاج المعاصرة.

المراجع (References)

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

looti, M. (2026, سبتمبر 2). بانداس: كيفية استخدام LIKE داخل ()query. عرب سايكلوجي. https://arabpsychology.com/pandas-how-to-use-like-inside-query/
looti, Mohammed. “بانداس: كيفية استخدام LIKE داخل ()query.” عرب سايكلوجي, 2 سبتمبر 2026, https://arabpsychology.com/pandas-how-to-use-like-inside-query/.
looti, Mohammed. “بانداس: كيفية استخدام LIKE داخل ()query.” عرب سايكلوجي. سبتمبر 2, 2026. https://arabpsychology.com/pandas-how-to-use-like-inside-query/.