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

الوحدة 1 · أساسيات Python للمحللين

الحلقات والشروط والـ comprehensions

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

الدرس في لمحة

عالج كل صف في مجموعة بيانات باستخدام حلقات for، واتخذ القرارات بـ if و elif و else، واحسب العدد بالقواميس، واكتب المنطق نفسه بإيجاز على هيئة comprehensions.

  1. 1حلقة for
  2. 2اتخاذ القرارات بـ if و elif و else
  3. 3العدّ والجمع بحسب المجموعة
  4. 4الـ comprehensions: بناء القوائم في سطر واحد
  5. 5الحلقات مقابل pandas

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

لديك ستة صفوف من ملف Cartwheel الفوضوي في قائمة من القواميس. تريد قيمتها الإجمالية، وكم جاء من كل قناة بيع، وأيّ الطلبات كانت كبيرة. كتابة سطر لكل صف تنجح مع ستة صفوف وتنهار مع ستة آلاف. حلقة التكرار (loop) تكتب المنطق مرة واحدة، وتكرّره Python لكل صف.

حلقة for

Python
orders = [
    {"order_id": 2574, "total": 65.0, "channel": "mobile app"},
    {"order_id": 2069, "total": 18.0, "channel": "mobile app"},
    {"order_id": 1200, "total": 33.0, "channel": "mobile app"},
    {"order_id": 1809, "total": 35.0, "channel": "web"},
    {"order_id": 1434, "total": 72.0, "channel": None},
    {"order_id": 2089, "total": 58.0, "channel": "marketplace"},
]

grand_total = 0
for order in orders:
    grand_total += order["total"]
print(grand_total)

اقرأها هكذا: “لكل طلب في orders، شغّل الأسطر المُزاحة”. في كل دورة يشير الاسم order إلى القاموس التالي. والسطر grand_total += order["total"] اختصار لـ grand_total = grand_total + order["total"].

الإزاحة (indentation) في Python ليست زينة: الأسطر المُزاحة (أربع مسافات بحسب العُرف) هي جسم الحلقة، وأول سطر يعود إلى الهامش الأيسر يعمل بعد انتهاء الحلقة. لهذا تقع print خارجها: أنت تريد نتيجة واحدة، لا ستًا.

اتخاذ القرارات بـ if و elif و else

الشروط تتيح لكل صف أن يسلك مسارًا مختلفًا. فريق العمليات في Cartwheel يصنّف الطلبات بحسب حجمها:

Python
totals = [65.0, 18.0, 33.0, 35.0, 72.0, 58.0, 329.0]

for t in totals:
    if t < 50:
        size = "small"
    elif t < 150:
        size = "medium"
    else:
        size = "large"
    print(t, size)

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

تستطيع دمج الشروط بـ and وor وnot. وتتيح لك Python أيضًا تسلسل المقارنات كما تكتبها في الرياضيات، وهذا مفيد مع النطاقات:

Python
t = 89.0
channel = "web"
print(t >= 50 and channel == "web")   # both must be true
print(50 <= t < 150)                  # chained: at least 50 and below 150
print(not channel == "marketplace")   # flips True and False

وحين تحتاج أيضًا موقع كل عنصر، لقائمة مرتّبة أو لرسالة مثل “الصف 3 معطوب”، غلّف القائمة بـ enumerate()، التي تعطيك الموقع والعنصر معًا: for position, t in enumerate(totals, start=1):. فضّلها على عدّ المواقع بيدك في متغيّر منفصل.

العدّ والجمع بحسب المجموعة

أكثر أنماط الحلقات فائدة في التحليل هو “لكل فئة، اجمع شيئًا”. يحتفظ القاموس بمجموع متراكم لكل مفتاح:

Python
orders = [
    {"order_id": 2574, "total": 65.0, "channel": "mobile app"},
    {"order_id": 2069, "total": 18.0, "channel": "mobile app"},
    {"order_id": 1200, "total": 33.0, "channel": "mobile app"},
    {"order_id": 1809, "total": 35.0, "channel": "web"},
    {"order_id": 1434, "total": 72.0, "channel": None},
    {"order_id": 2089, "total": 58.0, "channel": "marketplace"},
]

revenue_by_channel = {}
for order in orders:
    channel = order["channel"] or "unknown"
    revenue_by_channel[channel] = revenue_by_channel.get(channel, 0) + order["total"]

for channel, revenue in revenue_by_channel.items():
    print(channel, revenue)

تفصيلان يحملان هذا الكود. revenue_by_channel.get(channel, 0) تعيد المجموع المتراكم حتى الآن، أو 0 في أول مرة تظهر فيها القناة. وorder["channel"] or "unknown" تستبدل None بتسمية مقروءة، لأن None تُعامَل كقيمة خاطئة، وor تعيد الطرف الأيمن حين يكون الأيسر خاطئًا.

هذا groupby مكتوب باليد. في الدرس 4.1 تنجزه pandas في سطر واحد، وأنت الآن تعرف ما يفعله ذلك السطر في الخلفية: المرور على الصفوف، ومعرفة مجموعة كل صف، وتحديث مجموع تلك المجموعة.

الـ comprehensions: بناء القوائم في سطر واحد

كثيرًا ما توجد الحلقة فقط لتبني قائمة جديدة. والـ list comprehension تقول ذلك مباشرة:

Python
totals = [65.0, 18.0, 33.0, 35.0, 72.0, 58.0, 329.0]

