Pandas: إعادة تشكيل وتدوير

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

يمكن تخزين نفس البيانات كـ "جدول واسع" لسهولة القراءة البشرية أو "جدول طويل" للتحليل البرمجي. توفر Pandas أربعة أدوات أساسية للتحويل بين هذه التنسيقات: pivot / melt / stack / unstack. يستخدم هذا الدرس مخططات Mermaid لتوضيح اتجاه كل عملية إعادة تشكيل، مما يمنحك فهمًا بديهيًا لمعنى "الذوبان" و"التدوير" - وهي تصور يفتقر إليه معظم الدروس التعليمية المنافسة.

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

1. What You Will Learn


2. تحويل تقرير مبيعات أليس

(1) نقطة الألم: تنسيق التقرير ≠ تنسيق التحليل

يكون تقرير المبيعات الشهري لأليس بالتنسيق العريض (الأشهر موزعة عبر الأعمدة)، لكن التحليل يتطلب التنسيق الطويل (الأشهر كعمود واحد):

PYTHON
import pandas as pd

# Wide format — easy to read, hard to analyze
wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano'],
    'Jan': [320, 280, 200],
    'Feb': [350, 300, 220],
    'Mar': [380, 310, 240]
})
print(wide)
#     product  Jan  Feb  Mar
TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.

(2) الحل: صهر التنسيق العريض إلى تنسيق طويل

▶ مثال

TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.

: صهر التنسيق العريض إلى طويل (الصعوبة ⭐)

PYTHON
import pandas as pd

wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano'],
    'Jan': [320, 280, 200],
    'Feb': [350, 300, 220],
    'Mar': [380, 310, 240]
})

# Melt: month columns become rows
long = wide.melt(id_vars='product', var_name='month', value_name='sales')
print(long)
#     product month  sales
# 0     Latte   Jan    320
# 1     Mocha   Jan    280
# 2 Americano   Jan    200
# 3     Latte   Feb    350
# ...
TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.

3. الجداول العريضة مقابل الجداول الطويلة

(1) مخطط Mermaid: العريض ⇄ الطويل

100%
graph LR
    subgraph Wide["Wide Table — Human-Friendly"]
        W["product | Jan | Feb | Mar<br>Latte   | 320 | 350 | 380<br>Mocha  | 280 | 300 | 310"]
    end
    subgraph Long["Long Table — Machine-Analysis-Friendly"]
        L["product | month | sales<br>Latte   | Jan   | 320<br>Latte   | Feb   | 350<br>Latte   | Mar   | 380<br>Mocha  | Jan   | 280<br>..."]
    end
    Wide -->|"melt()"| Long
    Long -->|"pivot()"| Wide
TEXT 📖 للعرض فقط
> **الناتج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والتالي مع الشرح. قد تختلف القيم الفعليه قليلاً حسب إصدار مكتبة pandas.

(2) مقارنة العريض مقابل الطويل

الميزة الجدول العريض الجدول الطويل
أين تقيم القيم في أسماء الأعمدة في الصفوف
الأفضل لـ القراءة البشرية / التقارير التحليل الآلي / ggplot
إضافة شهر جديد إضافة عمود (يتغير الهيكل) إضافة صفوف (الهيكل دون تغيير)
groupby غير ملائم مدعوم بشكل طبيعي
الاستخدام النموذجي تقارير إكسل قواعد البيانات / البيانات المرتبة

4. pivot — التحريك الأساسي

(1) التحريك الأساسي

▶ مثال

TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعليّة قليلاً حسب إصدار pandas.

: تحريك من طويل إلى عريض (الصعوبة ⭐)

PYTHON
import pandas as pd

long = pd.DataFrame({
    'product': ['Latte', 'Latte', 'Latte', 'Mocha', 'Mocha', 'Mocha'],
    'month': ['Jan', 'Feb', 'Mar', 'Jan', 'Feb', 'Mar'],
    'sales': [320, 350, 380, 280, 300, 310]
})

# Pivot: unique row=product, unique column=month, values=sales
wide = long.pivot(index='product', columns='month', values='sales')
print(wide)
# month     Feb   Jan   Mar
# product
# Americano  220   200   240
# Latte      350   320   380
# Mocha      300   280   310

