EduFlow
Python لتحليل البيانات
⁦0%⁩

الوحدة 2 · أساسيات pandas

الـ Series والـ DataFrame

16 دقراءة 4 دتمرين Python

الدرس في لمحة

ابنِ Series و DataFrame في pandas من قوائم Python وقواميسها، وأجرِ الحسابات على أعمدة كاملة دفعة واحدة، وتوقّع كيف تحاذي pandas القيم بحسب تسميات الفهرس.

  1. 1الـ Series: عمود له تسميات
  2. 2الحساب على كل القيم دفعة واحدة
  3. 3المحاذاة: pandas تطابق التسميات لا المواقع
  4. 4الـ DataFrame: جدول من الـ Series

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

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

الـ Series: عمود له تسميات

الـ Series مصفوفة أحادية البعد من القيم، ومعها فهرس (index): تسمية لكل قيمة.

Python
import pandas as pd

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
revenue = pd.Series(
    [16167, 14906, 18208, 18817, 17428, 13933,
     22968, 32042, 25514, 30339, 75334, 41153],
    index=months,
    name="revenue_2025",
)
print(revenue.head(3))
print(revenue["Nov"], revenue.idxmax(), revenue.sum())

السطر import pandas as pd يحمّل المكتبة باسمها المختصر المتعارف عليه عالميًا؛ كل درس تعليمي عن pandas وكل إجابة على Stack Overflow تستخدم pd. وrevenue["Nov"] تبحث عن قيمة بتسميتها، وidxmax() تعيد تسمية أكبر قيمة، فيختصر كل ذلك العناء مع القائمتين المتوازيتين في القسم 1 في استدعاء واحد.

السطر الأخير من الـ Series المطبوعة يقول Name: revenue_2025, dtype: int64. الـ dtype هو نوع كل القيم في العمود: int64 للأعداد الصحيحة، وfloat64 للأعداد العشرية، وobject (أو str في pandas 3) للنصوص، وbool، وdatetime64 للتواريخ. عمود واحد، نوع واحد؛ وهذه القاعدة هي ما يجعل pandas سريعة.

الحساب على كل القيم دفعة واحدة

العمليات الحسابية والمقارنات على الـ Series تُطبَّق على كل قيمة، وتعيد Series جديدة لها الفهرس نفسه. يُسمّى هذا كودًا متّجهًا (vectorised): تصف العملية على العمود كله، وتنفّذ pandas الحلقة بكود مُترجَم سريع.

Python
import pandas as pd

revenue = pd.Series([30339, 75334, 41153], index=["Oct", "Nov", "Dec"])
print(revenue * 1.05)              # every month, 5% higher
print(revenue / revenue.sum())     # each month's share of Q4
print(revenue > 40000)             # a Series of True/False

الـ Series المكوّنة من True/False في السطر الأخير تبدو الآن مجرّد تفصيل جانبي. في الدرس 2.4 تصبح الطريقة الأساسية لتصفية الصفوف.

المحاذاة: pandas تطابق التسميات لا المواقع

حين تجمع بين Series وأخرى، تحاذي pandas بينهما بحسب تسمية الفهرس قبل أن تحسب. هذا الربع الرابع من 2025 مقابل الربع الرابع من 2024، والأشهر مكتوبة عمدًا بترتيب مختلف، وأحدها مفقود:

Python
import pandas as pd

q4_2025 = pd.Series([30339, 75334, 41153], index=["Oct", "Nov", "Dec"])
q4_2024 = pd.Series([29177, 9360], index=["Nov", "Oct"])
print(q4_2025 - q4_2024)

يُطرح أكتوبر من أكتوبر ونوفمبر من نوفمبر، بغضّ النظر عن الترتيب. ديسمبر موجود في 2025 فقط، فنتيجته NaN (“ليس رقمًا”)، وهي علامة pandas للقيمة المفقودة. لاحظ أن النتيجة أصبحت float64: في الأعمدة الكلاسيكية المبنية على NumPy، تكون NaN عددًا عشريًا، فالعمود الصحيح الذي يكتسب قيمة مفقودة يتحوّل إلى أعداد عشرية.

الـ Series على اليسار فيها Oct و Nov و Dec. والـ Series على اليمين فيها Nov و Oct بترتيب مختلف. خطوط تصل بين التسميات المتطابقة. النتيجة فيها Oct و Nov محسوبين، و Dec قيمته NaN لأنه بلا نظير. q4_2025 Oct 30339 Nov 75334 Dec 41153 q4_2024 Nov 29177 Oct 9360 الفرق Oct 20979 Nov 46157 Dec NaN تُطابَق التسميات أولًا؛ والتسمية التي بلا نظير تعطي NaN
تتّحد الـ Series بمطابقة تسميات الفهرس

الـ DataFrame: جدول من الـ Series

الـ DataFrame عدة Series جنبًا إلى جنب، تتشارك فهرسًا واحدًا. أسهل طريقة لبناء DataFrame صغير هي شكل قاموس القوائم الذي تعرّفت عليه في الدرس 1.3، مفتاح لكل عمود:

