المعمل الثالث: المعالجة المسبقة للبيانات والتجميع على بيانات واقعية باستخدام أطر بيانات Pandas
الفصل الدراسي: خريف 2025 الجامعة: كلية الحاسبات والمعلومات - جامعة المنصورة
أهداف المعمل
بعد إتمام هذا المعمل، سيكون الطالب قادراً على:
- تحميل واستكشاف مجموعات بيانات حقيقية (Load and Explore Real Datasets): استخدام pandas لتحميل مجموعات البيانات من مصادر مختلفة وإجراء استكشاف أولي لها
- فهم عمليات DataFrame (Understand DataFrame Operations): إتقان العمليات الأساسية في pandas، بما في ذلك الفهرسة (indexing)، والتصفية (filtering)، واختيار البيانات (data selection)
- المعالجة المسبقة للبيانات تمهيداً للتجميع (Preprocess Data for Clustering): التعامل مع القيم المفقودة، وترميز المتغيرات الفئوية (categorical variables)، وتطبيع (normalize) الخصائص
- تطبيق التجميع على بيانات حقيقية (Apply Clustering to Real Data): استخدام K-Means و SOM على مجموعات بيانات واقعية مع تجهيز مناسب للبيانات
- تصور النتائج وتفسيرها (Visualize and Interpret Results): إنشاء تصورات بيانية (visualizations) ذات معنى لفهم أنماط التجميع في البيانات الحقيقية
- مقارنة خوارزميات التجميع (Compare Clustering Algorithms): فهم متى نستخدم K-Means ومتى نستخدم SOM بناءً على خصائص مجموعة البيانات
الخطوة 1: استيراد المكتبات المطلوبة
بدايةً، لنستورد كل المكتبات اللازمة لمعالجة البيانات، والتجميع (clustering)، والتصور البياني (visualization).
# Data manipulation and analysis
import pandas as pd
import numpy as np
# Visualization
import matplotlib.pyplot as plt
import seaborn as sns
# Machine Learning
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, confusion_matrix
from sklearn_som.som import SOM
# Warnings
import warnings
warnings.filterwarnings('ignore')
# Set visualization style
plt.style.use('default')
sns.set_palette("husl")
print("✓ All libraries imported successfully!")✓ All libraries imported successfully!الخطوة 2: مقدمة عن أطر بيانات Pandas (Pandas DataFrames)
ما هو الـ DataFrame؟
الـ DataFrame هو بنية بيانات ثنائية الأبعاد (2-dimensional) ذات تسميات (labeled)، تحتوي على أعمدة يمكن أن تكون من أنواع بيانات مختلفة. تخيّله كجدول في ملف إكسل (spreadsheet) أو كجدول في قاعدة بيانات SQL.
الخصائص الأساسية:
- صفوف وأعمدة ذات تسميات (labels)
- يمكن أن تحتوي الأعمدة على أنواع بيانات مختلفة
- الحجم قابل للتغيير (size-mutable) (يمكن إضافة أو حذف صفوف وأعمدة)
- دوال قوية لمعالجة البيانات
# Create a simple DataFrame example
sample_data = {
'Name': ['Ahmed', 'Fatima', 'Omar', 'Nour'],
'Age': [22, 21, 23, 22 ],
'Grade': [85, 92, 78, 88 ]
}
df_sample = pd.DataFrame(sample_data)
print("Example DataFrame:")
print(df_sample)
print("\n" + "="*50)
# Basic DataFrame operations
print("\nDataFrame Shape (rows, columns):", df_sample.shape)
print("\nColumn Names:", df_sample.columns.tolist())
print("\nData Types:\n", df_sample.dtypes)
print("\n" + "="*50)
# Accessing data
print("\nAccessing 'Age' column:")
print(df_sample['Age'])
print("\n" + "="*50)
print("\nAccessing first 2 rows:")
print(df_sample.head(2))Example DataFrame:
Name Age Grade
0 Ahmed 22 85
1 Fatima 21 92
2 Omar 23 78
3 Nour 22 88
==================================================
DataFrame Shape (rows, columns): (4, 3)
Column Names: ['Name', 'Age', 'Grade']
Data Types:
Name object
Age int64
Grade int64
dtype: object
==================================================
Accessing 'Age' column:
0 22
1 21
2 23
3 22
Name: Age, dtype: int64
==================================================
Accessing first 2 rows:
Name Age Grade
0 Ahmed 22 85
1 Fatima 21 92الخطوة 3: تحميل مجموعة بيانات Iris
تُعد مجموعة بيانات Iris (Iris dataset) من أشهر مجموعات البيانات الكلاسيكية في تعلم الآلة، وتحتوي على قياسات لزهور الـ iris من ثلاثة أنواع مختلفة:
- Setosa
- Versicolor
- Virginica
الخصائص (Features):
- طول الكأس (Sepal Length) بالسنتيمتر (cm)
- عرض الكأس (Sepal Width) بالسنتيمتر (cm)
- طول البتلة (Petal Length) بالسنتيمتر (cm)
- عرض البتلة (Petal Width) بالسنتيمتر (cm)
# Load the Iris dataset
iris = load_iris()
# Create a DataFrame from the dataset
df_iris = pd.DataFrame(data=iris.data, columns=iris.feature_names)
# Add the target column (species)
df_iris['species'] = iris.target
# Map numeric labels to actual species names
species_map = {0: 'setosa', 1: 'versicolor', 2: 'virginica'}
df_iris['species_name'] = df_iris['species'].map(species_map)
print("Iris Dataset Loaded Successfully!")
print("\nDataset Shape:", df_iris.shape)
print("\nFirst 5 rows:")
print(df_iris.head())Iris Dataset Loaded Successfully!
Dataset Shape: (150, 6)
First 5 rows:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
species species_name
0 0 setosa
1 0 setosa
2 0 setosa
3 0 setosa
4 0 setosa# Load the Iris dataset from CSV
# Option 1: From a local file
df_iris = pd.read_csv('iris.csv')
# Option 2: From a URL (online source)
# df_iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
print("Iris Dataset Loaded Successfully!")
print("\nDataset Shape:", df_iris.shape)
print("\nFirst 5 rows:")
print(df_iris.head())
# Check column names to see what we're working with
print("\nColumn names:", df_iris.columns.tolist())
# If the CSV has species names directly (like 'setosa', 'versicolor', 'virginica')
# You may need to rename columns to match what we use later
df_iris.columns = ['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)', 'species_name']
# Create numeric species column for later use
species_map_reverse = {'setosa': 0, 'versicolor': 1, 'virginica': 2}
df_iris['species'] = df_iris['species_name'].map(species_map_reverse)
print("\nAfter renaming:")
print(df_iris.head())Iris Dataset Loaded Successfully!
Dataset Shape: (150, 5)
First 5 rows:
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
Column names: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
After renaming:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
species_name species
0 setosa 0
1 setosa 0
2 setosa 0
3 setosa 0
4 setosa 0الخطوة 4: التحليل الاستكشافي للبيانات (Exploratory Data Analysis - EDA)
4.1 معلومات أساسية
print("="*60)
print("DATASET INFORMATION")
print("="*60)
print("\nBasic Statistics:")
print(df_iris.describe())
print("\n" + "="*60)
print("\nData Types:")
print(df_iris.dtypes)
print("\n" + "="*60)
print("\nMissing Values:")
print(df_iris.isnull().sum())
print("\n" + "="*60)
print("\nSpecies Distribution:")
print(df_iris['species_name'].value_counts())============================================================
DATASET INFORMATION
============================================================
Basic Statistics:
sepal length (cm) sepal width (cm) petal length (cm) \
count 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667
std 0.828066 0.433594 1.764420
min 4.300000 2.000000 1.000000
25% 5.100000 2.800000 1.600000
50% 5.800000 3.000000 4.350000
75% 6.400000 3.300000 5.100000
max 7.900000 4.400000 6.900000
petal width (cm) species
count 150.000000 150.000000
mean 1.198667 1.000000
std 0.763161 0.819232
min 0.100000 0.000000
25% 0.300000 0.000000
50% 1.300000 1.000000
75% 1.800000 2.000000
max 2.500000 2.000000
============================================================
Data Types:
sepal length (cm) float64
sepal width (cm) float64
petal length (cm) float64
petal width (cm) float64
species_name object
species int64
dtype: object
============================================================
Missing Values:
sepal length (cm) 0
sepal width (cm) 0
petal length (cm) 0
petal width (cm) 0
species_name 0
species 0
dtype: int64
============================================================
Species Distribution:
species_name
setosa 50
versicolor 50
virginica 50
Name: count, dtype: int644.2 اختيار البيانات وتصفيتها (Data Selection and Filtering)
توفر أطر بيانات DataFrame دوالاً قوية لاختيار البيانات وتصفيتها.
# Select specific columns
print("Selecting only sepal measurements:")
sepal_data = df_iris[['sepal length (cm)', 'sepal width (cm)']]
print(sepal_data.head()) # 5 rows
print("\n" + "="*60)
# Filter rows based on condition
print("\nFiltering: Only Setosa species:")
setosa_only = df_iris[df_iris['species_name'] == 'setosa']
print(f"Number of Setosa samples: {len(setosa_only)}")
print(setosa_only.head())
print("\n" + "="*60)
# Multiple conditions
print("\nFiltering: Setosa with sepal length > 5.0:")
filtered_data = df_iris[(df_iris['species_name'] == 'setosa') &
(df_iris['sepal length (cm)'] > 5.0)]
print(f"Number of matching samples: {len(filtered_data)}")
print(filtered_data.head())Selecting only sepal measurements:
sepal length (cm) sepal width (cm)
0 5.1 3.5
1 4.9 3.0
2 4.7 3.2
3 4.6 3.1
4 5.0 3.6
============================================================
Filtering: Only Setosa species:
Number of Setosa samples: 50
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
species_name species
0 setosa 0
1 setosa 0
2 setosa 0
3 setosa 0
4 setosa 0
============================================================
Filtering: Setosa with sepal length > 5.0:
Number of matching samples: 22
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
5 5.4 3.9 1.7 0.4
10 5.4 3.7 1.5 0.2
14 5.8 4.0 1.2 0.2
15 5.7 4.4 1.5 0.4
species_name species
0 setosa 0
5 setosa 0
10 setosa 0
14 setosa 0
15 setosa 04.3 تصور البيانات بيانياً (Visualizing the Data)
# Create a figure with multiple subplots
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 0,0 | 0,1
# 1,0 | 1,1
#
# Plot 1: Distribution of sepal length
axes[0, 0].hist(df_iris['sepal length (cm)'], bins=20, color='skyblue', edgecolor='black')
axes[0, 0].set_title('Distribution of Sepal Length', fontsize=12, fontweight='bold')
axes[0, 0].set_xlabel('Sepal Length (cm)')
axes[0, 0].set_ylabel('Frequency')
axes[0, 0].grid(alpha=0.3)
# Plot 2: Species count
species_counts = df_iris['species_name'].value_counts()
axes[0, 1].bar(species_counts.index, species_counts.values, color=['#FF6B6B', '#4ECDC4', '#45B7D1'])
axes[0, 1].set_title('Species Distribution', fontsize=12, fontweight='bold')
axes[0, 1].set_xlabel('Species')
axes[0, 1].set_ylabel('Count')
axes[0, 1].grid(alpha=0.3, axis='y')
# Plot 3: Scatter plot - Sepal measurements
for species in df_iris['species_name'].unique():
species_data = df_iris[df_iris['species_name'] == species]
axes[1, 0].scatter(species_data['sepal length (cm)'],
species_data['sepal width (cm)'],
label=species, alpha=0.6, s=50)
axes[1, 0].set_title('Sepal Length vs Width by Species', fontsize=12, fontweight='bold')
axes[1, 0].set_xlabel('Sepal Length (cm)')
axes[1, 0].set_ylabel('Sepal Width (cm)')
axes[1, 0].legend()
axes[1, 0].grid(alpha=0.3)
# Plot 4: Scatter plot - Petal measurements
for species in df_iris['species_name'].unique():
species_data = df_iris[df_iris['species_name'] == species]
axes[1, 1].scatter(species_data['petal length (cm)'],
species_data['petal width (cm)'],
label=species, alpha=0.6, s=50)
axes[1, 1].set_title('Petal Length vs Width by Species', fontsize=12, fontweight='bold')
axes[1, 1].set_xlabel('Petal Length (cm)')
axes[1, 1].set_ylabel('Petal Width (cm)')
axes[1, 1].legend()
axes[1, 1].grid(alpha=0.3)
plt.tight_layout()
plt.show()
print("✓ Visualizations created successfully!")✓ Visualizations created successfully!الخطوة 5: المعالجة المسبقة للبيانات تمهيداً للتجميع (Data Preprocessing for Clustering)
5.1 اختيار الخصائص (Feature Selection)
# Select only the feature columns (exclude target variables)
feature_columns = ['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)']
X = df_iris[feature_columns]
print("Selected Features for Clustering:")
print(X.head())
print("\nFeature Matrix Shape:", X.shape)Selected Features for Clustering:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
Feature Matrix Shape: (150, 4)5.2 تطبيع الخصائص (Feature Normalization)
لماذا نطبّع البيانات (Why normalize)؟
قد تختلف مقاييس (scales) الخصائص المختلفة. على سبيل المثال:
- طول الكأس (Sepal length): من 4 إلى 8 سم
- عرض البتلة (Petal width): من 0.1 إلى 2.5 سم
تعمل خوارزميات التجميع (خصوصاً تلك التي تعتمد على مقاييس المسافة) بشكل أفضل عندما تكون الخصائص على نفس المقياس.
يقوم StandardScaler بتحويل الخصائص عن طريق:
- طرح المتوسط (centering)
- القسمة على الانحراف المعياري (scaling)
الصيغة (Formula): z = (x - μ) / σ
# Initialize the StandardScaler
scaler = StandardScaler()
# Fit and transform the data
X_scaled = scaler.fit_transform(X)
# Convert back to DataFrame for better visualization
X_scaled_df = pd.DataFrame(X_scaled, columns=feature_columns)
print("Original Data (first 5 rows):")
print(X.head())
print("\n" + "="*60)
print("\nScaled Data (first 5 rows):")
print(X_scaled_df.head())
print("\n" + "="*60)
print("\nOriginal Data Statistics:")
print(X.describe())
print("\n" + "="*60)
print("\nScaled Data Statistics (Notice mean ≈ 0, std ≈ 1):")
print(X_scaled_df.describe())Original Data (first 5 rows):
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
============================================================
Scaled Data (first 5 rows):
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 -0.900681 1.032057 -1.341272 -1.312977
1 -1.143017 -0.124958 -1.341272 -1.312977
2 -1.385353 0.337848 -1.398138 -1.312977
3 -1.506521 0.106445 -1.284407 -1.312977
4 -1.021849 1.263460 -1.341272 -1.312977
============================================================
Original Data Statistics:
sepal length (cm) sepal width (cm) petal length (cm) \
count 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667
std 0.828066 0.433594 1.764420
min 4.300000 2.000000 1.000000
25% 5.100000 2.800000 1.600000
50% 5.800000 3.000000 4.350000
75% 6.400000 3.300000 5.100000
max 7.900000 4.400000 6.900000
petal width (cm)
count 150.000000
mean 1.198667
std 0.763161
min 0.100000
25% 0.300000
50% 1.300000
75% 1.800000
max 2.500000
============================================================
Scaled Data Statistics (Notice mean ≈ 0, std ≈ 1):
sepal length (cm) sepal width (cm) petal length (cm) \
count 1.500000e+02 1.500000e+02 1.500000e+02
mean -4.736952e-16 -6.631732e-16 3.315866e-16
std 1.003350e+00 1.003350e+00 1.003350e+00
min -1.870024e+00 -2.438987e+00 -1.568735e+00
25% -9.006812e-01 -5.877635e-01 -1.227541e+00
50% -5.250608e-02 -1.249576e-01 3.362659e-01
75% 6.745011e-01 5.692513e-01 7.627586e-01
max 2.492019e+00 3.114684e+00 1.786341e+00
petal width (cm)
count 1.500000e+02
mean -2.842171e-16
std 1.003350e+00
min -1.444450e+00
25% -1.181504e+00
50% 1.332259e-01
75% 7.905908e-01
max 1.710902e+00الخطوة 6: تطبيق تجميع K-Means (Apply K-Means Clustering)
6.1 تحديد العدد الأمثل للمجموعات (Determine Optimal Number of Clusters)
سنستخدم طريقتين:
- طريقة المرفق (Elbow Method): البحث عن نقطة "المرفق" (elbow) في مخطط WCSS
- مقياس Silhouette (Silhouette Score): كلما ارتفعت القيمة، دلّ ذلك على تجميع أفضل
# We'll use the Elbow Method to find optimal k
max_k = 10
wcss = []
silhouette_scores = []
print("Computing optimal number of clusters...")
# Explain when python take the last value and when it doesn't
for k in range(2, max_k + 1):
kmeans = KMeans(n_clusters=k, init='k-means++',
max_iter=300, n_init=10, random_state=42)
kmeans.fit(X_scaled)
wcss.append(kmeans.inertia_)
# Calculate silhouette score
sil_score = silhouette_score(X_scaled, kmeans.labels_)
silhouette_scores.append(sil_score)
print(f" k={k}: WCSS={kmeans.inertia_:.2f}, Silhouette={sil_score:.3f}")
print("\n✓ Optimization complete!")Computing optimal number of clusters...
k=2: WCSS=223.73, Silhouette=0.580
k=3: WCSS=140.97, Silhouette=0.459
k=4: WCSS=114.62, Silhouette=0.385
k=5: WCSS=91.30, Silhouette=0.347
k=6: WCSS=81.76, Silhouette=0.341
k=7: WCSS=71.32, Silhouette=0.329
k=8: WCSS=62.65, Silhouette=0.340
k=9: WCSS=55.26, Silhouette=0.343
k=10: WCSS=50.62, Silhouette=0.340
✓ Optimization complete!# Plot the results
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# Elbow plot
ax1.plot(range(2, max_k + 1), wcss, marker='o', linewidth=2, markersize=8)
ax1.set_title('Elbow Method for Optimal K', fontsize=14, fontweight='bold')
ax1.set_xlabel('Number of Clusters (K)', fontsize=12)
ax1.set_ylabel('WCSS', fontsize=12)
ax1.axvline(x=3, color='red', linestyle='--', alpha=0.7, label='True k=3')
ax1.legend()
ax1.grid(alpha=0.3)
# Silhouette score plot
ax2.plot(range(2, max_k + 1), silhouette_scores, marker='s',
linewidth=2, markersize=8, color='green')
ax2.set_title('Silhouette Score vs K', fontsize=14, fontweight='bold')
ax2.set_xlabel('Number of Clusters (K)', fontsize=12)
ax2.set_ylabel('Silhouette Score', fontsize=12)
ax2.axvline(x=3, color='red', linestyle='--', alpha=0.7, label='True k=3')
ax2.legend()
ax2.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# Find best k
best_k_idx = np.argmax(silhouette_scores)
best_k = best_k_idx + 2
print(f"\n✓ Best k based on Silhouette Score: {best_k} (Score: {max(silhouette_scores):.3f})")
✓ Best k based on Silhouette Score: 2 (Score: 0.580)6.2 تدريب النموذج النهائي لـ K-Means (Train Final K-Means Model)
# Train K-Means with optimal k=3
optimal_k = 3
kmeans_final = KMeans(n_clusters=optimal_k, init='k-means++',
max_iter=300, n_init=10, random_state=42)
# Fit and predict
kmeans_labels = kmeans_final.fit_predict(X_scaled)
# Add predictions to the DataFrame
df_iris['kmeans_cluster'] = kmeans_labels
print("K-Means Clustering Complete!")
print(f"\nCluster Distribution:")
print(df_iris['kmeans_cluster'].value_counts().sort_index())
print("\n" + "="*60)
print("\nSample predictions:")
print(df_iris[['species_name', 'kmeans_cluster']].head(10))K-Means Clustering Complete!
Cluster Distribution:
kmeans_cluster
0 53
1 50
2 47
Name: count, dtype: int64
============================================================
Sample predictions:
species_name kmeans_cluster
0 setosa 1
1 setosa 1
2 setosa 1
3 setosa 1
4 setosa 1
5 setosa 1
6 setosa 1
7 setosa 1
8 setosa 1
9 setosa 16.3 تصور نتائج K-Means بيانياً (Visualize K-Means Results)
# Create visualization
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Plot 1: True Species
for species_idx, species in enumerate(['setosa', 'versicolor', 'virginica']):
species_data = df_iris[df_iris['species_name'] == species]
axes[0].scatter(species_data['sepal length (cm)'],
species_data['petal length (cm)'],
label=species, alpha=0.6, s=80)
axes[0].set_title('True Species Labels', fontsize=14, fontweight='bold')
axes[0].set_xlabel('Sepal Length (cm)', fontsize=12)
axes[0].set_ylabel('Petal Length (cm)', fontsize=12)
axes[0].legend()
axes[0].grid(alpha=0.3)
# Plot 2: K-Means Clusters
scatter = axes[1].scatter(df_iris['sepal length (cm)'],
df_iris['petal length (cm)'],
c=df_iris['kmeans_cluster'],
cmap='viridis', alpha=0.6, s=80)
# Add centroids
centroids_original = scaler.inverse_transform(kmeans_final.cluster_centers_)
axes[1].scatter(centroids_original[:, 0], centroids_original[:, 2],
marker='X', s=300, c='red', edgecolor='black',
linewidth=2, label='Centroids')
axes[1].set_title('K-Means Predicted Clusters', fontsize=14, fontweight='bold')
axes[1].set_xlabel('Sepal Length (cm)', fontsize=12)
axes[1].set_ylabel('Petal Length (cm)', fontsize=12)
axes[1].legend()
axes[1].grid(alpha=0.3)
plt.colorbar(scatter, ax=axes[1], label='Cluster')
plt.tight_layout()
plt.show()
# Calculate and display performance
sil_score = silhouette_score(X_scaled, kmeans_labels)
print(f"\n✓ Final K-Means Silhouette Score: {sil_score:.3f}")
✓ Final K-Means Silhouette Score: 0.459الخطوة 7: تطبيق الخرائط ذاتية التنظيم (Self-Organizing Maps - SOM)
7.1 تدريب نموذج SOM (Train SOM Model)
سنستخدم مكتبة sklearn_som التي توفر تنفيذاً لخوارزمية SOM متوافقاً مع scikit-learn.
المعاملات (Parameters):
m, n: أبعاد الشبكة (Grid dimensions) (سنستخدم 3×1 لثلاث مجموعات)dim: عدد خصائص الإدخال (4 في حالتنا)lr: معدل التعلم (Learning rate)max_iter: عدد تكرارات التدريب (training iterations)
# Initialize SOM with a 3x1 grid
# For 3 clusters, a smaller grid like 1x3 or 2x2 could also work
som_model = SOM(m=3, n=1, dim=4, lr=0.5, max_iter=1000, random_state=42)
print("Training Self-Organizing Map...")
print(f" Grid size: 3×1")
print(f" Input dimensions: 4 features")
print(f" Learning rate: 0.5")
print(f" Max iterations: 1000")
# Fit the model
som_model.fit(X_scaled)
# Predict cluster assignments
som_labels = som_model.predict(X_scaled)
# Add to DataFrame
df_iris['som_cluster'] = som_labels
print("\n✓ SOM Training Complete!")
print(f"\nCluster Distribution:")
print(df_iris['som_cluster'].value_counts().sort_index())
print("\n" + "="*60)
print("\nSample predictions:")
print(df_iris[['species_name', 'som_cluster']].head(10))Training Self-Organizing Map...
Grid size: 3×1
Input dimensions: 4 features
Learning rate: 0.5
Max iterations: 1000
✓ SOM Training Complete!
Cluster Distribution:
som_cluster
0 70
1 30
2 50
Name: count, dtype: int64
============================================================
Sample predictions:
species_name som_cluster
0 setosa 2
1 setosa 2
2 setosa 2
3 setosa 2
4 setosa 2
5 setosa 2
6 setosa 2
7 setosa 2
8 setosa 2
9 setosa 27.2 تصور نتائج SOM بيانياً (Visualize SOM Results)
# Create visualization
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Plot 1: True Species (for comparison)
for species in ['setosa', 'versicolor', 'virginica']:
species_data = df_iris[df_iris['species_name'] == species]
axes[0].scatter(species_data['sepal length (cm)'],
species_data['petal length (cm)'],
label=species, alpha=0.6, s=80)
axes[0].set_title('True Species Labels', fontsize=14, fontweight='bold')
axes[0].set_xlabel('Sepal Length (cm)', fontsize=12)
axes[0].set_ylabel('Petal Length (cm)', fontsize=12)
axes[0].legend()
axes[0].grid(alpha=0.3)
# Plot 2: SOM Clusters
scatter = axes[1].scatter(df_iris['sepal length (cm)'],
df_iris['petal length (cm)'],
c=df_iris['som_cluster'],
cmap='plasma', alpha=0.6, s=80)
axes[1].set_title('SOM Predicted Clusters', fontsize=14, fontweight='bold')
axes[1].set_xlabel('Sepal Length (cm)', fontsize=12)
axes[1].set_ylabel('Petal Length (cm)', fontsize=12)
axes[1].grid(alpha=0.3)
plt.colorbar(scatter, ax=axes[1], label='Cluster')
plt.tight_layout()
plt.show()
# Calculate performance
som_sil_score = silhouette_score(X_scaled, som_labels)
print(f"\n✓ SOM Silhouette Score: {som_sil_score:.3f}")
✓ SOM Silhouette Score: 0.412الخطوة 8: مقارنة K-Means بـ SOM
8.1 جدول المقارنة (Comparison Table)
# Create comparison DataFrame
comparison_df = df_iris[['species_name', 'species', 'kmeans_cluster', 'som_cluster']].copy()
print("="*60)
print("CLUSTERING COMPARISON")
print("="*60)
print("\nFirst 15 samples:")
print(comparison_df.head(15))
print("\n" + "="*60)
print("\nCluster Distribution Comparison:")
print("\nTrue Species:")
print(df_iris['species_name'].value_counts().sort_index())
print("\nK-Means Clusters:")
print(df_iris['kmeans_cluster'].value_counts().sort_index())
print("\nSOM Clusters:")
print(df_iris['som_cluster'].value_counts().sort_index())============================================================
CLUSTERING COMPARISON
============================================================
First 15 samples:
species_name species kmeans_cluster som_cluster
0 setosa 0 1 2
1 setosa 0 1 2
2 setosa 0 1 2
3 setosa 0 1 2
4 setosa 0 1 2
5 setosa 0 1 2
6 setosa 0 1 2
7 setosa 0 1 2
8 setosa 0 1 2
9 setosa 0 1 2
10 setosa 0 1 2
11 setosa 0 1 2
12 setosa 0 1 2
13 setosa 0 1 2
14 setosa 0 1 2
============================================================
Cluster Distribution Comparison:
True Species:
species_name
setosa 50
versicolor 50
virginica 50
Name: count, dtype: int64
K-Means Clusters:
kmeans_cluster
0 53
1 50
2 47
Name: count, dtype: int64
SOM Clusters:
som_cluster
0 70
1 30
2 50
Name: count, dtype: int648.2 تحليل الجدولة التقاطعية (Cross-Tabulation Analysis)
تساعدنا الجدولة التقاطعية (cross-tabulation) على معرفة مدى نجاح كل خوارزمية تجميع في استرجاع البنية الحقيقية للأنواع (species).
# Create cross-tabulation tables
print("\n" + "="*60)
print("K-MEANS: Species vs Predicted Clusters")
print("="*60)
kmeans_crosstab = pd.crosstab(df_iris['species_name'],
df_iris['kmeans_cluster'],
margins=True)
print(kmeans_crosstab)
print("\n" + "="*60)
print("SOM: Species vs Predicted Clusters")
print("="*60)
som_crosstab = pd.crosstab(df_iris['species_name'],
df_iris['som_cluster'],
margins=True)
print(som_crosstab)
# Performance comparison
print("\n" + "="*60)
print("PERFORMANCE METRICS")
print("="*60)
print(f"K-Means Silhouette Score: {sil_score:.3f}")
print(f"SOM Silhouette Score: {som_sil_score:.3f}")
print(f"\nBetter performing model: {'K-Means' if sil_score > som_sil_score else 'SOM'}")
============================================================
K-MEANS: Species vs Predicted Clusters
============================================================
kmeans_cluster 0 1 2 All
species_name
setosa 0 50 0 50
versicolor 39 0 11 50
virginica 14 0 36 50
All 53 50 47 150
============================================================
SOM: Species vs Predicted Clusters
============================================================
som_cluster 0 1 2 All
species_name
setosa 0 0 50 50
versicolor 21 29 0 50
virginica 49 1 0 50
All 70 30 50 150
============================================================
PERFORMANCE METRICS
============================================================
K-Means Silhouette Score: 0.459
SOM Silhouette Score: 0.412
Better performing model: K-Means8.3 تصور جنباً إلى جنب (Side-by-Side Visualization)
# Create comprehensive comparison plot
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# 0,0 | 0,1
# 1,0 | 1,1
# Sepal measurements - K-Means
scatter1 = axes[0, 0].scatter(df_iris['sepal length (cm)'],
df_iris['sepal width (cm)'],
c=df_iris['kmeans_cluster'],
cmap='viridis', alpha=0.6, s=80)
axes[0, 0].set_title('K-Means: Sepal Measurements', fontsize=12, fontweight='bold')
axes[0, 0].set_xlabel('Sepal Length (cm)')
axes[0, 0].set_ylabel('Sepal Width (cm)')
axes[0, 0].grid(alpha=0.3)
plt.colorbar(scatter1, ax=axes[0, 0], label='Cluster')
# Sepal measurements - SOM
scatter2 = axes[0, 1].scatter(df_iris['sepal length (cm)'],
df_iris['sepal width (cm)'],
c=df_iris['som_cluster'],
cmap='plasma', alpha=0.6, s=80)
axes[0, 1].set_title('SOM: Sepal Measurements', fontsize=12, fontweight='bold')
axes[0, 1].set_xlabel('Sepal Length (cm)')
axes[0, 1].set_ylabel('Sepal Width (cm)')
axes[0, 1].grid(alpha=0.3)
plt.colorbar(scatter2, ax=axes[0, 1], label='Cluster')
# Petal measurements - K-Means
scatter3 = axes[1, 0].scatter(df_iris['petal length (cm)'],
df_iris['petal width (cm)'],
c=df_iris['kmeans_cluster'],
cmap='viridis', alpha=0.6, s=80)
axes[1, 0].set_title('K-Means: Petal Measurements', fontsize=12, fontweight='bold')
axes[1, 0].set_xlabel('Petal Length (cm)')
axes[1, 0].set_ylabel('Petal Width (cm)')
axes[1, 0].grid(alpha=0.3)
plt.colorbar(scatter3, ax=axes[1, 0], label='Cluster')
# Petal measurements - SOM
scatter4 = axes[1, 1].scatter(df_iris['petal length (cm)'],
df_iris['petal width (cm)'],
c=df_iris['som_cluster'],
cmap='plasma', alpha=0.6, s=80)
axes[1, 1].set_title('SOM: Petal Measurements', fontsize=12, fontweight='bold')
axes[1, 1].set_xlabel('Petal Length (cm)')
axes[1, 1].set_ylabel('Petal Width (cm)')
axes[1, 1].grid(alpha=0.3)
plt.colorbar(scatter4, ax=axes[1, 1], label='Cluster')
plt.tight_layout()
plt.show()
print("✓ Comprehensive comparison visualizations created!")✓ Comprehensive comparison visualizations created!الخطوة 9: العمل مع DataFrames - عمليات متقدمة (Advanced Operations)
9.1 التجميع والتلخيص (Grouping and Aggregation)
يتيح لنا التجميع (grouping) إجراء عمليات على مجموعات جزئية (subsets) من البيانات.
print("="*60)
print("GROUPING AND AGGREGATION")
print("="*60)
# Group by species and calculate mean values
species_means = df_iris.groupby('species_name')[feature_columns].mean()
print("\nMean values by species:")
print(species_means)
print("\n" + "="*60)
# Group by K-Means cluster and calculate mean values
kmeans_cluster_means = df_iris.groupby('kmeans_cluster')[feature_columns].mean()
print("\nMean values by K-Means cluster:")
print(kmeans_cluster_means)
print("\n" + "="*60)
# Multiple aggregations
cluster_stats = df_iris.groupby('kmeans_cluster')[feature_columns].agg(['mean', 'std', 'min', 'max'])
print("\nDetailed statistics by K-Means cluster:")
print(cluster_stats)============================================================
GROUPING AND AGGREGATION
============================================================
Mean values by species:
sepal length (cm) sepal width (cm) petal length (cm) \
species_name
setosa 5.006 3.418 1.464
versicolor 5.936 2.770 4.260
virginica 6.588 2.974 5.552
petal width (cm)
species_name
setosa 0.244
versicolor 1.326
virginica 2.026
============================================================
Mean values by K-Means cluster:
sepal length (cm) sepal width (cm) petal length (cm) \
kmeans_cluster
0 5.801887 2.673585 4.369811
1 5.006000 3.418000 1.464000
2 6.780851 3.095745 5.510638
petal width (cm)
kmeans_cluster
0 1.413208
1 0.244000
2 1.972340
============================================================
Detailed statistics by K-Means cluster:
sepal length (cm) sepal width (cm) \
mean std min max mean
kmeans_cluster
0 5.801887 0.405487 4.9 6.6 2.673585
1 5.006000 0.352490 4.3 5.8 3.418000
2 6.780851 0.490624 5.9 7.9 3.095745
petal length (cm) \
std min max mean std min max
kmeans_cluster
0 0.254310 2.0 3.0 4.369811 0.560420 3.0 5.6
1 0.381024 2.3 4.4 1.464000 0.173511 1.0 1.9
2 0.262063 2.5 3.8 5.510638 0.638011 4.4 6.9
petal width (cm)
mean std min max
kmeans_cluster
0 1.413208 0.311348 1.0 2.4
1 0.244000 0.107210 0.1 0.6
2 1.972340 0.330811 1.4 2.59.2 إنشاء أعمدة جديدة (Creating New Columns)
يمكننا إنشاء خصائص مشتقة (derived features) بناءً على الأعمدة الموجودة.
# Create new derived features
df_iris['sepal_area'] = df_iris['sepal length (cm)'] * df_iris['sepal width (cm)']
df_iris['petal_area'] = df_iris['petal length (cm)'] * df_iris['petal width (cm)']
df_iris['sepal_to_petal_ratio'] = df_iris['sepal_area'] / df_iris['petal_area']
print("New derived features created:")
print(df_iris[['sepal_area', 'petal_area', 'sepal_to_petal_ratio']].head(10))
print("\n" + "="*60)
print("\nStatistics of derived features by species:")
print(df_iris.groupby('species_name')[['sepal_area', 'petal_area', 'sepal_to_petal_ratio']].mean())New derived features created:
sepal_area petal_area sepal_to_petal_ratio
0 17.85 0.28 63.750000
1 14.70 0.28 52.500000
2 15.04 0.26 57.846154
3 14.26 0.30 47.533333
4 18.00 0.28 64.285714
5 21.06 0.68 30.970588
6 15.64 0.42 37.238095
7 17.00 0.30 56.666667
8 12.76 0.28 45.571429
9 15.19 0.15 101.266667
============================================================
Statistics of derived features by species:
sepal_area petal_area sepal_to_petal_ratio
species_name
setosa 17.2088 0.3628 57.443120
versicolor 16.5262 5.7204 2.970577
virginica 19.6846 11.2962 1.7651149.3 حفظ النتائج (Saving Results)
يمكننا حفظ نتائجنا في ملف CSV لاستخدامها لاحقاً.
# Save the results to a CSV file
output_file = 'iris_clustering_results.csv'
df_iris.to_csv(output_file, index=False)
print(f"\n✓ Results saved to '{output_file}'")
# Demonstrate how to read it back
print("\nTo read the file later, use:")
print("df_loaded = pd.read_csv('iris_clustering_results.csv')")
print("\nExample of loaded data:")
df_loaded = pd.read_csv(output_file)
print(df_loaded.head())
✓ Results saved to 'iris_clustering_results.csv'
To read the file later, use:
df_loaded = pd.read_csv('iris_clustering_results.csv')
Example of loaded data:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
species_name species kmeans_cluster som_cluster sepal_area petal_area \
0 setosa 0 1 2 17.85 0.28
1 setosa 0 1 2 14.70 0.28
2 setosa 0 1 2 15.04 0.26
3 setosa 0 1 2 14.26 0.30
4 setosa 0 1 2 18.00 0.28
sepal_to_petal_ratio
0 63.750000
1 52.500000
2 57.846154
3 47.533333
4 64.285714الخطوة 10: الملخص وأهم النقاط المستفادة (Summary and Key Takeaways)
print("="*70)
print(" "*15 + "LAB SUMMARY AND KEY TAKEAWAYS")
print("="*70)
print("\n📊 DATASET OVERVIEW:")
print(f" • Dataset: Iris Flower Dataset")
print(f" • Samples: {len(df_iris)} flowers")
print(f" • Features: {len(feature_columns)} measurements")
print(f" • True Classes: 3 species")
print("\n🔧 DATA PREPROCESSING:")
print(f" • Feature selection using DataFrame column indexing")
print(f" • Standardization using StandardScaler")
print(f" • Missing values: None detected")
print("\n🎯 K-MEANS CLUSTERING:")
print(f" • Optimal k: {optimal_k} (determined by Elbow Method)")
print(f" • Silhouette Score: {sil_score:.3f}")
print(f" • Cluster sizes: {df_iris['kmeans_cluster'].value_counts().sort_index().tolist()}")
print("\n🗺️ SELF-ORGANIZING MAPS:")
print(f" • Grid size: 3×1")
print(f" • Silhouette Score: {som_sil_score:.3f}")
print(f" • Cluster sizes: {df_iris['som_cluster'].value_counts().sort_index().tolist()}")
print("\n📈 PERFORMANCE COMPARISON:")
print(f" • Best model: {'K-Means' if sil_score > som_sil_score else 'SOM'}")
print(f" • Score difference: {abs(sil_score - som_sil_score):.3f}")
print("\n💡 KEY LEARNINGS:")
print(" ✓ DataFrames provide powerful data manipulation capabilities")
print(" ✓ Feature scaling is crucial for distance-based algorithms")
print(" ✓ Both K-Means and SOM can identify natural groupings in data")
print(" ✓ Visualization helps validate clustering results")
print(" ✓ Real-world data requires careful preprocessing")
print("\n" + "="*70)======================================================================
LAB SUMMARY AND KEY TAKEAWAYS
======================================================================
📊 DATASET OVERVIEW:
• Dataset: Iris Flower Dataset
• Samples: 150 flowers
• Features: 4 measurements
• True Classes: 3 species
🔧 DATA PREPROCESSING:
• Feature selection using DataFrame column indexing
• Standardization using StandardScaler
• Missing values: None detected
🎯 K-MEANS CLUSTERING:
• Optimal k: 3 (determined by Elbow Method)
• Silhouette Score: 0.459
• Cluster sizes: [53, 50, 47]
🗺️ SELF-ORGANIZING MAPS:
• Grid size: 3×1
• Silhouette Score: 0.412
• Cluster sizes: [70, 30, 50]
📈 PERFORMANCE COMPARISON:
• Best model: K-Means
• Score difference: 0.047
💡 KEY LEARNINGS:
✓ DataFrames provide powerful data manipulation capabilities
✓ Feature scaling is crucial for distance-based algorithms
✓ Both K-Means and SOM can identify natural groupings in data
✓ Visualization helps validate clustering results
✓ Real-world data requires careful preprocessing
======================================================================أسئلة تدريبية (Exercise Questions)
أجب عن الأسئلة التالية بناءً على نتائجك:
1. عمليات DataFrame (DataFrame Operations):
- Q1.1: كيف يمكنك تصفية الـ DataFrame لعرض الزهور التي يزيد طول البتلة (petal length) فيها عن 4.5 سم فقط؟
- Q1.2: أنشئ عموداً جديداً باسم
total_sizeيمثل مجموع القياسات الأربعة جميعها.
2. استكشاف البيانات (Data Exploration):
- Q2.1: ما هو النوع (species) الذي يمتلك أكبر متوسط لمساحة البتلة (petal area)؟
- Q2.2: استخدم
groupby()لإيجاد أقصى طول للكأس (sepal length) في كل مجموعة (cluster) من مجموعات K-Means.
3. تحليل التجميع (Clustering Analysis):
- Q3.1: ماذا يحدث إذا غيّرت عدد مجموعات K-Means إلى 2 أو 4؟
- Q3.2: جرّب تدريب SOM بحجم شبكة (grid size) مختلف (مثلاً 2×2). كيف تتغير النتائج؟
4. هندسة الخصائص (Feature Engineering):
- Q4.1: أنشئ خاصية جديدة:
petal_sepal_length_ratio = petal length / sepal length - Q4.2: أعد تشغيل K-Means مع تضمين هذه الخاصية الجديدة. هل يحسّن ذلك مقياس Silhouette (Silhouette Score)؟
5. المقارنة (Comparison):
- Q5.1: متى تُفضّل استخدام K-Means على SOM؟
- Q5.2: متى يكون استخدام SOM أكثر ملاءمة؟
# Exercise Solutions - Try to solve before running!
# Q1.1: Filter flowers with petal length > 4.5
# large_petals = df_iris[df_iris['petal length (cm)'] > 4.5]
# print(large_petals)
# Q1.2: Create total_size column
# df_iris['total_size'] = df_iris[feature_columns].sum(axis=1)
# print(df_iris[['species_name', 'total_size']].head())
# Q2.1: Species with largest average petal area
# print(df_iris.groupby('species_name')['petal_area'].mean())
# Q2.2: Maximum sepal length by cluster
# print(df_iris.groupby('kmeans_cluster')['sepal length (cm)'].max())الخاتمة (Conclusion)
في هذا المعمل، تعلمت:
- ✅ كيفية العمل مع أطر بيانات pandas (DataFrames) لمعالجة البيانات
- ✅ خطوات المعالجة المسبقة الأساسية لخوارزميات التجميع
- ✅ كيفية تطبيق K-Means و SOM على بيانات واقعية
- ✅ تقنيات تقييم نتائج التجميع وتصويرها بياناً
- ✅ كيفية مقارنة خوارزميات التجميع المختلفة
تذكّر: مفتاح نجاح عملية التجميع يكمن في:
- فهم بياناتك من خلال التحليل الاستكشافي للبيانات "Exploratory Data Analysis" (EDA)
- المعالجة المسبقة الصحيحة (التحجيم (scaling)، والتعامل مع القيم المفقودة)
- اختيار مقاييس تقييم مناسبة
- تصور النتائج بياناً للتحقق من صحة الاستنتاجات