4
Matplotlib وSeaborn: أقوى أدوات التصور البياني في Python
📊 الدرس الرابع: Matplotlib وSeaborn للتصور البياني
🎯 أهداف الدرس
- فهم أساسيات التصور البياني وأهميته في علم البيانات
- إتقان Matplotlib لإنشاء الرسوم البيانية الأساسية والمتقدمة
- استخدام Seaborn للتصور الإحصائي الجميل والاحترافي
- تخصيص الألوان والأنماط والتصاميم
- إنشاء رسوم بيانية تفاعلية ومتحركة
- أفضل ممارسات التصور البياني
- تصدير الرسوم بجودة عالية للنشر
- إنشاء لوحات معلومات شاملة
📋 المتطلبات السابقة
- إكمال الدروس السابقة: Python وNumPy وPandas
- فهم البيانات: أنواع البيانات والإحصائيات الأساسية
- المكتبات مثبتة: pip install matplotlib seaborn
- اختياري: pip install plotly (للرسوم التفاعلية)
🎨 لماذا التصور البياني مهم؟
التصور البياني ليس مجرد "رسوم جميلة" - إنه لغة عالمية لفهم البيانات وإيصال الأفكار المعقدة بطريقة بسيطة ومؤثرة.
أفضل ممارسات التصور البياني لإيصال الرسالة بوضوح
📊 حقائق مذهلة عن التصور البياني:
- السرعة: الدماغ يعالج الصور 60,000 مرة أسرع من النص
- الفهم: 90% من المعلومات المرسلة للدماغ بصرية
- التذكر: الناس يتذكرون 65% من المعلومات البصرية بعد 3 أيام
- الإقناع: الرسوم البيانية تزيد قوة الإقناع بنسبة 43%
- الاستخدام: 99% من علماء البيانات يستخدمون التصور البياني
🛠️ مقدمة Matplotlib
Matplotlib هو المكتبة الأساسية للتصور البياني في Python. إنه يوفر تحكماً كاملاً في كل عنصر من عناصر الرسم البياني.
Matplotlib: المكتبة الأساسية للتصور البياني في Python
الإعداد الأساسي
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from datetime import datetime, timedelta
# إعداد النمط العام
plt.style.use('default') # أو 'seaborn', 'ggplot', 'bmh'
plt.rcParams['font.size'] = 12
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.family'] = 'DejaVu Sans'
# إعداد Seaborn
sns.set_palette("husl")
sns.set_context("notebook", font_scale=1.2)
أول رسم بياني
# بيانات بسيطة للتجربة
x = np.linspace(0, 10, 100)
y = np.sin(x)
# إنشاء الرسم البياني
plt.figure(figsize=(10, 6))
plt.plot(x, y, linewidth=2, color='blue', label='sin(x)')
plt.title('أول رسم بياني - دالة الجيب', fontsize=16, fontweight='bold')
plt.xlabel('المحور السيني (x)', fontsize=12)
plt.ylabel('المحور الصادي (y)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.show()
📈 أنواع الرسوم البيانية الأساسية
Matplotlib يدعم جميع أنواع الرسوم البيانية. دعنا نستكشف الأنواع الأساسية:
📊 الرسم الخطي (Line Plot)
الاستخدام: إظهار الاتجاهات عبر الزمن
# بيانات المبيعات الشهرية
months = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو']
sales = [15000, 18000, 22000, 19000, 25000, 28000]
plt.figure(figsize=(10, 6))
plt.plot(months, sales, marker='o', linewidth=3, markersize=8)
plt.title('مبيعات الشركة الشهرية', fontsize=16)
plt.ylabel('المبيعات (ريال)')
plt.xticks(rotation=45)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
📊 الرسم العمودي (Bar Chart)
الاستخدام: مقارنة الفئات المختلفة
# بيانات الأقسام
departments = ['المبيعات', 'التسويق', 'التطوير', 'الموارد البشرية']
employees = [45, 30, 60, 25]
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
plt.figure(figsize=(10, 6))
bars = plt.bar(departments, employees, color=colors, alpha=0.8)
plt.title('عدد الموظفين في كل قسم', fontsize=16)
plt.ylabel('عدد الموظفين')
plt.xticks(rotation=45)
# إضافة القيم على الأعمدة
for bar, value in zip(bars, employees):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(value), ha='center', va='bottom', fontweight='bold')
plt.tight_layout()
plt.show()
🥧 الرسم الدائري (Pie Chart)
الاستخدام: إظهار النسب والأجزاء من الكل
# بيانات الميزانية
categories = ['الرواتب', 'التسويق', 'التطوير', 'الإدارة', 'أخرى']
budget = [40, 25, 20, 10, 5]
colors = ['#FF9999', '#66B2FF', '#99FF99', '#FFCC99', '#FF99CC']
plt.figure(figsize=(10, 8))
wedges, texts, autotexts = plt.pie(budget, labels=categories, colors=colors,
autopct='%1.1f%%', startangle=90,
explode=(0.1, 0, 0, 0, 0))
plt.title('توزيع الميزانية السنوية', fontsize=16, fontweight='bold')
# تحسين النصوص
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
plt.axis('equal')
plt.tight_layout()
plt.show()
📊 الرسم المبعثر (Scatter Plot)
الاستخدام: إظهار العلاقة بين متغيرين
# بيانات العلاقة بين الخبرة والراتب
np.random.seed(42)
experience = np.random.randint(1, 20, 100)
salary = 3000 + experience * 500 + np.random.normal(0, 1000, 100)
plt.figure(figsize=(10, 6))
scatter = plt.scatter(experience, salary, alpha=0.6, s=60,
c=experience, cmap='viridis')
plt.colorbar(scatter, label='سنوات الخبرة')
plt.title('العلاقة بين سنوات الخبرة والراتب', fontsize=16)
plt.xlabel('سنوات الخبرة')
plt.ylabel('الراتب (ريال)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
🎨 التخصيص المتقدم
القوة الحقيقية لـ Matplotlib تكمن في قدرته على التخصيص الكامل:
تخصيص الألوان والأنماط
# إنشاء بيانات متعددة
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.sin(x + np.pi/4)
# إنشاء رسم بياني متقدم
fig, ax = plt.subplots(figsize=(12, 8))
# رسم الخطوط مع تخصيص متقدم
line1 = ax.plot(x, y1, linewidth=3, color='#E74C3C',
linestyle='-', label='sin(x)', alpha=0.8)
line2 = ax.plot(x, y2, linewidth=3, color='#3498DB',
linestyle='--', label='cos(x)', alpha=0.8)
line3 = ax.plot(x, y3, linewidth=3, color='#2ECC71',
linestyle='-.', label='sin(x+π/4)', alpha=0.8)
# تخصيص العناوين والتسميات
ax.set_title('مقارنة الدوال المثلثية', fontsize=20, fontweight='bold',
pad=20, color='#2C3E50')
ax.set_xlabel('المحور السيني (x)', fontsize=14, fontweight='bold')
ax.set_ylabel('المحور الصادي (y)', fontsize=14, fontweight='bold')
# تخصيص الشبكة
ax.grid(True, linestyle=':', alpha=0.6, color='gray')
ax.set_facecolor('#F8F9FA')
# تخصيص الأسطورة
legend = ax.legend(loc='upper right', frameon=True, shadow=True,
fancybox=True, fontsize=12)
legend.get_frame().set_facecolor('#FFFFFF')
legend.get_frame().set_alpha(0.9)
# تخصيص المحاور
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_linewidth(2)
ax.spines['bottom'].set_linewidth(2)
# إضافة تأثيرات بصرية
ax.fill_between(x, y1, alpha=0.2, color='#E74C3C')
ax.fill_between(x, y2, alpha=0.2, color='#3498DB')
plt.tight_layout()
plt.show()
الرسوم المتعددة (Subplots)
# إنشاء بيانات للتجربة
np.random.seed(42)
data1 = np.random.normal(100, 15, 1000)
data2 = np.random.exponential(2, 1000)
x = np.linspace(0, 10, 100)
y = np.sin(x) + np.random.normal(0, 0.1, 100)
# إنشاء شبكة من الرسوم البيانية
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('لوحة معلومات شاملة', fontsize=20, fontweight='bold', y=0.98)
# الرسم الأول: هيستوجرام
ax1.hist(data1, bins=30, color='#3498DB', alpha=0.7, edgecolor='black')
ax1.set_title('توزيع البيانات الطبيعي', fontweight='bold')
ax1.set_xlabel('القيم')
ax1.set_ylabel('التكرار')
ax1.grid(True, alpha=0.3)
# الرسم الثاني: رسم صندوقي
box_data = [data1, data2, np.random.normal(80, 20, 1000)]
box_plot = ax2.boxplot(box_data, labels=['مجموعة 1', 'مجموعة 2', 'مجموعة 3'],
patch_artist=True)
colors = ['#E74C3C', '#2ECC71', '#F39C12']
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.7)
ax2.set_title('مقارنة التوزيعات', fontweight='bold')
ax2.set_ylabel('القيم')
ax2.grid(True, alpha=0.3)
# الرسم الثالث: رسم مبعثر مع خط الاتجاه
ax3.scatter(x, y, alpha=0.6, color='#9B59B6', s=50)
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
ax3.plot(x, p(x), color='#E74C3C', linewidth=2, linestyle='--')
ax3.set_title('البيانات مع خط الاتجاه', fontweight='bold')
ax3.set_xlabel('المحور السيني')
ax3.set_ylabel('المحور الصادي')
ax3.grid(True, alpha=0.3)
# الرسم الرابع: رسم دائري
sizes = [30, 25, 20, 15, 10]
labels = ['فئة أ', 'فئة ب', 'فئة ج', 'فئة د', 'فئة هـ']
colors_pie = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
wedges, texts, autotexts = ax4.pie(sizes, labels=labels, colors=colors_pie,
autopct='%1.1f%%', startangle=90)
ax4.set_title('توزيع الفئات', fontweight='bold')
plt.tight_layout()
plt.show()
دليل Matplotlib السريع - جميع أنواع الرسوم البيانية
🌊 مقدمة Seaborn
Seaborn مبني على Matplotlib ويوفر واجهة أبسط وأجمل للتصور الإحصائي:
Seaborn: التصور الإحصائي الجميل والبسيط
إنشاء بيانات تجريبية شاملة
# إنشاء بيانات شاملة للتجربة
np.random.seed(42)
n_samples = 1000
# بيانات الموظفين
employees_data = {
'الاسم': [f'موظف_{i}' for i in range(1, n_samples + 1)],
'القسم': np.random.choice(['المبيعات', 'التسويق', 'التطوير', 'الموارد البشرية', 'المالية'], n_samples),
'العمر': np.random.randint(22, 65, n_samples),
'سنوات_الخبرة': np.random.randint(0, 20, n_samples),
'الراتب': np.random.normal(8000, 2500, n_samples),
'تقييم_الأداء': np.random.randint(60, 100, n_samples),
'الجنس': np.random.choice(['ذكر', 'أنثى'], n_samples),
'المدينة': np.random.choice(['الرياض', 'جدة', 'الدمام', 'مكة', 'المدينة'], n_samples),
'مستوى_التعليم': np.random.choice(['ثانوي', 'بكالوريوس', 'ماجستير', 'دكتوراه'], n_samples, p=[0.1, 0.6, 0.25, 0.05])
}
# تحسين البيانات لتكون أكثر واقعية
for i in range(n_samples):
# ربط الراتب بالخبرة والتعليم
experience_bonus = employees_data['سنوات_الخبرة'][i] * 200
education_bonus = {'ثانوي': 0, 'بكالوريوس': 1000, 'ماجستير': 2000, 'دكتوراه': 3500}[employees_data['مستوى_التعليم'][i]]
employees_data['الراتب'][i] = max(3000, employees_data['الراتب'][i] + experience_bonus + education_bonus)
# ربط تقييم الأداء بالخبرة
if employees_data['سنوات_الخبرة'][i] > 10:
employees_data['تقييم_الأداء'][i] = min(100, employees_data['تقييم_الأداء'][i] + 10)
df = pd.DataFrame(employees_data)
print(f"تم إنشاء بيانات {len(df)} موظف للتجربة")
print(f"أول 5 صفوف:
{df.head()}")
الرسوم الإحصائية الأساسية
# 1. رسم التوزيع (Distribution Plot)
plt.figure(figsize=(15, 10))
# الرسم الأول: توزيع الرواتب
plt.subplot(2, 3, 1)
sns.histplot(data=df, x='الراتب', kde=True, color='skyblue')
plt.title('توزيع الرواتب', fontweight='bold')
plt.xlabel('الراتب (ريال)')
# الرسم الثاني: رسم صندوقي للرواتب حسب القسم
plt.subplot(2, 3, 2)
sns.boxplot(data=df, x='القسم', y='الراتب', palette='Set2')
plt.title('الرواتب حسب القسم', fontweight='bold')
plt.xticks(rotation=45)
# الرسم الثالث: رسم كمان للأعمار حسب الجنس
plt.subplot(2, 3, 3)
sns.violinplot(data=df, x='الجنس', y='العمر', palette='pastel')
plt.title('توزيع الأعمار حسب الجنس', fontweight='bold')
# الرسم الرابع: رسم مبعثر للعلاقة بين الخبرة والراتب
plt.subplot(2, 3, 4)
sns.scatterplot(data=df, x='سنوات_الخبرة', y='الراتب',
hue='القسم', size='تقييم_الأداء', alpha=0.7)
plt.title('العلاقة بين الخبرة والراتب', fontweight='bold')
# الرسم الخامس: رسم عد للأقسام حسب المدينة
plt.subplot(2, 3, 5)
sns.countplot(data=df, x='المدينة', hue='القسم', palette='viridis')
plt.title('توزيع الأقسام حسب المدينة', fontweight='bold')
plt.xticks(rotation=45)
# الرسم السادس: رسم خطي لمتوسط الراتب حسب العمر
plt.subplot(2, 3, 6)
age_salary = df.groupby('العمر')['الراتب'].mean().reset_index()
sns.lineplot(data=age_salary, x='العمر', y='الراتب', marker='o')
plt.title('متوسط الراتب حسب العمر', fontweight='bold')
plt.tight_layout()
plt.show()
خريطة الحرارة (Heatmap)
# إنشاء مصفوفة الارتباط
numeric_cols = ['العمر', 'سنوات_الخبرة', 'الراتب', 'تقييم_الأداء']
correlation_matrix = df[numeric_cols].corr()
# رسم خريطة الحرارة
plt.figure(figsize=(10, 8))
mask = np.triu(np.ones_like(correlation_matrix, dtype=bool))
sns.heatmap(correlation_matrix,
mask=mask,
annot=True,
cmap='RdYlBu_r',
center=0,
square=True,
fmt='.2f',
cbar_kws={'shrink': 0.8})
plt.title('مصفوفة الارتباط بين المتغيرات الرقمية',
fontsize=16, fontweight='bold', pad=20)
plt.tight_layout()
plt.show()
# تحليل النتائج
print("تحليل الارتباطات:")
print("=" * 30)
for i in range(len(correlation_matrix.columns)):
for j in range(i+1, len(correlation_matrix.columns)):
corr_value = correlation_matrix.iloc[i, j]
var1 = correlation_matrix.columns[i]
var2 = correlation_matrix.columns[j]
if abs(corr_value) > 0.5:
strength = "قوي" if abs(corr_value) > 0.7 else "متوسط"
direction = "طردي" if corr_value > 0 else "عكسي"
print(f"{var1} و {var2}: ارتباط {strength} {direction} ({corr_value:.2f})")
الرسوم المتقدمة
# 1. رسم الزوج (Pair Plot)
plt.figure(figsize=(12, 10))
pair_plot = sns.pairplot(df[numeric_cols + ['القسم']],
hue='القسم',
diag_kind='kde',
plot_kws={'alpha': 0.6})
pair_plot.fig.suptitle('مصفوفة الرسوم المزدوجة', y=1.02, fontsize=16, fontweight='bold')
plt.show()
# 2. رسم التوزيع المشترك
plt.figure(figsize=(12, 8))
sns.jointplot(data=df, x='سنوات_الخبرة', y='الراتب',
kind='reg', height=8, color='coral')
plt.suptitle('التوزيع المشترك: الخبرة والراتب', y=1.02, fontsize=16, fontweight='bold')
plt.show()
# 3. رسم الكثافة ثنائي الأبعاد
plt.figure(figsize=(10, 8))
plt.subplot(2, 2, 1)
sns.kdeplot(data=df, x='العمر', y='الراتب', cmap='Blues', fill=True)
plt.title('كثافة العمر والراتب')
plt.subplot(2, 2, 2)
sns.kdeplot(data=df, x='سنوات_الخبرة', y='تقييم_الأداء', cmap='Reds', fill=True)
plt.title('كثافة الخبرة والأداء')
plt.subplot(2, 2, 3)
sns.regplot(data=df, x='العمر', y='الراتب', scatter_kws={'alpha': 0.5})
plt.title('الانحدار: العمر والراتب')
plt.subplot(2, 2, 4)
sns.residplot(data=df, x='سنوات_الخبرة', y='الراتب', color='green')
plt.title('بواقي الانحدار')
plt.tight_layout()
plt.show()
دليل شامل لأنواع التصور البياني المختلفة
🎨 التخصيص المتقدم في Seaborn
Seaborn يوفر إمكانيات تخصيص قوية مع سهولة الاستخدام:
الأنماط والألوان المخصصة
# إنشاء نمط مخصص
custom_style = {
'axes.facecolor': '#F8F9FA',
'axes.edgecolor': '#2C3E50',
'axes.linewidth': 2,
'axes.grid': True,
'grid.color': '#BDC3C7',
'grid.alpha': 0.6,
'xtick.color': '#2C3E50',
'ytick.color': '#2C3E50',
'text.color': '#2C3E50',
'font.size': 12,
'axes.titlesize': 16,
'axes.labelsize': 14
}
# تطبيق النمط المخصص
with plt.rc_context(custom_style):
# إنشاء لوحة ألوان مخصصة
custom_palette = ['#E74C3C', '#3498DB', '#2ECC71', '#F39C12', '#9B59B6']
sns.set_palette(custom_palette)
# رسم متقدم مع التخصيص
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
fig.suptitle('لوحة معلومات مخصصة', fontsize=20, fontweight='bold', y=0.98)
# الرسم الأول: رسم كمان متقدم
sns.violinplot(data=df, x='القسم', y='الراتب',
inner='quart', palette=custom_palette, ax=axes[0,0])
axes[0,0].set_title('توزيع الرواتب حسب القسم', fontweight='bold')
axes[0,0].tick_params(axis='x', rotation=45)
# الرسم الثاني: رسم شريطي متقدم
dept_avg = df.groupby('القسم')['تقييم_الأداء'].mean().sort_values(ascending=False)
sns.barplot(x=dept_avg.values, y=dept_avg.index,
palette=custom_palette, ax=axes[0,1])
axes[0,1].set_title('متوسط تقييم الأداء حسب القسم', fontweight='bold')
axes[0,1].set_xlabel('متوسط التقييم')
# الرسم الثالث: رسم نقطي متقدم
sns.stripplot(data=df, x='مستوى_التعليم', y='الراتب',
hue='الجنس', size=8, alpha=0.7, ax=axes[1,0])
axes[1,0].set_title('الرواتب حسب التعليم والجنس', fontweight='bold')
axes[1,0].tick_params(axis='x', rotation=45)
# الرسم الرابع: رسم سداسي
axes[1,1].hexbin(df['العمر'], df['الراتب'], gridsize=20, cmap='YlOrRd')
axes[1,1].set_title('الكثافة السداسية: العمر والراتب', fontweight='bold')
axes[1,1].set_xlabel('العمر')
axes[1,1].set_ylabel('الراتب')
plt.tight_layout()
plt.show()
الرسوم التفاعلية مع Plotly
# تثبيت Plotly إذا لم يكن مثبتاً
# pip install plotly
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# 1. رسم مبعثر تفاعلي
fig_scatter = px.scatter(df, x='سنوات_الخبرة', y='الراتب',
color='القسم', size='تقييم_الأداء',
hover_data=['العمر', 'المدينة'],
title='العلاقة التفاعلية بين الخبرة والراتب')
fig_scatter.update_layout(
font=dict(size=14),
title_font_size=18,
width=800,
height=600
)
# عرض الرسم (في Jupyter Notebook)
# fig_scatter.show()
# 2. رسم خطي تفاعلي للاتجاهات
monthly_data = {
'الشهر': ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو'],
'المبيعات': [15000, 18000, 22000, 19000, 25000, 28000],
'الأرباح': [3000, 4500, 5500, 4000, 6200, 7000],
'العملاء_الجدد': [120, 150, 180, 160, 200, 220]
}
monthly_df = pd.DataFrame(monthly_data)
fig_lines = make_subplots(
rows=2, cols=2,
subplot_titles=('المبيعات الشهرية', 'الأرباح الشهرية',
'العملاء الجدد', 'مقارنة شاملة'),
specs=[[{'secondary_y': False}, {'secondary_y': False}],
[{'secondary_y': False}, {'secondary_y': True}]]
)
# إضافة الرسوم
fig_lines.add_trace(
go.Scatter(x=monthly_df['الشهر'], y=monthly_df['المبيعات'],
mode='lines+markers', name='المبيعات', line=dict(color='#E74C3C')),
row=1, col=1
)
fig_lines.add_trace(
go.Scatter(x=monthly_df['الشهر'], y=monthly_df['الأرباح'],
mode='lines+markers', name='الأرباح', line=dict(color='#2ECC71')),
row=1, col=2
)
fig_lines.add_trace(
go.Bar(x=monthly_df['الشهر'], y=monthly_df['العملاء_الجدد'],
name='العملاء الجدد', marker_color='#3498DB'),
row=2, col=1
)
# رسم مقارن
fig_lines.add_trace(
go.Scatter(x=monthly_df['الشهر'], y=monthly_df['المبيعات'],
mode='lines', name='المبيعات', line=dict(color='#E74C3C')),
row=2, col=2
)
fig_lines.add_trace(
go.Scatter(x=monthly_df['الشهر'], y=monthly_df['العملاء_الجدد'],
mode='lines', name='العملاء الجدد', line=dict(color='#3498DB'),
yaxis='y2'),
row=2, col=2, secondary_y=True
)
fig_lines.update_layout(height=800, title_text="لوحة معلومات تفاعلية")
# fig_lines.show()
print("تم إنشاء الرسوم التفاعلية! (استخدم fig.show() في Jupyter Notebook)")
📊 مثال عملي شامل: تحليل بيانات المبيعات
الآن سنطبق جميع ما تعلمناه في مثال واقعي ومعقد:
إنشاء بيانات مبيعات شاملة
# إنشاء بيانات مبيعات واقعية
np.random.seed(42)
# إنشاء بيانات زمنية
start_date = datetime(2023, 1, 1)
end_date = datetime(2024, 12, 31)
date_range = pd.date_range(start=start_date, end=end_date, freq='D')
# إنشاء بيانات المبيعات اليومية
sales_data = []
base_sales = 10000
for date in date_range:
# تأثير الموسمية
month_factor = 1 + 0.3 * np.sin(2 * np.pi * date.month / 12)
# تأثير يوم الأسبوع
weekday_factor = 1.2 if date.weekday() < 5 else 0.8 # أيام العمل أعلى
# تأثير الأعياد والمناسبات
holiday_factor = 1.5 if date.month in [12, 1, 6, 7] else 1.0
# اتجاه النمو
growth_factor = 1 + (date - start_date).days * 0.0001
# مبيعات اليوم
daily_sales = base_sales * month_factor * weekday_factor * holiday_factor * growth_factor
daily_sales += np.random.normal(0, daily_sales * 0.1) # ضوضاء عشوائية
# بيانات إضافية
customers = int(daily_sales / np.random.uniform(80, 120))
avg_order = daily_sales / customers if customers > 0 else 0
sales_data.append({
'التاريخ': date,
'المبيعات': max(0, daily_sales),
'عدد_العملاء': customers,
'متوسط_الطلب': avg_order,
'اليوم': date.strftime('%A'),
'الشهر': date.strftime('%B'),
'السنة': date.year,
'الربع': f'Q{(date.month-1)//3 + 1}',
'نهاية_الأسبوع': date.weekday() >= 5
})
sales_df = pd.DataFrame(sales_data)
sales_df['المبيعات'] = sales_df['المبيعات'].round(2)
sales_df['متوسط_الطلب'] = sales_df['متوسط_الطلب'].round(2)
print(f"تم إنشاء بيانات مبيعات لـ {len(sales_df)} يوم")
print(f"إجمالي المبيعات: {sales_df['المبيعات'].sum():,.2f} ريال")
print(f"متوسط المبيعات اليومية: {sales_df['المبيعات'].mean():,.2f} ريال")
تحليل بصري شامل للمبيعات
# إنشاء لوحة معلومات شاملة للمبيعات
fig = plt.figure(figsize=(20, 16))
gs = fig.add_gridspec(4, 4, hspace=0.3, wspace=0.3)
# 1. اتجاه المبيعات عبر الزمن
ax1 = fig.add_subplot(gs[0, :2])
ax1.plot(sales_df['التاريخ'], sales_df['المبيعات'],
linewidth=1, alpha=0.7, color='#3498DB')
ax1.plot(sales_df['التاريخ'], sales_df['المبيعات'].rolling(30).mean(),
linewidth=3, color='#E74C3C', label='المتوسط المتحرك (30 يوم)')
ax1.set_title('اتجاه المبيعات عبر الزمن', fontsize=16, fontweight='bold')
ax1.set_ylabel('المبيعات (ريال)')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. المبيعات الشهرية
ax2 = fig.add_subplot(gs[0, 2:])
monthly_sales = sales_df.groupby('الشهر')['المبيعات'].sum().reindex([
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
])
bars = ax2.bar(range(len(monthly_sales)), monthly_sales.values,
color=plt.cm.viridis(np.linspace(0, 1, 12)))
ax2.set_title('إجمالي المبيعات الشهرية', fontsize=16, fontweight='bold')
ax2.set_ylabel('المبيعات (ريال)')
ax2.set_xticks(range(12))
ax2.set_xticklabels(['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو',
'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
rotation=45)
# 3. المبيعات حسب يوم الأسبوع
ax3 = fig.add_subplot(gs[1, :2])
weekday_sales = sales_df.groupby('اليوم')['المبيعات'].mean().reindex([
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
])
weekday_names = ['الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت', 'الأحد']
colors = ['#E74C3C' if day in ['Saturday', 'Sunday'] else '#3498DB'
for day in weekday_sales.index]
ax3.bar(weekday_names, weekday_sales.values, color=colors, alpha=0.8)
ax3.set_title('متوسط المبيعات حسب يوم الأسبوع', fontsize=16, fontweight='bold')
ax3.set_ylabel('متوسط المبيعات (ريال)')
ax3.tick_params(axis='x', rotation=45)
# 4. توزيع المبيعات
ax4 = fig.add_subplot(gs[1, 2:])
ax4.hist(sales_df['المبيعات'], bins=50, alpha=0.7, color='#2ECC71', edgecolor='black')
ax4.axvline(sales_df['المبيعات'].mean(), color='#E74C3C', linestyle='--',
linewidth=2, label=f'المتوسط: {sales_df["المبيعات"].mean():.0f}')
ax4.axvline(sales_df['المبيعات'].median(), color='#F39C12', linestyle='--',
linewidth=2, label=f'الوسيط: {sales_df["المبيعات"].median():.0f}')
ax4.set_title('توزيع المبيعات اليومية', fontsize=16, fontweight='bold')
ax4.set_xlabel('المبيعات (ريال)')
ax4.set_ylabel('التكرار')
ax4.legend()
# 5. العلاقة بين عدد العملاء ومتوسط الطلب
ax5 = fig.add_subplot(gs[2, :2])
scatter = ax5.scatter(sales_df['عدد_العملاء'], sales_df['متوسط_الطلب'],
c=sales_df['المبيعات'], cmap='viridis', alpha=0.6, s=30)
ax5.set_title('العلاقة بين عدد العملاء ومتوسط الطلب', fontsize=16, fontweight='bold')
ax5.set_xlabel('عدد العملاء')
ax5.set_ylabel('متوسط الطلب (ريال)')
plt.colorbar(scatter, ax=ax5, label='المبيعات')
# 6. المبيعات الربعية
ax6 = fig.add_subplot(gs[2, 2:])
quarterly_sales = sales_df.groupby(['السنة', 'الربع'])['المبيعات'].sum().reset_index()
quarterly_sales['الفترة'] = quarterly_sales['السنة'].astype(str) + '-' + quarterly_sales['الربع']
ax6.plot(quarterly_sales['الفترة'], quarterly_sales['المبيعات'],
marker='o', linewidth=3, markersize=8, color='#9B59B6')
ax6.set_title('المبيعات الربعية', fontsize=16, fontweight='bold')
ax6.set_ylabel('المبيعات (ريال)')
ax6.tick_params(axis='x', rotation=45)
ax6.grid(True, alpha=0.3)
# 7. مقارنة أيام العمل ونهاية الأسبوع
ax7 = fig.add_subplot(gs[3, :2])
weekend_comparison = sales_df.groupby('نهاية_الأسبوع')['المبيعات'].agg(['mean', 'std'])
categories = ['أيام العمل', 'نهاية الأسبوع']
means = [weekend_comparison.loc[False, 'mean'], weekend_comparison.loc[True, 'mean']]
stds = [weekend_comparison.loc[False, 'std'], weekend_comparison.loc[True, 'std']]
bars = ax7.bar(categories, means, yerr=stds, capsize=5,
color=['#3498DB', '#E74C3C'], alpha=0.8)
ax7.set_title('مقارنة المبيعات: أيام العمل vs نهاية الأسبوع', fontsize=16, fontweight='bold')
ax7.set_ylabel('متوسط المبيعات (ريال)')
# إضافة القيم على الأعمدة
for bar, mean in zip(bars, means):
ax7.text(bar.get_x() + bar.get_width()/2, bar.get_height() + bar.get_height()*0.01,
f'{mean:.0f}', ha='center', va='bottom', fontweight='bold')
# 8. خريطة حرارة للمبيعات (الشهر × يوم الأسبوع)
ax8 = fig.add_subplot(gs[3, 2:])
pivot_data = sales_df.pivot_table(values='المبيعات',
index='الشهر',
columns='اليوم',
aggfunc='mean')
# إعادة ترتيب الأعمدة والصفوف
month_order = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December']
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
pivot_data = pivot_data.reindex(month_order).reindex(columns=weekday_order)
im = ax8.imshow(pivot_data.values, cmap='YlOrRd', aspect='auto')
ax8.set_title('خريطة حرارة: المبيعات حسب الشهر ويوم الأسبوع', fontsize=16, fontweight='bold')
ax8.set_xticks(range(7))
ax8.set_xticklabels(['الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت', 'الأحد'],
rotation=45)
ax8.set_yticks(range(12))
ax8.set_yticklabels(['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو',
'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'])
# إضافة شريط الألوان
cbar = plt.colorbar(im, ax=ax8, shrink=0.8)
cbar.set_label('متوسط المبيعات (ريال)')
plt.suptitle('لوحة معلومات شاملة لتحليل المبيعات', fontsize=24, fontweight='bold', y=0.98)
plt.show()
تحليل إحصائي متقدم
# تحليل إحصائي شامل للمبيعات
print("📊 التحليل الإحصائي الشامل للمبيعات")
print("=" * 60)
# 1. الإحصائيات الأساسية
print("
1. الإحصائيات الأساسية:")
print(f"إجمالي المبيعات: {sales_df['المبيعات'].sum():,.2f} ريال")
print(f"متوسط المبيعات اليومية: {sales_df['المبيعات'].mean():,.2f} ريال")
print(f"الوسيط: {sales_df['المبيعات'].median():,.2f} ريال")
print(f"الانحراف المعياري: {sales_df['المبيعات'].std():,.2f} ريال")
print(f"أعلى مبيعات يومية: {sales_df['المبيعات'].max():,.2f} ريال")
print(f"أقل مبيعات يومية: {sales_df['المبيعات'].min():,.2f} ريال")
# 2. تحليل النمو
sales_df['المبيعات_المتحركة'] = sales_df['المبيعات'].rolling(30).mean()
first_month_avg = sales_df.head(30)['المبيعات'].mean()
last_month_avg = sales_df.tail(30)['المبيعات'].mean()
growth_rate = ((last_month_avg - first_month_avg) / first_month_avg) * 100
print(f"
2. تحليل النمو:")
print(f"متوسط أول شهر: {first_month_avg:,.2f} ريال")
print(f"متوسط آخر شهر: {last_month_avg:,.2f} ريال")
print(f"معدل النمو: {growth_rate:.1f}%")
# 3. تحليل الموسمية
print(f"
3. تحليل الموسمية:")
seasonal_analysis = sales_df.groupby('الشهر')['المبيعات'].agg(['mean', 'std']).round(2)
best_month = seasonal_analysis['mean'].idxmax()
worst_month = seasonal_analysis['mean'].idxmin()
print(f"أفضل شهر: {best_month} ({seasonal_analysis.loc[best_month, 'mean']:,.2f} ريال)")
print(f"أسوأ شهر: {worst_month} ({seasonal_analysis.loc[worst_month, 'mean']:,.2f} ريال)")
# 4. تحليل أيام الأسبوع
print(f"
4. تحليل أيام الأسبوع:")
weekday_analysis = sales_df.groupby('اليوم')['المبيعات'].mean().round(2)
best_day = weekday_analysis.idxmax()
worst_day = weekday_analysis.idxmin()
print(f"أفضل يوم: {best_day} ({weekday_analysis[best_day]:,.2f} ريال)")
print(f"أسوأ يوم: {worst_day} ({weekday_analysis[worst_day]:,.2f} ريال)")
# 5. تحليل التقلبات
print(f"
5. تحليل التقلبات:")
volatility = (sales_df['المبيعات'].std() / sales_df['المبيعات'].mean()) * 100
print(f"معامل التقلب: {volatility:.1f}%")
# تحديد الأيام الاستثنائية
q1 = sales_df['المبيعات'].quantile(0.25)
q3 = sales_df['المبيعات'].quantile(0.75)
iqr = q3 - q1
outliers = sales_df[(sales_df['المبيعات'] < q1 - 1.5*iqr) |
(sales_df['المبيعات'] > q3 + 1.5*iqr)]
print(f"عدد الأيام الاستثنائية: {len(outliers)} يوم ({len(outliers)/len(sales_df)*100:.1f}%)")
# 6. تحليل الارتباطات
print(f"
6. تحليل الارتباطات:")
correlations = sales_df[['المبيعات', 'عدد_العملاء', 'متوسط_الطلب']].corr()
print(f"الارتباط بين المبيعات وعدد العملاء: {correlations.loc['المبيعات', 'عدد_العملاء']:.3f}")
print(f"الارتباط بين المبيعات ومتوسط الطلب: {correlations.loc['المبيعات', 'متوسط_الطلب']:.3f}")
# 7. توقعات بسيطة
print(f"
7. توقعات بسيطة (بناءً على الاتجاه الحالي):")
trend = np.polyfit(range(len(sales_df)), sales_df['المبيعات'], 1)
next_month_prediction = trend[0] * (len(sales_df) + 30) + trend[1]
print(f"توقع متوسط المبيعات للشهر القادم: {next_month_prediction:,.2f} ريال")
💡 أفضل ممارسات التصور البياني
حفظ وتصدير الرسوم البيانية
# إنشاء رسم بياني للحفظ
plt.figure(figsize=(12, 8))
plt.plot(sales_df['التاريخ'], sales_df['المبيعات'].rolling(7).mean(),
linewidth=3, color='#E74C3C', label='المتوسط المتحرك (7 أيام)')
plt.fill_between(sales_df['التاريخ'],
sales_df['المبيعات'].rolling(7).mean() - sales_df['المبيعات'].rolling(7).std(),
sales_df['المبيعات'].rolling(7).mean() + sales_df['المبيعات'].rolling(7).std(),
alpha=0.3, color='#E74C3C')
plt.title('اتجاه المبيعات مع نطاق الثقة', fontsize=18, fontweight='bold', pad=20)
plt.xlabel('التاريخ', fontsize=14)
plt.ylabel('المبيعات (ريال)', fontsize=14)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.tight_layout()
# حفظ بتنسيقات مختلفة
plt.savefig('sales_trend.png', dpi=300, bbox_inches='tight',
facecolor='white', edgecolor='none')
plt.savefig('sales_trend.pdf', bbox_inches='tight',
facecolor='white', edgecolor='none')
plt.savefig('sales_trend.svg', bbox_inches='tight',
facecolor='white', edgecolor='none')
print("تم حفظ الرسم البياني بتنسيقات PNG وPDF وSVG")
# حفظ رسم Seaborn
plt.figure(figsize=(10, 8))
correlation_plot = sns.heatmap(sales_df[['المبيعات', 'عدد_العملاء', 'متوسط_الطلب']].corr(),
annot=True, cmap='RdYlBu_r', center=0, square=True,
cbar_kws={'shrink': 0.8})
plt.title('مصفوفة الارتباط', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.savefig('correlation_heatmap.png', dpi=300, bbox_inches='tight')
plt.show()
print("تم حفظ خريطة الحرارة")
🎯 تمارين عملية
تمرين 1: تحليل بيانات الطقس
أنشئ وحلل بيانات الطقس لمدة سنة:
- درجات الحرارة اليومية والرطوبة
- رسم خطي للاتجاهات الموسمية
- خريطة حرارة للشهر × درجة الحرارة
- رسم مبعثر للعلاقة بين الحرارة والرطوبة
تمرين 2: تحليل بيانات الأسهم
حلل أداء محفظة أسهم:
- رسم خطي لأسعار 5 أسهم مختلفة
- رسم شمعي (Candlestick) لسهم واحد
- حساب وعرض المتوسطات المتحركة
- تحليل التقلبات والمخاطر
تمرين 3: لوحة معلومات تفاعلية
أنشئ لوحة معلومات شاملة:
- استخدم Plotly لإنشاء رسوم تفاعلية
- اربط عدة رسوم بيانية معاً
- أضف فلاتر وخيارات تفاعلية
- صدر اللوحة كملف HTML
⚠️ أخطاء شائعة يجب تجنبها:
- الرسوم المزدحمة: تجنب وضع معلومات كثيرة في رسم واحد
- الألوان المتضاربة: استخدم لوحات ألوان متناسقة
- المحاور المضللة: لا تقطع المحاور بدون مبرر
- عدم وضوح التسميات: تأكد من وضوح جميع النصوص
- تجاهل السياق: أضف عناوين وتفسيرات واضحة
📝 ملخص الدرس
في هذا الدرس الشامل تعلمنا:
- ✅ أساسيات التصور البياني: أهميته وأفضل الممارسات
- ✅ Matplotlib الأساسي: الرسوم الخطية والعمودية والدائرية
- ✅ التخصيص المتقدم: الألوان والأنماط والتأثيرات
- ✅ الرسوم المتعددة: Subplots ولوحات المعلومات
- ✅ Seaborn الإحصائي: الرسوم الإحصائية الجميلة
- ✅ خرائط الحرارة: تصور الارتباطات والأنماط
- ✅ الرسوم التفاعلية: Plotly للتفاعل المتقدم
- ✅ مثال عملي شامل: تحليل بيانات المبيعات
- ✅ الحفظ والتصدير: تنسيقات عالية الجودة
- ✅ أفضل الممارسات: قواعد التصور الفعال
➡️ الدرس التالي
في الدرس التالي سنتعلم Scikit-learn الأساسيات:
- مقدمة التعلم الآلي والمفاهيم الأساسية
- تحضير البيانات والمعالجة المسبقة
- خوارزميات التصنيف والانحدار
- تقييم النماذج ومقاييس الأداء
- التحقق المتقاطع وضبط المعاملات
- مشاريع عملية في التعلم الآلي