# Reset index to make product a regular column
wide = wide.reset_index()
print(wide.columns)  # MultiIndex → flatten
TEXT 📖 للعرض فقط
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعليّة قليلاً حسب إصدار pandas.
⚠️ ملاحظة: يتطلب pivot أن تكون تركيبة الفهرس+الأعمدة فريدة (غير مكررة). إذا كانت هناك قيم مكررة (مثل: سجلات متعددة لنفس المنتج في نفس الشهر)، سيُصدر خطأً. في هذه الحالة، استخدم pivot_table (الذي يقوم بالتجميع تلقائياً).


5. pivot_table — التحريك التجميعي

(1) التحريك التجميعي

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلاً بناءً على إصدار pandas.

: تحريك تجميعي pivot_table (الصعوبة ⭐⭐)

PYTHON
import pandas as pd

# Multiple entries per product+month (need aggregation)
df = pd.DataFrame({
    'product': ['Latte', 'Latte', 'Latte', 'Latte', 'Mocha', 'Mocha'],
    'month': ['Jan', 'Jan', 'Feb', 'Feb', 'Jan', 'Feb'],
    'sales': [150, 170, 180, 170, 140, 160],
    'quantity': [30, 34, 36, 34, 28, 32]
})

# pivot_table with aggregation function
result = pd.pivot_table(
    df,
    values='sales',
    index='product',
    columns='month',
    aggfunc='sum'   # default is 'mean'
)
print(result)
# month     Feb   Jan
# product
# Latte     350   320
# Mocha     160   140

# Multiple aggregation functions
result2 = pd.pivot_table(
    df,
    values=['sales', 'quantity'],
    index='product',
    columns='month',
    aggfunc={'sales': 'sum', 'quantity': 'mean'}
)

# Margins — add row/column totals
result3 = pd.pivot_table(
    df, values='sales', index='product', columns='month',
    aggfunc='sum', margins=True, margins_name='Total'
)
print(result3)
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلاً بناءً على إصدار pandas.

(2) pivot مقابل pivot_table

الميزة pivot pivot_table
القيم المكررة يسبب خطأ يقوم بالتجميع
دالة التجميع لا توجد sum/mean/count, إلخ.
الحواف (margins) غير مدعوم ✅ إجماليات الصفوف/الأعمدة
أعمدة القيم المتعددة عمود واحد أعمدة متعددة
الأفضل لـ بيانات نظيفة بدون تكرارات بيانات تحتوي على تكرارات تحتاج إلى تجميع

6. melt — تعميق

(1) معلمات melt

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Piston على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار pandas.

: شرح معلمات melt (الصعوبة ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'product': ['Latte', 'Mocha'],
    'region': ['North', 'South'],
    'Jan_sales': [320, 280],
    'Feb_sales': [350, 300],
    'Jan_quantity': [64, 56],
    'Feb_quantity': [70, 60]
})

# Basic melt — all non-id columns become rows
result1 = df.melt(id_vars=['product', 'region'])
print(result1)

# Specify value_vars — only melt certain columns
result2 = df.melt(
    id_vars=['product', 'region'],
    value_vars=['Jan_sales', 'Feb_sales'],
    var_name='month',
    value_name='sales'
)
print(result2)

# Multiple id_vars
result3 = df.melt(
    id_vars=['product'],
    value_vars=['Jan_sales', 'Feb_sales'],
    var_name='month_sales',
    value_name='amount'
)
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Piston على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار pandas.

7. stack / unstack

(1) stack — تحويل الأعمدة إلى صفوف

▶ مثال

TEXT 📖 للعرض فقط
> **Output:** Run 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 the pandas version.

: stack/unstack مع فهرس متعدد المستويات (الصعوبة ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'Jan': [320, 280],
    'Feb': [350, 300],
    'Mar': [380, 310]
}, index=['Latte', 'Mocha'])
df.index.name = 'product'

# stack: columns → row level (wide → long)
stacked = df.stack()
print(stacked)
# product  month
# Latte    Jan      320
#          Feb      350
#          Mar      380
# Mocha    Jan      280
# ...

# unstack: row level → columns (long → wide)
unstacked = stacked.unstack()
print(unstacked)
#          Jan   Feb   Mar
# product
# Latte    320   350   380
# Mocha    280   300   310

