بايثون لعلوم البيانات: الخطوات التسع الرئيسية للانتقال من الصفر إلى التشغيل

بايثون لعلوم البيانات: الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة مكونة من 36 درسًا.

بايثون لعلوم البيانات: الخطوات التسع الرئيسية للانتقال من الصفر إلى التشغيل

يمكن للجميع تعلم Python Data Science — بشرط اتباع الخطوات بالترتيب الصحيح. لقد لخصنا دورة كاملة من 36 درسًا في مسار واضح، مع أكثر مقتطفات الكود فائدة.

tl;dr
  • مقدمة وتثبيت
  • NumPy الأساسي
  • Pandas Series و DataFrames
  • قراءة وكتابة البيانات
  • تنظيف البيانات
~$ cat ./parcours.md # Python Data Science — 10 فصول
01
مقدمة وتثبيت
→ عرض الدورة→ تثبيت بيئة علم البيانات+ 1 دروس أخرى
02
NumPy الأساسي
→ مصفوفات NumPy مقابل قوائم Python→ عمليات متجهية+ 1 دروس أخرى
03
Pandas Series و DataFrames
→ إنشاء Series→ إنشاء DataFrame+ 2 دروس أخرى
04
قراءة وكتابة البيانات
→ CSV باستخدام read_csv و to_csv→ Excel و JSON و Parquet+ 1 دروس أخرى
05
تنظيف البيانات
→ القيم المفقودة (NaN)→ التكرارات والتناقضات+ 1 دروس أخرى
06
التجميعات و groupby
→ groupby الأساسي→ agg على أعمدة متعددة+ 1 دروس أخرى
07
الربط والدمج
→ merge كما في SQL→ concat مقابل merge+ 1 دروس أخرى
08
تصورات سريعة
→ رسم Pandas مباشر→ Matplotlib لعلم البيانات+ 1 دروس أخرى
🏁
المشروع النهائي (+ 2 فصول في الطريق)
→ تعود بمشروع ملموس وقابل للعرض

التكرارات والتناقضات

NOTEالهدف — تعلم كيفية اكتشاف وإزالة التكرارات الدقيقة أو التقريبية، وتوحيد سلاسل الأحرف (الأحرف الكبيرة، المسافات، الحروف المشكولة) التي تُفسد تحليلاتك بصمت.

الأهداف التعليمية

TIPعند نهاية هذه الوحدة — ستتمكن من اكتشاف الصفوف المكررة، وتحديد أي نسخة تحتفظ بها، وتطبيع أعمدة النص، وتصحيح التناقضات الشهيرة مثل « Paris »، « paris »، « PARIS ».

لماذا التكرارات خطيرة

التكرار هو نفس الواقع محسوب مرتين. النتائج: إيرادات مبالغ فيها، متوسطات متحيزة، نماذج مفرطة التدريب. قد يأتي التكرار من استيراد مكرر، أو ربط سيئ، أو نموذج تم إرساله مرتين…

WARNINGقصة حقيقية — كانت شركة تحسب إيراداتها من ملف Excel يُدمج كل شهر… دون إزالة التكرارات. كانت إيراداتها مبالغًا فيها بنسبة 12 %% منذ عامين.

اكتشاف التكرارات

output
import pandas as pd

df = pd.DataFrame({
    "id": [1, 2, 3, 2, 4, 1],
    "nom": ["Alice", "Bob", "Chloe", "Bob", "David", "Alice"],
    "ville": ["Paris", "Lyon", "Nice", "Lyon", "Lille", "Paris"]
})

# قناع منطقي: True إذا كان الصف مكررًا
df.duplicated()

# كم عدد التكرارات الإجمالي؟
df.duplicated().sum()

# عرض التكرارات فقط
df[df.duplicated()]

# التكرارات بناءً على أعمدة معينة فقط
df.duplicated(subset=["id"])
df.duplicated(subset=["nom", "ville"])
NOTEافتراضيًاduplicated() يضع علامة True بدءًا من الثانية مرة. يُعتبر النسخة الأولى هي الأصلية.

حذف التكرارات

output
# الاحتفاظ بالظهور الأول (افتراضي)
df.drop_duplicates()

# الاحتفاظ بالظهور الأخير
df.drop_duplicates(keep="last")

# حذف جميع التكرارات
df.drop_duplicates(keep=False)

# على أعمدة معينة فقط (مفيد إذا كان أحد الأعمدة طابعًا زمنيًا)
df.drop_duplicates(subset=["id"])

# Inplace لتعديل DataFrame مباشرة
df.drop_duplicates(inplace=True)
TIPاختيار keep"first" للاحتفاظ بالسجل الأقدم، "last" للأحدث (غالبًا الخيار الجيد إذا تم تحديث البيانات)، False لحذف الكل والتحقيق.