Python
import pandas as pd

products = pd.DataFrame({
    "name": ["Cast Iron Skillet 26cm", "Burr Coffee Grinder", "Standing Desk 140cm", "Cork Yoga Mat"],
    "unit_price": [39, 129, 449, 72],
    "unit_cost": [17.45, 76.99, 204.98, 41.52],
})
print(products)
print(products.shape)       # (rows, columns)
print(products.dtypes)

ثلاث خصائص (attributes) تصف أي DataFrame، وستطبعها باستمرار. shape هو زوج (الصفوف، الأعمدة)، وcolumns تسرد أسماء الأعمدة، وindex يحمل تسميات الصفوف. لا أقواس بعد أيٍّ منها، لأنها حقائق مخزّنة عن الجدول وليست أفعالًا؛ أما head() وsum() فتقومان بعمل، لذا تُستدعيان بأقواس. الخلط بين الاثنين يعطيك إما method مطبوعة بالشكل <bound method ...> أو TypeError: 'tuple' object is not callable، وكلاهما صار بإمكانك التعرّف عليه الآن.

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

Python
import pandas as pd

products = pd.DataFrame({
    "name": ["Cast Iron Skillet 26cm", "Burr Coffee Grinder", "Standing Desk 140cm", "Cork Yoga Mat"],
    "unit_price": [39, 129, 449, 72],
    "unit_cost": [17.45, 76.99, 204.98, 41.52],
})
margin = products["unit_price"] - products["unit_cost"]
print(margin)
print(type(products["unit_price"]))

تربح المقلاة 21.55 دولارًا عن كل وحدة، والمكتب الواقف 244.02. أربعة صفوف أو أربعة ملايين، الكود هو نفسه، وهذا هو الوعد الحقيقي لـ pandas: تفكّر بالأعمدة لا بالخلايا.

بناء الجداول باليد للتعلّم فقط. في الدرس التالي تحمّل ملفات CSV الحقيقية الخاصة بـ Cartwheel، وتتعلّم روتين الفحص الذي تشغّله قبل أن تثق بأيٍّ منها.

أهم النقاط

  • الـ Series عمود من القيم له فهرس (index) من التسميات؛ والـ DataFrame جدول من Series تتشارك فهرسًا واحدًا.
  • العمليات الحسابية على الـ Series تُطبَّق على كل القيم دفعة واحدة، فتكتب prices * 1.05 بدلًا من حلقة تكرار.
  • العمليات بين Series وأخرى تتحاذى بحسب تسمية الفهرس لا بحسب الموقع؛ والتسميات الموجودة في طرف واحد فقط تنتج NaN.
  • df["col"] تعطيك عمودًا واحدًا على هيئة Series، وdf.shape وdf.columns وdf.index وdf.dtypes تصف الجدول.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ما قيمة growth["Oct"]؟

Python
y2024 = pd.Series([9360, 29177], index=["Oct", "Nov"])
y2025 = pd.Series([75334, 30339], index=["Nov", "Oct"])
growth = y2025 - y2024
السؤال 2 من 4

products هو DataFrame فيه 48 صفًا و7 أعمدة. ماذا تعيد products.shape؟

السؤال 3 من 4

أيّ سطر يرفع سعر كل منتج بنسبة 5% دون حلقة تكرار؟

السؤال 4 من 4

في آخر تمرين من القسم 1 احتفظت بأسماء الأشهر والإيرادات في قائمتين متوازيتين. ما المشكلة التي تحلّها الـ Series؟

تدريب

تمرين Python

أول Series وأول DataFrame لك

ابنِ revenue، وهي Series من values مسمّاة بـ months. اجعل best_month تسمية أكبر قيمة فيها، وshare حصة كل شهر من مجموع السنة (Series مجموعها 1). ثم ابنِ products، وهو DataFrame له الأعمدة name وunit_price وunit_cost ويحتوي المنتجات الثلاثة المذكورة في التعليقات، بهذا الترتيب، وmargin، وهي Series تساوي unit_price ناقص unit_cost.

import pandas as pd

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
values = [16167, 14906, 18208, 18817, 17428, 13933,
          22968, 32042, 25514, 30339, 75334, 41153]

# 1. A Series of the values, labelled by month
revenue = None

# 2. Label of the best month
best_month = None

# 3. Each month's share of the year (a Series that sums to 1)
share = None

# 4. A DataFrame with columns name, unit_price, unit_cost for these products
#    Cast Iron Skillet 26cm  39  17.45
#    Burr Coffee Grinder    129  76.99
#    2-Person Dome Tent     149  83.09
products = None

# 5. A Series: unit_price minus unit_cost for each product
margin = None
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

(لا مخرجات)

الفحوص

  • revenue هي Series مسمّاة من Jan إلى Dec
  • best_month يساوي "Nov"
  • مجموع share يساوي 1، ونوفمبر نحو 23%
  • products هو DataFrame بحجم 3 × 3 وبالأعمدة الصحيحة
  • margin تحتوي 21.55 و52.01 و65.91