{"id":"l-pda-3-3","courseId":"python-data-analysis","lang":"ar","kind":"lesson","summary":"حوّل المبالغ المالية المخزّنة كنص، مثل “$1,234.50”، إلى عمود رقمي بحذف الرموز، والتحويل بـ astype أو to_numeric، ثم فحص كل قيمة فشل تحويلها.","html":"<p>اسأل الملف الفوضوي عن إجمالي مبيعاته، فتحصل على شيء غريب. لا خطأ، ولا رقم: نص طويل يبدأ بـ <code dir=\"ltr\">'$65.0018.0033.00...'</code>. يبدو عمود <code dir=\"ltr\">total</code> رقميًا في جدول البيانات، لكن نحو نصف قيمه يبدأ بعلامة دولار، فخزّنت pandas العمود كله كنص، وجمع النصوص يعني لصقها. وصول المبالغ المالية كنصوص من أشيع المشكلات في البيانات المُصدَّرة، وتحتاج إصلاحًا مقصودًا ومفحوصًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">اكتشفها: النوع object في عمود أرقام</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\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].dtype)</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\">\"total\"</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\">).tolist())</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\">repr</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()[:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">40</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\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].str.startswith(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"values start with $\"</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>يلتقط روتين الفحص من الدرس 2.2 هذا فورًا: كانت <code dir=\"ltr\">info()</code> ستعرض <code dir=\"ltr\">total</code> كـ <code dir=\"ltr\">object</code> حيث توقّعت <code dir=\"ltr\">float64</code>. كلما كان عمود يُفترض أن يحمل أرقامًا من النوع <code dir=\"ltr\">object</code>، فهناك قيمة واحدة على الأقل فيها شيء ليس رقمًا. هنا، 76 قيمة من 168 تبدأ بـ <code dir=\"ltr\">$</code>.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">أصلحها: احذف الرموز ثم حوّل</h2>\n<p>أزِل كل حرف ليس جزءًا من الرقم، ثم حوّل النص النظيف:</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\">total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .str.strip()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\",\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .astype(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">float</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:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total.dtype, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total.sum(), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</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\">(total.describe().round(</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=\"1\" hidden></div></div>\n<p>تغليف السلسلة بقوسين يتيح لك وضع خطوة في كل سطر، وهذا أسهل في القراءة وفي تعطيل خطوة بتعليق أثناء تتبّع الأخطاء. وفاصل الآلاف يُزال أيضًا: هذا الملف المُصدَّر تصادف أنه لا يحتوي مجاميع فوق 1,000 دولار، لكن الملف التالي سيحتوي، و<code dir=\"ltr\">&quot;1,234.50&quot;</code> ستكسر <code dir=\"ltr\">astype(float)</code>.</p>\n<p><code dir=\"ltr\">describe()</code> هي فحص السلامة. تتراوح المجاميع بين 18 و942.90 دولارًا بوسيط 72، وهذا يطابق كتالوج Cartwheel: أرخص منتج حبل قفز بـ 18 دولارًا، وأكبر بند مكتبان واقفان. أما حد أدنى قدره 0 أو حد أقصى قدره 94,290 فسيخبرك بأن فاصلة عشرية ضاعت.</p>\n<p>مجموع 23,500.67 ليس رقم إيراد بعد: الملف المُصدَّر ما زال يحتوي تسعة صفوف مكرّرة. ستزيلها في الدرس 3.5.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">فخ علامة الدولار</h2>\n<p>تستطيع <code dir=\"ltr\">str.replace</code> أيضًا العمل مع التعابير النمطية (regular expressions)، وهي لغة أنماط لبعض الحروف فيها معانٍ خاصة. و<code dir=\"ltr\">$</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\">as_pattern </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\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">True</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\">(as_pattern.head(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).tolist())   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># still '$65.00'</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_numeric(as_pattern)</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:#0A3069;--shiki-dark:#A5D6FF\">\"ValueError:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, error)</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>طابق النمط <code dir=\"ltr\">$</code> الموضع الفارغ في نهاية كل نص واستبدله بلا شيء، فنجت كل علامات الدولار. في pandas 2 تكون القيمة الافتراضية لـ <code dir=\"ltr\">str.replace</code> هي <code dir=\"ltr\">regex=False</code>، لكن الدروس التعليمية القديمة وإصدارات pandas الأقدم كانت تفترض الأنماط، وستقابل كودًا مكتوبًا بالطريقتين. كتابة <code dir=\"ltr\">regex=False</code> صراحةً توضّح القصد لكل قارئ ولكل إصدار. وإذا أردت نمطًا فعلًا، فاهرب من الحرف الخاص (escape) أو استخدم فئة حروف: <code dir=\"ltr\">str.replace(r&quot;[$,]&quot;, &quot;&quot;, regex=True)</code> تزيل الرمزين في خطوة واحدة.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">حين لا تتحوّل بعض القيم</h2>\n<p><code dir=\"ltr\">astype(float)</code> كل شيء أو لا شيء: قيمة سيئة واحدة ويفشل التحويل كله. أما <code dir=\"ltr\">pd.to_numeric</code> مع <code dir=\"ltr\">errors=&quot;coerce&quot;</code> فتحوّل ما تستطيع وتحوّل البقية إلى <code dir=\"ltr\">NaN</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\">raw </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.Series([</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$1,234.50\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"18.00\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"N/A\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"€89.00\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\" $65.00 \"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cleaned </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> raw.str.strip().str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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\">).str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\",\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_numeric(cleaned, </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\">(amount)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">failed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount.isna() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> raw.notna()</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:#0A3069;--shiki-dark:#A5D6FF\">\"Could not convert:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, raw[failed].tolist())</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>يسرد الفحص ما فشل بالضبط: <code dir=\"ltr\">N/A</code>، وهي قيمة مفقودة فعلًا، و<code dir=\"ltr\">€89.00</code>، وهي عملية بيع حقيقية بعملة لم تتوقّعها أداة التنظيف. وهاتان تحتاجان معاملة مختلفة. الأولى تبقى مفقودة؛ والثانية تحتاج قاعدة تحويل، أو على الأقل حديثًا مع من أنتج الملف. الإجبار على التحويل دون نظر كان سيُسقط بهدوء 89 يورو من المبيعات من كل مجموع.</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\">pd.to_numeric(col, errors=&quot;coerce&quot;)</code> ثم <code dir=\"ltr\">.sum()</code> “تعمل” دائمًا. إذا انتقل ملف مُصدَّر جديد إلى الصيغة الأوروبية، فستحوّل أداة التنظيف <code dir=\"ltr\">1.234,50</code> إلى 1.2345 و<code dir=\"ltr\">65,00</code> إلى 6500، وتصبح أي صيغة أغرب <code dir=\"ltr\">NaN</code>، فيختلّ الإيراد دون أي رسالة خطأ. عُدّ قيم <code dir=\"ltr\">NaN</code> الجديدة كل مرة، فيجب أن يكون <code dir=\"ltr\">(amount.isna() &amp; raw.notna()).sum()</code> رقمًا تستطيع تفسيره، وتأكّد أن الحدين الأدنى والأقصى ما زالا منطقيين.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">اجعلها دالة</h2>\n<p>ستنظّف أعمدة مبالغ مالية مرة أخرى، في هذه الدورة وفي عملك. ضع المنطق حيث يمكن إعادة استخدامه واختباره:</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:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> parse_money</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(values):</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"\"\"Convert text like ' $1,234.50' to floats; unparseable values become NaN.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    cleaned </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        values.astype(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"string\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        .str.strip()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        .str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        .str.replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\",\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">regex</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    )</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.to_numeric(cleaned, </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>\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\">messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> parse_money(messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total\"</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\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].dtype, messy[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].isna().sum(), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unparsed\"</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>تجعل <code dir=\"ltr\">astype(&quot;string&quot;)</code> في البداية الدالة آمنة مع الأعمدة التي قرأتها pandas أصلًا كأرقام (حين يتصادف ألّا يحتوي ملف على <code dir=\"ltr\">$</code> إطلاقًا)، لأن methods الـ <code dir=\"ltr\">.str</code> لا تعمل إلا على النصوص. ولأن المُدخل عمود من النوع <code dir=\"ltr\">string</code>، تعيد <code dir=\"ltr\">to_numeric</code> النوع <code dir=\"ltr\">Float64</code> القابل للقيم المفقودة الذي رأيته في الدرس 3.1، مع <code dir=\"ltr\">&lt;NA&gt;</code> حيث فشل التحويل. وتعيد الدالة Series جديدة، ويقرّر المستدعي أين يخزّنها، وهو المبدأ نفسه في الإعادة بدلًا من الطباعة من الدرس 1.5.</p>\n<aside class=\"callout callout-why\" 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\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>لماذا يهم هذا</span></p>\n<p>الإيراد هو الرقم الذي يقتبسه الناس في الاجتماعات. وحين يكون خاطئًا لأن علامة دولار نجت أو قيمة باليورو اختفت، لا يلوم أحد pandas؛ بل يتوقّفون عن الثقة بالتحليل. سطرا فحص بعد كل تحويل هما أرخص تأمين في العمل على البيانات.</p>\n</aside>\n<p>في التمرين تبني أداة التحويل وتطبّقها على الملف المُصدَّر. وبعدها يأتي أصعب عمود في الملف: تواريخ مكتوبة بثلاث طرق مختلفة.</p>\n","toc":[{"id":"sec-1","text":"اكتشفها: النوع object في عمود أرقام","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":2}],"takeaways":["العمود الذي يبدو رقميًا ونوعه <code dir=\"ltr\">object</code> هو نص، و<code dir=\"ltr\">sum()</code> عليه تلصق النصوص ببعضها بدلًا من جمع الأرقام.","أزِل رموز العملة وفواصل الآلاف بـ <code dir=\"ltr\">str.replace(..., regex=False)</code>، ثم حوّل بـ <code dir=\"ltr\">astype(float)</code> أو <code dir=\"ltr\">pd.to_numeric</code>.","في التعابير النمطية تعني <code dir=\"ltr\">$</code> “نهاية النص”، فلا تزيل <code dir=\"ltr\">str.replace(&quot;$&quot;, &quot;&quot;, regex=True)</code> شيئًا.","تحوّل <code dir=\"ltr\">pd.to_numeric(errors=&quot;coerce&quot;)</code> القيم التي لا يمكن تحليلها إلى <code dir=\"ltr\">NaN</code>؛ فاسرد دائمًا القيم التي فشلت قبل أن تتابع.","غلّف التنظيف في دالة، وافحص سلامة النتيجة بـ <code dir=\"ltr\">describe()</code> والحد الأدنى والحد الأقصى."],"further":[{"title":"pandas.to_numeric","url":"https://pandas.pydata.org/docs/reference/api/pandas.to_numeric.html"},{"title":"pandas.Series.astype","url":"https://pandas.pydata.org/docs/reference/api/pandas.Series.astype.html"},{"title":"Regular expression syntax (re module)","url":"https://docs.python.org/3/library/re.html#regular-expression-syntax"}],"quiz":[{"q":"<p>نوع <code dir=\"ltr\">messy[&quot;total&quot;]</code> هو <code dir=\"ltr\">object</code>. ماذا تعيد <code dir=\"ltr\">messy[&quot;total&quot;].sum()</code>؟</p>","options":[{"text":"المجموع الصحيح كعدد عشري","why":"تجمع pandas النصوص بـ <code dir=\"ltr\">+</code>، وهذا يصل النصوص ببعضها. لا تحصل على مجموع رقمي إلا بعد التحويل."},{"text":"خطأ <code dir=\"ltr\">TypeError</code>","why":"العامل <code dir=\"ltr\">+</code> معرَّف للنصوص، فتلصقها pandas ببعضها دون أي اعتراض أو خطأ، وهذا ما يجعل هذا الفخ خطيرًا."},{"text":"<code dir=\"ltr\">NaN</code>، لأن بعض القيم فيها <code dir=\"ltr\">$</code>","why":"لا يُحلَّل شيء، فلا يوجد فشل تحويل ينتج <code dir=\"ltr\">NaN</code>."},{"text":"نص طويل واحد فيه كل المجاميع ملتصقة ببعضها","why":"صحيح. النصوص تتّصل ببعضها مع <code dir=\"ltr\">+</code>، فيكون “المجموع” نصًا مثل <code dir=\"ltr\">'$65.0018.0033.00...'</code>."}],"answer":3},{"q":"<p>أيّ سطر يزيل علامات الدولار من قيم بشكل <code dir=\"ltr\">&quot;$65.00&quot;</code>؟</p>","options":[{"text":"<code dir=\"ltr\">s.str.replace(&quot;$&quot;, &quot;&quot;, regex=False)</code>","why":"صحيح. مع <code dir=\"ltr\">regex=False</code> تُطابَق <code dir=\"ltr\">$</code> كحرف عادي. (وهذا أيضًا السلوك الافتراضي لـ <code dir=\"ltr\">str.replace</code> في pandas 2.)"},{"text":"<code dir=\"ltr\">s.str.replace(&quot;$&quot;, &quot;&quot;, regex=True)</code>","why":"كنمط، تطابق <code dir=\"ltr\">$</code> نهاية النص، وهي موضع فارغ، فلا يُزال شيء مرئي."},{"text":"<code dir=\"ltr\">s.str.strip(&quot;$&quot;)</code> ثم <code dir=\"ltr\">astype(int)</code>","why":"حذف <code dir=\"ltr\">$</code> بـ strip يعمل مع العلامات في البداية، لكن <code dir=\"ltr\">astype(int)</code> تفشل مع <code dir=\"ltr\">&quot;65.00&quot;</code> لأن فيها كسورًا عشرية."}],"answer":0},{"q":"<p>أعادت <code dir=\"ltr\">pd.to_numeric(cleaned, errors=&quot;coerce&quot;)</code> أربع قيم <code dir=\"ltr\">NaN</code> لم تكن مفقودة من قبل. ماذا تفعل بعد ذلك؟</p>","options":[{"text":"املأها بـ 0 كي تتطابق المجاميع.","why":"هذا يخفي أربع عمليات بيع حقيقية. اعرف ما هي أولًا."},{"text":"اطبع النصوص الأصلية لتلك الصفوف وقرّر كيف تتعامل مع كل صيغة.","why":"صحيح. الإجبار على التحويل (coercion) وسيلة للعثور على المشكلات، لا لإخفائها. قد تحمل تلك الصفوف علامة يورو أو فاصلة عشرية أوروبية فاتت أداة التنظيف لديك."},{"text":"احذف تلك الصفوف، فهي غير صالحة.","why":"إنها مبيعات صالحة بصيغة لم تتوقّعها. حذفها يقلّل الإيراد عن حقيقته."},{"text":"انتقل إلى <code dir=\"ltr\">errors=&quot;ignore&quot;</code> كي يبقى العمود دون تغيير.","why":"عندها لن يُحوَّل شيء إطلاقًا، وهذا الخيار مُهمَل (deprecated) في إصدارات pandas الحديثة."}],"answer":1}],"exercise":{"kind":"python","files":["orders_messy.csv"],"starter":"import pandas as pd\n\ndef parse_money(values):\n    \"\"\"Convert a Series of text like ' $1,234.50' to floats.\n    Values that cannot be converted become NaN.\"\"\"\n    return values\n\n\nmessy = pd.read_csv(\"orders_messy.csv\")\n\n# 1. Replace messy[\"total\"] with the parsed numbers\n\n# 2. Sum of all totals, rounded to 2 decimals\ntotal_sum = None\n\n# 3. Number of rows with a total above 200\nover_200 = None\n","solution":"import pandas as pd\n\ndef parse_money(values):\n    \"\"\"Convert a Series of text like ' $1,234.50' to floats.\n    Values that cannot be converted become NaN.\"\"\"\n    cleaned = (\n        values.astype(\"string\")\n        .str.strip()\n        .str.replace(\"$\", \"\", regex=False)\n        .str.replace(\",\", \"\", regex=False)\n    )\n    return pd.to_numeric(cleaned, errors=\"coerce\")\n\n\nmessy = pd.read_csv(\"orders_messy.csv\")\n\nmessy[\"total\"] = parse_money(messy[\"total\"])\ntotal_sum = round(float(messy[\"total\"].sum()), 2)\nover_200 = int((messy[\"total\"] > 200).sum())\n","checks":[{"id":"parse-basic","test":"[float(x) for x in parse_money(pd.Series(['$65.00', '18.00', ' $129.00 ']))] == [65.0, 18.0, 129.0]"},{"id":"parse-thousands","test":"float(parse_money(pd.Series(['$1,234.50'])).iloc[0]) == 1234.5"},{"id":"parse-bad","test":"pd.isna(parse_money(pd.Series(['N/A', '$12.00'])).iloc[0])"},{"id":"column-numeric","test":"pd.api.types.is_float_dtype(messy['total']) and int(messy['total'].isna().sum()) == 0"},{"id":"sum","test":"round(float(total_sum), 2) == 23500.67"},{"id":"over-200","test":"int(over_200) == 31"}],"title":"ابنِ أداة تحليل للمبالغ المالية","prompt":"<p>أكمل <code dir=\"ltr\">parse_money(values)</code> بحيث تأخذ Series من نصوص مثل <code dir=\"ltr\">&quot; $1,234.50&quot;</code> أو <code dir=\"ltr\">&quot;$65.00&quot;</code> أو <code dir=\"ltr\">&quot;18.00&quot;</code> وتعيد Series من الأعداد العشرية، وتحوّل أيّ قيمة لا يمكن تحليلها (مثل <code dir=\"ltr\">&quot;N/A&quot;</code>) إلى <code dir=\"ltr\">NaN</code> بدلًا من إطلاق خطأ. ثم استبدل <code dir=\"ltr\">messy[&quot;total&quot;]</code> بالقيم المحلَّلة، واجعل <code dir=\"ltr\">total_sum</code> مجموعها مقرّبًا إلى منزلتين عشريتين، واجعل <code dir=\"ltr\">over_200</code> عدد الصفوف التي مجموعها أكبر من 200.</p>\n","hints":["داخل الدالة: احذف المسافات، وأزِل <code dir=\"ltr\">$</code> و<code dir=\"ltr\">,</code> بـ <code dir=\"ltr\">.str.replace(..., regex=False)</code>، ثم حوّل.","<code dir=\"ltr\">pd.to_numeric(cleaned, errors=&quot;coerce&quot;)</code> تحوّل ما تستطيع وتعيد <code dir=\"ltr\">NaN</code> للبقية.","أسنِد النتيجة بـ <code dir=\"ltr\">messy[&quot;total&quot;] = parse_money(messy[&quot;total&quot;])</code>؛ وعُدّ بـ <code dir=\"ltr\">(messy[&quot;total&quot;] &gt; 200).sum()</code>."],"explanation":"<p>تزيل الدالة الرمزين كنص عادي، وتترك التحويل لـ <code dir=\"ltr\">to_numeric</code> مع <code dir=\"ltr\">errors=&quot;coerce&quot;</code>، فتصبح قيمة شاردة مثل <code dir=\"ltr\">N/A</code> قيمةَ <code dir=\"ltr\">NaN</code> بدلًا من أن تُفشل تحويل العمود كله. في هذا الملف المُصدَّر تُحلَّل كل القيم (لا قيم <code dir=\"ltr\">NaN</code> جديدة)، وهذا ما يؤكّده الفحص <code dir=\"ltr\">column-numeric</code>. و<code dir=\"ltr\">astype(&quot;string&quot;)</code> في البداية اختيارية هنا، لكنها تجعل الدالة آمنة مع عمود قرأته pandas أصلًا كأرقام. والمجموع 23,500.67 ما زال يحسب تسعة صفوف مكرّرة مرتين؛ وستصلح ذلك في الدرس 3.5.</p>\n","checkLabels":{"parse-basic":"<code dir=\"ltr\">parse_money</code> تتعامل مع <code dir=\"ltr\">$</code> والأرقام العادية والمسافات الشاردة","parse-thousands":"<code dir=\"ltr\">parse_money</code> تزيل فواصل الآلاف","parse-bad":"<code dir=\"ltr\">parse_money</code> تحوّل <code dir=\"ltr\">&quot;N/A&quot;</code> إلى <code dir=\"ltr\">NaN</code> بدلًا من إطلاق خطأ","column-numeric":"<code dir=\"ltr\">messy[&quot;total&quot;]</code> عمود أعداد عشرية بلا تحويلات فاشلة","sum":"<code dir=\"ltr\">total_sum</code> يساوي 23,500.67","over-200":"31 صفًا مجموعها أكبر من 200"}},"words":659,"readMinutes":4}