{"id":"l-pda-3-4","courseId":"python-data-analysis","lang":"ar","kind":"lesson","summary":"حلّل عمود تواريخ يخلط بين صيغة ISO وصيغة اليوم أولًا وصيغة الشهر أولًا باستخدام صيغ to_datetime صريحة، وضع علامة على القيم الملتبسة، وتحقّق من النتيجة مقابل مصدر موثوق.","html":"<p>يكتب الملف الفوضوي التواريخ بثلاث طرق: <code dir=\"ltr\">2024-03-05</code> و<code dir=\"ltr\">05/03/2024</code> و<code dir=\"ltr\">03/05/2024</code>. الأولى لا لبس فيها. والأخريان اليوم نفسه مكتوبًا بجدول بيانات أوروبي وآخر أمريكي، والمشكلة أن <code dir=\"ltr\">03/05/2024</code> قد تكون أيضًا الطريقة الأوروبية لكتابة 3 مايو. التواريخ هي العمود الذي تتباعد فيه عبارة “تم التحليل دون أخطاء” عن عبارة “النتيجة صحيحة” أكثر من أي عمود آخر.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ترك pandas تخمّن</h2>\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\">messy </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_messy.csv\"</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\">(messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sample(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">random_state</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).tolist())</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    pd.to_datetime(messy[</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:#CF222E;--shiki-dark:#FF7B72\">except</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ValueError</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> error:</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\">str</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(error).splitlines()[</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=\"0\" hidden></div></div>\n<p>تستنتج pandas 2 صيغة واحدة من القيمة الأولى، <code dir=\"ltr\">2024-01-02</code>، وتطبّقها على العمود كله. الصف 6، <code dir=\"ltr\">08/04/2024</code>، لا يناسبها، فيتوقّف التحويل. هذه الصرامة ميزة: الإصدارات الأقدم كانت تخمّن صفًا بصف، وقد تخلط بين الأيام والأشهر بصمت.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">حلّل كل صيغة صراحةً</h2>\n<p>أنت تعرف أيّ الصيغ موجودة، فقل ذلك. رموز الصيغة تصف الترتيب: <code dir=\"ltr\">%Y</code> سنة من أربعة أرقام، و<code dir=\"ltr\">%m</code> شهر من رقمين، و<code dir=\"ltr\">%d</code> يوم من رقمين. حلّل العمود مرة لكل صيغة مع <code dir=\"ltr\">errors=&quot;coerce&quot;</code>، التي تحوّل الصفوف غير المطابقة إلى <code dir=\"ltr\">NaT</code> (“ليس وقتًا” في pandas)، ثم املأ الفجوات بحسب ترتيب الأفضلية:</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\">messy </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_messy.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">raw </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> messy[</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\">iso </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"%Y-%m-</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">dmy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%m/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">mdy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"%m/</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</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\">(iso.notna().sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"ISO |\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, dmy.notna().sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fit day-first |\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, mdy.notna().sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"fit month-first\"</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\">parsed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> iso.fillna(dmy).fillna(mdy)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(parsed.isna().sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unparsed\"</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\">(parsed.min(), parsed.max())</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>حُلّل كل صف، والنطاق صحيح: من يناير 2024 إلى ديسمبر 2025. يأتي اليوم أولًا قبل الشهر أولًا في السلسلة لأن معظم دول Cartwheel تكتب التواريخ بهذه الطريقة. وتستطيع pandas فعل الشيء نفسه في استدعاء واحد بـ <code dir=\"ltr\">pd.to_datetime(raw, format=&quot;mixed&quot;, dayfirst=True)</code>، التي تحلّل كل قيمة وحدها؛ النسخة الصريحة أطول، لكنها تبيّن لك بالضبط أيّ قاعدة حسمت كل صف.</p>\n<p>لكن انظر إلى الأعداد. 57 قيمة تناسب اليوم أولًا و44 تناسب الشهر أولًا، من 68 قيمة فقط فيها شرطات مائلة. إذن 33 منها تناسب <strong>الاثنتين</strong>.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 220\" role=\"img\" aria-labelledby=\"l-pda-3-4-t1\"><defs><marker id=\"arrow-l-pda-3-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-3-4-t1\">ثلاثة صناديق. 25/10/2025 لا يمكن أن تكون إلا اليوم أولًا لأنه لا يوجد شهر 25. و 10/25/2025 لا يمكن أن تكون إلا الشهر أولًا. أما 05/08/2025 فتناسب القراءتين، 5 أغسطس أو 8 مايو، فهي ملتبسة وتحتاج معلومات خارجية.</title>\n  <rect class=\"d-box-success\" x=\"20\" y=\"30\" width=\"200\" height=\"110\" rx=\"12\"/>\n  <text class=\"d-code\" x=\"120\" y=\"65\" text-anchor=\"middle\">25/10/2025</text>\n  <text class=\"d-label\" x=\"120\" y=\"95\" text-anchor=\"middle\">لا يوجد شهر 25</text>\n  <text class=\"d-label-strong\" x=\"120\" y=\"120\" text-anchor=\"middle\">اليوم أولًا فقط</text>\n  <rect class=\"d-box-success\" x=\"250\" y=\"30\" width=\"200\" height=\"110\" rx=\"12\"/>\n  <text class=\"d-code\" x=\"350\" y=\"65\" text-anchor=\"middle\">10/25/2025</text>\n  <text class=\"d-label\" x=\"350\" y=\"95\" text-anchor=\"middle\">لا يوجد شهر 25</text>\n  <text class=\"d-label-strong\" x=\"350\" y=\"120\" text-anchor=\"middle\">الشهر أولًا فقط</text>\n  <rect class=\"d-box-warn\" x=\"480\" y=\"30\" width=\"200\" height=\"110\" rx=\"12\"/>\n  <text class=\"d-code\" x=\"580\" y=\"65\" text-anchor=\"middle\">05/08/2025</text>\n  <text class=\"d-label\" x=\"580\" y=\"95\" text-anchor=\"middle\">5 أغسطس أم 8 مايو؟</text>\n  <text class=\"d-label-strong\" x=\"580\" y=\"120\" text-anchor=\"middle\">ملتبس</text>\n  <text class=\"d-label-muted\" x=\"235\" y=\"180\" text-anchor=\"middle\">أداة التحليل تستطيع حسم هذه</text>\n  <text class=\"d-label-muted\" x=\"580\" y=\"180\" text-anchor=\"middle\">وحدها بيانات خارجية تحسم</text>\n  <path class=\"d-line\" d=\"M30 160 L440 160\"/>\n  <path class=\"d-line\" d=\"M490 160 L670 160\"/>\n</svg><figcaption>ثلاثة أنواع من التواريخ ذات الشرطات المائلة</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">ضع علامة على الالتباس</h2>\n<p>القيمة التي تُحلَّل بالطريقتين إلى تاريخين <strong>مختلفين</strong> ملتبسة. ثلاث من القيم الـ 33 تواريخ مثل <code dir=\"ltr\">05/05/2025</code>، حيث تتّفق القراءتان، فهي غير مؤذية:</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\">messy </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_messy.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">raw </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> messy[</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\">dmy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%m/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">mdy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"%m/</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ambiguous </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> dmy.notna() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> mdy.notna() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (dmy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> mdy)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(ambiguous.sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"ambiguous dates\"</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\">(raw[ambiguous].head(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">6</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).tolist())</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>ثلاثون صفًا تعتمد على افتراض. تستطيع أن تفترض، كما فعلت سلسلة <code dir=\"ltr\">fillna</code>، لكن يجب أن تعرف أنك افترضت، ويجب أن يعرف ذلك أيضًا من يقرأ تقريرك.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">تحقّق مقابل مصدر الحقيقة</h2>\n<p>جدول <code dir=\"ltr\">orders</code> النظيف لدى Cartwheel هو نظام السجلات الأساسي: كل معرّف طلب في الملف المُصدَّر يظهر هناك أيضًا مع وقته الحقيقي. البحث بمعرّف الطلب يتيح لك اختبار الافتراض مباشرة. تحوّل <code dir=\"ltr\">set_index(&quot;order_id&quot;)</code> التواريخ إلى Series مسمّاة بالمعرّف، وتبحث <code dir=\"ltr\">map()</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\">messy </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_messy.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">raw </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> messy[</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\">iso </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"%Y-%m-</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">dmy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%m/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">mdy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_datetime(raw, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">format</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"%m/</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/%Y\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">errors</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coerce\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">parsed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> iso.fillna(dmy).fillna(mdy)</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\">true_dates </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_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\">].dt.normalize()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">truth </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].map(true_dates)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">wrong </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> parsed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> truth</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(wrong.sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"dates disagree with the order system\"</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\">(messy.loc[wrong, [</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\">]].assign(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">parsed</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">parsed[wrong].dt.date, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">real</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">truth[wrong].dt.date))</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>تُسقط <code dir=\"ltr\">dt.normalize()</code> الوقت من اليوم كي تكون المقارنة تاريخًا بتاريخ. خمسة تواريخ خاطئة، وكل واحد منها قيمة ملتبسة كانت فعلًا بصيغة الشهر أولًا: <code dir=\"ltr\">02/08/2025</code> هو 8 فبراير، لا 2 أغسطس. كان افتراض اليوم أولًا صحيحًا في 25 من 30 صفًا ملتبسًا، وهذا يبدو جيدًا حتى تدرك أنه ينقل خمسة طلبات إلى الشهر الخطأ، وبعضها إلى الربع الخطأ.</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>الثقة بـ dayfirst=True</span></p>\n<p><code dir=\"ltr\">dayfirst=True</code> تفضيل لا ضمان: تستخدمه pandas فقط حين تكون القيمة ملتبسة. وحين يخلط ملف بين الأعراف، كما تفعل كثيرًا الملفات المجمّعة من عدة أنظمة، فستكون بعض التواريخ الملتبسة خاطئة أيًّا كان التفضيل الذي تختاره. إذا لم تستطع التحقّق، فاحتفظ بعلامة <code dir=\"ltr\">is_ambiguous</code> في البيانات المنظّفة، وأبلغ عن حجم الإيراد الذي يعتمد عليها.</p>\n</aside>\n<h3 id=\"sec-5\" tabindex=\"-1\">الأوقات والمناطق الزمنية</h3>\n<p>الملف المُصدَّر يحمل تواريخ فقط، لكن جدول <code dir=\"ltr\">orders</code> فيه طوابع زمنية كاملة مثل <code dir=\"ltr\">2025-12-30 21:19:03</code>، ولا يذكر أيٌّ منها منطقته الزمنية. تسجّل Cartwheel كل شيء بساعة متجر واحدة، فتستطيع معاملتها كأوقات محلية والمضيّ قُدمًا. أما حين يحتوي ملف على فروق توقيت مثل <code dir=\"ltr\">+04:00</code>، فمرّر <code dir=\"ltr\">utc=True</code> إلى <code dir=\"ltr\">pd.to_datetime</code> كي تستقر كل القيم على ساعة مشتركة واحدة قبل أن تقارنها أو تجمّعها؛ ترك المناطق الزمنية مختلطة هو ما يجعل طلبين قُدّما في اللحظة نفسها في دبي ولندن ينتهيان في تاريخين مختلفين، أو حتى في سنتين مختلفتين حول منتصف ليل 31 ديسمبر.</p>\n<h2 id=\"sec-6\" tabindex=\"-1\">ما يعطيك إياه عمود تواريخ حقيقي</h2>\n<p>حين يصبح العمود <code dir=\"ltr\">datetime64</code>، يكشف الـ accessor المسمّى <code dir=\"ltr\">.dt</code> أجزاءه:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"4\"><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=\"4\" 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\">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\">(d.dt.year.value_counts().sort_index())</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(d.dt.day_name().value_counts().head(</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:#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\">\"M\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).value_counts().sort_index().tail(</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=\"4\" hidden></div></div>\n<p>السنة، والشهر، واسم يوم الأسبوع، والشهر التقويمي كفترة (period): هذه هي المفاتيح التي ستجمّع بحسبها في القسم 4، حيث يشرح الدرس 4.4 السلاسل الزمنية (time series) بالتفصيل.</p>\n<p>في التمرين تحلّل تواريخ الملف المُصدَّر وتضع عليها العلامات وتصلحها. ثم تبقى مشكلة واحدة قبل أن يصبح الملف نظيفًا: صفوف تظهر مرتين.</p>\n","toc":[{"id":"sec-1","text":"ترك pandas تخمّن","level":2},{"id":"sec-2","text":"حلّل كل صيغة صراحةً","level":2},{"id":"sec-3","text":"ضع علامة على الالتباس","level":2},{"id":"sec-4","text":"تحقّق مقابل مصدر الحقيقة","level":2},{"id":"sec-5","text":"الأوقات والمناطق الزمنية","level":3},{"id":"sec-6","text":"ما يعطيك إياه عمود تواريخ حقيقي","level":2}],"takeaways":["بدون <code dir=\"ltr\">format=</code> تخمّن pandas 2 الصيغة من القيمة الأولى، وتفشل مع أيّ صف مكتوب بشكل مختلف.","حلّل كل صيغة معروفة صراحةً بـ <code dir=\"ltr\">pd.to_datetime(col, format=..., errors=&quot;coerce&quot;)</code>، ثم ادمج النتائج بـ <code dir=\"ltr\">fillna</code>.","تاريخ مثل <code dir=\"ltr\">05/08/2025</code> يُحلَّل وفق قاعدتي اليوم أولًا والشهر أولًا كلتيهما؛ ولا تستطيع أيّ أداة تحليل أن تخبرك أيّهما الصحيح.","ضع علامة على القيم الملتبسة وحلّها من مصدر موثوق (نظام السجلات الأساسي، أو عمود آخر، أو الشخص الذي أعدّ الملف)، لا بالافتراض وحده أبدًا.","حين يصبح العمود <code dir=\"ltr\">datetime64</code>، يعطيك الـ accessor المسمّى <code dir=\"ltr\">.dt</code> كلًّا من <code dir=\"ltr\">year</code> و<code dir=\"ltr\">month</code> و<code dir=\"ltr\">day_name()</code> و<code dir=\"ltr\">to_period()</code> للتجميع."],"further":[{"title":"pandas.to_datetime","url":"https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html"},{"title":"strftime() and strptime() format codes","url":"https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes"},{"title":"Time series / date functionality","url":"https://pandas.pydata.org/docs/user_guide/timeseries.html"}],"quiz":[{"q":"<p>يُطلق <code dir=\"ltr\">pd.to_datetime(messy[&quot;order_date&quot;])</code> الخطأ <code dir=\"ltr\">ValueError: time data &quot;08/04/2024&quot; doesn't match format &quot;%Y-%m-%d&quot;</code>. لماذا؟</p>","options":[{"text":"التاريخ 8 أبريل 2024 غير موجود في نطاق البيانات.","why":"القيمة تاريخ صالح؛ المشكلة في صيغته لا في معناه."},{"text":"استنتجت pandas صيغة ISO من القيمة الأولى ثم قابلت صفًا مكتوبًا بشكل مختلف.","why":"صحيح. تستنتج pandas 2 صيغة واحدة للعمود كله، والقيمة الأولى <code dir=\"ltr\">2024-01-02</code> هي التي حدّدتها."},{"text":"التواريخ ذات الشرطات المائلة غير مدعومة في pandas.","why":"التواريخ ذات الشرطات المائلة تُحلَّل بلا مشكلة مع الـ <code dir=\"ltr\">format</code> الصحيح، مثل <code dir=\"ltr\">%d/%m/%Y</code>."},{"text":"العمود فيه قيم مفقودة.","why":"القيم المفقودة تصبح <code dir=\"ltr\">NaT</code> (ليس وقتًا)؛ ولا تسبّب خطأ صيغة."}],"answer":1},{"q":"<p>أيّ هذه القيم ملتبس، أي أن قراءتي اليوم أولًا والشهر أولًا تعطيان تاريخين صالحين لكن مختلفين؟</p>","options":[{"text":"<code dir=\"ltr\">25/10/2025</code>","why":"لا يوجد شهر رقمه 25، فالقراءة الوحيدة الصالحة هي اليوم أولًا."},{"text":"<code dir=\"ltr\">10/25/2025</code>","why":"لا يوجد شهر 25 في قراءة اليوم أولًا أيضًا، فلا بد أن تكون الشهر أولًا."},{"text":"<code dir=\"ltr\">2025-10-05</code>","why":"تواريخ ISO تضع دائمًا السنة ثم الشهر ثم اليوم، فلها قراءة واحدة فقط."},{"text":"<code dir=\"ltr\">05/08/2025</code>","why":"صحيح. قد يكون 5 أغسطس أو 8 مايو. وحدها المعلومات الخارجية تستطيع الحسم."}],"answer":3},{"q":"<p>تفترض أداة التحليل لديك أن اليوم أولًا في التواريخ الملتبسة. كيف تتحقّق من صحة هذا الافتراض؟</p>","options":[{"text":"قارن التواريخ المحلَّلة بمصدر موثوق للسجلات نفسها، مثل نظام الطلبات، وعُدّ حالات الاختلاف.","why":"صحيح. هنا يُظهر جدول <code dir=\"ltr\">orders</code> النظيف أن الافتراض خاطئ في 5 من الصفوف الملتبسة الـ 30."},{"text":"تحقّق من أنه لا يوجد تاريخ محلَّل قيمته <code dir=\"ltr\">NaT</code>.","why":"التاريخ الملتبس لا يفشل تحليله أبدًا؛ بل يُحلَّل إلى تاريخ خاطئ، وهذا ما لا يستطيع هذا الفحص رؤيته."},{"text":"تحقّق من أن كل التواريخ تقع بين 2024 و2025.","why":"تبديل اليوم والشهر داخل السنة نفسها يبقى ضمن النطاق، فلا يستطيع هذا الفحص التقاط الخطأ."}],"answer":0}],"exercise":{"kind":"python","files":["orders_messy.csv","orders.csv"],"starter":"import pandas as pd\n\nmessy = pd.read_csv(\"orders_messy.csv\")\norders = pd.read_csv(\"orders.csv\", parse_dates=[\"order_date\"])\nraw = messy[\"order_date\"]\n\n# 1. Parse raw three ways (ISO, day-first, month-first) and combine: ISO, then day-first, then month-first\nparsed = None\n\n# 2. Column is_ambiguous: True where raw parses both day-first and month-first to DIFFERENT dates\n\n# 3. The real date of each export row, looked up by order_id in orders (date only, no time)\ntruth = None\n\n# 4. How many parsed dates disagree with truth\nn_wrong = None\n\n# 5. messy[\"order_date\"]: parsed dates, but use truth for the ambiguous rows\n","solution":"import pandas as pd\n\nmessy = pd.read_csv(\"orders_messy.csv\")\norders = pd.read_csv(\"orders.csv\", parse_dates=[\"order_date\"])\nraw = messy[\"order_date\"]\n\niso = pd.to_datetime(raw, format=\"%Y-%m-%d\", errors=\"coerce\")\ndmy = pd.to_datetime(raw, format=\"%d/%m/%Y\", errors=\"coerce\")\nmdy = pd.to_datetime(raw, format=\"%m/%d/%Y\", errors=\"coerce\")\nparsed = iso.fillna(dmy).fillna(mdy)\n\nmessy[\"is_ambiguous\"] = dmy.notna() & mdy.notna() & (dmy != mdy)\n\ntrue_dates = orders.set_index(\"order_id\")[\"order_date\"].dt.normalize()\ntruth = messy[\"order_id\"].map(true_dates)\n\nn_wrong = int((parsed != truth).sum())\n\nmessy[\"order_date\"] = parsed.where(~messy[\"is_ambiguous\"], truth)\nprint(n_wrong, messy[\"is_ambiguous\"].sum())\n","checks":[{"id":"parsed","test":"isinstance(parsed, pd.Series) and pd.api.types.is_datetime64_any_dtype(parsed) and int(parsed.isna().sum()) == 0"},{"id":"ambiguous","test":"'is_ambiguous' in messy.columns and int(messy['is_ambiguous'].sum()) == 30"},{"id":"wrong","test":"int(n_wrong) == 5"},{"id":"fixed-column","test":"pd.api.types.is_datetime64_any_dtype(messy['order_date']) and messy.loc[messy['order_id'] == 2401, 'order_date'].iloc[0] == pd.Timestamp('2025-02-08')"},{"id":"all-correct","test":"(messy['order_date'] == messy['order_id'].map(orders.set_index('order_id')['order_date'].dt.normalize())).all()"}],"title":"حلّل التواريخ وضع العلامات وأصلحها","prompt":"<p>باستخدام <code dir=\"ltr\">raw = messy[&quot;order_date&quot;]</code>: ابنِ <code dir=\"ltr\">parsed</code> بتحليل صيغة ISO (<code dir=\"ltr\">%Y-%m-%d</code>)، ثم اليوم أولًا (<code dir=\"ltr\">%d/%m/%Y</code>)، ثم الشهر أولًا (<code dir=\"ltr\">%m/%d/%Y</code>)، ودمجها بهذا الترتيب. أضِف عمودًا منطقيًا <code dir=\"ltr\">is_ambiguous</code> يساوي <code dir=\"ltr\">True</code> حيث ينجح تحليلا اليوم أولًا والشهر أولًا كلاهما ويعطيان تاريخين مختلفين. اجعل <code dir=\"ltr\">truth</code> التاريخ الحقيقي لكل صف من <code dir=\"ltr\">orders</code>، بالبحث بـ <code dir=\"ltr\">order_id</code> ومع حذف الوقت، و<code dir=\"ltr\">n_wrong</code> عدد تواريخ <code dir=\"ltr\">parsed</code> التي تختلف عنه. وأخيرًا، اجعل <code dir=\"ltr\">messy[&quot;order_date&quot;]</code> مساويًا لـ <code dir=\"ltr\">parsed</code>، إلا في الصفوف الملتبسة، حيث يأخذ القيمة من <code dir=\"ltr\">truth</code>.</p>\n","hints":["حلّل مرة لكل صيغة بـ <code dir=\"ltr\">pd.to_datetime(raw, format=..., errors=&quot;coerce&quot;)</code>، ثم سلسِل <code dir=\"ltr\">.fillna()</code> بحسب ترتيب الأفضلية.","الملتبس: <code dir=\"ltr\">dmy.notna() &amp; mdy.notna() &amp; (dmy != mdy)</code>. وللحقيقة: <code dir=\"ltr\">orders.set_index(&quot;order_id&quot;)[&quot;order_date&quot;].dt.normalize()</code> هي Series للبحث؛ و<code dir=\"ltr\">messy[&quot;order_id&quot;].map(...)</code> تستخدمها.","تحتفظ <code dir=\"ltr\">parsed.where(condition, other)</code> بـ <code dir=\"ltr\">parsed</code> حيث يكون الشرط <code dir=\"ltr\">True</code> وتأخذ <code dir=\"ltr\">other</code> في غير ذلك، فيكون الشرط <code dir=\"ltr\">~messy[&quot;is_ambiguous&quot;]</code>."],"explanation":"<p>الصيغ الصريحة مع <code dir=\"ltr\">errors=&quot;coerce&quot;</code> تحلّل كل صف وتسجّل أيّ قاعدة طابقت. ثلاثون قيمة تناسب صيغتي الشرطة المائلة بنتيجتين مختلفتين؛ ويصيب تفضيل اليوم أولًا في 25 منها ويخطئ في 5، وهذا ما لا يمكنك اكتشافه إلا بالفحص مقابل نظام الطلبات. تحتفظ <code dir=\"ltr\">Series.where(cond, other)</code> بالقيم حيث يتحقّق الشرط وتستبدلها في غير ذلك، فلا تُستبدل إلا الصفوف الملتبسة، ويطابق العمود المُصلَح الآن مصدر الحقيقة في الصفوف الـ 168 كلها. في المشاريع الحقيقية، احتفظ بعلامة <code dir=\"ltr\">is_ambiguous</code> حتى بعد الإصلاح: فهي توثّق أيّ القيم اعتمدت على بحث خارجي.</p>\n","checkLabels":{"parsed":"<code dir=\"ltr\">parsed</code> هي Series من التواريخ ليس فيها قيم غير محلَّلة","ambiguous":"<code dir=\"ltr\">is_ambiguous</code> تضع علامة على 30 صفًا","wrong":"<code dir=\"ltr\">n_wrong</code> يساوي 5","fixed-column":"تاريخ الطلب 2401 هو 8 فبراير 2025، لا 2 أغسطس","all-correct":"كل تاريخ مُصلَح يطابق نظام الطلبات"}},"words":624,"readMinutes":4}