Pandas: الترصيف والإلحاق

آخر تحديث: 2026-08-26

يقوم الدمج "بالضم الأفقي" (مطابقة الأعمدة بمفتاح)، بينما يقوم الربط بالضم "الأفقي/الرأسي" (تجميع الصفوف أو الأعمدة). دمج بيانات المبيعات لمدة 12 شهرًا في جدول سنوي واحد، أو دمج التقارير من 3 فروع في تقرير رئيسي — هذا هو الهدف من استخدام concat. يتناول هذا القسم اتجاه المحور في concat، والفهرسة، والمحاذاة، والمفاتيح للعلامات متعددة المستويات، إلى جانب استراتيجيات الاختيار بين concat وmerge.

⚠️ ملاحظة: يتطلب الكود أدناه بيئة بايثون محلية للتشغيل.

1. What You Will Learn


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)

100%
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 أبسط.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): قم بإنشاء 3 DataFrames بنفس البنية (بيانات مبيعات Q1/Q2/Q3)، قم بدمجها عموديًا باستخدام concat (ignore_index=True)، واحسب إجمالي المبيعات.
  2. متوسط (الصعوبة ⭐⭐): قم بإنشاء 2 DataFrames بأسماء أعمدة مختلفة جزئيًا، قم بدمجها باستخدام join='outer' و join='inner' على التوالي، وقارن الاختلافات في الأعمدة.
  3. تحدي (الصعوبة ⭐⭐⭐): قم بمحاكاة بيانات 6 أشهر لـ 4 مناطق، ادمجها باستخدام concat(keys=regions) → نظفها باستخدام reset_index → احسب الإجماليات حسب المنطقة → افحص الاتجاهات الشهرية.

← Previous: Merge and Join · Next: Reshaping and Pivoting →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%