{"id":"l-pda-4-4","courseId":"python-data-analysis","lang":"ar","kind":"lesson","summary":"حوّل الطلبات ذات الطوابع الزمنية إلى سلاسل شهرية وفصلية وأسبوعية باستخدام resample، وقارن الفترات بـ pct_change، وخفّف الضوضاء بـ rolling، واستخرج أجزاء التقويم بالـ accessor المسمّى dt.","html":"<p>الأرقام الشهرية التي استخدمتها منذ الدرس 1.3 جاءت من مكان ما: بضعة آلاف من الطلبات ذات الطوابع الزمنية، موزّعة بحسب الشهر التقويمي. الزمن هو محور كل سؤال عمل تقريبًا (“هل ينمو الإيراد؟”، “هل نوفمبر هذا العام طبيعي؟”، “متى يتسوّق الناس؟”)، ولدى pandas أدوات مبنية له. وقبل استخدامها، تحتاج صفًا لكل طلب مع إيراده وطابعه الزمني.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"0\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_items.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">parse_dates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"quantity\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unit_price\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"discount\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">order_revenue </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">as_index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">False</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.merge(order_revenue, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">on</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">how</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"left\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">validate</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"one_to_one\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"status\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"cancelled\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(orders[[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]].head(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>كل كتلة Run فيما يلي تبدأ بهذه الأسطر، مختصرة لتبقى مقروءة. وفي الـ notebook الخاص بك كنت ستشغّلها مرة واحدة.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">resample: حاويات من الزمن</h2>\n<p>تجمّع <code dir=\"ltr\">resample</code> الصفوف في فترات زمنية منتظمة، وهو ما نسمّيه إعادة التجميع الزمني. وتحتاج أن تكون التواريخ في الفهرس:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"1\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_items.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">parse_dates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"quantity\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unit_price\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"discount\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.merge(items.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">as_index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">False</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum(), </span><span style=\"color:#953800;--shiki-dark:#FFA657\">on</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">validate</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"one_to_one\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"status\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"cancelled\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">by_time </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.set_index(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">monthly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by_time.resample(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"ME\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">quarterly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by_time.resample(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"QE\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">len</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(monthly), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"months\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(monthly.tail(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(quarterly.round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>النص اسم مستعار للتكرار (frequency alias): <code dir=\"ltr\">&quot;D&quot;</code> للأيام، و<code dir=\"ltr\">&quot;W&quot;</code> للأسابيع المنتهية يوم الأحد، و<code dir=\"ltr\">&quot;ME&quot;</code> لنهايات الأشهر، و<code dir=\"ltr\">&quot;QE&quot;</code> لنهايات الأرباع، و<code dir=\"ltr\">&quot;YE&quot;</code> لنهايات السنوات. أعادت pandas 2.2 تسمية <code dir=\"ltr\">&quot;M&quot;</code> و<code dir=\"ltr\">&quot;Q&quot;</code> و<code dir=\"ltr\">&quot;Y&quot;</code> إلى نسخ الـ <code dir=\"ltr\">E</code> لتوضّح أن التسميات هي <strong>نهايات</strong> الفترات، والأسماء القديمة تُطلق الآن تحذير إهمال. أما الفترات (periods) فكائن مختلف يحتفظ بالأسماء القصيرة: <code dir=\"ltr\">to_period(&quot;M&quot;)</code> و<code dir=\"ltr\">to_period(&quot;Q&quot;)</code> صحيحتان، لأن الفترة تمثّل الشهر أو الربع كله لا آخر يوم فيه. والسلسلة الفصلية هي التي يحدّق فيها رئيس النمو: 80,523 دولارًا في الربع الثالث من 2025، ثم 146,825 في الربع الرابع.</p>\n<h3 id=\"sec-2\" tabindex=\"-1\">الفرق بين resample و to_period</h3>\n<p>تستطيع أيضًا التجميع بحسب فترة: <code dir=\"ltr\">orders.groupby(orders[&quot;order_date&quot;].dt.to_period(&quot;M&quot;))[&quot;revenue&quot;].sum()</code>. تبدو النتيجة هنا متطابقة، بتسميات مثل <code dir=\"ltr\">2025-11</code> بدلًا من <code dir=\"ltr\">2025-11-30</code>. ويظهر الفرق حين لا تكون في فترة ما بيانات: تشملها <code dir=\"ltr\">resample</code> بمجموع 0، بينما يتخطّاها التجميع تمامًا. وللرسوم البيانية ومعدّلات النمو تريد كل فترة حاضرة، فتكون <code dir=\"ltr\">resample</code> الخيار الافتراضي الأكثر أمانًا.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 180\" role=\"img\" aria-labelledby=\"l-pda-4-4-t1\"><defs><marker id=\"arrow-l-pda-4-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-pda-4-4-t1\">خط زمني من أكتوبر إلى ديسمبر عليه نقاط طلبات متفرّقة. خطوط عمودية تقسمه إلى ثلاث حاويات شهرية. كل حاوية مسمّاة بآخر يوم فيها: 2025-10-31 و 2025-11-30 و 2025-12-31، ومجموع الإيراد تحت كل تسمية.</title>\n  <path class=\"d-line\" d=\"M30 70 L670 70\"/>\n  <path class=\"d-line\" d=\"M30 45 L30 95\"/>\n  <path class=\"d-line\" d=\"M243 45 L243 95\"/>\n  <path class=\"d-line\" d=\"M457 45 L457 95\"/>\n  <path class=\"d-line\" d=\"M670 45 L670 95\"/>\n  <circle class=\"d-dot\" cx=\"60\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"110\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"190\" cy=\"70\" r=\"5\"/>\n  <circle class=\"d-dot\" cx=\"260\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"290\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"320\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"350\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"380\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"400\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"420\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"440\" cy=\"70\" r=\"5\"/>\n  <circle class=\"d-dot\" cx=\"490\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"540\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"590\" cy=\"70\" r=\"5\"/><circle class=\"d-dot\" cx=\"630\" cy=\"70\" r=\"5\"/>\n  <text class=\"d-label-muted\" x=\"136\" y=\"35\" text-anchor=\"middle\">أكتوبر</text>\n  <text class=\"d-label-muted\" x=\"350\" y=\"35\" text-anchor=\"middle\">نوفمبر</text>\n  <text class=\"d-label-muted\" x=\"563\" y=\"35\" text-anchor=\"middle\">ديسمبر</text>\n  <text class=\"d-code\" x=\"136\" y=\"125\" text-anchor=\"middle\">2025-10-31</text>\n  <text class=\"d-code\" x=\"350\" y=\"125\" text-anchor=\"middle\">2025-11-30</text>\n  <text class=\"d-code\" x=\"563\" y=\"125\" text-anchor=\"middle\">2025-12-31</text>\n  <text class=\"d-label\" x=\"136\" y=\"152\" text-anchor=\"middle\">30,339</text>\n  <text class=\"d-label-strong\" x=\"350\" y=\"152\" text-anchor=\"middle\">75,334</text>\n  <text class=\"d-label\" x=\"563\" y=\"152\" text-anchor=\"middle\">41,153</text>\n</svg><figcaption>تضع resample كل طابع زمني في حاوية ثابتة وتسمّي الحاوية بنهايتها</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">مقارنة الفترات</h2>\n<p>السلاسل الخام تجيب عن “كم”؛ والتغيّرات تجيب عن “بأيّ سرعة”:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"2\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_items.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">parse_dates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"quantity\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unit_price\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"discount\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.merge(items.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">as_index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">False</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum(), </span><span style=\"color:#953800;--shiki-dark:#FFA657\">on</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">validate</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"one_to_one\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"status\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"cancelled\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">by_time </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.set_index(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">quarterly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by_time.resample(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"QE\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(pd.DataFrame({</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: quarterly.round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"vs_prev_quarter\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: quarterly.pct_change().round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"vs_year_ago\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: quarterly.pct_change(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">monthly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by_time.resample(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"ME\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(monthly.rolling(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).mean().tail(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>تقسم <code dir=\"ltr\">pct_change()</code> كل قيمة على القيمة التي قبلها وتطرح 1. الربع الرابع من 2025 أعلى بنسبة 82% من الثالث، وهي القفزة التي لاحظها الجميع. لكن <code dir=\"ltr\">pct_change(4)</code> تقارن كل ربع بالربع نفسه قبل سنة، وهذا العمود يحكي قصة أهدأ: الربع الرابع من 2025 أعلى بنسبة 180% من الربع الرابع من 2024، بينما نما الأول والثاني والثالث بنسب 200% و145% و165%. نما الربع الرابع بسرعة تقارب بقية السنة. جزء كبير من “القفزة” نمط موسمي كان موجودًا في 2024 أيضًا: كان الربع الرابع من 2024 أعلى بنسبة 73% من الربع الثالث من 2024.</p>\n<p>تحسب <code dir=\"ltr\">rolling(3).mean()</code> متوسط كل شهر مع الشهرين اللذين قبله، أي المتوسط المتحرك، فتنعّم القفزات المفردة مثل نوفمبر. استخدمها لترى الاتجاه؛ واستخدم السلسلة الخام لترى الأحداث.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>مقارنة فترة جزئية بفترات كاملة</span></p>\n<p>الحاويات الأسبوعية تنتهي يوم الأحد، فتسمّي <code dir=\"ltr\">resample(&quot;W&quot;)</code> الحاوية الأخيرة 2026-01-04 مع أن البيانات تنتهي في 30 ديسمبر 2025، وذلك “الأسبوع” لا يحمل إلا يومين من الطلبات. الفترات الجزئية في البداية والنهاية تبدو دائمًا انهيارًا أو تراجعًا. قارن <code dir=\"ltr\">series.index.min()</code> و<code dir=\"ltr\">max()</code> بالنطاق الزمني الحقيقي للبيانات، واحذف الفترات الجزئية أو ضع عليها علامة قبل حساب النمو.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ accessor المسمّى dt: أجزاء التقويم</h2>\n<p>للأنماط داخل الفترة، مثل أيام الأسبوع أو ساعات اليوم، استخرج الجزء بـ <code dir=\"ltr\">.dt</code> وجمّع بحسبه:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"3\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">parse_dates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"status\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"cancelled\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">d </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(orders.groupby(d.dt.day_name()).size().sort_values())</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(orders.groupby(d.dt.hour).size().head(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(d.dt.to_period(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Q\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).value_counts().sort_index().tail(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>الطلبات موزّعة بالتساوي تقريبًا على أيام الأسبوع، من 241 يوم الاثنين إلى 298 يومي الثلاثاء والسبت، فلا يوجد أثر قوي ليوم الأسبوع يحتاج تفسيرًا. أجزاء مفيدة أخرى: <code dir=\"ltr\">dt.year</code> و<code dir=\"ltr\">dt.quarter</code> و<code dir=\"ltr\">dt.month</code> و<code dir=\"ltr\">dt.isocalendar().week</code> و<code dir=\"ltr\">dt.date</code> و<code dir=\"ltr\">dt.normalize()</code> (منتصف ليل اليوم نفسه، وقد استخدمتها في الدرس 3.4).</p>\n<p>إعادة التجميع اليومية هي المكان الذي تُختبر فيه الافتراضات. تعطي <code dir=\"ltr\">by_time.resample(&quot;D&quot;).size()</code> عددًا لكل يوم تقويمي، 729 يومًا من أول طلب إلى آخره، وتظهر حقيقتان دفعة واحدة: 119 يومًا لم تشهد أي طلب (وفي سلسلة مبنية بـ <code dir=\"ltr\">groupby</code> كانت تلك الأيام ستغيب ببساطة)، وأكثر يوم ازدحامًا كان 10 نوفمبر 2025 بـ 16 طلبًا، قبل الجمعة السوداء (28 نوفمبر) بنحو ثلاثة أسابيع. والأيام الأعلى الأخرى، 6 و8 و26 نوفمبر، موزّعة على الشهر أيضًا. أيًّا كان ما دفع نوفمبر، فلم يكن يومًا ترويجيًا كبيرًا واحدًا؛ تذكّر ذلك في دراسة الحالة.</p>\n<p>في التمرين تبني السلسلة الشهرية ونظرة “سنة مقابل سنة” بنفسك. وفي الدرس التالي ترتّب العملاء وتقسّمهم إلى فئات، فتحوّل مقياسًا متصلًا مثل إجمالي الإنفاق إلى شرائح.</p>\n","toc":[{"id":"sec-1","text":"resample: حاويات من الزمن","level":2},{"id":"sec-2","text":"الفرق بين resample و to_period","level":3},{"id":"sec-3","text":"مقارنة الفترات","level":2},{"id":"sec-4","text":"الـ accessor المسمّى dt: أجزاء التقويم","level":2}],"takeaways":["<code dir=\"ltr\">df.set_index(&quot;order_date&quot;).resample(&quot;ME&quot;)[&quot;revenue&quot;].sum()</code> توزّع الصفوف على الأشهر التقويمية؛ و<code dir=\"ltr\">&quot;QE&quot;</code> و<code dir=\"ltr\">&quot;W&quot;</code> و<code dir=\"ltr\">&quot;D&quot;</code> تعطي الأرباع والأسابيع والأيام.","في pandas 2.2 وما بعدها، تكتب <code dir=\"ltr\">resample</code> نهايات الأشهر والأرباع <code dir=\"ltr\">&quot;ME&quot;</code> و<code dir=\"ltr\">&quot;QE&quot;</code>؛ أما الاسمان القديمان <code dir=\"ltr\">&quot;M&quot;</code> و<code dir=\"ltr\">&quot;Q&quot;</code> فمُهمَلان (deprecated) هناك، بينما تحتفظ الفترات مثل <code dir=\"ltr\">to_period(&quot;M&quot;)</code> بالأسماء القصيرة.","تشمل resample الفترات الفارغة (الشهر الذي بلا طلبات يظهر بـ 0)، بينما التجميع بحسب <code dir=\"ltr\">dt.to_period(&quot;M&quot;)</code> لا يسرد إلا الفترات التي فيها بيانات.","تقارن <code dir=\"ltr\">pct_change()</code> كل فترة بالتي قبلها، و<code dir=\"ltr\">pct_change(4)</code> على الأرباع تقارن بالربع نفسه قبل سنة.","تسميات resample تحدّد نهاية كل حاوية زمنية، فقد تعرض سلسلة أسبوعية تسمية من 2026 لطلبات قُدّمت في ديسمبر 2025؛ افحص الفترتين الأولى والأخيرة بحثًا عن بيانات جزئية."],"further":[{"title":"Time series / date functionality - resampling","url":"https://pandas.pydata.org/docs/user_guide/timeseries.html#resampling"},{"title":"Offset aliases","url":"https://pandas.pydata.org/docs/user_guide/timeseries.html#offset-aliases"},{"title":"pandas.Series.pct_change","url":"https://pandas.pydata.org/docs/reference/api/pandas.Series.pct_change.html"}],"quiz":[{"q":"<p>الإيراد الفصلي موجود في <code dir=\"ltr\">q</code>، الأقدم أولًا. أيّ تعبير يعطي نمو كل ربع مقارنةً بالربع نفسه قبل سنة؟</p>","options":[{"text":"<code dir=\"ltr\">q.pct_change()</code>","why":"هذا يقارن كل ربع بالربع الذي قبله مباشرة، فيُقارَن الربع الرابع بالثالث، وهذا يخلط النمو بالموسمية."},{"text":"<code dir=\"ltr\">q.pct_change(4)</code>","why":"صحيح. مع البيانات الفصلية، أربع فترات إلى الوراء هي الربع نفسه في السنة الماضية، وهذا يزيل النمط الموسمي من المقارنة."},{"text":"<code dir=\"ltr\">q.diff(4)</code>","why":"تعطي <code dir=\"ltr\">diff</code> التغيّر بالدولار، لا كنسبة مئوية."},{"text":"<code dir=\"ltr\">q.rolling(4).mean()</code>","why":"هذا ينعّم السلسلة على مدى سنة؛ ولا يقارن الفترات."}],"answer":1},{"q":"<p>عدد الطلبات الأسبوعي المحسوب بـ <code dir=\"ltr\">resample(&quot;W&quot;)</code> ينتهي بأسبوع تسميته 2026-01-04 فيه 11 طلبًا فقط. ما الذي يحدث؟</p>","options":[{"text":"البيانات تحتوي طلبات من 2026 يجب حذفها.","why":"تنتهي البيانات في 30 ديسمبر 2025. التسمية هي تاريخ نهاية الأسبوع، لا تواريخ الطلبات."},{"text":"اخترعت <code dir=\"ltr\">resample</code> أسبوعًا غير موجود.","why":"الأسبوع من 29 ديسمبر إلى 4 يناير موجود؛ البيانات فقط تتوقّف في منتصفه."},{"text":"الأسبوع الأخير مسمّى بيوم الأحد، 4 يناير، ولا يحتوي إلا يومين من البيانات، فيبدو منخفضًا بشكل مصطنع.","why":"صحيح. الحاويات الأسبوعية تنتهي يوم الأحد افتراضيًا. احذف الفترات الجزئية أو ضع عليها علامة قبل مقارنتها بالفترات الكاملة."}],"answer":2},{"q":"<p>أيّ كود يعطي عدد الطلبات لكل اسم يوم من أيام الأسبوع؟</p>","options":[{"text":"<code dir=\"ltr\">orders.groupby(orders[&quot;order_date&quot;].dt.day_name()).size()</code>","why":"صحيح. يستخرج الـ accessor <code dir=\"ltr\">dt</code> اسم يوم الأسبوع لكل صف، والتجميع بحسب تلك الـ Series يعدّ الطلبات لكل اسم يوم."},{"text":"<code dir=\"ltr\">orders.resample(&quot;D&quot;).size()</code>","why":"هذا يعدّ الطلبات لكل يوم تقويمي (729 صفًا)، لا لكل يوم من أيام الأسبوع، ويحتاج فهرسًا من نوع datetime."},{"text":"<code dir=\"ltr\">orders[&quot;order_date&quot;].day_name().value_counts()</code>","why":"methods التواريخ على العمود موجودة خلف <code dir=\"ltr\">.dt</code>؛ وبدونها تُطلق pandas الخطأ <code dir=\"ltr\">AttributeError</code>."}],"answer":0}],"exercise":{"kind":"python","files":["order_items.csv","orders.csv"],"starter":"import pandas as pd\n\nitems = pd.read_csv(\"order_items.csv\")\norders = pd.read_csv(\"orders.csv\", parse_dates=[\"order_date\"])\nitems[\"revenue\"] = items[\"quantity\"] * items[\"unit_price\"] * (1 - items[\"discount\"])\norder_revenue = items.groupby(\"order_id\", as_index=False)[\"revenue\"].sum()\norders = orders.merge(order_revenue, on=\"order_id\", how=\"left\", validate=\"one_to_one\")\norders = orders[orders[\"status\"] != \"cancelled\"]\n\n# 1. Monthly revenue (Series, one value per calendar month, month-end labels)\nmonthly = None\n\n# 2. Monthly number of orders\nmonthly_orders = None\n\n# 3. Quarterly revenue, and its growth versus the same quarter a year earlier\nquarterly = None\nyoy = None\n\n# 4. Weekday name with the highest total revenue, e.g. \"Monday\"\nbest_weekday = None\n","solution":"import pandas as pd\n\nitems = pd.read_csv(\"order_items.csv\")\norders = pd.read_csv(\"orders.csv\", parse_dates=[\"order_date\"])\nitems[\"revenue\"] = items[\"quantity\"] * items[\"unit_price\"] * (1 - items[\"discount\"])\norder_revenue = items.groupby(\"order_id\", as_index=False)[\"revenue\"].sum()\norders = orders.merge(order_revenue, on=\"order_id\", how=\"left\", validate=\"one_to_one\")\norders = orders[orders[\"status\"] != \"cancelled\"]\n\nby_time = orders.set_index(\"order_date\")\nmonthly = by_time.resample(\"ME\")[\"revenue\"].sum()\nmonthly_orders = by_time.resample(\"ME\")[\"order_id\"].count()\n\nquarterly = by_time.resample(\"QE\")[\"revenue\"].sum()\nyoy = quarterly.pct_change(4)\n\nbest_weekday = orders.groupby(orders[\"order_date\"].dt.day_name())[\"revenue\"].sum().idxmax()\nprint(yoy.round(3))\n","checks":[{"id":"monthly","test":"isinstance(monthly, pd.Series) and len(monthly) == 24 and abs(float(monthly.loc['2025-11'].iloc[0]) - 75333.9) < 1"},{"id":"monthly-orders","test":"isinstance(monthly_orders, pd.Series) and len(monthly_orders) == 24 and int(monthly_orders.loc['2025-11'].iloc[0]) == 325"},{"id":"quarterly","test":"isinstance(quarterly, pd.Series) and len(quarterly) == 8 and abs(float(quarterly.iloc[-1]) - 146825.44) < 1"},{"id":"yoy","test":"isinstance(yoy, pd.Series) and round(float(yoy.iloc[-1]), 3) == 1.795 and yoy.iloc[:4].isna().all()"},{"id":"weekday","test":"best_weekday == 'Saturday'"}],"title":"شهريًا، وفصليًا، وسنة مقابل سنة","prompt":"<p>في <code dir=\"ltr\">orders</code> صف لكل طلب غير ملغى، مع <code dir=\"ltr\">order_date</code> و<code dir=\"ltr\">revenue</code>. ابنِ <code dir=\"ltr\">monthly</code> (الإيراد لكل شهر تقويمي، بتسميات نهاية الشهر) و<code dir=\"ltr\">monthly_orders</code> (عدد الطلبات لكل شهر) باستخدام <code dir=\"ltr\">resample</code>. وابنِ <code dir=\"ltr\">quarterly</code> (الإيراد لكل ربع) و<code dir=\"ltr\">yoy</code>، نموّه مقارنةً بالربع نفسه قبل سنة ككسر (1.0 تعني ‎+100%). وأخيرًا اجعل <code dir=\"ltr\">best_weekday</code> اسم يوم الأسبوع صاحب أعلى إيراد إجمالي.</p>\n","hints":["ضع التواريخ في الفهرس أولًا: <code dir=\"ltr\">by_time = orders.set_index(&quot;order_date&quot;)</code>، ثم <code dir=\"ltr\">by_time.resample(&quot;ME&quot;)[&quot;revenue&quot;].sum()</code>.","عُدّ الطلبات بـ <code dir=\"ltr\">[&quot;order_id&quot;].count()</code> (أو <code dir=\"ltr\">.size()</code>) على الـ resampler نفسه. والأرباع تستخدم <code dir=\"ltr\">&quot;QE&quot;</code>.","تقارن <code dir=\"ltr\">quarterly.pct_change(4)</code> بأربعة أرباع سابقة. ولليوم: <code dir=\"ltr\">orders.groupby(orders[&quot;order_date&quot;].dt.day_name())[&quot;revenue&quot;].sum().idxmax()</code>."],"explanation":"<p>تحتاج <code dir=\"ltr\">resample</code> فهرسًا من نوع datetime، وتوفّره <code dir=\"ltr\">set_index(&quot;order_date&quot;)</code>؛ و<code dir=\"ltr\">&quot;ME&quot;</code> و<code dir=\"ltr\">&quot;QE&quot;</code> تسمّيان كل حاوية بآخر يوم فيها. شهد نوفمبر 2025 ما مجموعه 325 طلبًا و75,334 دولارًا من الإيراد. و<code dir=\"ltr\">pct_change(4)</code> على سلسلة فصلية هي نظرة سنة مقابل سنة: القيم الأربع الأولى <code dir=\"ltr\">NaN</code> لأن 2023 ليست في البيانات، ونما الربع الرابع من 2025 بنسبة 179.5% على الربع الرابع من 2024، بما يتّسق مع نمو أرباع 2025 الأخرى بين 145% و200%. يوم السبت يجلب أعلى إيراد، مع أن الفروق بين أيام الأسبوع صغيرة مقارنةً بالفروق الموسمية.</p>\n","checkLabels":{"monthly":"في <code dir=\"ltr\">monthly</code> 24 شهرًا، ونوفمبر 2025 نحو 75,334","monthly-orders":"تعرض <code dir=\"ltr\">monthly_orders</code> 325 طلبًا في نوفمبر 2025","quarterly":"في <code dir=\"ltr\">quarterly</code> 8 أرباع، تنتهي عند نحو 146,825","yoy":"<code dir=\"ltr\">yoy</code> تساوي 1.795 للربع الرابع من 2025 وفارغة لعام 2024","weekday":"<code dir=\"ltr\">best_weekday</code> هو يوم الأسبوع الأعلى إيرادًا"}},"words":646,"readMinutes":4}