Pandas: إعادة تشكيل وتدوير
آخر تحديث: 2026-08-26
يمكن تخزين نفس البيانات كـ "جدول واسع" لسهولة القراءة البشرية أو "جدول طويل" للتحليل البرمجي. توفر Pandas أربعة أدوات أساسية للتحويل بين هذه التنسيقات: pivot / melt / stack / unstack. يستخدم هذا الدرس مخططات Mermaid لتوضيح اتجاه كل عملية إعادة تشكيل، مما يمنحك فهمًا بديهيًا لمعنى "الذوبان" و"التدوير" - وهي تصور يفتقر إليه معظم الدروس التعليمية المنافسة.
1. What You Will Learn
- ❶ pivot — basic pivoting
- ❷ pivot_table — aggregation pivoting
- ❸ melt — wide to long
- ❹ stack / unstack
- ❺ Wide vs. long tables and Tidy Data
2. تحويل تقرير مبيعات أليس
(1) نقطة الألم: تنسيق التقرير ≠ تنسيق التحليل
يكون تقرير المبيعات الشهري لأليس بالتنسيق العريض (الأشهر موزعة عبر الأعمدة)، لكن التحليل يتطلب التنسيق الطويل (الأشهر كعمود واحد):
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
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.
(2) الحل: صهر التنسيق العريض إلى تنسيق طويل
▶ مثال
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.
: صهر التنسيق العريض إلى طويل (الصعوبة ⭐)
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
# ...
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas.
3. الجداول العريضة مقابل الجداول الطويلة
(1) مخطط Mermaid: العريض ⇄ الطويل
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
> **الناتج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والتالي مع الشرح. قد تختلف القيم الفعليه قليلاً حسب إصدار مكتبة pandas.
(2) مقارنة العريض مقابل الطويل
| الميزة | الجدول العريض | الجدول الطويل |
|---|---|---|
| أين تقيم القيم | في أسماء الأعمدة | في الصفوف |
| الأفضل لـ | القراءة البشرية / التقارير | التحليل الآلي / ggplot |
| إضافة شهر جديد | إضافة عمود (يتغير الهيكل) | إضافة صفوف (الهيكل دون تغيير) |
| groupby | غير ملائم | مدعوم بشكل طبيعي |
| الاستخدام النموذجي | تقارير إكسل | قواعد البيانات / البيانات المرتبة |
4. pivot — التحريك الأساسي
(1) التحريك الأساسي
▶ مثال
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعليّة قليلاً حسب إصدار pandas.
: تحريك من طويل إلى عريض (الصعوبة ⭐)
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
> **الخرج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعليّة قليلاً حسب إصدار pandas.
pivot أن تكون تركيبة الفهرس+الأعمدة فريدة (غير مكررة). إذا كانت هناك قيم مكررة (مثل: سجلات متعددة لنفس المنتج في نفس الشهر)، سيُصدر خطأً. في هذه الحالة، استخدم pivot_table (الذي يقوم بالتجميع تلقائياً).
5. pivot_table — التحريك التجميعي
(1) التحريك التجميعي
▶ مثال
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلاً بناءً على إصدار pandas.
: تحريك تجميعي pivot_table (الصعوبة ⭐⭐)
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)
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Pandas على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تتغير القيم الفعلية قليلاً بناءً على إصدار pandas.
(2) pivot مقابل pivot_table
| الميزة | pivot | pivot_table |
|---|---|---|
| القيم المكررة | يسبب خطأ | يقوم بالتجميع |
| دالة التجميع | لا توجد | sum/mean/count, إلخ. |
| الحواف (margins) | غير مدعوم | ✅ إجماليات الصفوف/الأعمدة |
| أعمدة القيم المتعددة | عمود واحد | أعمدة متعددة |
| الأفضل لـ | بيانات نظيفة بدون تكرارات | بيانات تحتوي على تكرارات تحتاج إلى تجميع |
6. melt — تعميق
(1) معلمات melt
▶ مثال
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Piston على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار pandas.
: شرح معلمات melt (الصعوبة ⭐⭐)
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'
)
> **الإخراج:** قم بالتشغيل في بيئة بايثون محلية (pandas 2.x). لا يحتوي خادم Piston على مكتبة pandas مثبتة مسبقاً. يرجى تثبيتها محلياً (`pip install pandas`) والمتابعة. قد تختلف القيم الفعلية قليلاً اعتماداً على إصدار pandas.
7. stack / unstack
(1) stack — تحويل الأعمدة إلى صفوف
▶ مثال
> **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 مع فهرس متعدد المستويات (الصعوبة ⭐⭐)
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'))
> **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. مثال كامل: خط أنابيب التحويل الكامل من العريض إلى الطويل
▶ مثال
> **الإخراج:** قم بالتشغيل في بيئة Python محلية (pandas 2.x). لا تحتوي خادم Piston على pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليًلاً اعتمادًا على إصدار pandas.
: خط أنابيب التحويل الكامل من العريض إلى الطويل (الصعوبة ⭐⭐⭐)
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))
> **الإخراج:** قم بالتشغيل في بيئة Python محلية (pandas 2.x). لا تحتوي خادم Piston على pandas مثبتة مسبقًا. يرجى تثبيتها محليًا (`pip install pandas`) واتباع الخطوات. قد تختلف القيم الفعلية قليًلاً اعتمادًا على إصدار pandas.
❓ أسئلة شائعة
df.stack().unstack() يعيد الجدول الأصلي. تعملان على مستويات MultiIndex - تضيف stack مستوى صف، وتحذف unstack مستوى عمود. يتحكم المعامل level في المستوى المتأثر.long.pivot(index='product', columns='month', values='sales'). الـ melt والـ pivot عمليتان متعاكستان. لاحظ أن أنواع البيانات قد تتغير بعد الـ melt (قد تصبح الأعمدة الرقمية من نوع كائن)، لذا قد تحتاج إلى التحويل باستخدام astype قبل إعادة التشكيل.📖 ملخص
pivotتحول البيانات من الشكل الطويل إلى العريض (عندما لا تكون هناك قيم مكررة)؛ بينماpivot_tableتحول البيانات من الطويل إلى العريض مع إمكانية التجميع (عندما تكون هناك قيم مكررة).meltتحول البيانات من الشكل العريض إلى الطويل:id_varsهي الأعمدة المحفوظة؛ وأسماء الأعمدة المتبقية تتحول إلى عمود متغير، وقيمها تصبح عمود القيمة.stackتدفع الأعمدة لتصبح صفوفًا (من العريض → الطويل)؛ بينماunstackتنشر الصفوف لتصبح أعمدة (من الطويل → العريض) — وهما عمليات عكسية.- الجداول ذات الشكل العريض سهلة القراءة للبشر؛ بينما الجداول ذات الشكل الطويل مناسبة للتحليل الآلي؛ وتوصي مبادئ "البيانات المرتبة" باستخدام الشكل الطويل.
- سير العمل النموذجي: البيانات الخام →
meltلتحويلها إلى شكل مرتب →groupbyللتحليل →pivot_tableلإنشاء التقرير. - إذا أحدث
pivotخطأ بسبب القيم المكررة → انتقل إلىpivot_tableمعaggfunc.
📝 تمارين
- أساسي (الصعوبة ⭐): أنشئ جدولًا عريضًا (3 منتجات × 3 أشهر)، استخدم
dissolveلتحويله إلى تنسيق طويل، ثم استخدمpivotلتحويله مرة أخرى إلى تنسيق عريض، وتحقق من تطابق النتائج. - متوسط (الصعوبة ⭐⭐): أنشئ بيانات مبيعات تتضمن قيمًا مكررة (سجلات متعددة لنفس المنتج في نفس الشهر)، ثم استخدم
pivot_table(aggfunc='sum')معmargins=Trueلإنشاء جدول محوري يحتوي على مجاميع الصفوف والأعمدة. - تحدي (الصعوبة ⭐⭐⭐): قم بمحاكاة بيانات بتنسيق طويل لـ 3 مناطق × 4 منتجات × 3 أشهر، ثم أكمل ما يلي:
set_indexلإنشاء فهرس متعدد →unstackللنشر حسب الشهر →stackللضغط مرة أخرى →dissolveلتحويل بديل →pivot_tableللتحليل المتقاطع.
← Previous: Concatenation and Appending · Next: String Operations →