الجداول مع pandas
درس 3 من 9
3الجداول مع pandas
الصورة الكبيرةبين الملف الخام والنموذج أربع عمليات تتكرّر في كل مشروع: اقرأ، جمّع، ادمج، وقرّر ماذا تفعل بالناقص. وكل قرار منها يغيّر ما يتعلّمه النموذج.
ماذا تشاهد في الرسم: أربعة صفوف خام تمرّ بالتجميع: تُقسَم بحسب المدينة، ثم يُجمع عمود الطلبات في كل مجموعة، فتخرج ثلاث مدن. الرياض ظهرت مرتين وصارت صفًّا واحدًا مجموعه 180.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
pandas يعطيك جدولًا مثل Excel لكن داخل الكود: أعمدة لها أسماء، وصفوف، وتستطيع أن تسأله «ما متوسط المبيعات لكل مدينة؟» بسطر واحد. والخانات الفارغة لا تحذفها فورًا: تسأل أولًا لماذا هي فارغة، لأن حذفها قد يحذف قصة مهمة.
DataFrame أعمدة Series لها فهرس مشترك. groupby ثم agg يجمّعان، وmerge يدمج جدولين بمفتاح مثل JOIN. القيم الناقصة NaN تُفحص بـisna() وتُعالج بالحذف أو الملء fillna حسب سبب غيابها، مع عمود يسجّل أنها كانت ناقصة.
قبل أن تبدأ
🧱المصفوفات
تعرف shape وdtype من درس المصفوفات، فالإطار في جوهره أعمدة من مصفوفات لها أسماء.
📄ملفات CSV
رأيت ملف CSV أو جدول بيانات من قبل، وتعرف أن كل صف سجلّ وكل عمود حقل.
الفكرة الجوهرية
هذا الدرس يمرّ على أربع عمليات بالترتيب: تقرأ الجدول وتعرف الإطار والسلسلة، ثم تجمّع، ثم تدمج جدولين، ثم تقرّر ماذا تفعل بالناقص.
1الإطار والسلسلة
الإطار جدول بفهرس وأعمدة مسمّاة، وكل عمود فيه سلسلة تتشارك الفهرس نفسه. لذلك كل اختيار من الإطار يعيد لك واحدًا من الاثنين.
انظر إلى الرسم: اسم عمود واحد بين قوسين يعيد سلسلة لها بعد واحد.
هذا الفرق الصغير هو سبب أغرب رسائل الخطأ عند المبتدئين، لأن السلسلة والإطار لا يقبلان العمليات نفسها.
2التجميع: قسّم ثم طبّق ثم اجمع
أي سؤال من نوع «كم لكل مدينة» هو groupby بلا استثناء. يقسّم الصفوف بحسب قيمة عمود، ويطبّق دالة على كل مجموعة، ثم يجمع النتائج.
import pandas as pd
rows = [
{"city": "الرياض", "orders": 120, "revenue": 4800.0},
{"city": "جدة", "orders": 90, "revenue": 3150.0},
{"city": "الرياض", "orders": 60, "revenue": 2100.0},
{"city": "الدمام", "orders": 40, "revenue": None},
]
frame = pd.DataFrame(rows)
print(frame.shape)
print(list(frame.columns))
print(int(frame["orders"].sum()))
print(int(frame["revenue"].isna().sum()))
by_city = frame.groupby("city")["orders"].sum().sort_values(ascending=False)
for city, total in by_city.items():
print(f"{city}: {int(total)}")
أربعة أسطر أعطتك الشكل والأعمدة والمجموع وعدد الناقص، ثم أعطاك groupby ترتيب المدن. لاحظ أن الرياض ظهرت مرتين في البيانات الخام وأصبحت صفًّا واحدًا بمجموع 180.
3الدمج يقرّر من يبقى
الدمج يربط جدولين بمفتاح، هنا اسم المدينة. الدمام موجودة في جدول الإيراد وليست في جدول السكان.
انظر إلى الرسم: الدمج اليساري يبقي الصفوف الثلاثة ويترك خانة فارغة للدمام. الدمج الداخلي يحذفها بصمت.
import pandas as pd
frame = pd.DataFrame({
"city": ["الرياض", "جدة", "الدمام"],
"revenue": [6900.0, 3150.0, None],
})
population = pd.DataFrame({
"city": ["الرياض", "جدة", "أبها"],
"people": [7_000_000, 3_700_000, 1_100_000],
})
inner = frame.merge(population, on="city", how="inner")
left = frame.merge(population, on="city", how="left")
print(inner.shape, left.shape)
print(int(left["people"].isna().sum()))
filled = frame.copy()
filled["revenue"] = filled["revenue"].fillna(0.0)
print(float(frame["revenue"].mean()))
print(float(filled["revenue"].mean()))
الدمج الداخلي أعاد صفّين، واليساري أعاد ثلاثة بخانة فارغة واحدة. هذا الفرق هو الذي يجعل مشروعك يفقد عشرين بالمئة من بياناته بلا تحذير. ولاحظ المتوسط: 5025.0 قبل التعويض و3350.0 بعد تعويض الناقص بصفر، لأن الصفر ليس قيمة محايدة بل رقم يسحب المتوسط إليه.
4الناقص: احذف أم عوّض
NaN ليس صفرًا ولا نصًّا فارغًا. انظر إلى الرسم: متوسط الإيراد في المثال السابق قبل التعويض وبعد تعويض الناقص بصفر.
القرار يعتمد على سبب النقص لا على عدده. إن كان النقص غير عشوائي، فالحذف نفسه يُحدث انحيازًا في بياناتك.
اسأل أولًا: لماذا هذه الخانة فارغة؟ ثم اختر الحذف، أو الوسيط، أو قيمة من صفوف مشابهة، واكتب السبب.
كيف يعمل، خطوة بخطوة: كيف يجيب groupby عن «كم لكل مدينة»
يقرأ عمود المفتاح، هنا city، ويعرف قيمه المختلفة.
يقسم الصفوف إلى مجموعات، فصفّا الرياض يذهبان معًا، ولجدة والدمام مجموعة لكل منهما.
يطبّق الدالة المطلوبة، مثل sum، على العمود المختار داخل كل مجموعة وحدها.
يجمع النتائج في سلسلة واحدة فهرسها أسماء المدن، ثم ترتّبها أنت بـ sort_values.
مثال محلول
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| القيمة الناقصة يمكن معاملتها كصفر. | NaN ليس صفرًا. التعويض بصفر أنزل متوسط الإيرادات في الدرس من 5025.0 إلى 3350.0، لأن الصفر رقم يسحب المتوسط إليه. |
| الدمج الذي لم يُظهر خطأ نجح كما أردت. | الدمج الداخلي يحذف بصمت كل صف بلا مقابل، فالصمت لا يعني السلامة. قارن عدد الصفوف قبل الدمج وبعده. |
| حذف الصفوف الناقصة هو الخيار المحايد. | إذا كان النقص غير عشوائي، فالحذف نفسه يُحدث انحيازًا، لأنك تُسقط نوعًا كاملًا من الصفوف لا صفوفًا عشوائية. |
جرّب بنفسك
عندك جدول مبيعات فيه عمود city وجدول سكان فيه نفس العمود، لكن أسماء بعض المدن مكتوبة بالهاء وبعضها بالتاء المربوطة. دمجتهما بـhow="inner" على city. ما النتيجة الأرجح؟
الدمج يقارن النصوص حرفًا بحرف ولا يعرف أن «جدة» و«جده» مدينة واحدة. الدمج الداخلي يُسقط ما لا يتطابق بلا رسالة، فتخسر صفوفًا وأنت تظن أن العملية نجحت. طبّع عمود المفتاح قبل الدمج، واطبع عدد الصفوف قبله وبعده كعادة ثابتة.
بطاقات مراجعة سريعة
groupby فعلًاhow="inner" في الدمجتحقق من فهمك
لماذا يختلف mean() قبل التعويض وبعده في المثال أعلاه؟
لأن mean() يتجاهل NaN تمامًا ويقسم على عدد القيم الموجودة فقط، فحصل على متوسط قيمتين. بعد التعويض بصفر صارت القيمة الثالثة رقمًا يشارك في القسمة، فانخفض المتوسط. الدرس أن التعويض ليس عملية محايدة: كل تعويض هو فرضية تضيفها إلى بياناتك.
ماذا يعيد frame["orders"]، وماذا يعيد frame[["orders"]]؟
الأول سلسلة لأنك اخترت عمودًا واحدًا، والثاني إطار بعمود واحد لأنك مرّرت قائمة أعمدة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.