مشروع مصغر: تنظيف وتحليل ملف يحتوي على 10,000 سجل مبيعات وتلخيص الأرباح
ماذا سنتعلم اليوم؟ سنتعلم كيفية التعامل مع مجموعة بيانات مبيعات كبيرة نسبياً (10,000 سجل)، بدءاً من تحميلها وتنظيفها، وصولاً إلى تحليلها وتلخيص الأرباح باستخدام مكتبات Python القوية مثل Pandas و Scikit-Learn.
هذا الدرس سيزودك بالمهارات الأساسية لمعالجة البيانات الحقيقية واستخلاص رؤى قيمة منها.
الخطوة 1: إعداد البيانات وتحميلها
في هذه الخطوة، سنقوم بإنشاء ملف CSV وهمي يحتوي على 10,000 سجل مبيعات لغرض المحاكاة، ثم نقوم بتحميله إلى DataFrame باستخدام مكتبة Pandas. سنلقي نظرة أولية على بنية البيانات.
ملاحظة تقنية: في المشاريع الحقيقية، ستقوم بقراءة البيانات من مصادر موجودة مثل قواعد البيانات، ملفات CSV/Excel، أو واجهات برمجة التطبيقات (APIs). هنا، نولدها لضمان بيئة عمل متكاملة.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
import os
# اسم الملف الذي سنقوم بإنشائه
file_name = 'sales_data_10k.csv'
# التحقق مما إذا كان الملف موجودًا بالفعل لتجنب إعادة الإنشاء
if not os.path.exists(file_name):
print("جاري إنشاء ملف بيانات المبيعات الوهمي...")
# إنشاء بيانات وهمية
np.random.seed(42) # لضمان إمكانية تكرار النتائج
num_records = 10000
dates = pd.to_datetime(pd.date_range(start='2022-01-01', periods=num_records, freq='H')) # تواريخ المبيعات
products = ['Laptop', 'Mouse', 'Keyboard', 'Monitor', 'Webcam', 'Headphones', 'Speaker', 'Printer']
categories = ['Electronics', 'Accessories', 'Peripherals']
regions = ['North', 'South', 'East', 'West', 'Central']
data = {
'OrderID': range(1, num_records + 1),
'Date': np.random.choice(dates, num_records), # اختيار تواريخ عشوائية من النطاق
'Product': np.random.choice(products, num_records),
'Category': np.random.choice(categories, num_records),
'Price': np.round(np.random.uniform(10, 1500, num_records), 2), # أسعار المنتجات
'Cost': np.round(np.random.uniform(5, 1000, num_records), 2), # تكلفة المنتجات
'Quantity': np.random.randint(1, 10, num_records), # كمية المبيعات
'Region': np.random.choice(regions, num_records)
}
df_raw = pd.DataFrame(data)
# إدخال بعض القيم المفقودة عن قصد لاختبار التنظيف
for col in ['Price', 'Quantity', 'Category']:
missing_indices = np.random.choice(df_raw.index, int(num_records * 0.01), replace=False) # 1% قيم مفقودة
df_raw.loc[missing_indices, col] = np.nan
# حفظ البيانات في ملف CSV
df_raw.to_csv(file_name, index=False)
print(f"تم إنشاء الملف '{file_name}' بنجاح.")
else:
print(f"الملف '{file_name}' موجود بالفعل. تخطي إنشاء الملف.")
# تحميل البيانات من ملف CSV
print("\nجاري تحميل البيانات...")
df = pd.read_csv(file_name)
# عرض أول 5 صفوف من البيانات
print("أول 5 صفوف من البيانات:")
print(df.head())
# عرض معلومات موجزة عن DataFrame (أنواع البيانات والقيم غير المفقودة)
print("\nمعلومات موجزة عن DataFrame:")
print(df.info())
الخطوة 2: تنظيف البيانات ومعالجتها
تتضمن هذه الخطوة معالجة القيم المفقودة، تحويل أنواع البيانات إلى التنسيقات الصحيحة، وإزالة أي سجلات مكررة. سنقوم أيضاً بحساب عمود "الربح" (Profit) الذي يعتبر أساس تحليلنا.
ملاحظة تقنية: تنظيف البيانات هو أحد أهم المراحل في أي مشروع تحليل بيانات. البيانات النظيفة تضمن دقة التحليلات والنتائج المستخلصة.
# التحقق من القيم المفقودة قبل التنظيف
print("\nالقيم المفقودة قبل التنظيف:")
print(df.isnull().sum())
# معالجة القيم المفقودة:
# في هذا المثال، سنقوم بإسقاط الصفوف التي تحتوي على قيم مفقودة في الأعمدة الأساسية
# يمكن استخدام استراتيجيات أخرى مثل الملء بالمتوسط أو الوسيط حسب طبيعة البيانات
df.dropna(subset=['Price', 'Quantity', 'Category'], inplace=True) # إزالة الصفوف التي بها قيم مفقودة في هذه الأعمدة
# تحويل أنواع البيانات:
# تحويل عمود 'Date' إلى نوع DateTime
df['Date'] = pd.to_datetime(df['Date'])
# التأكد من أن 'Price', 'Cost', 'Quantity' هي أرقام (float/int)
df['Price'] = pd.to_numeric(df['Price'])
df['Cost'] = pd.to_numeric(df['Cost'])
df['Quantity'] = pd.to_numeric(df['Quantity'])
# إزالة السجلات المكررة بناءً على 'OrderID' (إذا كان فريدًا) أو جميع الأعمدة
initial_rows = len(df)
df.drop_duplicates(inplace=True) # إزالة الصفوف المكررة بالكامل
rows_after_duplicates = len(df)
if initial_rows > rows_after_duplicates:
print(f"\nتم إزالة {initial_rows - rows_after_duplicates} صفوف مكررة.")
# حساب عمود الربح (Profit)
# الربح = (السعر - التكلفة) * الكمية
df['Profit'] = (df['Price'] - df['Cost']) * df['Quantity']
# عرض معلومات موجزة عن DataFrame بعد التنظيف
print("\nمعلومات موجزة عن DataFrame بعد التنظيف:")
print(df.info())
# عرض القيم المفقودة بعد التنظيف للتأكد
print("\nالقيم المفقودة بعد التنظيف:")
print(df.isnull().sum())
# عرض أول 5 صفوف بعد إضافة عمود الربح
print("\nأول 5 صفوف بعد إضافة عمود الربح:")
print(df.head())
الخطوة 3: تحليل البيانات وتلخيص الأرباح
بعد تنظيف البيانات، حان وقت تحليلها. سنقوم بتلخيص إجمالي الأرباح، تحديد المنتجات الأكثر ربحية، وتحليل الأرباح حسب المنطقة. سنستخدم أيضاً Scikit-Learn لتوضيح كيفية تطبيق تحويل بسيط على البيانات مثل Scaler، والذي يمكن أن يكون مفيداً للنماذج المستقبلية.
ملاحظة تقنية: مكتبة Scikit-Learn تستخدم بشكل أساسي للتعلم الآلي. في هذا المشروع الصغير، سنستخدمها لتوضيح خطوة معالجة مسبقة بسيطة (Feature Scaling) يمكن أن تكون جزءاً من عملية بناء نموذج أكبر.
# 1. إجمالي الأرباح
total_profit = df['Profit'].sum()
print(f"\nإجمالي الأرباح من جميع المبيعات: {total_profit:,.2f} وحدة عملة")
# 2. متوسط الأرباح لكل عملية بيع
average_profit_per_sale = df['Profit'].mean()
print(f"متوسط الربح لكل عملية بيع: {average_profit_per_sale:,.2f} وحدة عملة")
# 3. المنتجات الأكثر ربحية
print("\nأكثر 5 منتجات ربحية:")
top_products = df.groupby('Product')['Profit'].sum().sort_values(ascending=False).head(5)
print(top_products)
# 4. الأرباح حسب الفئة
print("\nإجمالي الأرباح حسب الفئة:")
profit_by_category = df.groupby('Category')['Profit'].sum().sort_values(ascending=False)
print(profit_by_category)
# 5. الأرباح حسب المنطقة
print("\nإجمالي الأرباح حسب المنطقة:")
profit_by_region = df.groupby('Region')['Profit'].sum().sort_values(ascending=False)
print(profit_by_region)
# 6. وصف إحصائي لعمود الربح
print("\nوصف إحصائي لعمود الربح:")
print(df['Profit'].describe())
# 7. مثال على استخدام Scikit-Learn: تطبيق StandardScaler على عمود الربح
# StandardScaler يقوم بتحويل البيانات بحيث يكون متوسطها 0 وانحرافها المعياري 1.
# هذا مفيد للعديد من خوارزميات التعلم الآلي.
print("\nتطبيق StandardScaler على عمود الربح:")
# تهيئة StandardScaler
scaler = StandardScaler()
# إعادة تشكيل عمود 'Profit' ليكون ثنائي الأبعاد (مطلوب من StandardScaler)
profit_scaled = scaler.fit_transform(df[['Profit']])
# إضافة العمود الجديد إلى DataFrame
df['Profit_Scaled'] = profit_scaled
# عرض أول 5 صفوف مع عمود الربح المقيس
print(df[['Profit', 'Profit_Scaled']].head())
# عرض الوصف الإحصائي للعمود المقيس
print("\nوصف إحصائي لعمود الربح المقيس:")
print(df['Profit_Scaled'].describe())
الكود النهائي الكامل
إليك الكود كاملاً، جاهزاً للنسخ والتشغيل:
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
import os
# --- الخطوة 1: إعداد البيانات وتحميلها ---
# اسم الملف الذي سنقوم بإنشائه
file_name = 'sales_data_10k.csv'
# التحقق مما إذا كان الملف موجودًا بالفعل لتجنب إعادة الإنشاء
if not os.path.exists(file_name):
print("جاري إنشاء ملف بيانات المبيعات الوهمي...")
np.random.seed(42) # لضمان إمكانية تكرار النتائج
num_records = 10000
dates = pd.to_datetime(pd.date_range(start='2022-01-01', periods=num_records, freq='H'))
products = ['Laptop', 'Mouse', 'Keyboard', 'Monitor', 'Webcam', 'Headphones', 'Speaker', 'Printer']
categories = ['Electronics', 'Accessories', 'Peripherals']
regions = ['North', 'South', 'East', 'West', 'Central']
data = {
'OrderID': range(1, num_records + 1),
'Date': np.random.choice(dates, num_records),
'Product': np.random.choice(products, num_records),
'Category': np.random.choice(categories, num_records),
'Price': np.round(np.random.uniform(10, 1500, num_records), 2),
'Cost': np.round(np.random.uniform(5, 1000, num_records), 2),
'Quantity': np.random.randint(1, 10, num_records),
'Region': np.random.choice(regions, num_records)
}
df_raw = pd.DataFrame(data)
# إدخال بعض القيم المفقودة عن قصد لاختبار التنظيف
for col in ['Price', 'Quantity', 'Category']:
missing_indices = np.random.choice(df_raw.index, int(num_records * 0.01), replace=False)
df_raw.loc[missing_indices, col] = np.nan
df_raw.to_csv(file_name, index=False)
print(f"تم إنشاء الملف '{file_name}' بنجاح.")
else:
print(f"الملف '{file_name}' موجود بالفعل. تخطي إنشاء الملف.")
print("\nجاري تحميل البيانات...")
df = pd.read_csv(file_name)
print("أول 5 صفوف من البيانات الأولية:")
print(df.head())
print("\nمعلومات موجزة عن البيانات الأولية:")
print(df.info())
# --- الخطوة 2: تنظيف البيانات ومعالجتها ---
print("\nالقيم المفقودة قبل التنظيف:")
print(df.isnull().sum())
df.dropna(subset=['Price', 'Quantity', 'Category'], inplace=True)
df['Date'] = pd.to_datetime(df['Date'])
df['Price'] = pd.to_numeric(df['Price'])
df['Cost'] = pd.to_numeric(df['Cost'])
df['Quantity'] = pd.to_numeric(df['Quantity'])
initial_rows = len(df)
df.drop_duplicates(inplace=True)
rows_after_duplicates = len(df)
if initial_rows > rows_after_duplicates:
print(f"\nتم إزالة {initial_rows - rows_after_duplicates} صفوف مكررة.")
df['Profit'] = (df['Price'] - df['Cost']) * df['Quantity']
print("\nمعلومات موجزة عن DataFrame بعد التنظيف:")
print(df.info())
print("\nالقيم المفقودة بعد التنظيف:")
print(df.isnull().sum())
print("\nأول 5 صفوف بعد إضافة عمود الربح:")
print(df.head())
# --- الخطوة 3: تحليل البيانات وتلخيص الأرباح ---
total_profit = df['Profit'].sum()
print(f"\nإجمالي الأرباح من جميع المبيعات: {total_profit:,.2f} وحدة عملة")
average_profit_per_sale = df['Profit'].mean()
print(f"متوسط الربح لكل عملية بيع: {average_profit_per_sale:,.2f} وحدة عملة")
print("\nأكثر 5 منتجات ربحية:")
top_products = df.groupby('Product')['Profit'].sum().sort_values(ascending=False).head(5)
print(top_products)
print("\nإجمالي الأرباح حسب الفئة:")
profit_by_category = df.groupby('Category')['Profit'].sum().sort_values(ascending=False)
print(profit_by_category)
print("\nإجمالي الأرباح حسب المنطقة:")
profit_by_region = df.groupby('Region')['Profit'].sum().sort_values(ascending=False)
print(profit_by_region)
print("\nوصف إحصائي لعمود الربح:")
print(df['Profit'].describe())
# تطبيق StandardScaler على عمود الربح
scaler = StandardScaler()
profit_scaled = scaler.fit_transform(df[['Profit']])
df['Profit_Scaled'] = profit_scaled
print("\nأول 5 صفوف مع عمود الربح المقيس:")
print(df[['Profit', 'Profit_Scaled']].head())
print("\nوصف إحصائي لعمود الربح المقيس:")
print(df['Profit_Scaled'].describe())
النتيجة المتوقعة
عند تشغيل السكربت، ستحصل على المخرجات التالية في الطرفية (Terminal):
- رسالة تفيد بإنشاء ملف
sales_data_10k.csv(إذا لم يكن موجوداً) أو تخطي الإنشاء. - أول 5 صفوف من البيانات الأولية قبل التنظيف.
- معلومات موجزة عن DataFrame (
df.info()) قبل التنظيف، تظهر أنواع البيانات والعدد الأولي للقيم غير المفقودة. - عدد القيم المفقودة لكل عمود قبل التنظيف.
- رسالة إذا تم إزالة صفوف مكررة.
- معلومات موجزة عن DataFrame بعد التنظيف، تظهر أنواع البيانات الصحيحة والعدد النهائي للقيم غير المفقودة.
- عدد القيم المفقودة لكل عمود بعد التنظيف (يجب أن تكون صفرًا للأعمدة المستهدفة).
- أول 5 صفوف من البيانات بعد إضافة عمود
Profit. - إجمالي الأرباح من جميع المبيعات.
- متوسط الربح لكل عملية بيع.
- قائمة بأكثر 5 منتجات ربحية.
- إجمالي الأرباح مجمعة حسب الفئة.
- إجمالي الأرباح مجمعة حسب المنطقة.
- وصف إحصائي كامل لعمود
Profit(العدد، المتوسط، الانحراف المعياري، القيم الدنيا والقصوى، الربيعيات). - أول 5 صفوف من DataFrame تظهر عمودي
ProfitوProfit_Scaledجنباً إلى جنب. - وصف إحصائي لعمود
Profit_Scaled، حيث ستلاحظ أن المتوسط قريب من الصفر والانحراف المعياري قريب من الواحد.
هذه المخرجات توضح لك عملية تنظيف البيانات، استخلاص المعلومات الأساسية، وكيفية تجهيز البيانات لخطوات تحليلية أو نمذجة أكثر تقدماً.