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

الوحدة 3 · تنظيف البيانات الحقيقية

تحويل المبالغ النصية إلى أرقام

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

الدرس في لمحة

حوّل المبالغ المالية المخزّنة كنص، مثل “$1,234.50”، إلى عمود رقمي بحذف الرموز، والتحويل بـ astype أو to_numeric، ثم فحص كل قيمة فشل تحويلها.

  1. 1اكتشفها: النوع object في عمود أرقام
  2. 2أصلحها: احذف الرموز ثم حوّل
  3. 3فخ علامة الدولار
  4. 4حين لا تتحوّل بعض القيم
  5. 5اجعلها دالة

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

اسأل الملف الفوضوي عن إجمالي مبيعاته، فتحصل على شيء غريب. لا خطأ، ولا رقم: نص طويل يبدأ بـ '$65.0018.0033.00...'. يبدو عمود total رقميًا في جدول البيانات، لكن نحو نصف قيمه يبدأ بعلامة دولار، فخزّنت pandas العمود كله كنص، وجمع النصوص يعني لصقها. وصول المبالغ المالية كنصوص من أشيع المشكلات في البيانات المُصدَّرة، وتحتاج إصلاحًا مقصودًا ومفحوصًا.

اكتشفها: النوع object في عمود أرقام

Python
import pandas as pd

messy = pd.read_csv("orders_messy.csv")
print(messy["total"].dtype)
print(messy["total"].head(3).tolist())
print(repr(messy["total"].sum()[:40]))
print(messy["total"].str.startswith("$").sum(), "values start with $")

يلتقط روتين الفحص من الدرس 2.2 هذا فورًا: كانت info() ستعرض total كـ object حيث توقّعت float64. كلما كان عمود يُفترض أن يحمل أرقامًا من النوع object، فهناك قيمة واحدة على الأقل فيها شيء ليس رقمًا. هنا، 76 قيمة من 168 تبدأ بـ $.

أصلحها: احذف الرموز ثم حوّل

أزِل كل حرف ليس جزءًا من الرقم، ثم حوّل النص النظيف:

Python
import pandas as pd

messy = pd.read_csv("orders_messy.csv")
total = (
    messy["total"]
    .str.strip()
    .str.replace("$", "", regex=False)
    .str.replace(",", "", regex=False)
    .astype(float)
)
print(total.dtype, round(total.sum(), 2))
print(total.describe().round(2))

تغليف السلسلة بقوسين يتيح لك وضع خطوة في كل سطر، وهذا أسهل في القراءة وفي تعطيل خطوة بتعليق أثناء تتبّع الأخطاء. وفاصل الآلاف يُزال أيضًا: هذا الملف المُصدَّر تصادف أنه لا يحتوي مجاميع فوق 1,000 دولار، لكن الملف التالي سيحتوي، و"1,234.50" ستكسر astype(float).

describe() هي فحص السلامة. تتراوح المجاميع بين 18 و942.90 دولارًا بوسيط 72، وهذا يطابق كتالوج Cartwheel: أرخص منتج حبل قفز بـ 18 دولارًا، وأكبر بند مكتبان واقفان. أما حد أدنى قدره 0 أو حد أقصى قدره 94,290 فسيخبرك بأن فاصلة عشرية ضاعت.

مجموع 23,500.67 ليس رقم إيراد بعد: الملف المُصدَّر ما زال يحتوي تسعة صفوف مكرّرة. ستزيلها في الدرس 3.5.

فخ علامة الدولار

تستطيع str.replace أيضًا العمل مع التعابير النمطية (regular expressions)، وهي لغة أنماط لبعض الحروف فيها معانٍ خاصة. و$ واحدة منها: تعني “نهاية النص”. انظر ماذا يحدث حين تطلب نمطًا بالخطأ:

Python
import pandas as pd

messy = pd.read_csv("orders_messy.csv")
as_pattern = messy["total"].str.replace("$", "", regex=True)
print(as_pattern.head(1).tolist())   # still '$65.00'
try:
    pd.to_numeric(as_pattern)
except ValueError as error:
    print("ValueError:", error)

طابق النمط $ الموضع الفارغ في نهاية كل نص واستبدله بلا شيء، فنجت كل علامات الدولار. في pandas 2 تكون القيمة الافتراضية لـ str.replace هي regex=False، لكن الدروس التعليمية القديمة وإصدارات pandas الأقدم كانت تفترض الأنماط، وستقابل كودًا مكتوبًا بالطريقتين. كتابة regex=False صراحةً توضّح القصد لكل قارئ ولكل إصدار. وإذا أردت نمطًا فعلًا، فاهرب من الحرف الخاص (escape) أو استخدم فئة حروف: str.replace(r"[$,]", "", regex=True) تزيل الرمزين في خطوة واحدة.

حين لا تتحوّل بعض القيم

astype(float) كل شيء أو لا شيء: قيمة سيئة واحدة ويفشل التحويل كله. أما pd.to_numeric مع errors="coerce" فتحوّل ما تستطيع وتحوّل البقية إلى NaN. هذا مفيد وخطير بالقدر نفسه، لذا يأتي دائمًا مع فحص لاحق:

Python
import pandas as pd

