Machine Learning: معالجة البيانات باستخدام Pandas
آخر تحديث: 2026-08-26
Pandas هو سكين الجيش السويسري لعلوم البيانات — التحميل والتنظيف والتحويل والتجميع، كلها في أداة واحدة.
1. ما ستتعلمه
- العمليات الأساسية لـ DataFrame و Series: الإنشاء والفهرسة والاختيار والتصفية
- تنظيف البيانات عمليًا: معالجة القيم المفقودة، وإزالة التكرارات، واكتشاف القيم المتطرفة
- تحويل البيانات: apply/map/transform، وتجميع groupby، و pivot_table
- دمج مصادر بيانات متعددة: merge/join/concat — دمج طلبات Alice من الولايات المتحدة مع طلبات Bob من الصين للتحليل
- معالجة السلاسل الزمنية: تحليل التواريخ وإعادة العينات والنوافذ المتدرجة
2. قصة حقيقية من محلل بيانات
(1) نقطة الألم: بيانات CSV فوضوية، والتنظيف يستهلك 80% من الوقت
حصل Bob على بيانات الطلبات الخام من SalesPredict: من أصل 200 ألف سجل، كانت 5% من المبالغ فارغة، و3% طلبات مكررة، وكانت صيغ معرفات المستخدم غير متسقة. بيانات Alice الأمريكية تستخدم أسماء حقول مختلفة عن بيانات Bob، ومبالغ Charlie باليورو تحتاج تحويل عملة. أصبح تنظيف البيانات أكبر مستهلك للوقت في مشروع ML.
(2) حل Pandas
يوفر Pandas سلسلة أدوات كاملة لتنظيف البيانات — معالجة القيم المفقودة، واكتشاف التكرارات، وتحويل الأنواع، ودمج المصادر المتعددة — مما يحوّل تنظيف البيانات من عمل يدوي إلى كود قابل للتكرار.
import pandas as pd
# تحميل وتنظيف في خط أنابيب
df = pd.read_csv("orders.csv")
df_clean = (df
.drop_duplicates()
.fillna({"amount": df["amount"].median()})
.assign(amount_usd=lambda x: x["amount"] * x["exchange_rate"])
)
print(f"Clean rows: {len(df_clean)}, Original: {len(df)}")
(3) النتيجة: انخفض وقت التنظيف من 3 أيام إلى 30 دقيقة
بعد أن قنّن Bob سير عمل تنظيف البيانات باستخدام Pandas، انخفض ما كان يستغرق 3 أيام من التنظيف اليدوي إلى 30 دقيقة من التنفيذ التلقائي — ويمكن إعادة استخدام نفس خط الأنابيب في كل مرة تصل فيها بيانات جديدة.
3. العمليات الأساسية لـ DataFrame و Series
(1) إنشاء DataFrame
import pandas as pd
import numpy as np
# من قاموس
sales_data = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=5, freq="D"),
"category": ["Electronics", "Clothing", "Food", "Books", "Home"],
"revenue_k_usd": [250, 145, 90, 70, 400],
"orders": [1200, 800, 3000, 500, 600],
})
# من مصفوفة NumPy
arr = np.random.rand(3, 4)
df_from_arr = pd.DataFrame(arr, columns=["A", "B", "C", "D"])
print(sales_data)
print(f"\nShape: {sales_data.shape}")
print(f"Dtypes:\n{sales_data.dtypes}")
▶ مثال: تحميل بيانات SalesPredict الحقيقية
import pandas as pd
# تحميل CSV مع تلميحات النوع
df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])
# نظرة عامة سريعة
print(f"Shape: {df.shape}")
print(f"\nFirst 5 rows:\n{df.head()}")
print(f"\nData types:\n{df.dtypes}")
print(f"\nMemory usage:\n{df.memory_usage(deep=True)}")
Output:
# Executed successfully
(2) الاختيار والتصفية
▶ مثال: طرق متعددة لاختيار البيانات
import pandas as pd
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet", "Monitor", "Keyboard"],
"category": ["Electronics", "Electronics", "Electronics", "Electronics", "Accessories"],
"price": [999, 699, 399, 299, 49],
"stock": [50, 200, 100, 80, 500],
})
# اختيار العمود
prices = df["price"] # Series
subset = df[["product", "price"]] # DataFrame
# اختيار الصفوف باستخدام loc (التسمية) و iloc (الموضع)
row = df.loc[0] # الصف الأول حسب التسمية
rows = df.iloc[1:3] # الصفوف 1-2 حسب الموضع
# تصفية شرطية
expensive = df[df["price"] > 400]
elec_cheap = df[(df["category"] == "Electronics") & (df["price"] < 500)]
print(f"Expensive items:\n{expensive}")
Output:
# Executed successfully
| طريقة الاختيار | الصيغة | حالة الاستخدام |
|---|---|---|
| اختيار العمود | df["col"] / df[["c1","c2"]] |
الحصول على عمود واحد أو أكثر |
| loc | df.loc[row, col] |
الفهرسة حسب التسمية |
| iloc | df.iloc[r, c] |
الفهرسة حسب الموضع |
| التصفية الشرطية | df[df["col"] > val] |
التصفية بشرط |
| query | df.query("price > 400") |
تصفية بأسلوب SQL |
4. تنظيف البيانات عمليًا
تنظيف البيانات في Pandas هو عملية خط أنابيب — كل خطوة تتعامل مع فئة واحدة من المشاكل، فتحوّل تدريجيًا البيانات المتسخة إلى بيانات نظيفة:
graph LR
RAW[بيانات خام<br/>5% مفقود, 3% مكرر] --> DEDUP[إزالة التكرارات<br/>drop_duplicates]
DEDUP --> FILL[ملء القيم المفقودة<br/>fillna median/mode]
FILL --> OUTLIER[إزالة القيم المتطرفة<br/>IQR clipping]
OUTLIER --> CONVERT[تحويل النوع<br/>astype / to_datetime]
CONVERT --> CLEAN[بيانات نظيفة<br/>جاهزة لـ ML]
(1) معالجة القيم المفقودة
▶ مثال: تشخيص ومعالجة القيم المفقودة في SalesPredict
import pandas as pd
import numpy as np
# محاكاة بيانات ذات قيم مفقودة
df = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005, 1006],
"amount_usd": [150, np.nan, 280, np.nan, 95, 320],
"category": ["Electronics", "Clothing", np.nan, "Food", "Books", "Electronics"],
"user_rating": [4.5, 3.8, np.nan, 4.2, np.nan, 5.0],
})
# تشخيص القيم المفقودة
print(f"Missing count:\n{df.isnull().sum()}")
print(f"\nMissing ratio:\n{df.isnull().mean().round(3)}")
# الاستراتيجية 1: حذف الصفوف ذات القيم المفقودة
df_drop = df.dropna()
# الاستراتيجية 2: الملء بالمتوسط/المنوال
df_fill = df.fillna({
"amount_usd": df["amount_usd"].median(),
"category": df["category"].mode()[0],
"user_rating": df["user_rating"].mean(),
})
print(f"\nFilled data:\n{df_fill}")
Output:
# Executed successfully
| الاستراتيجية | الطريقة | حالة الاستخدام | المخاطرة |
|---|---|---|---|
| الحذف | dropna() |
نسبة المفقود < 5% | فقدان المعلومات |
| ملء بالمتوسط | fillna(df["col"].mean()) |
رقمي، طبيعي تقريبًا | يقلل التباين |
| ملء بالوسيط | fillna(df["col"].median()) |
رقمي، ذو قيم متطرفة | تقدير محافظ |
| ملء بالمنوال | fillna(df["col"].mode()[0]) |
فئوي | قد يُبالغ في تمثيل الأغلبية |
| ملء أمامي/خلفي | fillna(method="ffill") |
سلاسل زمنية | ينشر الانحياز |
(2) التكرارات والقيم المتطرفة
▶ مثال: اكتشاف ومعالجة التكرارات والقيم المتطرفة
import pandas as pd
import numpy as np
df = pd.DataFrame({
"order_id": [1001, 1002, 1002, 1003, 1004],
"amount": [150, 280, 280, 95000, 95],
})
# اكتشاف التكرارات
dupes = df.duplicated(subset=["order_id", "amount"])
print(f"Duplicated rows:\n{df[dupes]}")
# إزالة التكرارات
df_clean = df.drop_duplicates(subset=["order_id"], keep="first")
# اكتشاف القيم المتطرفة بطريقة IQR
def detect_outliers_iqr(series, factor=1.5):
q1, q3 = series.quantile([0.25, 0.75])
iqr = q3 - q1
lower, upper = q1 - factor * iqr, q3 + factor * iqr
return (series < lower) | (series > upper)
outlier_mask = detect_outliers_iqr(df_clean["amount"])
print(f"\nOutliers:\n{df_clean[outlier_mask]}")
# تحديد القيم المتطرفة عند المئين 99
cap = df_clean["amount"].quantile(0.99)
df_capped = df_clean.assign(amount=df_clean["amount"].clip(upper=cap))
Output:
# Functions defined successfully
5. تحويل البيانات وتجميعها
(1) apply/map/transform
▶ مثال: تحويل الميزات
import pandas as pd
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet"],
"price_usd": [999, 699, 399],
"cost_usd": [600, 350, 180],
})
# map: تحويل عنصر-بعنصر على Series
df["price_level"] = df["price_usd"].map(
lambda x: "High" if x > 700 else ("Mid" if x > 400 else "Low")
)
# apply: تحويل صف-/عمود-بعنصر
df["margin_pct"] = df.apply(
lambda row: (row["price_usd"] - row["cost_usd"]) / row["price_usd"] * 100,
axis=1
)
# transform: مخرجات بنفس الشكل
df["price_zscore"] = df["price_usd"].transform(
lambda x: (x - x.mean()) / x.std()
)
print(df)
Output:
# Executed successfully
(2) التجميع حسب المجموعات مع groupby
▶ مثال: تجميع مقاييس المبيعات حسب الفئة
import pandas as pd
df = pd.DataFrame({
"category": ["Elec", "Elec", "Cloth", "Cloth", "Food", "Food"],
"month": ["Jan", "Feb", "Jan", "Feb", "Jan", "Feb"],
"revenue_k": [250, 260, 145, 150, 90, 95],
"orders": [1200, 1250, 800, 820, 3000, 3100],
})
# تجميع واحد
cat_revenue = df.groupby("category")["revenue_k"].sum()
print(f"Revenue by category:\n{cat_revenue}")
# تجميعات متعددة
cat_stats = df.groupby("category").agg({
"revenue_k": ["sum", "mean", "std"],
"orders": ["sum", "mean"],
})
print(f"\nCategory stats:\n{cat_stats}")
# تجميعات مسماة
cat_named = df.groupby("category").agg(
total_revenue=("revenue_k", "sum"),
avg_revenue=("revenue_k", "mean"),
total_orders=("orders", "sum"),
)
Output:
# Executed successfully
(3) الجداول المحورية مع pivot_table
▶ مثال: جدول محوري للمبيعات الشهرية حسب الفئة
import pandas as pd
df = pd.DataFrame({
"category": ["Elec"]*3 + ["Cloth"]*3 + ["Food"]*3,
"month": ["Jan", "Feb", "Mar"]*3,
"revenue_k": [250, 260, 270, 145, 150, 155, 90, 95, 100],
})
# Pivot: الفئات كصفوف، الأشهر كأعمدة
pivot = df.pivot_table(
values="revenue_k",
index="category",
columns="month",
aggfunc="sum",
margins=True, # إضافة مجاميع الصفوف/الأعمدة
)
print(pivot)
Output:
# Executed successfully
| البُعد | groupby | pivot_table |
|---|---|---|
| شكل المخرجات | طويل | عريض |
| مقاييس متعددة | ✅ تجميع على أعمدة متعددة | ✅ قيم على أعمدة متعددة |
| المجاميع الفرعية | ❌ يتطلب عملًا يدويًا | ✅ margins=True |
| المرونة | عالية (أي تجميع) | متوسطة (aggfunc ثابت) |
6. دمج مصادر بيانات متعددة والسلاسل الزمنية
(1) عمليات الدمج
▶ مثال: دمج بيانات Alice من الولايات المتحدة + بيانات Bob من الصين
import pandas as pd
# طلبات Alice من الولايات المتحدة
us_orders = pd.DataFrame({
"order_id": ["US001", "US002", "US003"],
"amount_usd": [150, 280, 95],
"product": ["Laptop", "Phone", "Book"],
})
# طلبات Bob من الصين (تحتاج تحويل العملة)
cn_orders = pd.DataFrame({
"order_id": ["CN001", "CN002"],
"amount_cny": [1200, 3500],
"product": ["Phone", "Laptop"],
})
# Concat عموديًا (إضافة صفوف)
cn_orders_usd = cn_orders.assign(
amount_usd=cn_orders["amount_cny"] * 0.14 # CNY إلى USD
).drop(columns=["amount_cny"])
all_orders = pd.concat([us_orders, cn_orders_usd], ignore_index=True)
print(f"Combined orders:\n{all_orders}")
# الدمج مع كتالوج المنتجات
catalog = pd.DataFrame({
"product": ["Laptop", "Phone", "Book", "Tablet"],
"category": ["Electronics", "Electronics", "Books", "Electronics"],
"margin_pct": [35, 45, 20, 30],
})
enriched = all_orders.merge(catalog, on="product", how="left")
print(f"\nEnriched orders:\n{enriched}")
Output:
# Executed successfully
| نوع الدمج | الصيغة | مكافئ SQL | الوصف |
|---|---|---|---|
| داخلي | merge(how="inner") |
INNER JOIN | التقاطع |
| يسار | merge(how="left") |
LEFT JOIN | الاحتفاظ بجميع صفوف الجدول الأيسر |
| خارجي | merge(how="outer") |
FULL JOIN | الاتحاد |
| Concat | concat(axis=0) |
UNION | تكديس الصفوف عموديًا |
| Concat | concat(axis=1) |
— | تكديس الأعمدة جنبًا إلى جنب |
(2) معالجة السلاسل الزمنية
▶ مثال: إعادة العينات والإحصائيات المتدرجة على بيانات SalesPredict اليومية
import pandas as pd
import numpy as np
# بيانات المبيعات اليومية
rng = pd.date_range("2024-01-01", periods=90, freq="D")
daily = pd.DataFrame({
"date": rng,
"revenue": np.random.normal(5000, 1000, 90).cumsum(),
}, index=rng)
# إعادة العينة إلى شهرية
monthly = daily["revenue"].resample("M").agg(["first", "last", "mean", "sum"])
print(f"Monthly stats:\n{monthly}")
# نافذة متدرجة: متوسط متحرك 7 أيام
daily["ma_7d"] = daily["revenue"].rolling(window=7).mean()
daily["ma_30d"] = daily["revenue"].rolling(window=30).mean()
# التغيير النسبي
daily["pct_change"] = daily["revenue"].pct_change()
print(f"\nLast 5 rows with rolling stats:\n{daily.tail()}")
Output:
# Executed successfully
❓ أسئلة شائعة
.copy() لإنشاء نسخة بشكل صريح، أو استبدل العمليات المتسلسلة بتعيين .loc[] واحد. على سبيل المثال، استخدم df.loc[mask, "col"] = value بدلاً من df[mask]["col"] = value.as_index=False: df.groupby("category", as_index=False).agg(...)، أو استدعِ .reset_index() بعد الـ groupby.df.duplicated(subset=key).sum() للتحقق. إذا كانت هناك تكرارات، حدد استراتيجية الاحتفاظ أولًا (إزالة التكرارات أو التجميع) قبل الدمج.chunksize للقراءة على دفعات؛ 3) استخدم مكتبة Dask لمجموعات البيانات الكبيرة جدًا.df.asfreq("D") لضمان تردد منتظم، أو استخدم df.resample("D").asfreq(). تحتاج السلاسل الزمنية غير المنتظمة إلى إعادة عينة قبل التحليل.📖 ملخص
- DataFrame هو بنية بيانات جدولية ثنائية الأبعاد و Series هو مصفوفة أحادية البُعد؛ ويشكلان معًا جوهر Pandas
- ثلاث استراتيجيات رئيسية للقيم المفقودة: الحذف (dropna)، والملء (fillna)، والاستيفاء (interpolate) — اختر حسب الموقف
- يتعامل groupby + agg مع التجميع المجمع، بينما ينتج pivot_table محاور عريضة؛ لكل منها حالة استخدام خاصة
- يؤدي merge عمليات دمج الجداول بأسلوب SQL، ويقوم concat بتكديس الصفوف/الأعمدة — انتبه لاختيار المعامل how
- معالجة السلاسل الزمنية: parse_dates لتحليل التواريخ، و resample لإعادة العينات، و rolling للإحصائيات المتدرجة، و pct_change لحساب معدلات التغير
📝 تمارين
- أساسي (الصعوبة ⭐): حمّل ملف CSV، واستخدم
info()وdescribe()للحصول على نظرة عامة، واحسب عدد القيم المفقودة. تلميح:df.isnull().sum(). - متوسط (الصعوبة ⭐⭐): أنشئ DataFrame (مبيعات Alice الأمريكية ومبيعات Charlie الأوروبية)، وادمجهما حسب المنتج باستخدام merge، واحسب إجمالي المبيعات العالمية لكل منتج. تلميح: وحّد وحدة المبلغ إلى USD أولًا، ثم ادمج.
- تحدي (الصعوبة ⭐⭐⭐): على بيانات المبيعات اليومية، نفّذ: 1) إعادة عينة إلى أسبوعية؛ 2) حساب متوسط متحرك 4 أسابيع؛ 3) اكتشاف القيم المتطرفة (النقاط التي تنحرف أكثر من 2 انحراف معياري عن المتوسط المتدرج). تلميح:
resample("W")+rolling(4)+ boolean indexing.
← الدرس السابق: دورة مكثفة في NumPy | الدرس التالي: التصور باستخدام Matplotlib و Seaborn →