فخ التكرارات التقريبية

« Paris » و « paris » مختلفان بالنسبة لـ Pandas، حتى لو كانا نفس المدينة بالنسبة لك. قبل البحث عن التكرارات، طبّع.

output
df = pd.DataFrame({
    "email": ["Alice@MAIL.com", " alice@mail.com ", "bob@mail.com"],
    "ville": ["Paris", "PARIS", "Lyon"]
})

# تطبيع الإيميلات (مسافات + أحرف صغيرة)
df["email"] = df["email"].str.strip().str.lower()
df["ville"] = df["ville"].str.strip().str.title()

# الآن يمكن اكتشاف التكرارات الحقيقية
df.duplicated(subset=["email"])
print(df)

صندوق أدوات .str

يمنحك الوصول .str جميع طرق سلاسل Python، موجهة:

output
s = pd.Series(["  Alice DUPONT  ", "bob martin", "CHLOE.LEROY"])

s.str.strip()                  # إزالة المسافات
s.str.lower()                  # أحرف صغيرة
s.str.upper()                  # أحرف كبيرة
s.str.title()                  # كل كلمة تبدأ بحرف كبير
s.str.replace(".", " ")      # استبدال
s.str.contains("alice", case=False)  # تصفية
s.str.startswith("A")
s.str.len()                    # عدد الأحرف
s.str.split(" ", expand=True)  # فصل إلى أعمدة
NOTEنصيحة الحروف المشكولة — لتطبيع الحروف المشكولة (é → e)، استخدم unicodedata أو مكتبة unidecode : df["ville"].apply(unidecode).

التناقضات الفئوية

غالبًا ما تُدخل نفس الفئة بطرق مختلفة: « H »، « Homme »، « M »، « Masculin »… يراها Pandas كـ 4 فئات مختلفة.

output
df = pd.DataFrame({
    "sexe": ["H", "Homme", "M", "Masculin", "F", "Femme", "f"]
})

# قبل: عرض القيم الفريدة
print(df["sexe"].value_counts())

# الاستراتيجية: قاموس تعيين
mapping = {
    "H": "Homme", "M": "Homme", "Masculin": "Homme",
    "F": "Femme", "f": "Femme", "Femme": "Femme"
}
df["sexe"] = df["sexe"].map(mapping)

print(df["sexe"].value_counts())
TIPرد الفعل الذهبي — قبل أي تحليل فئوي، نفذ df["col"].value_counts(). ستظهر التناقضات فورًا.

مشروع مصغر: تنظيف قاعدة موظفين

output
import pandas as pd

df = pd.DataFrame({
    "id":    [1, 2, 3, 2, 4, 5],
    "nom":   ["Alice", " Bob ", "Chloe", "BOB", "David", "emma"],
    "poste": ["Dev", "DEV", "PM", "dev", "Pm", "Designer"],
    "ville": ["Paris", "paris", "Lyon", "PARIS", "Lille", "Lyon"]
})

# 1. تطبيع السلاسل
for col in ["nom", "poste", "ville"]:
    df[col] = df[col].str.strip().str.title()

# 2. اكتشاف التكرارات على id
print("التكرارات :", df.duplicated(subset=["id"]).sum())

# 3. الاحتفاظ بالسجل الأخير (الأحدث)
df = df.drop_duplicates(subset=["id"], keep="last")

# 4. التحقق
print(df)
print(df["poste"].value_counts())

القيم المفقودة (NaN)

NOTEالهدف — فهم ما هي القيمة المفقودة في Pandas، وكيفية اكتشافها، وتحليلها، واختيار الاستراتيجية المناسبة: حذف أو تعويض (ملء). لا يتم أي تحليل نظيف بدون هذه الخطوة.

الأهداف التعليمية

TIPعند نهاية هذه الوحدة — ستتمكن من عد NaN لكل عمود، وحذف الصفوف غير المكتملة، أو ملئها بذكاء (المتوسط، الوسيط، قيمة ثابتة، الانتشار). وستعرف أيضًا متى تكون كل استراتيجية مناسبة.

ما هو NaN؟

NaN = « Not a Number ». هذه هي الطريقة التي يمثل بها Pandas (الموروث من NumPy) قيمة مفقودة في عمود رقمي. للتواريخ، يكون NaT؛ وللكائنات العامة، None. يعالجها Pandas الثلاثة بشكل موحد عبر isnull().

output
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "nom": ["Alice", "Bob", None, "David"],
    "age": [30, np.nan, 25, 35],
    "date": pd.to_datetime(["2025-01-01", None, "2025-01-03", "2025-01-04"])
})
print(df)
WARNINGالفخ الكلاسيكيNaN != NaN في Python النقي (وهذا مقصود). لذا x == np.nan يكون خطأ دائمًا. استخدم دائمًا isnull() أو isna() للاختبار.

