Pandas: الترصيف والإلحاق
آخر تحديث: 2026-08-26
يقوم الدمج "بالضم الأفقي" (مطابقة الأعمدة بمفتاح)، بينما يقوم الربط بالضم "الأفقي/الرأسي" (تجميع الصفوف أو الأعمدة). دمج بيانات المبيعات لمدة 12 شهرًا في جدول سنوي واحد، أو دمج التقارير من 3 فروع في تقرير رئيسي — هذا هو الهدف من استخدام concat. يتناول هذا القسم اتجاه المحور في concat، والفهرسة، والمحاذاة، والمفاتيح للعلامات متعددة المستويات، إلى جانب استراتيجيات الاختيار بين concat وmerge.
⚠️ ملاحظة: يتطلب الكود أدناه بيئة بايثون محلية للتشغيل.
1. What You Will Learn
- ❶ concat axis-based concatenation
- ❷ axis=0 vs axis=1
- ❸ ignore_index for rebuilding the index
- ❹ keys for multi-level labels
- ❺ join alignment strategies
2. تجميع بيانات شارلي لمدة 12 شهرًا
(1) نقطة الألم: قراءة 12 ملف CSV واحدًا تلو الآخر
يحتوي شارلي على 12 ملف CSV للمبيعات الشهرية، وتجميعها يدويًا أمر مُضجر:
PYTHON
import pandas as pd
# تخيل 12 DataFrame منفصلة
jan = pd.DataFrame({'date': ['2024-01-15'], 'sales': [5000]})
feb = pd.DataFrame({'date': ['2024-02-20'], 'sales': [4500]})
mar = pd.DataFrame({'date': ['2024-03-10'], 'sales': [5200]})
# ... 9 أشهر أخرى
TEXT
📖 للعرض فقط
> **الإخراج:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). لا يحتوي خادم Piston على pandas مثبتة مسبقًا — يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
(2) الحل: التجميع دفعة واحدة باستخدام concat
▶ مثال: التجميع العمودي باستخدام concat (صعوبة ⭐)
PYTHON
import pandas as pd
jan = pd.DataFrame({'date': ['2024-01-15'], 'sales': [5000]})
feb = pd.DataFrame({'date': ['2024-02-20'], 'sales': [4500]})
mar = pd.DataFrame({'date': ['2024-03-10'], 'sales': [5200]})
# التجميع عموديًا (axis=0 هو الافتراضي)
year = pd.concat([jan, feb, mar], ignore_index=True)
print(year)
# date sales
# 0 2024-01-15 5000
# 1 2024-02-20 4500
# 2 2024-03-10 5200
TEXT
📖 للعرض فقط
> **الإخراج:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). لا يحتوي خادم Piston على pandas مثبتة مسبقًا — يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
3. المعلمات الأساسية لـ concat
(1) الأفقي مقابل العمودي
▶ مثال
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: axis=0 مقابل axis=1 (الصعوبة ⭐)
PYTHON
import pandas as pd
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})
# axis=0: stack rows (vertical)
print("axis=0 (vertical):")
print(pd.concat([df1, df2], axis=0))
# A B
# 0 1 3
# 1 2 4
# 0 5 7 ← index repeats!
# 1 6 8
# axis=1: stack columns (horizontal)
print("\naxis=1 (horizontal):")
print(pd.concat([df1, df2], axis=1))
# A B A B
# 0 1 3 5 7
# 1 2 4 6 8
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
(2) إعادة بناء الفهرس باستخدام ignore_index
▶ مثال
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: إعادة بناء الفهرس باستخدام ignore_index (الصعوبة ⭐)
PYTHON
import pandas as pd
df1 = pd.DataFrame({'A': [1, 2]})
df2 = pd.DataFrame({'A': [3, 4]})
df3 = pd.DataFrame({'A': [5, 6]})
# Without ignore_index — original indices preserved (gaps possible)
result1 = pd.concat([df1, df2, df3])
print(result1.index.tolist()) # [0, 1, 0, 1, 0, 1] — duplicate!
# With ignore_index — clean sequential index
result2 = pd.concat([df1, df2, df3], ignore_index=True)
print(result2.index.tolist()) # [0, 1, 2, 3, 4, 5] — clean!
print(result2)
# A
# 0 1
# 1 2
# 2 3
# 3 4
# 4 5
# 5 6
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
4. التسميات متعددة المستويات باستخدام المفاتيح
(1) وسم مصادر البيانات
▶ مثال
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: وسم المصادر باستخدام المفاتيح (الصعوبة ⭐⭐)
PYTHON
import pandas as pd
q1 = pd.DataFrame({'sales': [100, 200, 300]})
q2 = pd.DataFrame({'sales': [150, 250, 350]})
q3 = pd.DataFrame({'sales': [180, 280, 380]})
# المفاتيح تنشئ مؤشرًا متعدد المستويات — تتبع من أي DataFrame جاء كل صف
result = pd.concat([q1, q2, q3], keys=['Q1', 'Q2', 'Q3'])
print(result)
# sales
# Q1 0 100
# 1 200
# 2 300
# Q2 0 150
# 1 250
# 2 350
# Q3 0 180
# 1 280
# 2 380
# التحديد حسب المفتاح
print(result.loc['Q2'])
# sales
# 0 150
# 1 250
# 2 350
# إضافة عمود المصدر بدلاً من المؤشر المتعدد المستويات
result2 = pd.concat([q1, q2, q3], keys=['Q1', 'Q2', 'Q3'], names=['quarter'])
result2 = result2.reset_index(level=0)
print(result2)
# quarter sales
# 0 Q1 100
# 1 Q1 200
# ...
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
5. استراتيجيات محاذاة الدمج (join Alignment Strategies)
(1) عندما لا تتطابق أسماء الأعمدة بالكامل
▶ مثال
TEXT
📖 للعرض فقط
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً - يرجى تثبيتها محلياً (`pip install pandas`) واتباع الخطوات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas المستخدم.
: join='outer' مقابل 'inner' (الصعوبة ⭐⭐)
PYTHON
import pandas as pd
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4], 'C': [5, 6]})
df2 = pd.DataFrame({'B': [7, 8], 'C': [9, 10], 'D': [11, 12]})
# join='outer' (الافتراضي) — الاحتفاظ بجميع الأعمدة، ملء القيم المفقودة بـ NaN
print("outer:")
print(pd.concat([df1, df2], axis=0, join='outer'))
# A B C D
# 0 1.0 3 5 NaN
# 1 2.0 4 6 NaN
# 0 NaN 7 9 11.0
# 1 NaN 8 10 12.0
# join='inner' — فقط الأعمدة المشتركة
print("\ninner:")
print(pd.concat([df1, df2], axis=0, join='inner'))
# B C
# 0 3 5
# 1 4 6
# 0 7 9
# 1 8 10
TEXT
📖 للعرض فقط
> **الإخراج:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً - يرجى تثبيتها محلياً (`pip install pandas`) واتباع الخطوات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas المستخدم.
6. concat مقابل دمج
| الميزة | concat | دمج |
|---|---|---|
| العملية | التكديس (رأسي/أفقي) | الدمج (مطابقة بمفتاح) |
| المفتاح | غير مطلوب | يتطلب on/left_on |
| عدد الصفوف | مجموع الصفوف المصدرية (axis=0) | ≤ مجموع الصفوف المصدرية |
| عدد الأعمدة | مجموع الأعمدة المصدرية (axis=1) | اتحاد الأعمدة المصدرية |
| حالة الاستخدام | تكديس البيانات ذات البنية المتطابقة | دمج البيانات ذات البنيات المختلفة |
| السيناريو النموذجي | 12 شهرًا مدمجة في جدول سنوي | المستخدمون + الطلبات مدمجة معًا |
▶ مثال
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: متى تستخدم concat مقابل دمج (الصعوبة ⭐⭐)
PYTHON
import pandas as pd
# Use CONCAT: same structure, stack vertically
jan_sales = pd.DataFrame({'product': ['A', 'B'], 'sales': [100, 200]})
feb_sales = pd.DataFrame({'product': ['A', 'B'], 'sales': [150, 250]})
annual = pd.concat([jan_sales, feb_sales], keys=['Jan', 'Feb'])
# Use MERGE: different structures, link by key
products = pd.DataFrame({'product': ['A', 'B'], 'price': [10, 20]})
categories = pd.DataFrame({'product': ['A', 'B'], 'category': ['Electronics', 'Home']})
enriched = pd.merge(products, categories, on='product')
TEXT
📖 للعرض فقط
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
7. مثال كامل: تجميع وتحليل بيانات متعددة المصادر
(5) ▶ أنواع التجميع (concat)
graph TB
A[بيانات متعددة] --> B{اتجاه التجميع?}
B -->|العمود الرأسي=0| C[تكدس الصفوف من الأعلى للأسفل]
B -->|العمود الأفقي=1| D[وضع الأعمدة جنبًا إلى جنب]
C --> E{هل أسماء الأعمدة متطابقة?}
E -->|نعم| F[تجميع مثالي]
E -->|لا| G[الربط الخارجي outer يملأ NaN / الربط الداخلي inner يحتفظ بالتقاطع]
D --> H{هل فهارس الصفوف متطابقة?}
H -->|نعم| I[تجميع محاذاة]
H -->|لا| J[تعبئة متقاطعة بـ NaN]
TEXT
📖 للعرض فقط
> **الخرج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبتًا مسبقًا — يرجى تثبيته محليًا (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلًا اعتمادًا على إصدار pandas لديك.
▶ مثال
TEXT
📖 للعرض فقط
> **الخرج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبتًا مسبقًا — يرجى تثبيته محليًا (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلًا اعتمادًا على إصدار pandas لديك.
: سير عمل كامل للتجميع من مصادر متعددة (صعوبة ⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# مثال شامل: تجميع من مصادر متعددة
# 3 تقارير إقليمية → تحليل سنوي
# ============================================
# 1. التقارير الشهرية الإقليمية
np.random.seed(42)
regions = {
'الشمال': pd.DataFrame({
'الشهر': pd.date_range('2024-01', periods=6, freq='MS'),
'المبيعات': np.random.randint(3000, 8000, 6),
'الطلبات': np.random.randint(50, 150, 6)
}),
'الجنوب': pd.DataFrame({
'الشهر': pd.date_range('2024-01', periods=6, freq='MS'),
'المبيعات': np.random.randint(2000, 6000, 6),
'الطلبات': np.random.randint(30, 120, 6)
}),
'الشرق': pd.DataFrame({
'الشهر': pd.date_range('2024-01', periods=6, freq='MS'),
'المبيعات': np.random.randint(1500, 5000, 6),
'الطلبات': np.random.randint(20, 100, 6)
})
}
# 2. التجميع مع مفاتيح لتحديد المنطقة
all_data = pd.concat(regions, names=['المنطقة', 'المؤشر'])
all_data = all_data.reset_index(level=0).reset_index(drop=True)
print(f"إجمالي عدد الصفوف: {len(all_data)}")
# 3. إضافة أعمدة مشتقة
all_data['متوسط قيمة الطلب'] = (all_data['المبيعات'] / all_data['الطلبات']).round(2)
# 4. التحليل حسب المنطقة
region_summary = all_data.groupby('المنطقة').agg(
إجمالي_المبيعات=('المبيعات', 'sum'),
إجمالي_الطلبات=('الطلبات', 'sum'),
متوسط_المبيعات_الشهرية=('المبيعات', 'mean')
).round(0)
print("\n=== ملخص حسب المنطقة ===")
print(region_summary)
# 5. الاتجاه الشهري عبر جميع المناطق
monthly = all_data.groupby('الشهر')['المبيعات'].sum()
print(f"\nالشهر ذو الأعلى مبيعًا: {monthly.idxmax().strftime('%Y-%m')}")
TEXT
📖 للعرض فقط
> **الخرج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبتًا مسبقًا — يرجى تثبيته محليًا (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلًا اعتمادًا على إصدار pandas لديك.
❓ أسئلة شائعة
س ما الفرق بين concat و دمج؟
ج concat يكدس البيانات — يدمج الصفوف عموديًا أو الأعمدة أفقيًا دون الحاجة إلى مفتاح مطابق. دمج يدمج البيانات — يطابق الصفوف من جدولين باستخدام المفتاح، مشابه لـ JOIN في SQL. جداول متعددة بنفس الهيكل مدمجة في واحد → concat. جداول بهياكل مختلفة مرتبطة بعمود → دمج. قاعدة عامة: "نفس الهيكل؟كدس باستخدام concat. هيكل مختلف؟دمج باستخدام دمج."
س ماذا يفعلان axis=0 و axis=1؟
ج axis=0 (الافتراضي) يدمج الصفوف عموديًا — يكدس من أعلى إلى أسفل، مما يزيد عدد الصفوف. axis=1 يدمج الأعمدة أفقيًا — يضعها جنبًا إلى جنب، مما يزيد عدد الأعمدة. الدمج العمودي يحاذي الأعمدة تلقائيًا (الأعمدة المفقودة تملأ بـ NaN); الدمج الأفقي يحاذي الصفوف تلقائيًا (الصفوف المفقودة تملأ بـ NaN).
س متى يجب استخدام ignore_index؟
ج للدمج العمودي، تكاد ترغب دائمًا في ignore_index=True — فإنه يعيد بناء فهرس تسلسلي ويتجنب الفهارس المكررة ([0,1,0,1,0,1]). احتفظ فقط بالفهرس الأصلي إذا كان يحمل معنى (مثل فهرس تاريخي). الدمج الأفقي لا يحتاج إلى ignore_index (محاذاة الصفوف تعتمد على الفهرس).
س لماذا تم إهمال append؟
ج تم تحديد df.append() كمهمل في Pandas 1.4 وإزالته تمامًا في 2.0. السبب: append ينشئ كائنًا جديدًا في كل استدعاء، مما يؤدي إلى أداء O(n²) في حلقة؛ pd.concat يدمج كل شيء في مرورة واحدة بـ O(n). البديل:
pd.concat([df1, df2]). للإلحاح القائم على الحلقات: اجمعها في قائمة أولاً، ثم ادمج مرة واحدة في النهاية.س ماذا لو كانت أسماء الأعمدة مختلفة؟
ج أثناء الدمج العمودي، أسماء الأعمدة غير المتطابقة تنتج NaN — فقط الأعمدة بنفس الاسم يتم دمج بياناتها. الحلول: ① أعد تسمية الأعمدة قبل الدمج لتوحيد الأسماء؛ ② استخدم join='inner' للاحتفاظ فقط بالأعمدة المشتركة؛ ③ تعامل مع أعمدة NaN يدويًا بعد الدمج. أفضل الممارسات: توحيد أسماء الأعمدة قبل استدعاء concat.
س كيفية تحسين أداء concat؟
ج تجنب استدعاء concat بشكل متكرر داخل حلقة — كل استدعاء ينشئ DataFrame جديدًا. النهج الصحيح: اجمع جميع DataFrames في قائمة، ثم ادمج مرة واحدة.
frames = [df1, df2, ..., df100]; result = pd.concat(frames). هذا يصنع الفرق بين O(n) و O(n²) مقارنة بالإلحاح القائم على الحلقات.س ماذا يفعل verify_integrity؟
ج verify_integrity=True يتحقق مما إذا كان الفهرس المدمج يحتوي على تكرارات ويثير ValueError إذا كان كذلك. استخدمه لضمان سلامة البيانات — على سبيل المثال، المعرفات الفريدة لا ينبغي أن تظهر مرتين. ومع ذلك، التحقق له تكلفة أداء، لذا تخطه لمجموعات البيانات الكبيرة. للاستخدام اليومي، ignore_index=True أبسط.
📖 ملخص
- تقوم concat بدمج البيانات (دمج الصفوف عمودياً / دمج الأعمدة أفقياً) دون الحاجة إلى مطابقة المفاتيح
- axis=0 تدمج الصفوف عمودياً (الافتراضي)، axis=1 تدمج الأعمدة أفقياً
- ignore_index=True تعيد بناء فهرس متسلسل — مطلوب دائمًا تقريبًا للدمج العمودي
- keys تُعلّم مصادر البيانات، لإنشاء MultiIndex يتعقب المصدر الذي جاء منه كل صف
- join='outer' تحتفظ بجميع الأعمدة (مع ملء NaN)، join='inner' تحتفظ فقط بالأعمدة المشتركة
- concat مقابل دمج: استخدم concat لدمج هياكل متطابقة، واستخدم دمج لدمج هياكل مختلفة
- تجنب استخدام concat داخل حلقة تكرارية — اجمع البيانات في قائمة أولاً، ثم استخدم concat مرة واحدة في النهاية
📝 تمارين
- أساسي (الصعوبة ⭐): قم بإنشاء 3 DataFrames بنفس البنية (بيانات مبيعات Q1/Q2/Q3)، قم بدمجها عموديًا باستخدام concat (ignore_index=True)، واحسب إجمالي المبيعات.
- متوسط (الصعوبة ⭐⭐): قم بإنشاء 2 DataFrames بأسماء أعمدة مختلفة جزئيًا، قم بدمجها باستخدام join='outer' و join='inner' على التوالي، وقارن الاختلافات في الأعمدة.
- تحدي (الصعوبة ⭐⭐⭐): قم بمحاكاة بيانات 6 أشهر لـ 4 مناطق، ادمجها باستخدام concat(keys=regions) → نظفها باستخدام reset_index → احسب الإجماليات حسب المنطقة → افحص الاتجاهات الشهرية.