الوحدة 2 · أساسيات pandas
الـ Series والـ DataFrame
16 دقراءة 4 دتمرين Python
الدرس في لمحة
ابنِ Series و DataFrame في pandas من قوائم Python وقواميسها، وأجرِ الحسابات على أعمدة كاملة دفعة واحدة، وتوقّع كيف تحاذي pandas القيم بحسب تسميات الفهرس.
- 1الـ Series: عمود له تسميات
- 2الحساب على كل القيم دفعة واحدة
- 3المحاذاة: pandas تطابق التسميات لا المواقع
- 4الـ DataFrame: جدول من الـ Series
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
في تمرين القسم السابق، كانت أسماء الأشهر في قائمة والإيرادات في قائمة أخرى، واضطررت إلى إيجاد أفضل شهر بموقعه ثم البحث عن اسمه في القائمة الأخرى. رتّب إحدى القائمتين وانسَ الأخرى، فيحصل كل شهر على إيراد خاطئ دون أي رسالة خطأ. وُجدت pandas لتجعل هذا النوع من الأخطاء صعبًا: القيم تحمل تسمياتها معها.
الـ Series: عمود له تسميات
الـ Series مصفوفة أحادية البعد من القيم، ومعها فهرس (index): تسمية لكل قيمة.
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 الحلقة بكود مُترجَم سريع.
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، والأشهر مكتوبة عمدًا بترتيب مختلف، وأحدها مفقود:
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 عددًا عشريًا، فالعمود الصحيح الذي يكتسب قيمة مفقودة يتحوّل إلى أعداد عشرية.
الـ DataFrame: جدول من الـ Series
الـ DataFrame عدة Series جنبًا إلى جنب، تتشارك فهرسًا واحدًا. أسهل طريقة لبناء DataFrame صغير هي شكل قاموس القوائم الذي تعرّفت عليه في الدرس 1.3، مفتاح لكل عمود:
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 تستطيع استخراجها بالأقواس المربعة، والعمليات الحسابية بين الأعمدة تتحاذى صفًا بصف لأن الأعمدة تتشارك ذلك الفهرس:
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تصف الجدول.