اكتشاف القيم المفقودة

output
# قناع منطقي خلية بخلية
df.isnull()                # True إذا كان NaN
df.notna()                 # العكس

# العد حسب العمود
df.isnull().sum()

# نسبة NaN لكل عمود
(df.isnull().sum() / len(df) * 100).round(2)

# الإجمالي في كل DataFrame
df.isnull().sum().sum()

# الصفوف التي تحتوي على NaN واحد على الأقل
df[df.isnull().any(axis=1)]
TIPرد الفعل الذهبي — بمجرد وصول مجموعة بيانات، نفذ df.isnull().sum(). هذا في ثانيتين لوحة تحكم جودة بياناتك.

الاستراتيجية 1 — الحذف (dropna)

عندما تكون NaN نادرة أو يتطلب التحليل بيانات كاملة:

output
# حذف أي صف يحتوي على NaN واحد على الأقل
df.dropna()

# حذف فقط إذا كانت كل القيم NaN
df.dropna(how="all")

# حذف إذا كان NaN في عمود محدد
df.dropna(subset=["age"])

# الاحتفاظ بصف إذا كان لديه 3 قيم غير NaN على الأقل
df.dropna(thresh=3)

# حذف عمود كامل غير مكتمل جدًا
df.dropna(axis=1, thresh=100)   # الأعمدة < 100 قيمة محذوفة
WARNINGمتى تتجنب dropna؟ — على مجموعة بيانات من 100 000 صف حيث كل عمود لديه نسبة قليلة من NaN، قد يحذف dropna كل شيء! تحقق قبل ذلك: df.dropna().shape vs df.shape.

الاستراتيجية 2 — الملء (fillna)

بقيمة ثابتة

output
df["age"].fillna(0)
df["ville"].fillna("Inconnu")

# على كل DataFrame بقاموس لكل عمود
df.fillna({"age": 0, "ville": "Inconnu", "salaire": 2000})

بإحصائية من العمود

output
df["age"].fillna(df["age"].mean())     # المتوسط
df["age"].fillna(df["age"].median())   # الوسيط (قوي)
df["ville"].fillna(df["ville"].mode()[0])  # القيمة الأكثر تكرارًا

بالانتشار (مفيد للسلاسل الزمنية)

output
# الملء بالقيمة السابقة (forward fill)
df["prix"].ffill()

# بالقيمة التالية (backward fill)
df["prix"].bfill()

# تحديد عدد الملءات المتتالية
df["prix"].ffill(limit=3)

أنواع البيانات والتحويل

NOTEالهدف — فهم أنواع Pandas (int, float, object, datetime, category, bool)، ولماذا تهم كثيرًا، وكيفية التحويل بشكل نظيف دون تعطل على قيم غير نظيفة.

الأهداف التعليمية

TIPعند نهاية هذه الوحدة — ستتمكن من فحص dtypes، والتحويل إلى رقم/تاريخ/فئة، واستخدام errors="coerce" للقيم غير النظيفة، وتقليل بصمة الذاكرة لـ DataFrame بتحويل objectcategory.

لماذا يهم النوع

الـ « 42 » المخزن كنص (object) لا يمكن جمعه، ولا مقارنته رقميًا. التاريخ المخزن كنص لا يسمح بـ .dt.year. نوع سيء = أخطاء صامتة أو فقدان وظائف.

output
import pandas as pd

df = pd.DataFrame({
    "prix": ["12.50", "8.99", "15.00"],
    "date": ["2025-01-01", "2025-02-15", "2025-03-10"]
})

print(df.dtypes)
# prix    object  <-- نص!
# date    object  <-- نص!

df["prix"].sum()   # يربط « 12.508.9915.00 » !
WARNINGالفخ الكلاسيكيobject = « أي شيء »، غالبًا نص. إذا رأيت object على عمود من المفترض أن يكون رقميًا، فهناك مشكلة يجب حلها قبل أي تحليل.

أنواع Pandas باختصار

Dtypeالاستخداممثال
int64عدد صحيحالأعمار، الكميات
float64عدد حقيقيالأسعار، درجات الحرارة
boolصح/خطأنشط، مدفوع
objectعام (غالبًا str)الأسماء، الإيميلات
datetime64[ns]تاريخ/وقتالطلب، الميلاد
timedelta64مدةالفرق بين تاريخين
categoryفئات مكررةالجنس، المدينة، الحالة

التحويل بـ astype

output
# تحويل بسيط، يفشل إذا لم تناسب قيمة
df["age"] = df["age"].astype(int)
df["prix"] = df["prix"].astype(float)
df["actif"] = df["actif"].astype(bool)
df["sexe"] = df["sexe"].astype("category")

