ورقة pandas المرجعية
آخر تحديث
إنشاء DataFrame
جداول DataFrame صغيرة تُبنى يدويًا للأمثلة والاختبارات وجداول البحث.
| المهمة | الكود |
|---|---|
| استيراد pandas و NumPy | import pandas as pd
import numpy as np |
| من قاموس أعمدة | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| من قائمة صفوف | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| من مصفوفة NumPy | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Series بتسميات | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| نطاق من التواريخ | pd.date_range("2026-01-01", periods=7, freq="D") |
قراءة الملفات وكتابتها
استدعاء واحد يحمّل الملف إلى DataFrame، واستدعاء واحد يكتبه من جديد.
| المهمة | الكود |
|---|---|
| قراءة ملف CSV | df = pd.read_csv("sales.csv") |
| أعمدة محددة فقط مع تواريخ حقيقية | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| استخدام عمود كفهرس | pd.read_csv("sales.csv", index_col="id") |
| فاصل Tab أو فاصل آخر | pd.read_csv("sales.tsv", sep="\t") |
| اعتبار علامات إضافية قيمًا مفقودة | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| قراءة الصفوف الأولى فقط | pd.read_csv("sales.csv", nrows=1000) |
| قراءة ورقة Excel (تتطلب openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| قراءة سجلات JSON | pd.read_json("records.json") |
| القراءة من قاعدة بيانات SQL | pd.read_sql("SELECT * FROM orders", conn) |
| كتابة CSV بدون الفهرس | df.to_csv("clean.csv", index=False) |
| الكتابة إلى Excel أو Parquet | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
فحص البيانات
أول الاستدعاءات التي تجريها على أي مجموعة بيانات جديدة.
| المهمة | الكود |
|---|---|
| الصفوف الأولى والأخيرة | df.head(), df.tail(3) |
| عدد الصفوف والأعمدة | df.shape # (6, 7) |
| أنواع الأعمدة وعدد القيم غير الفارغة | df.info() |
| إحصاءات موجزة للأعمدة الرقمية | df.describe() |
| ملخص لكل الأعمدة | df.describe(include="all") |
| أسماء الأعمدة وأنواعها | df.columns, df.dtypes |
| تكرار كل قيمة | df["city"].value_counts() |
| نسبة كل قيمة | df["city"].value_counts(normalize=True) |
| القيم المميزة وعددها | df["city"].unique(), df["city"].nunique() |
| القيم المفقودة في كل عمود | df.isna().sum() |
| عينة عشوائية من الصفوف | df.sample(3, random_state=0) |
| الارتباط بين الأعمدة الرقمية | df.corr(numeric_only=True) |
اختيار الأعمدة والصفوف
يختار loc حسب التسمية ويشمل نهاية الشريحة. ويختار iloc حسب الموضع ويستثنيها.
| المهمة | الكود |
|---|---|
| عمود واحد على شكل Series | df["age"] |
| عدة أعمدة على شكل DataFrame | df[["name", "age"]] |
| صفوف وأعمدة حسب التسمية | df.loc[0:2, ["name", "age"]] |
| صفوف وأعمدة حسب الموضع | df.iloc[0:2, 0:3] |
| قيمة واحدة | df.at[0, "age"], df.iat[0, 3] |
| الصف الأخير | df.iloc[-1] |
| أعمدة من نوع واحد | df.select_dtypes(include="number") |
| أعمدة النصوص (pandas 3) | df.select_dtypes(include="str") |
| أعمدة حسب تطابق الاسم | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| البحث عن صفوف بعمود | df.set_index("name").loc["Ada", "salary"] |
| العودة إلى فهرس 0، 1، 2 | df.reset_index(drop=True) |
| حذف أعمدة أو صفوف | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
فلترة الصفوف
ضع كل شرط بين قوسين واجمع الشروط باستخدام & (و) و | (أو) و ~ (نفي).
| المهمة | الكود |
|---|---|
| الصفوف التي تحقق شرطًا | df[df["age"] > 30] |
| الشرطان معًا | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| أحد الشرطين | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| قيمة ضمن قائمة | df[df["city"].isin(["Oslo", "Rome"])] |
| قيمة خارج قائمة | df[~df["city"].isin(["Oslo", "Rome"])] |
| بين قيمتين، مع الطرفين | df[df["age"].between(30, 40)] |
| الفلترة بنص استعلام | df.query("age > 30 and city == 'Oslo'") |
| استخدام متغير في استعلام | min_age = 30
df.query("age > @min_age") |
| النص يحتوي على | df[df["name"].str.contains("e", case=False)] |
| قيم مفقودة أو موجودة | df[df["age"].isna()], df[df["age"].notna()] |
| فلترة الصفوف واختيار الأعمدة | df.loc[df["age"] > 30, ["name", "salary"]] |
| أعلى أو أدنى n صفوف | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| صفوف بقيمة مكررة | df[df.duplicated(subset=["city"], keep=False)] |
الترتيب والتصنيف
| المهمة | الكود |
|---|---|
| الترتيب حسب عمود | df.sort_values("salary") |
| الأكبر أولًا | df.sort_values("salary", ascending=False) |
| الترتيب حسب عدة أعمدة | df.sort_values(["city", "salary"], ascending=[True, False]) |
| القيم المفقودة أولًا | df.sort_values("age", na_position="first") |
| الترتيب حسب الفهرس | df.sort_index() |
| تصنيف القيم، 1 للأعلى | df["salary"].rank(ascending=False) |
إضافة الأعمدة وتعديلها
العمليات على العمود كاملًا سريعة. لا تلجأ إلى apply إلا عندما لا توجد دالة مدمجة تؤدي المهمة.
| المهمة | الكود |
|---|---|
| عمود جديد من أعمدة أخرى | df["bonus"] = df["salary"] * 0.1 |
| إضافة عمود داخل سلسلة استدعاءات | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| قيمة حسب شرط | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| قيمة حسب عدة شروط | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| تغيير القيم حيث يتحقق شرط | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| ربط القيم بقاموس | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| تطبيق دالة على عمود | df["salary"].apply(lambda x: round(x, -3)) |
| تطبيق دالة على كل خلية | df[["age", "salary"]].map(lambda x: x / 1000) |
| إعادة تسمية الأعمدة | df.rename(columns={"salary": "pay"}) |
| تغيير نوع عمود | df["salary"].astype("float64") |
| نص إلى أرقام، والقيم غير الصالحة إلى NaN | pd.to_numeric(s, errors="coerce") |
| أرقام إلى فئات مسماة | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| أرقام إلى مجموعات متساوية الحجم | pd.qcut(df["salary"], q=4, labels=False) |
| المرور على الصفوف (بطيء، كحل أخير) | for row in df.itertuples():
print(row.name, row.salary) |
القيم المفقودة والتنظيف
تظهر الأرقام المفقودة على شكل NaN والتواريخ المفقودة على شكل NaT . كل استدعاء هنا يُرجع كائنًا جديدًا، لذا أسند النتيجة إلى متغير.
| المهمة | الكود |
|---|---|
| نسبة القيم المفقودة في كل عمود | df.isna().mean() |
| حذف الصفوف التي فيها أي قيمة مفقودة | df.dropna() |
| حذف الصفوف التي ينقصها عمود أساسي | df.dropna(subset=["age"]) |
| التعبئة بقيمة لكل عمود | df.fillna({"age": df["age"].median()}) |
| التعبئة من الصف السابق أو التالي | df["age"].ffill(), df["age"].bfill() |
| ملء الفجوات بالاستيفاء | df["age"].interpolate() |
| تحويل العلامات إلى قيم مفقودة | df.replace(["n/a", "-", ""], np.nan) |
| حذف الصفوف المكررة | df.drop_duplicates() |
| الإبقاء على آخر صف لكل مفتاح | df.drop_duplicates(subset=["city"], keep="last") |
| ترتيب أسماء الأعمدة | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| تقييد القيم المتطرفة | df["salary"].clip(lower=4000, upper=6000) |
| نوع category للنصوص المتكررة | df["city"] = df["city"].astype("category") |
أعمدة النصوص (.str)
في pandas 3 تأخذ أعمدة النصوص نوع البيانات str افتراضيًا. تعمل دوال .str على العمود كاملًا، وتبقى القيم المفقودة مفقودة.
| المهمة | الكود |
|---|---|
| أحرف كبيرة أو صغيرة أو بأول حرف كبير | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| إزالة المسافات | df["name"].str.strip() |
| استبدال نص | df["email"].str.replace("mail.com", "example.com") |
| الاستبدال بتعبير نمطي (regex) | df["email"].str.replace(r"@.*", "", regex=True) |
| يحتوي على، يبدأ بـ | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| طول كل قيمة | df["name"].str.len() |
| الأحرف الأولى | df["name"].str[:2] |
| التقسيم إلى أعمدة | df["email"].str.split("@", expand=True) |
| الاستخراج بمجموعة regex | df["email"].str.extract(r"@(\w+)") |
| دمج أعمدة النصوص | df["name"] + " (" + df["city"] + ")" |
| إكمال الأرقام بأصفار | df["salary"].astype("str").str.zfill(6) |
| نوع النصوص الجديد | df["name"].dtype # str |
التواريخ والأوقات
تعمل الواجهة .dt على أعمدة التاريخ والوقت. والفهرس الزمني المرتب ( ts أدناه) يتيح التقطيع بالنص وإعادة التجميع الزمني (resampling).
| المهمة | الكود |
|---|---|
| تحويل عمود إلى تواريخ | df["date"] = pd.to_datetime(df["date"]) |
| التحويل بصيغة محددة | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| السنة والشهر واسم اليوم | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| تجميع التواريخ حسب الشهر | df["date"].dt.to_period("M") |
| عدد الأيام بين تاريخين | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| إضافة أيام أو أشهر | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| صفوف ضمن نطاق تواريخ | df[df["date"].between("2021-01-01", "2022-12-31")] |
| فهرس زمني يُقطَّع بالنص | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| المجموع لكل شهر | ts["salary"].resample("ME").sum() |
المتوسط الأسبوعي؛ "QE" ربع سنوي و "YE" سنوي | ts["salary"].resample("W").mean() |
| متوسط متحرك | ts["salary"].rolling(3).mean() |
| التغير عن الصف السابق | ts["salary"].diff(), ts["salary"].pct_change() |
| إزاحة القيم صفًا واحدًا للأسفل | ts["salary"].shift(1) |
| مجموع تراكمي | ts["salary"].cumsum() |
| تعيين المناطق الزمنية وتحويلها | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
التجميع والتلخيص
تقسم groupby الصفوف حسب المفتاح، وتطبق دالة على كل مجموعة، ثم تجمع النتائج.
| المهمة | الكود |
|---|---|
| المتوسط لكل مجموعة | df.groupby("city")["salary"].mean() |
| عدة إحصاءات معًا | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| أعمدة نتائج بأسماء | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| التجميع حسب عمودين | df.groupby(["city", "team"])["salary"].sum() |
| إبقاء المفاتيح كأعمدة | df.groupby("city", as_index=False)["salary"].mean() |
| عدد الصفوف في كل مجموعة | df.groupby("city").size() |
| نتيجة المجموعة في كل صف | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| الإبقاء على بعض المجموعات فقط | df.groupby("city").filter(lambda g: len(g) >= 2) |
| الصف ذو القيمة القصوى في كل مجموعة | df.loc[df.groupby("city")["salary"].idxmax()] |
| أعلى n صفوف في كل مجموعة | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| ترقيم الصفوف داخل كل مجموعة | df.groupby("city").cumcount() |
| المرور على المجموعات | for city, group in df.groupby("city"):
print(city, len(group)) |
| الفلترة والتجميع والترتيب في سلسلة واحدة | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
الجداول المحورية وإعادة التشكيل
البيانات الطويلة فيها صف واحد لكل ملاحظة. والبيانات العريضة توزّع متغيرًا واحدًا على عدة أعمدة.
| المهمة | الكود |
|---|---|
| جدول محوري | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| جدول محوري مع المجاميع | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| عدّ التوليفات | pd.crosstab(df["city"], df["team"]) |
| النسب في كل صف | pd.crosstab(df["city"], df["team"], normalize="index") |
| من عريض إلى طويل | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| من طويل إلى عريض | long.pivot(index="name", columns="field", values="value") |
| مستوى الفهرس الداخلي إلى أعمدة | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| صف لكل عنصر في القائمة | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| ترميز عمود بطريقة one-hot | pd.get_dummies(df["team"], prefix="team") |
| تبديل الصفوف والأعمدة | df.T |
دمج جداول DataFrame
تكدّس concat الجداول، وتربط merge بينها عبر أعمدة مفتاحية كما يفعل SQL.
| المهمة | الكود |
|---|---|
| تكديس الصفوف | pd.concat([df_2025, df_2026], ignore_index=True) |
| وضع الجداول جنبًا إلى جنب | pd.concat([left, right], axis=1) |
| الربط عبر عمود مفتاحي | orders.merge(customers, on="customer_id", how="left") |
| أنواع الربط | how="inner", "left", "right", "outer" |
| مفاتيح بأسماء مختلفة | orders.merge(users, left_on="customer_id", right_on="id") |
| التحقق من الربط وتحديد مصدر كل صف | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| الربط عبر الفهرس | left.join(right, how="inner") |
| صفوف ليس لها مقابل في جدول آخر | orders[~orders["customer_id"].isin(customers["customer_id"])] |
الرسم باستخدام pandas
ترسم pandas باستخدام Matplotlib. كل استدعاء رسم يُرجع كائن ax من Matplotlib، فيمكنك متابعة تنسيقه.
| المهمة | الكود |
|---|---|
| مخطط خطي لسلسلة | ts["salary"].plot() |
| مخطط خطي من عمودين | df.plot(x="date", y="salary") |
| مخطط أعمدة لملخص | df.groupby("city")["salary"].mean().plot.bar() |
| أشرطة أفقية مرتبة | df["city"].value_counts().sort_values().plot.barh() |
| مدرج تكراري | df["salary"].plot.hist(bins=20) |
| مخطط انتشار | df.plot.scatter(x="age", y="salary") |
| مخطط صندوقي لكل مجموعة | df.boxplot(column="salary", by="city") |
| العنوان وتسمية المحور | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| عرض المخطط أو حفظه | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3: الصيغ القديمة وما تكتبه الآن
الكود المنسوخ من الدروس القديمة كثيرًا ما يفشل أو يُظهر تحذيرات في pandas 3. اكتب الصيغ الموجودة في عمود pandas 3 بدلًا منه.
| المهمة | pandas 3 | الصيغة القديمة |
|---|---|---|
| دالة على كل خلية | df.map(f) | df.applymap(f) |
| التعبئة من الصف السابق | df.ffill() | df.fillna(method="ffill") |
| إضافة صفوف | pd.concat([df, new_rows]) | df.append(new_rows) |
| إعادة التجميع بنهاية الشهر | ts["salary"].resample("ME").sum() | resample("M") |
| تواريخ بالساعة | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| تعيين قيمة واحدة | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| تعديل جزء مفلتر | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| هل هذا عمود نصوص؟ | df["name"].dtype == "str" | df["name"].dtype == object |
أكواد pandas التي يحتاجها محلل البيانات كل يوم، مرتبة حسب المهمة: إنشاء البيانات وتحميلها، فحصها، اختيار الصفوف وفلترتها، إضافة الأعمدة، تنظيف القيم المفقودة، التعامل مع النصوص والتواريخ، التجميع والتلخيص، إعادة التشكيل، الدمج والرسم. كل صف مقتطف قصير يمكنك لصقه مباشرة، مع سطر واحد يشرح ما يفعله.
كل ما هنا مكتوب لإصدار pandas 3، وهو الإصدار الذي يشغّله Coddy: أعمدة النصوص تستخدم نوع البيانات الجديد str ، وخاصية Copy-on-Write مفعّلة دائمًا، والواجهات المحذوفة مثل applymap و fillna(method=...) لا تظهر إلا في جدول الصيغ القديمة. تستخدم الأمثلة جدول DataFrame باسم df فيه الأعمدة name و city و team و age و salary و date و email . هل أنت جديد على Python نفسها؟ ابدأ بـ ورقة Python المرجعية و ساحة تجربة Python، واحتفظ بأوراق NumPy و Matplotlib و scikit-learn المرجعية بجانب هذه الورقة.
الأسئلة الشائعة حول ورقة pandas المرجعية
هل ورقة pandas المرجعية هذه مجانية؟
ما إصدار pandas الذي تغطيه هذه الورقة المرجعية؟
str ، وخاصية Copy-on-Write مفعّلة دائمًا، والاستدعاءات المحذوفة في pandas 3 ( applymap و fillna(method=...) و append واختصارا التردد M و H ) لا تظهر إلا في الجدول الأخير، بجانب ما تكتبه بدلًا منها. تعمل معظم المقتطفات أيضًا على pandas 2.2، باستثناء الصفوف التي تعتمد على نوع str أو على Copy-on-Write.ما الفرق بين loc و iloc؟
loc حسب التسمية: قيم الفهرس وأسماء الأعمدة، والشريحة مثل df.loc[0:2] تشمل نهايتها. أما iloc فيختار حسب الموضع الرقمي مثل قائمة Python، لذا يُرجع df.iloc[0:2] أول صفين ويستثني النهاية. استخدم loc مع شرط لفلترة الصفوف واختيار الأعمدة في خطوة واحدة: df.loc[df["age"] > 30, ["name"]] .كيف أفلتر DataFrame بعدة شروط؟
& للعطف (و)، و | للتخيير (أو)، و ~ للنفي: df[(df["age"] > 30) & (df["city"] == "Oslo")] . الكلمتان and و or في Python لا تعملان على الأعمدة الكاملة. وفي الفلاتر الطويلة تكون الصيغة df.query("age > 30 and city == 'Oslo'") أسهل في القراءة.ما الفرق بين merge و join و concat؟
merge جدولي DataFrame عبر أعمدة مفتاحية مثل الربط في SQL، مع how="inner" أو "left" أو "right" أو "outer" . و join اختصار يدمج عبر الفهرس. أما concat فلا تطابق المفاتيح أبدًا: بل تكدّس الجداول فوق بعضها، أو جنبًا إلى جنب مع axis=1 .لماذا لا يغيّر الإسناد جدول DataFrame عندي؟
df["age"][0] = 37 يغيّر كائنًا مؤقتًا ويترك df كما هو. اكتبه بدلًا من ذلك كاستدعاء loc واحد: df.loc[0, "age"] = 37 . كذلك تُرجع دوال مثل dropna و fillna كائنًا جديدًا، لذا أسند النتيجة.