with_rise = [round(t * 1.05, 2) for t in totals]
large = [t for t in totals if t >= 60]
print(with_rise)
print(large, len(large))

اقرأ [t for t in totals if t >= 60] هكذا: “t، لكل t في totals، إذا كانت t ‏60 على الأقل”. الجزء الذي قبل for هو ما يدخل القائمة، والـ if في النهاية تصفّي. والـ dictionary comprehension تعمل بالطريقة نفسها مع زوج key: value:

Python
countries = ["Canada", "united kingdom", "UNITED KINGDOM", "Saudi Arabia"]
clean = [c.strip().title() for c in countries]
print(clean)
print(sorted(set(clean)))            # unique values, sorted
print({c: len(c) for c in set(clean)})

الـ set لا يحتفظ إلا بالقيم الفريدة، فيدمج set(clean) الكتابتين المختلفتين للمملكة المتحدة في واحدة بعد تنظيفهما. ستطبّق الإصلاح نفسه على عمود كامل باستخدام pandas في الدرس 3.2.

الحلقات مقابل pandas

في pandas لن تكتب حلقة على الصفوف تقريبًا أبدًا. orders["total"].sum() تقوم بحلقة الجمع، وorders.groupby("channel")["total"].sum() تقوم بنمط القاموس السابق، وكلاهما بكود مُترجَم سريع. فلماذا تتعلّم الحلقات؟ لأنها طريقتك للتحقّق من فهمك لما تفعله عملية pandas، ولأن كثيرًا من العمل الحقيقي (المرور على ملفات، أو على أشهر، أو على قائمة دول لبناء تقرير) يبقى حلقة تحيط بكود pandas.

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

أهم النقاط

  • حلقة for تشغّل جسمها المُزاح (indented) مرة لكل عنصر؛ أنشئ المتغيّرات التراكمية مثل total = 0 قبل الحلقة، لا داخلها.
  • سلسلة if / elif / else تفحص الشروط من الأعلى إلى الأسفل، ولا تشغّل إلا أول فرع يكون شرطه صحيحًا.
  • النمط counts[key] = counts.get(key, 0) + 1 هو الطريقة المعتادة للعدّ أو الجمع بحسب المجموعة باستخدام قاموس.
  • الـ list comprehension بالشكل [expr for x in items if condition] تبني قائمة جديدة في سطر واحد مقروء.
  • في pandas نادرًا ما تكتب حلقات، لأن العمليات على الأعمدة تقوم بالتكرار نيابةً عنك؛ لكن الحلقات تبقى طريقتك لفهم ما تفعله تلك العمليات.

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

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

ماذا يطبع هذا الكود؟

Python
totals = [65, 18, 33]
for t in totals:
    running = 0
    running += t
print(running)
السؤال 2 من 3

طلب مجموعه 150 يمرّ عبر if t < 50: "small" ثم elif t < 150: "medium" ثم else: "large". أيّ تصنيف يحصل عليه؟

السؤال 3 من 3

أيّ comprehension تحتفظ بمجاميع طلبات الويب فقط من قائمة قواميس الطلبات؟

تدريب

تمرين Python

لخّص اثني عشر طلبًا بحسب الدولة

تحتوي orders على اثني عشر صفًا من الملف الفوضوي، وأسماء الدول مكتوبة بأحرف كبيرة وصغيرة غير متّسقة. ابنِ revenue_by_country، قاموسًا من اسم الدولة بعد تنظيفه (استخدم .strip().title()) إلى مجموع total. وابنِ large_ids، قائمة بأرقام order_id للطلبات التي مجموعها 60 أو أكثر، بترتيبها الأصلي. واجعل web_count عدد الطلبات التي قناتها "web" بالضبط.

orders = [
    {"order_id": 2574, "total": 65.0, "channel": "mobile app", "country": "Canada"},
    {"order_id": 2069, "total": 18.0, "channel": "mobile app", "country": "United States"},
    {"order_id": 1200, "total": 33.0, "channel": "mobile app", "country": "united kingdom"},
    {"order_id": 1809, "total": 35.0, "channel": "web", "country": "United States"},
    {"order_id": 1434, "total": 72.0, "channel": None, "country": "Saudi Arabia"},
    {"order_id": 2089, "total": 58.0, "channel": "marketplace", "country": "UNITED KINGDOM"},
    {"order_id": 2490, "total": 89.0, "channel": "web", "country": "United Arab Emirates"},
    {"order_id": 2985, "total": 24.0, "channel": "web", "country": "United Kingdom"},
    {"order_id": 2544, "total": 58.0, "channel": "mobile app", "country": "Jordan"},
    {"order_id": 2375, "total": 329.0, "channel": "mobile app", "country": "United Kingdom"},
    {"order_id": 1129, "total": 29.0, "channel": "web", "country": "United States"},
    {"order_id": 1176, "total": 44.0, "channel": "web", "country": "United States"},
]
# 1. Revenue per country, with country names cleaned by .strip().title()
revenue_by_country = {}

# 2. order_id of every order with a total of 60 or more, in the original order
large_ids = []

# 3. How many orders came through the "web" channel
web_count = 0
جارٍ تحميل المحرّر…

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

(لا مخرجات)

الفحوص

  • ستة أسماء دول منظّفة بالضبط، دون اختلافات في حالة الأحرف
  • مجموع United Kingdom يساوي 444.0
  • مجاميع الدول تساوي 854.0، وهو مجموع الطلبات الاثني عشر كلها
  • large_ids تساوي [2574, 1434, 2490, 2375]
  • web_count يساوي 5