# على عدة أعمدة في وقت واحد
df = df.astype({"age": int, "prix": float, "ville": "category"})
WARNINGالحدastype(int) يتعطل فورًا إذا لم تتحول قيمة (مثل: "12 EUR"). للتعامل مع هذه الحالات، استخدم pd.to_numeric.

pd.to_numeric — المتسامح

output
s = pd.Series(["12.5", "8.99", "oops", "15"])

# الوضع الصارم: يتعطل إذا كانت قيمة غير رقمية
pd.to_numeric(s)   # ValueError

# الوضع المتسامح: القيم غير النظيفة -> NaN
pd.to_numeric(s, errors="coerce")
# 0    12.50
# 1     8.99
# 2      NaN  <-- "oops" يصبح NaN
# 3    15.00

# اختيار النوع الفرعي لتوفير الذاكرة
pd.to_numeric(s, errors="coerce", downcast="float")
pd.to_numeric(s, errors="coerce", downcast="integer")
TIPسير عمل نظيفerrors="coerce" → عد NaN الناتجة → قرر ما تفعل (تعويض، حذف، إبلاغ).

pd.to_datetime — كل التواريخ

output
# يتعرف على معظم التنسيقات تلقائيًا
pd.to_datetime(["2025-01-15", "15/01/2025", "Jan 15, 2025"])

# تنسيق صريح (أسرع وأأمن)
pd.to_datetime(df["date"], format="%%d/%%m/%%Y")

# متسامح مع القيم غير النظيفة
pd.to_datetime(df["date"], errors="coerce")

# بمجرد أن يصبح datetime، يمكن الوصول إلى المكونات
df["date"] = pd.to_datetime(df["date"])
df["annee"]    = df["date"].dt.year
df["mois"]     = df["date"].dt.month
df["jour_sem"] = df["date"].dt.day_name()
NOTEالتنسيق الصريح — على كميات كبيرة، تحديد format= قد يضاعف السرعة 100 مرة. لم يعد Pandas بحاجة للتخمين.

النوع category : سحر الذاكرة

لعمود « ville » مكرر مليون مرة، تخزين كل سلسلة يهدر المساحة. category يخزن كل قيمة فريدة مرة واحدة، ويشير إلى فهرس عدد صحيح.

output
import pandas as pd

df = pd.DataFrame({
    "ville": ["Paris", "Lyon", "Paris"] * 1_000_000
})

print(df.memory_usage(deep=True).sum() / 1e6, "Mo")
# حوالي 200 Mo

df["ville"] = df["ville"].astype("category")
print(df.memory_usage(deep=True).sum() / 1e6, "Mo")
# حوالي 8 Mo -- توفير x25 !
TIPمتى تستخدم category؟ — عندما يكون عدد القيم الفريدة صغيرًا مقارنة بعدد الصفوف الإجمالي (عادة < 5 %%). وإلا، فائدة قليلة.

مشروع مصغر: تنظيف تصدير محاسبي

output
import pandas as pd

df = pd.DataFrame({
    "montant":    ["125,50", "99,00", "N/D", "42,75"],
    "date":       ["01/03/2025", "02/03/2025", "oops", "04/03/2025"],
    "categorie":  ["Achat", "Vente", "Achat", "Vente"],
    "paye":       ["Oui", "Non", "Oui", "Oui"]
})

# 1. المبلغ: فاصلة -> نقطة، ثم رقمي
df["montant"] = df["montant"].str.replace(","span>, ".")
df["montant"] = pd.to_numeric(df["montant"], errors="coerce")

# 2. التاريخ بتنسيق يوم/شهر/سنة
df["date"] = pd.to_datetime(df["date"], format="%%d/%%m/%%Y", errors="coerce")

# 3. الفئة -> نوع category
df["categorie"] = df["categorie"].astype("category")

# 4. Oui/Non -> bool
df["paye"] = df["paye"].map({"Oui": True, "Non": False})

print(df.dtypes)
print(df)
va-plus-loin

يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة Python Data Science (11 فصول، 36 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.

./acceder-au-cours-complet cours gratuit : Maîtriser Claude Code

الأسئلة الشائعة

كم من الوقت لتعلم Python Data Science؟
مع تقدم منظم (11 فصول، 36 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في أسابيع قليلة بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو تطبيق كل مفهوم فورًا.
هل هناك متطلبات مسبقة؟
تكفي أساسيات في الحوسبة. إذا كنت تعرف استخدام الطرفية وقراءة كود بسيط، فأنت جاهز.
من أين أبدأ عمليًا؟
أعد إنتاج أوامر هذا المقال، ثم تابع الدورة الكاملة Python Data Science: تربط الـ 36 درسًا بالترتيب، مع تمارين ومشروع نهائي.

📬 Tu veux recevoir ce type de guide chaque semaine ? Abonne-toi gratuitement — code réel, zéro blabla.