Pandas: التعامل مع البيانات المكررة
آخر تحديث: 2026-08-26
دمج البيانات، عمليات التكرار في جمع البيانات، أخطاء النظام — البيانات المكررة موجودة في كل مكان. ظهور سجل عميل واحد ثلاث مرات يُضخم إحصائياتك بثلاث مرات. تتيح لك الدوال duplicated / drop_duplicates في مكتبة Pandas الكشف عن البيانات المكررة وتنظيفها بدقة، بينما يوفر معلمتا subset و keep تحكماً مرنًا في مستوى التفصيل. يغطي هذا القسم سير العمل الكامل من الكشف إلى الإزالة.
1. ماذا ستتعلم
- ❶ الكشف عن المكررات باستخدام
duplicated - ❷ إزالة المكررات باستخدام
drop_duplicates - ❸ إزالة التكرار على أساس العمود باستخدام
subset - ❹ استراتيجيات الاستبقاء باستخدام
keep - ❺ إزالة التكرار متعدد الأعمدة والاستراتيجيات الواقعية
2. طلبات القهوة المكررة لـ Alice
(1) نقطة الألم: خلل في النظام يسبب طلبات مكررة
تعرض نظام مقهى Alice لخلل خلال فترة الذروة، وتم تقديم 3 طلبات مرتين:
import pandas as pd
orders = pd.DataFrame({
'order_id': ['O001', 'O001', 'O002', 'O003', 'O003', 'O003', 'O004'],
'customer': ['Alice', 'Alice', 'Bob', 'Charlie', 'Charlie', 'Charlie', 'Carol'],
'drink': ['Latte', 'Latte', 'Americano', 'Mocha', 'Mocha', 'Mocha', 'Espresso'],
'price': [4.50, 4.50, 3.00, 5.50, 5.50, 5.50, 2.50]
})
print(f"Total rows: {len(orders)}")
print(f"Duplicates: {orders.duplicated().sum()}")
# Total rows: 7, Duplicates: 3
> **الإخراج:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas الخاص بك.
(2) الحل: تنظيف بسطر واحد باستخدام drop_duplicates
▶ مثال
> **الإخراج:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas الخاص بك.
: إزالة التكرارات الأساسية (الصعوبة ⭐)
import pandas as pd
orders = pd.DataFrame({
'order_id': ['O001', 'O001', 'O002', 'O003', 'O003', 'O003', 'O004'],
'customer': ['Alice', 'Alice', 'Bob', 'Charlie', 'Charlie', 'Charlie', 'Carol'],
'drink': ['Latte', 'Latte', 'Americano', 'Mocha', 'Mocha', 'Mocha', 'Espresso'],
'price': [4.50, 4.50, 3.00, 5.50, 5.50, 5.50, 2.50]
})
# إزالة الصفوف المكررة تمامًا
clean = orders.drop_duplicates()
print(clean)
# order_id customer drink price
# 0 O001 Alice Latte 4.5
# 2 O002 Bob Americano 3.0
# 3 O003 Charlie Mocha 5.5
# 6 O004 Carol Espresso 2.5
print(f"Before: {len(orders)} → After: {len(clean)}")
# Before: 7 → After: 4
> **الإخراج:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas الخاص بك.
3. الكشف عن التكرارات باستخدام duplicated
(1) الكشف الأساسي
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: الكشف عن التكرارات باستخدام duplicated (الصعوبة ⭐)
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],
'age': [28, 34, 28, 25, 35]
})
# Mark duplicates (first occurrence = False, subsequent = True)
print(df.duplicated())
# 0 False ← first Alice (keep)
# 1 False ← first Bob (keep)
# 2 True ← second Alice (duplicate!)
# 3 False ← first Charlie (keep)
# 4 True ← Bob but age=35 (NOT duplicate — row is different)
# Filter to see only duplicate rows
print(df[df.duplicated()])
# name age
# 2 Alice 28
# Count duplicates
print(f"Duplicate rows: {df.duplicated().sum()}") # 1
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
(2) معلمة `تحكم في منطق التصنيف
قيمة keep |
السلوك | أول تكرار | آخر تكرار |
|---|---|---|---|
| 'first' | الاحتفاظ بأول ظهور | تصنيف False |
تصنيف True |
| 'last' | الاحتفاظ بآخر ظهور | تصنيف True |
تصنيف False |
False |
تصنيف جميع التكرارات | تصنيف True |
تصنيف True |
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: مقارنة معلمة keep (الصعوبة ⭐⭐)
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Alice', 'Bob'],
'score': [85, 92, 85, 85, 92]
})
# keep='first' (default) — first occurrence is NOT duplicate
print("keep='first':", df.duplicated(keep='first').tolist())
# [False, False, True, True, True]
# keep='last' — last occurrence is NOT duplicate
print("keep='last':", df.duplicated(keep='last').tolist())
# [True, True, True, False, False]
# keep=False — ALL duplicates are marked True
print("keep=False:", df.duplicated(keep=False).tolist())
# [True, True, True, True, True]
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
4. إزالة التكرارات باستخدام drop_duplicates
(1) الإزالة الأساسية
▶ مثال
> **الخرج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع الشرح. قد تختلف القيم الفعلي قليلاً حسب إصدار pandas لديك.
: إزالة التكرارات باستخدام drop_duplicates (الصعوبة ⭐)
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice'],
'age': [28, 34, 28, 25, 28],
'city': ['NYC', 'LA', 'NYC', 'Chicago', 'Boston'] # مدينة مختلفة لآخر Alice
})
# إزالة التكرارات الكاملة للصفوف
print(df.drop_duplicates())
# name age city
# 0 Alice 28 NYC
# 1 Bob 34 LA
# 3 Charlie 25 Chicago
# 4 Alice 28 Boston ← تم الاحتفاظ بها (المدينة مختلفة عن الصف 0)
# باستخدام معامل keep
print(df.drop_duplicates(keep='last'))
# name age city
# 1 Bob 34 LA
# 3 Charlie 25 Chicago
# 4 Alice 28 Boston ← تم الاحتفاظ بآخر Alice
# إزالة جميع التكرارات (الاحتفاظ فقط بالصفوف الفريدة)
print(df.drop_duplicates(keep=False))
# name age city
# 1 Bob 34 LA
# 3 Charlie 25 Chicago
# تم إزالة جميع صفوف Alice (لأن هناك أضعاف منها)
> **الخرج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع الشرح. قد تختلف القيم الفعلي قليلاً حسب إصدار pandas لديك.
(2) inplace وإعادة تعيين الفهرس
▶ مثال
> **الخرج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع الشرح. قد تختلف القيم الفعلي قليلاً حسب إصدار pandas لديك.
: inplace و reset_index (الصعوبة ⭐)
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Charlie'],
'score': [85, 92, 85, 78]
})
# الطريقة 1: إعادة التعيين (موصى بها)
df = df.drop_duplicates().reset_index(drop=True)
print(df)
# name score
# 0 Alice 85
# 1 Bob 92
# 2 Charlie 78
# الطريقة 2: استخدام inplace
# df.drop_duplicates(inplace=True)
# df.reset_index(inplace=True, drop=True)
> **الخرج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع الشرح. قد تختلف القيم الفعلي قليلاً حسب إصدار pandas لديك.
5. إزالة التكرار القائمة على الأعمدة مع مجموعة فرعية
(1) النظر فقط إلى أعمدة المفتاح
▶ مثال
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقًا — قم بتثبيتها محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليه قليلاً حسب إصدار pandas الذي تستخدمه.
: إزالة التكرار القائمة على الأعمدة مع مجموعة فرعية (الصعوبة ⭐⭐)
import pandas as pd
df = pd.DataFrame({
'student_id': ['S001', 'S001', 'S002', 'S003', 'S002'],
'name': ['Alice', 'Alice Smith', 'Bob', 'Charlie', 'Robert Bob'],
'math_score': [85, 85, 92, 78, 92],
'attempt': [1, 2, 1, 1, 2]
})
# تكرار الصف الكامل: لا يوجد (عمود الاسم يختلف)
print(f"Full-row duplicates: {df.duplicated().sum()}") # 0
# لكن student_id يجب أن يكون فريدًا!
print(f"student_id duplicates: {df.duplicated(subset='student_id').sum()}") # 2
# إسقاط التكرارات بناءً على student_id فقط
clean = df.drop_duplicates(subset='student_id', keep='first')
print(clean)
# student_id name math_score attempt
# 0 S001 Alice 85 1
# 2 S002 Bob 92 1
# 3 S003 Charlie 78 1
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقًا — قم بتثبيتها محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليه قليلاً حسب إصدار pandas الذي تستخدمه.
(2) إزالة التكرار بناءً على تركيبة أعمدة متعددة
▶ مثال
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقًا — قم بتثبيتها محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليه قليلاً حسب إصدار pandas الذي تستخدمه.
: إزالة التكرار بناءً على تركيبة أعمدة متعددة (الصعوبة ⭐⭐)
import pandas as pd
# نفس الطالب يمكن أن يكون لديه عدة محاولات اختبار
# لكن نفس الطالب + نفس المادة يجب أن يكون فريدًا
df = pd.DataFrame({
'student_id': ['S001', 'S001', 'S001', 'S002', 'S002'],
'subject': ['Math', 'English', 'Math', 'Math', 'English'],
'score': [85, 90, 88, 92, 87],
'attempt_date': ['2024-01-10', '2024-01-10', '2024-02-15', '2024-01-12', '2024-01-12']
})
# التكرار: تركيبة student_id + subject
dupes = df.duplicated(subset=['student_id', 'subject'], keep=False)
print("All rows involved in duplicates:")
print(df[dupes])
# S001 + Math يظهر مرتين (صف 0 و 2)
# الاحتفاظ بأحدث محاولة لكل طالب + مادة
clean = df.drop_duplicates(subset=['student_id', 'subject'], keep='last')
print("\nAfter dedup (keep last attempt):")
print(clean)
# student_id subject score attempt_date
# 0 S001 English 90 2024-01-10
# 2 S001 Math 88 2024-02-15 ← الأحدث
# 3 S002 Math 92 2024-01-12
# 4 S002 English 87 2024-01-12
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقًا — قم بتثبيتها محليًا (`pip install pandas`) وتابع. قد تختلف القيم الفعليه قليلاً حسب إصدار pandas الذي تستخدمه.
6. استراتيجيات للتعامل مع البيانات المكررة
(1) جدول قرارات السيناريوهات
| السيناريو | الاستراتيجية | الكود |
|---|---|---|
| صفوف مكررة بالكامل | drop_duplicates() | df.drop_duplicates() |
| أعمدة مفتاحية مكررة | إزالة التكرارات باستخدام مجموعة فرعية | df.drop_duplicates(subset=['id']) |
| الاحتفاظ بأحدث قيمة | keep='last' | df.drop_duplicates(subset=['id'], keep='last') |
| يحتاج إلى مراجعة يدوية | تحديد بدون حذف | df[df.duplicated(keep=False)] |
| تجميع بدلاً من الحذف | groupby + agg | df.groupby('id').agg({'val': 'mean'}) |
▶ مثال
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً - قم بتثبيته محلياً (`pip install pandas`) وتابع. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
: التجميع بدلاً من الحذف (الصعوبة ⭐⭐⭐)
import pandas as pd
# Multiple readings for same sensor — average instead of drop
df = pd.DataFrame({
'sensor_id': ['T01', 'T01', 'T01', 'T02', 'T02'],
'location': ['Lab A', 'Lab A', 'Lab A', 'Lab B', 'Lab B'],
'temperature': [22.5, 22.7, 22.3, 18.1, 18.3],
'humidity': [45, 47, 43, 55, 57]
})
# Instead of dropping, aggregate: keep mean
agg = df.groupby('sensor_id').agg({
'location': 'first',
'temperature': 'mean',
'humidity': 'mean'
}).reset_index()
print(agg)
# sensor_id location temperature humidity
# 0 T01 Lab A 22.500 45.0
# 1 T02 Lab B 18.200 56.0
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً - قم بتثبيته محلياً (`pip install pandas`) وتابع. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
(2) مخطط تدفق قرار إزالة التكرارات
graph TB
Q["Found duplicates?"] -->|No| DONE["✅ Data is clean"]
Q -->|Yes| TYPE{"Full-row or key-column?"}
TYPE -->|Full-row| SIMPLE["drop_duplicates()"]
TYPE -->|Key-column| SUBSET["drop_duplicates(subset=['id'])"]
SIMPLE --> WHICH["keep='first'/'last'/False?"]
SUBSET --> WHICH
WHICH -->|Keep first| FIRST["keep='first'"]
WHICH -->|Keep last| LAST["keep='last'"]
WHICH -->|Need all for review| REVIEW["duplicated(keep=False) → manual"]
WHICH -->|Values differ| AGG["groupby().agg() → merge"]
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً - قم بتثبيته محلياً (`pip install pandas`) وتابع. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
7. مثال كامل: سير عمل كامل لإزالة تكرار العملاء
▶ مثال
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقمًا - قم بتثبيتها محليًا (`pip install pandas`) واتبع الخطوات. القيم الفعلية قد تختلف قليًباً حسب إصدار pandas الخاص بك.
: سير عمل كامل لإزالة تكرار العملاء (الصعوبة ⭐⭐⭐)
import pandas as pd
# ============================================
# Comprehensive example: Customer dedup
# with multiple strategies
# ============================================
# 1. Raw data with various duplicate patterns
df = pd.DataFrame({
'email': ['alice@example.com', 'alice@example.com', 'bob@example.com',
'charlie@example.com', 'bob@example.com', 'alice@example.com',
'david@example.com'],
'name': ['Alice', 'Alice Smith', 'Bob', 'Charlie', 'Bob Jones',
'Alice', 'David'],
'phone': ['555-0100', '555-0100', '555-0200', '555-0300',
'555-0201', '555-0100', '555-0400'],
'signup_date': ['2024-01-15', '2024-02-01', '2024-01-20',
'2024-03-01', '2024-03-15', '2024-01-15',
'2024-04-01']
})
print("=== Step 1: Detect Duplicates ===")
print(f"Full-row dupes: {df.duplicated().sum()}")
print(f"Email dupes: {df.duplicated(subset='email').sum()}")
print(f"Email+Phone dupes: {df.duplicated(subset=['email','phone']).sum()}")
# 2. Review all rows involved in email duplicates
print("\n=== Step 2: Review Email Duplicates ===")
email_dupes = df[df.duplicated(subset='email', keep=False)]
print(email_dupes.sort_values('email'))
# 3. Strategy: keep the latest signup per email
df['signup_date'] = pd.to_datetime(df['signup_date'])
clean = df.sort_values('signup_date').drop_duplicates(subset='email', keep='last')
print("\n=== Step 3: After Dedup (keep latest) ===")
print(clean[['email', 'name', 'signup_date']])
# 4. Verify
print(f"\nBefore: {len(df)} rows → After: {len(clean)} rows")
print(f"Unique emails: {clean['email'].nunique()}")
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقًا - قم بتثبيتها محليًا (`pip install pandas`) واتبع الخطوات. القيم الفعلية قد تختلف قليًباً حسب إصدار pandas الخاص بك.
❓ أسئلة شائعة
reset_index(drop=True) لإعادة تعيين الفهرس ليكون متتاليًا. drop=True يتخلص من الفهرس القديم؛ وإلا يصبح الفهرس القديم عمودًا جديدًا باسم 'index'. الشكل الموصى به متسلسل هو: df.drop_duplicates().reset_index(drop=True).df = df.drop_duplicates()، الذي يتميز بعلم الدلالة أوضح.📖 ملخص
- الدالة
duplicated()تحدد الصفوف المكررة وتُرجع سلسلة منطقية (Boolean Series)؛ بينماdrop_duplicates()تقوم بإزالة التكرارات مباشرة - يتحكم معلمة
keepفي استراتيجية الاحتفاظ:first(الافتراضي) /last/False(تحديد الكل) - تُحدد معلمة
subsetالأعمدة المستخدمة للحكم على التكرارات، مع دعم الأعمدة المفردة والتركيبات متعددة الأعمدة - بعد إزالة التكرارات، استخدم
reset_index(drop=True)لإعادة تعيين فهرس متتالي - الصفوف مكررة بالكامل → احذفها مباشرة؛ أعمدة المفتاح مكررة → أزل التكرارات باستخدام
subset؛ القيم مختلفة → قم بالتجميع بدلاً من الحذف - اكتشف ومراجعة أولاً (
duplicated)، ثم قرر الاستراتيجية (حذف / تجميع / مراجعة يدوية)
📝 تمارين
- أساسي (مست الصعوبة ⭐): قم بإنشاء DataFrame يحتوي على 3 صفوف مكررة بالكامل (8 صفوف إجمالي)، قم بعدهم باستخدام
duplicated().sum()، نظفهم باستخدامdrop_duplicates()، وقارن عدد الصفوف قبل وبعد التنظيف. - متوسط (مست الصعوبة ⭐⭐): قم بإنشاء DataFrame لدرجات الطلاب (نفس الطالب يظهر عدة مرات لنفس المادة)، قم بإزالة التكرارات باستخدام
subset=['student_id','subject']، واستخدمkeep='last'للاحتفاظ بأحدث درجة. - تحدي (مست الصعوبة ⭐⭐⭐): قم بمحاكاة بيانات تسجيل العملاء (البريد الإلكتروني/الاسم/رقم الهاتف/تاريخ التسجيل)، بما في حالات تكرار البريد الإلكتروني مع اختلاف الاسم/رقم الهاتف. أكمل الخطوات التالية: اكتشف تكرارات البريد الإلكتروني → راجع الصفوف المكررة → قم بالفرز حسب تاريخ التسجيل ثم أزل التكرارات باستخدام
keep='last'→ تحقق من عدد عناوين البريد الإلكتروني الفريدة.
← Previous Lesson: Handling Missing Values · Next Lesson: Data Transformation →