# With MultiIndex: choose which level to unstack
multi = pd.DataFrame({
    'sales': [320, 350, 280, 300],
    'region': ['North', 'North', 'South', 'South'],
    'month': ['Jan', 'Feb', 'Jan', 'Feb']
})
pivot = multi.set_index(['region', 'month'])['sales']
print(pivot.unstack(level='month'))
TEXT 📖 للعرض فقط
> **Output:** Run 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 the pandas version.

(2) مقارنة بين أربع طرق لإعادة التشكيل

الطريقة الاتجاه الإدخال → الإخراج الأفضل لـ
pivot طويل → عريض تحديد الفهرس/الأعمدة/القيم الجداول الطويلة بدون تكرارات
pivot_table طويل → عريض + aggfunc للتجميع الجداول الطويلة مع تكرارات
melt عريض → طويل تحديد id_vars أسماء الأعمدة تصبح قيم صفوف
stack أعمدة → صفوف أعمدة → مستوى فهرس عمليات الفهرس المتعدد
unstack صفوف → أعمدة مستوى فهرس → أعمدة العكس لـ stack

8. مثال كامل: خط أنابيب التحويل الكامل من العريض إلى الطويل

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python محلية (pandas 2.x). لا تحتوي خادم Piston على pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليًلاً اعتمادًا على إصدار pandas.

: خط أنابيب التحويل الكامل من العريض إلى الطويل (الصعوبة ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# مثال شامل: العريض ↔ الطويل
# خط أنابيب تشكيل بيانات المبيعات
# ============================================

# 1. البدء بالتنسيق العريض (نمط التقرير)
np.random.seed(42)
wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano', 'Espresso', 'Cappuccino'],
    'region': ['North', 'South', 'East', 'West', 'North'],
    'Jan': np.random.randint(200, 500, 5),
    'Feb': np.random.randint(220, 520, 5),
    'Mar': np.random.randint(240, 540, 5),
    'Apr': np.random.randint(250, 550, 5)
})

print("=== التنسيق العريض (5 منتجات × 4 أشهر) ===")
print(wide)

# 2. الذوبان (Melt) إلى التنسيق الطويل
long = wide.melt(
    id_vars=['product', 'region'],
    var_name='month',
    value_name='sales'
)
print(f"\n=== التنسيق الطويل: {len(long)} صف ===")
print(long.head(8))

# 3. التحليل في التنسيق الطويل (ال تجميع (groupby) طبيعي)
monthly_total = long.groupby('month')['sales'].sum()
print(f"\n=== المجاميع الشهرية ===\n{monthly_total}")

region_avg = long.groupby('region')['sales'].mean().round(0)
print(f"\n=== متوسط المنطقة ===\n{region_avg}")

# 4. التدوير (Pivot) مرة أخرى إلى العريض (ملخص المنتج × الشهر)
summary_wide = pd.pivot_table(
    long, values='sales', index='product', columns='month',
    aggfunc='sum', margins=True, margins_name='Total'
)
print(f"\n=== ملخص جدول المحاور ===")
print(summary_wide)

# 5. التكديس/فك التكديس (Stack/unstack) مع فهرس متعدد
multi = long.set_index(['region', 'product', 'month'])['sales']
by_region = multi.unstack(level='month')
print(f"\n=== فك التكديس حسب الشهر (المنطقة×المنتج) ===")
print(by_region.head(8))
TEXT 📖 للعرض فقط
> **الإخراج:** قم بالتشغيل في بيئة Python محلية (pandas 2.x). لا تحتوي خادم Piston على pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليًلاً اعتمادًا على إصدار pandas.

❓ أسئلة شائعة