raw = pd.Series(["$1,234.50", "18.00", "N/A", "€89.00", " $65.00 "])
cleaned = raw.str.strip().str.replace("$", "", regex=False).str.replace(",", "", regex=False)
amount = pd.to_numeric(cleaned, errors="coerce")
print(amount)

failed = amount.isna() & raw.notna()
print("Could not convert:", raw[failed].tolist())

يسرد الفحص ما فشل بالضبط: N/A، وهي قيمة مفقودة فعلًا، و€89.00، وهي عملية بيع حقيقية بعملة لم تتوقّعها أداة التنظيف. وهاتان تحتاجان معاملة مختلفة. الأولى تبقى مفقودة؛ والثانية تحتاج قاعدة تحويل، أو على الأقل حديثًا مع من أنتج الملف. الإجبار على التحويل دون نظر كان سيُسقط بهدوء 89 يورو من المبيعات من كل مجموع.

اجعلها دالة

ستنظّف أعمدة مبالغ مالية مرة أخرى، في هذه الدورة وفي عملك. ضع المنطق حيث يمكن إعادة استخدامه واختباره:

Python
import pandas as pd

def parse_money(values):
    """Convert text like ' $1,234.50' to floats; unparseable values become NaN."""
    cleaned = (
        values.astype("string")
        .str.strip()
        .str.replace("$", "", regex=False)
        .str.replace(",", "", regex=False)
    )
    return pd.to_numeric(cleaned, errors="coerce")

messy = pd.read_csv("orders_messy.csv")
messy["total"] = parse_money(messy["total"])
print(messy["total"].dtype, messy["total"].isna().sum(), "unparsed")

تجعل astype("string") في البداية الدالة آمنة مع الأعمدة التي قرأتها pandas أصلًا كأرقام (حين يتصادف ألّا يحتوي ملف على $ إطلاقًا)، لأن methods الـ .str لا تعمل إلا على النصوص. ولأن المُدخل عمود من النوع string، تعيد to_numeric النوع Float64 القابل للقيم المفقودة الذي رأيته في الدرس 3.1، مع <NA> حيث فشل التحويل. وتعيد الدالة Series جديدة، ويقرّر المستدعي أين يخزّنها، وهو المبدأ نفسه في الإعادة بدلًا من الطباعة من الدرس 1.5.

في التمرين تبني أداة التحويل وتطبّقها على الملف المُصدَّر. وبعدها يأتي أصعب عمود في الملف: تواريخ مكتوبة بثلاث طرق مختلفة.

أهم النقاط

  • العمود الذي يبدو رقميًا ونوعه object هو نص، وsum() عليه تلصق النصوص ببعضها بدلًا من جمع الأرقام.
  • أزِل رموز العملة وفواصل الآلاف بـ str.replace(..., regex=False)، ثم حوّل بـ astype(float) أو pd.to_numeric.
  • في التعابير النمطية تعني $ “نهاية النص”، فلا تزيل str.replace("
    quot;, "", regex=True)
    شيئًا.
  • تحوّل pd.to_numeric(errors="coerce") القيم التي لا يمكن تحليلها إلى NaN؛ فاسرد دائمًا القيم التي فشلت قبل أن تتابع.
  • غلّف التنظيف في دالة، وافحص سلامة النتيجة بـ describe() والحد الأدنى والحد الأقصى.

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

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

نوع messy["total"] هو object. ماذا تعيد messy["total"].sum()؟

السؤال 2 من 3

أيّ سطر يزيل علامات الدولار من قيم بشكل "$65.00"؟

السؤال 3 من 3

أعادت pd.to_numeric(cleaned, errors="coerce") أربع قيم NaN لم تكن مفقودة من قبل. ماذا تفعل بعد ذلك؟

تدريب

تمرين Python

ابنِ أداة تحليل للمبالغ المالية

أكمل parse_money(values) بحيث تأخذ Series من نصوص مثل " $1,234.50" أو "$65.00" أو "18.00" وتعيد Series من الأعداد العشرية، وتحوّل أيّ قيمة لا يمكن تحليلها (مثل "N/A") إلى NaN بدلًا من إطلاق خطأ. ثم استبدل messy["total"] بالقيم المحلَّلة، واجعل total_sum مجموعها مقرّبًا إلى منزلتين عشريتين، واجعل over_200 عدد الصفوف التي مجموعها أكبر من 200.

ملفات البيانات: orders_messy.csv

import pandas as pd

def parse_money(values):
    """Convert a Series of text like ' $1,234.50' to floats.
    Values that cannot be converted become NaN."""
    return values


messy = pd.read_csv("orders_messy.csv")

# 1. Replace messy["total"] with the parsed numbers

# 2. Sum of all totals, rounded to 2 decimals
total_sum = None

# 3. Number of rows with a total above 200
over_200 = None
جارٍ تحميل المحرّر…

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

(لا مخرجات)

الفحوص

  • parse_money تتعامل مع $ والأرقام العادية والمسافات الشاردة
  • parse_money تزيل فواصل الآلاف
  • parse_money تحوّل "N/A" إلى NaN بدلًا من إطلاق خطأ
  • messy["total"] عمود أعداد عشرية بلا تحويلات فاشلة
  • total_sum يساوي 23,500.67
  • 31 صفًا مجموعها أكبر من 200