س ما الفرق بين pivot و pivot_table؟
ج تتطلب pivot أن يكون مزيج الفهرس+الأعمدة فريدًا - تُصدر خطأً إذا وُجدت تكرارات. بينما تتعامل pivot_table مع التكرارات عن طريق تجميعها (المتوسط هو الافتراضي، لكن يمكنك تحديد المجموع/العدد، إلخ) وتدعم أيضًا الهوامش (إجماليات الصف/العمود). استخدم pivot للبيانات البسيطة التي لا تحتوي تكرارات؛ واستخدم pivot_table عند وجود تكرارات أو الحاجة للتجميع. عمليًا، تعد pivot_table الخيار الأكثر أمانًا لـ 80% من السيناريوهات اليومية.
س ما هي id_vars في melt؟
ج id_vars هي الأعمدة التي لا يتم دمجها (melt) - تظل كمُعرّفات للصفوف. يتم جمع أسماء جميع الأعمدة الأخرى في عمود متغير (var_name) وقيمها في عمود القيمة (value_name). على سبيل المثال، بعد دمج أعمدة الأشهر Jan/Feb/Mar، تصبح السلاسل 'Jan'/'Feb'/'Mar' قيماً في عمود الشهر، والأرقام المقابلة تصبح قيماً في عمود المبيعات.
س هل stack و unstack عمليتان متعاكستان لبعضهما؟
ج نعم - تدفع stack الأعمدة إلى صفوف (واسع → طويل)، بينما تنشر unstack الصفوف في أعمدة (طويل → واسع). df.stack().unstack() يعيد الجدول الأصلي. تعملان على مستويات MultiIndex - تضيف stack مستوى صف، وتحذف unstack مستوى عمود. يتحكم المعامل level في المستوى المتأثر.
س متى يجب استخدام الجداول الواسعة مقابل الطويلة؟
ج الجداول الواسعة مثالية للقراءة البشرية (التقارير، العروض التقديمية، Excel). الجداول الطويلة مثالية للتحليل البرمجي (groupby، دمج، التصور). يوصي مبدأ Tidy Data بالصيغة الطويلة: متغير واحد لكل عمود، وملاحظة واحدة لكل صف. احفظ البيانات بالصيغة الطويلة (بنية مستقرة) واعرضها بالصيغة الواسعة (مخرجات pivot).
س ماذا أفعل عندما يُبلّغ pivot عن قيم مكررة؟
ج هذا يعني أن مزيج الفهرس+الأعمدة يحتوي على صفوف مكررة (مثل: سجلات متعددة لنفس المنتج في نفس الشهر). لديك خياران: ① استخدام pivot_table مع aggfunc='sum'/'mean' للتجميع التلقائي؛ ② التجميع أولاً باستخدام groupby ثم الـ pivot. تكون pivot_table أكثر إيجازًا وتُفضل عمومًا.
س كيف أعكس عملية الـ melt؟
ج استخدم pivot أو pivot_table لإعادة الشكل الأصلي: long.pivot(index='product', columns='month', values='sales'). الـ melt والـ pivot عمليتان متعاكستان. لاحظ أن أنواع البيانات قد تتغير بعد الـ melt (قد تصبح الأعمدة الرقمية من نوع كائن)، لذا قد تحتاج إلى التحويل باستخدام astype قبل إعادة التشكيل.
س ما هي Tidy Data؟
ج Tidy Data هو مبدأ تنظيم البيانات اقترحه هادلي ويكهام: ① يشغل كل متغير عمودًا واحدًا؛ ② تشغر كل ملاحظة صفًا واحدًا؛ ③ يشغل كل نوع من الوحدات المراقبة جدولًا واحدًا. تفي الجداول الطويلة بمبدأ Tidy Data بشكل طبيعي؛ بينما لا تفي الجداول الواسعة (حيث يكون متغير الشهر مخفيًا في أسماء الأعمدة). الهدف من وجود دالة melt في Pandas هو بالضبط لتنظيف (tidy) الجداول الواسعة إلى صيغة Tidy Data.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): أنشئ جدولًا عريضًا (3 منتجات × 3 أشهر)، استخدم dissolve لتحويله إلى تنسيق طويل، ثم استخدم pivot لتحويله مرة أخرى إلى تنسيق عريض، وتحقق من تطابق النتائج.
  2. متوسط (الصعوبة ⭐⭐): أنشئ بيانات مبيعات تتضمن قيمًا مكررة (سجلات متعددة لنفس المنتج في نفس الشهر)، ثم استخدم pivot_table(aggfunc='sum') مع margins=True لإنشاء جدول محوري يحتوي على مجاميع الصفوف والأعمدة.
  3. تحدي (الصعوبة ⭐⭐⭐): قم بمحاكاة بيانات بتنسيق طويل لـ 3 مناطق × 4 منتجات × 3 أشهر، ثم أكمل ما يلي: set_index لإنشاء فهرس متعدد → unstack للنشر حسب الشهر → stack للضغط مرة أخرى → dissolve لتحويل بديل → pivot_table للتحليل المتقاطع.

← Previous: Concatenation and Appending · Next: String Operations →

Web-Tutorial.com

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

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

100%