تحليل البيانات في بايثون: تصور مجموعات بيانات Kaggle باستخدام Pandas و Matplotlib و Seaborn

دقائق القراءة: 17

تحليل البيانات في بايثون: تصور مجموعات بيانات Kaggle باستخدام Pandas و Matplotlib و Seaborn

دوري الكريكيت الهندي الممتاز (IPL) هو بطولة كريكيت T20 تنظمها سنويًا هيئة التحكم في الكريكيت في الهند (BCCI). تتنافس ثمانية فرق من مدن مختلفة على مدار 6 أسابيع لتحديد الفائز. في هذا المقال، سنقوم بتحليل بيانات مواسم دوري IPL السابقة لمعرفة الفرق التي فازت بأكبر عدد من المباريات، وكيف تتصرف الفرق عند الفوز بالقرعة، ومن يمتلك الإرث الأكبر، وما إلى ذلك. لقد قمنا بهذا التحليل من منظور تاريخي، مقدمين نظرة عامة على ما حدث في دوري IPL على مر السنين. استخدمنا أدوات مثل Pandas و Matplotlib و Seaborn جنبًا إلى جنب مع Python لتقديم تمثيل مرئي ورقمي للبيانات.

تُعد مكتبة Pandas اختصارًا لـ Python Data Analysis library، وتُستخدم عادةً للعمل مع البيانات الجدولية (على غرار البيانات المخزنة في جداول البيانات). توفر Pandas دوال مساعدة لقراءة البيانات من تنسيقات ملفات مختلفة مثل CSV، جداول Excel، جداول HTML، JSON، SQL، وإجراء العمليات عليها.

أما Matplotlib و Seaborn فهما مكتبتان من مكتبات Python تُستخدمان لإنشاء الرسوم البيانية. تُستخدم Matplotlib بشكل عام لرسم الخطوط والرسوم البيانية الدائرية والرسوم البيانية الشريطية. بينما توفر Seaborn ميزات تصور أكثر تقدمًا مع بناء جملة أقل وخيارات تخصيص أكثر. سنتنقل بينهما أثناء التحليل.

جدول المحتويات

1. الحصول على مجموعة البيانات

لقد قمت بتنزيل مجموعة البيانات من Kaggle. ستجد هناك ملفين بتنسيق CSV (قيم مفصولة بفاصلات): matches.csv و deliveries.csv. اخترت إجراء تحليلي على ملف matches.csv. للعثور على مجموعات بيانات أكثر إثارة للاهتمام، يمكنك زيارة هذه الصفحة.

2. إعداد البيانات وتنظيفها

تحتوي مجموعة البيانات على العديد من الأعمدة والصفوف. من الممكن دائمًا أن تحتوي بعض الصفوف على قيم مفقودة أو NaN لعمود واحد أو أكثر. ومن الممكن أيضًا أن تكون هناك بعض الأعمدة أو الصفوف التي ترغب في استبعادها من تحليلك. يمكنك أيضًا دمج مجموعتي بيانات أو أكثر لإجراء تحليل متعمق. يتضمن تنظيف البيانات إجراء تصحيحات على تلك البيانات، واستبعاد الأعمدة أو الصفوف غير الضرورية، ودمج مجموعات البيانات، وما إلى ذلك.

قبل اتخاذ هذه الخطوات، احتجت إلى تثبيت واستيراد الأدوات (المكتبات) التي ستُستخدم أثناء التحليل. قمت باستيراد المكتبات بأسماء مستعارة مختلفة مثل pd و plt و sns. ثم قمت بتعيين بعض الأنماط الأساسية للرسوم البيانية. لاحظ الأمر الخاص %matplotlib inline. إنه يضمن عرض الرسوم البيانية وتضمينها داخل دفتر ملاحظات Jupyter نفسه. بدون هذا الأمر، قد تظهر الرسوم البيانية أحيانًا في نوافذ منبثقة.


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

plt.style.use('ggplot') # Optional: Set a plotting style
plt.rcParams['figure.figsize'] = (12, 6) # Optional: Set default figure size

باستخدام الدالة read_csv() من مكتبة Pandas، قمت بتحميل ملف matches.csv. تُقرأ البيانات من الملف وتُخزن في كائن DataFrame – وهو أحد هياكل البيانات الأساسية في Pandas لتخزين البيانات الجدولية والعمل عليها. استخدمت اللاحقة _df في أسماء المتغيرات لإطارات البيانات. استخدمت الاسم matches_raw_df لإطار البيانات. يشير هذا إلى أنها بيانات غير معالجة سأقوم بتنظيفها وتصفيتها وتعديلها لإعداد إطار بيانات جاهز للتحليل.


matches_raw_df = pd.read_csv('matches.csv')

باستخدام خاصية shape لكائن Dataframe، وجدت أن مجموعة البيانات تحتوي على 756 صفًا و 18 عمودًا. لمعرفة أسماء تلك الأعمدة، استخدمت خاصية columns. لقد أعادت قائمة بالأعمدة في إطار البيانات. للحصول على ملخص لما يحتويه إطار البيانات، استخدمت الدالة info(). يعطي هذا معلومات حول الأعمدة، وعدد القيم غير الفارغة في كل عمود، ونوع بياناتها، واستخدام الذاكرة.


print("Shape of the DataFrame:", matches_raw_df.shape)
print("Column names:", matches_raw_df.columns)
matches_raw_df.info()

تقريبًا جميع الأعمدة باستثناء umpire3 لا تحتوي على قيم فارغة أو تحتوي على عدد قليل جدًا منها. يمكن أن يكون وجود قيم فارغة ناتجًا عن نقص المعلومات أو إدخال بيانات غير صحيح. الشيء المثير للاهتمام الذي يجب ملاحظته هو أنه على الرغم من عدم وجود قيم فارغة لعمود result، إلا أن هناك بعض القيم لعمودي winner و player_of_match. دعنا نكتشف السبب.

قمت أولاً بالوصول إلى عمود result باستخدام ترميز النقطة (matches_raw_df.result). ثم استخدمت الدالة value_counts() على عمود result. تعيد value_counts() سلسلة تحتوي على عدد القيم الفريدة. هنا، تخبرنا عن القيم المختلفة الموجودة في result والعدد الإجمالي لكل منها. لذا، من أصل 756 مباراة (صفًا)، انتهت 4 مباريات بدون نتيجة. الكريكيت رياضة خارجية، وعلى عكس كرة القدم على سبيل المثال، لا يمكن اللعب عندما تمطر. من الشائع جدًا أن تُعلّق المباريات بسبب الأمطار الغزيرة. لذلك، ليس لدينا فائزون أو لاعب المباراة لتلك المباريات الأربع.


matches_raw_df.result.value_counts()

بالنسبة لهذا التحليل، عمود umpire3 غير مطلوب. لذلك قمت بإزالة العمود باستخدام الدالة drop() عن طريق تمرير اسم العمود وقيمة المحور. إذا كنت ترغب في إزالة أعمدة متعددة، فيجب إعطاء أسماء الأعمدة في قائمة. قمت بتعيين إطار البيانات المنظف هذا إلى matches_df. استخدمت إطار البيانات هذا لمزيد من التحليل.


matches_df = matches_raw_df.drop('umpire3', axis=1)
matches_df.info()

3. التحليل الاستكشافي والتصور

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

عدد المباريات والفرق

حاولت معرفة عدد المباريات التي لُعبت في كل موسم في دوري IPL منذ بدايته وحتى عام 2019. نظرًا لأنني احتجت إلى المباريات التي لُعبت في كل موسم، كان من المنطقي تجميع بياناتنا وفقًا للمواسم المختلفة. تحتوي Pandas على دالة groupby() لتحقيق ذلك، حيث مررت season كمعامل. نظرًا لأن id فريد لكل مباراة (صف)، فإن عدّ عدد المعرفات لكل موسم يؤدي إلى ما نريده. استخدمت الدالة count() على عمود id لمعرفة عدد المباريات التي أُقيمت في كل موسم. هذه السلسلة مُسندة إلى المتغير matches_per_season. ثم استخدمت الدالة barplot() من مكتبة Seaborn لرسم السلسلة. تم إعطاء فهرس السلسلة، أي المواسم، كقيمة x بينما تم إعطاء قيم تلك الفهارس كقيم y. استخدمت طرقًا مختلفة من matplotlib.pyplot مثل figure() و xticks() و title() لتعيين حجم الرسم البياني وعنوانه وما إلى ذلك. تأخذ figure معاملًا، figsize، الذي قمت بتعيينه إلى (12,6). لاحظ أن الحجم أُعطي كـ tuple. لـ xticks()، أعطيت معامل rotation قيمة 75 لتسهيل القراءة.


matches_per_season = matches_df.groupby('season')['id'].count()

plt.figure(figsize=(12, 6))
sns.barplot(x=matches_per_season.index, y=matches_per_season.values, palette='viridis')
plt.title('عدد المباريات التي لُعبت في كل موسم (2008-2019)', fontsize=16)
plt.xlabel('الموسم', fontsize=12)
plt.ylabel('عدد المباريات', fontsize=12)
plt.xticks(rotation=75)
plt.grid(axis='y', linestyle='--')
plt.show()

رسم بياني يوضح عدد مباريات IPL في كل موسم من 2008 إلى 2019

في كل موسم، لُعبت ما يقرب من 60 مباراة. ومع ذلك، نرى ارتفاعًا في عدد المباريات من عام 2011 إلى عام 2013. هذا يرجع إلى إدخال فريقين جديدين، وهما Pune Warriors و Kochi Tuskers Kerala، مما زاد عدد الفرق إلى 10. ومع ذلك، أُزيل Kochi في الموسم التالي مباشرة، بينما أُزيل Pune Warriors في عام 2013، مما أدى إلى خفض العدد إلى 8 من عام 2014 فصاعدًا.

قبل بداية موسم 2016، حُظر فريقان، وهما Chennai Super Kings و Rajasthan Royals لمدة موسمين. لتعويض غيابهم، دخل فريقان جديدان (Rising Pune Supergiants و Gujarat Lions) المنافسة. عندما عاد Chennai Super Kings و Rajasthan Royals، أُزيل هذان الفريقان من المنافسة.

تحليل نتائج القرعة

تُعد القرعة أحد أهم الأحداث في أي مباراة كريكيت، وتحدث في بداية المباراة. يمكن للفائز بالقرعة اختيار ما إذا كان يريد الضرب أولاً أو ثانيًا (الدفاع أولاً). دعنا نرى ما هو الاتجاه بين الفرق عبر المواسم المختلفة. مرة أخرى، قمت بتجميع الصفوف حسب الموسم ثم عدّ القيم المختلفة لعمود toss_decision باستخدام value_counts(). نظرًا لأن النسبة المئوية تعطي صورة أوضح، فقد قسمت النتيجة أعلاه على matches_per_season وضربتها في 100. هذه السلسلة مُسندة إلى toss_decision_percentage. هنا، toss_decision_percentage هي سلسلة ذات فهرس متعدد. إذا طبعنا فهرس السلسلة باستخدام خاصية index، نرى أنها على شكل (2008, 'bat')، (2008, 'field') وهكذا. استخدمت السلسلة كلاً من season و toss_decision كفهرس. لكنني أردت فقط أن تكون المواسم فهرسًا. استخدمت unstack() لتحقيق ذلك. باستخدام الدالة unstack() على السلسلة، حولت قيم toss_decision (أي bat و field) إلى أعمدة منفصلة. بعد ذلك، استخدمت الدالة plot() من Matplotlib لتمثيل هذه القيم كرسوم بيانية شريطية. تحتوي plot() على معامل kind الذي يحدد نوع الرسم البياني المراد رسمه. تم تعيين القيمة على bar.


toss_decision_counts = matches_df.groupby('season')['toss_decision'].value_counts()
toss_decision_percentage = (toss_decision_counts / matches_per_season * 100).unstack()

plt.figure(figsize=(12, 6))
toss_decision_percentage.plot(kind='bar', stacked=False, figsize=(12, 6), colormap='coolwarm')
plt.title('نسبة قرارات القرعة حسب الموسم', fontsize=16)
plt.xlabel('الموسم', fontsize=12)
plt.ylabel('النسبة المئوية (%)', fontsize=12)
plt.xticks(rotation=75)
plt.legend(title='قرار القرعة')
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح نسبة قرارات القرعة (الضرب أولاً أو الدفاع أولاً) في كل موسم من مواسم IPL

بالنسبة للفترة 2008-2013، بدت الفرق تفضل الضرب أولاً والدفاع ثانيًا. خلال هذه الفترة، اختارت الفرق الضرب أولاً أكثر في 2009 و 2010 و 2013. من ناحية أخرى، اختارت الدفاع أولاً أكثر في 2008 و 2011. كانت الأمور متساوية في عام 2012. قد يكون هذا لأن دوري IPL وكريكيت T20 بشكل عام كانا في مراحلهما الأولى. لذلك، كانت الفرق على الأرجح تتعلم وتحاول معرفة أي الخيارات سيكون أكثر فائدة. ومع ذلك، منذ عام 2014، اختارت الفرق الضرب ثانيًا بشكل ساحق. خاصة منذ عام 2016، اختارت الفرق الدفاع أولاً أكثر من 80% من الوقت. يتطلب الضرب أولاً أن يقيم الفريق الظروف والملعب ثم يحدد هدفًا وفقًا لذلك. المطاردة أقل تعقيدًا، حيث يوجد هدف ثابت يجب تحقيقه. أصبحت الظروف أيضًا أكثر ملاءمة للضاربين، وزادت مهارات الضاربين بشكل كبير.

عدد الانتصارات

رأينا كيف اختارت الفرق في الماضي القريب الدفاع ثانيًا أكثر من 4 مرات من أصل 5. هل أدى هذا القرار إلى تغيير النتائج؟ دعنا نرى. بالنسبة لـ wins_batting_first، يجب أن تكون قيم win_by_wickets 0. كما يجب أن يكون لعمود result قيمة normal نظرًا لأن المباريات المتعادلة لها أيضًا هوامش فوز 0. تم تخزين هذا الشرط كـ filter1. وبالمثل، بالنسبة لـ wins_fielding_first، يجب أن تكون قيمة win_by_runs 0 ويجب أن يكون لعمود result قيمة normal. تم تخزين هذا الشرط كـ filter2. في كلتا السلسلتين، استخدمت الدالة count() على عمود winner للعثور على المباريات الفائزة في الظروف المفلترة. قسمت النتائج على matches_per_season المحسوبة سابقًا لإعطاء فهم أفضل. لرسم هاتين السلسلتين معًا، قمت بدمجهما باستخدام دالة concat() من Pandas. مررت اسمي السلسلتين كقائمة وعينت قيمة axis على 1. يعطينا هذا إطار بيانات جديدًا تم تخزينه كـ combined_wins_df. بعد ذلك، قمت برسم combined_wins_df كـ bar chart باستخدام plot().


wins_batting_first = matches_df[(matches_df['win_by_wickets'] == 0) & (matches_df['result'] == 'normal')].groupby('season')['winner'].count()
wins_fielding_first = matches_df[(matches_df['win_by_runs'] == 0) & (matches_df['result'] == 'normal')].groupby('season')['winner'].count()

wins_batting_first_percentage = (wins_batting_first / matches_per_season * 100).fillna(0)
wins_fielding_first_percentage = (wins_fielding_first / matches_per_season * 100).fillna(0)

combined_wins_df = pd.concat([wins_batting_first_percentage, wins_fielding_first_percentage], axis=1)
combined_wins_df.columns = ['الانتصارات بالضرب أولاً', 'الانتصارات بالدفاع أولاً']

plt.figure(figsize=(12, 6))
combined_wins_df.plot(kind='bar', figsize=(12, 6), colormap='plasma')
plt.title('نسبة الانتصارات حسب قرار القرعة والموسم', fontsize=16)
plt.xlabel('الموسم', fontsize=12)
plt.ylabel('نسبة الانتصارات (%)', fontsize=12)
plt.xticks(rotation=75)
plt.legend(title='قرار الفوز')
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح نسبة الانتصارات للفرق التي ضربت أولاً مقابل الفرق التي دافعت أولاً في كل موسم من IPL

رأينا سابقًا أنه في الفترة 2008-2013، واجهت الفرق معضلة حول ما إذا كان الأفضل الضرب أولاً أو الدفاع أولاً. هذا مرئي جزئيًا في النتائج أيضًا. الانتصارات من الضرب أولاً قريبة جدًا من تلك التي جاءت من الدفاع أولاً. ومع ذلك، هناك موسم واحد فقط فازت فيه الفرق التي ضربت أولاً أكثر، مع تساوي الأمور في عام 2013. مرة أخرى، منذ عام 2014، كانت الأمور في صالح الفرق التي تلاحق الهدف باستثناء عام 2015. باستثناء عام 2015، كانت الأمور في صالح الفرق التي تدافع أولاً بشكل ساحق. لذا، فإن قرار الفرق بالدفاع أكثر كان مبررًا.

الفرق ذات “التاريخ”

في الدوريات الرياضية المختلفة، هناك دائمًا حديث عن الفرق ذات “التاريخ” – الفرق التي لعبت أكثر في الدوري وتستمر في ذلك. دعنا نجد هذه الفرق في دوري IPL. الآن، بين فريقين A و B، يمكن أن تكون “A ضد B” أو “B ضد A”، اعتمادًا على كيفية إدخال البيانات. لذلك قررت عدّ العدد الإجمالي للقيم المختلفة لكلا العمودين team1 و team2 باستخدام value_counts(). ثم أضفتها معًا. قمت بفرز النتائج بترتيب تنازلي باستخدام الدالة sort_values() من Pandas. تم تعيين معامل ascending على False. هنا، استخدمت sns.barplot() لرسم الرسم البياني.


team_appearances = matches_df['team1'].value_counts() + matches_df['team2'].value_counts()
team_appearances = team_appearances.sort_values(ascending=False)

plt.figure(figsize=(12, 6))
sns.barplot(x=team_appearances.index, y=team_appearances.values, palette='cubehelix')
plt.title('عدد المباريات التي لُعبت بواسطة كل فريق في IPL', fontsize=16)
plt.xlabel('الفريق', fontsize=12)
plt.ylabel('عدد المباريات', fontsize=12)
plt.xticks(rotation=75)
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح عدد المباريات التي لعبها كل فريق في تاريخ IPL

لعب فريق Mumbai Indians أكبر عدد من المباريات. يليه Royal Challengers Bangalore، Kolkata Knight Riders، Kings XI Punjab و Chennai Super Kings. كان من الممكن أن يكون Chennai Super Kings و Rajasthan Royals في مرتبة أعلى لو لم يُحظروا. ستلاحظ وجود فريقين من دلهي، Delhi Daredevils و Delhi Capitals. نتج هذا عن تغيير في الملكية ثم اسم الفريق في عام 2018. وهي قصة مماثلة لـ Deccan Chargers و Sunrisers Hyderabad، حيث أُزيل Deccan Chargers من دوري IPL في عام 2013 وجاء Sunrisers في مكانه. أيضًا، هناك فريقان لهما نفس الاسم تقريبًا: Rising Pune Supergiants و Rising Pune Supergiant. إنهما نفس الفريق، ولم يكن هناك تغيير في الملكية – له علاقة بالخرافات. في موسم 2016، احتل Rising Pune Supergiants المركز السابع. غير الملاك القائد لعام 2017 وأسقطوا أيضًا حرف ‘s’ من Supergiants. لقد أتى ذلك بثماره حيث احتلوا المركز الثاني في ذلك الموسم!

الفرق ذات “الإرث”

قد يكون للفرق تاريخ طويل، لكن “إرثها” – مدى تكرار فوزها – هو ما يجعلها شعبية ويجذب المشجعين الجدد والمحايدين. للعثور على مثل هذه الفرق، استخدمت ببساطة value_counts() على عمود winner. يعطينا هذا عدد المباريات التي فاز بها كل فريق. لذا، فإن Mumbai لديها أكبر عدد من الانتصارات. لكن المقياس الأفضل للحكم سيكون نسبة الفوز. للعثور على نسبة الفوز، قسمت most_wins على total_matches_played للعثور على win_percentage لكل فريق.


most_wins = matches_df['winner'].value_counts()
win_percentage = (most_wins / team_appearances * 100).sort_values(ascending=False).fillna(0)

plt.figure(figsize=(12, 6))
sns.barplot(x=win_percentage.index, y=win_percentage.values, palette='rocket')
plt.title('نسبة الفوز لكل فريق في IPL', fontsize=16)
plt.xlabel('الفريق', fontsize=12)
plt.ylabel('نسبة الفوز (%)', fontsize=12)
plt.xticks(rotation=75)
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح نسبة الفوز لكل فريق في تاريخ IPL

لدى Rising Pune Supergiant و Delhi Capitals أعلى نسبة فوز. يرجع هذا إلى حد كبير إلى أنهم لعبوا عددًا أقل من المباريات مقارنة بمعظم الفرق. خاصة Rising Pune Supergiant، الذي أصبح فريقًا جديدًا من الناحية الفنية بعد إسقاط حرف ‘s’. حقق Chennai Super Kings، على الرغم من لعبه موسمين أقل من Mumbai Indians، 9 انتصارات أقل فقط. هما، جنبًا إلى جنب مع Mumbai Indians، الفريقان الوحيدان في المراكز الخمسة الأولى اللذان كانا جزءًا من دوري IPL في عام 2008. Chennai و Mumbai هما الفريقان الأكثر إرثًا.

4. طرح الأسئلة والإجابة عليها من البيانات

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

س: من فاز ببطولة IPL؟

قم بتجميع الصفوف حسب المواسم باستخدام groupby(). ابحث عن المباراة الأخيرة لكل موسم، أي النهائي باستخدام tail(). تعيد هذه الدالة آخر n صفوف من كائن Dataframe أو سلسلة بناءً على الموضع. قم بفرز القيم لكل موسم باستخدام sort_values(). عدّ الفائزين المختلفين وعدد مرات فوزهم باستخدام value_counts() على عمود winner. ثم رسمت السلسلة ipl_winners باستخدام sns.barplot().


ipl_winners = matches_df.groupby('season')['winner'].tail(1).value_counts()

plt.figure(figsize=(12, 6))
sns.barplot(x=ipl_winners.index, y=ipl_winners.values, palette='magma')
plt.title('الفائزون ببطولة IPL عبر المواسم', fontsize=16)
plt.xlabel('الفريق الفائز', fontsize=12)
plt.ylabel('عدد مرات الفوز', fontsize=12)
plt.xticks(rotation=75)
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح الفرق الفائزة ببطولة IPL وعدد مرات فوز كل منها

فاز فريقا Mumbai و Chennai، وهما فريقا الإرث لدينا، بدوري IPL ثلاث مرات على الأقل. Sunrisers Hyderabad هو الفريق الوحيد الذي انضم إلى الدوري لاحقًا وفاز باللقب.

س: ما هي الفرق الأكثر والأقل اتساقًا عبر جميع المواسم؟

أنشأت إطار بيانات بين القيم المختلفة لـ winner و season باستخدام pd.crosstab(). رسمت إطار البيانات كخريطة حرارية (heatmap). تعطي pd.crosstab() جدول تقاطع بسيط لأعمدة winner و season. لكل قيمة مختلفة لـ winner، تجد pd.crosstab() تكرارها لكل قيمة مختلفة في season. ثم رسمت matches_won_each_season باستخدام sns.heatmap(). مررت إطار البيانات matches_won_each_season، مع annot كـ True لإظهار القيم أيضًا.


matches_won_each_season = pd.crosstab(matches_df['winner'], matches_df['season'])

plt.figure(figsize=(14, 8))
sns.heatmap(matches_won_each_season, annot=True, fmt='d', cmap='YlGnBu', linewidths=.5)
plt.title('عدد المباريات التي فاز بها كل فريق في كل موسم', fontsize=16)
plt.xlabel('الموسم', fontsize=12)
plt.ylabel('الفريق الفائز', fontsize=12)
plt.tight_layout()
plt.show()

خريطة حرارية توضح عدد المباريات التي فاز بها كل فريق في كل موسم من مواسم IPL

هنا، يشير اللون الداكن إلى المزيد من المباريات الفائزة. كان Chennai Super Kings الفريق الأكثر اتساقًا، حيث فاز بثماني مباريات على الأقل في كل موسم لعب فيه. يدعم هذا حقيقة أنه الفريق الوحيد الذي وصل إلى مرحلة التصفيات في كل موسم. في الطرف الآخر من الطيف توجد 3 فرق: Delhi Daredevils، Kings XI Punjab، و Rajasthan Royals. حققت جميعها موسمين قدمت فيهما أداءً جيدًا حقًا. ومع ذلك، فقد كانت متوسطة جدًا خلال المواسم الأخرى.

س: ما هو أكبر هامش فوز من حيث النقاط في دوري IPL؟

قم بتصفية إطار البيانات باستخدام الشرط المطلوب. قم بفرز القيم بترتيب تنازلي باستخدام sort_values(). ابحث عن أكبر 10 انتصارات في القائمة باستخدام الدالة head(). تعمل هذه الدالة عكس tail()، حيث تعيد أول n صفوف. رسمت إطار البيانات المفلتر highest_wins_by_runs_df باستخدام sns.scatterplot(). بالنسبة لمعامل x استخدمت season، واستخدمت win_by_runs كمعامل y. جعلت حجم النقاط أكبر لأكبر 10 انتصارات باستخدام معامل s. للتركيز على أكبر 10 انتصارات، استخدمت لونًا مختلفًا وقمت أيضًا بتعليق نقاط البيانات هذه باستخدام plt.annotate(). المعامل الأول هو نص التعليق التوضيحي. يتم إعطاء موضع النقطة المراد التعليق عليها كـ tuple.


highest_wins_by_runs_df = matches_df[matches_df['win_by_runs'] > 0].sort_values(by='win_by_runs', ascending=False).head(10)

plt.figure(figsize=(12, 6))
sns.scatterplot(x='season', y='win_by_runs', data=matches_df[matches_df['win_by_runs'] > 0], s=100, alpha=0.6, color='skyblue')
sns.scatterplot(x='season', y='win_by_runs', data=highest_wins_by_runs_df, s=300, color='red', marker='X', label='أكبر 10 انتصارات')

for index, row in highest_wins_by_runs_df.iterrows():
    plt.annotate(f"{row['win_by_runs']} ({row['winner']})", (row['season'], row['win_by_runs']), textcoords="offset points", xytext=(0,10), ha='center', fontsize=9)

plt.title('أكبر هوامش الفوز بالنقاط في IPL', fontsize=16)
plt.xlabel('الموسم', fontsize=12)
plt.ylabel('هامش الفوز بالنقاط', fontsize=12)
plt.grid(True, linestyle='--')
plt.legend()
plt.tight_layout()
plt.show()

رسم بياني مبعثر يوضح أكبر هوامش الفوز بالنقاط في تاريخ IPL، مع إبراز أكبر 10 انتصارات

أكبر هامش فوز بالنقاط هو 146 نقطة. في عام 2017، هزم Mumbai Indians فريق Delhi Daredevils بهذا الهامش. لدى Royal Challengers Bangalore 3 انتصارات ضمن المراكز الخمسة الأولى.

س: Mumbai و Chennai هما الفريقان الأكثر نجاحًا حتى الآن. أي فريق يتصدر في سجل المواجهات المباشرة؟

قم بتصفية إطار البيانات باستخدام الشرط المطلوب للعثور على المباريات التي لُعبت بين الفريقين. استخدم value_counts() على عمود winner لمعرفة عدد المرات التي فاز فيها كل فريق. رسمت السلسلة mivcsk كـ bar chart لتصور أفضل.


mi_csk_matches = matches_df[((matches_df['team1'] == 'Mumbai Indians') & (matches_df['team2'] == 'Chennai Super Kings')) |
                            ((matches_df['team1'] == 'Chennai Super Kings') & (matches_df['team2'] == 'Mumbai Indians'))]

mivcsk = mi_csk_matches['winner'].value_counts()

plt.figure(figsize=(8, 5))
sns.barplot(x=mivcsk.index, y=mivcsk.values, palette='viridis')
plt.title('سجل المواجهات المباشرة: Mumbai Indians vs Chennai Super Kings', fontsize=14)
plt.xlabel('الفريق الفائز', fontsize=12)
plt.ylabel('عدد الانتصارات', fontsize=12)
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()

رسم بياني يوضح سجل المواجهات المباشرة بين Mumbai Indians و Chennai Super Kings

سيطر MI على CSK ويتصدر سجل المواجهات المباشرة بنتيجة 17-11. يمكننا رؤية هيمنتهم خاصة في موسم 2019، حيث هزم MI فريق CSK 4 مرات من أصل 4 لقاءات، بما في ذلك التصفيات والنهائي.

5. الاستنتاجات من التحليل

لقد استخلصنا بعض الاستنتاجات المثيرة للاهتمام ونعرف الآن المزيد عن دوري IPL مما كنا نعرفه عندما بدأنا. إليك ملخص لما تعلمناه من خلال تحليلنا:

  • تُلعب حوالي 60 مباراة في كل موسم من مواسم IPL بين 8 فرق. كانت هناك محاولة لتوسيع دوري IPL إلى 10 فرق ولكن فكرة الفرق الثمانية أُعيدت واستمرت منذ ذلك الحين.
  • خلال المواسم الستة الأولى (2008-2013)، كانت الفرق تحاول معرفة ما إذا كان الضرب أولاً أو المطاردة سيكون أفضل بعد الفوز بالقرعة. قد يرجع هذا إلى حقيقة أن دوري IPL وكريكيت T20 كانا في مراحلهما المبكرة، لذا كانت الفرق تجرب استراتيجيات مختلفة.
  • ولكن، منذ عام 2014، فضلت الفرق المطاردة، خاصة في المواسم الأربعة الماضية (2016-2019) حيث اختارت الفرق الدفاع أكثر من 4 مرات من أصل 5. من المرجح أن يكون هذا بسبب أن وجود مجموع محدد للمطاردة يجعل الأمور أبسط. قد ينتج هذا أيضًا عن تفضيل الفرق للمطاردة في مباريات اليوم الواحد (ODIs) أيضًا.
  • على الرغم من أن الفرق اختارت الدفاع أولاً بشكل ساحق، إلا أن نسبة الفوز بعد اختيار الضرب أو الدفاع ليست بهذا القدر من الانحياز. ومع ذلك، فإن الفرق بينهما آخذ في الارتفاع.
  • لعب Mumbai Indians أكبر عدد من المباريات في دوري IPL. بسبب التوسع الوجيز، وتغيير الملاك، وإزالة وحظر الفرق، كان هناك 15 فريقًا لعبوا في دوري IPL.
  • Chennai و Mumbai هما الفريقان اللذان يتمتعان بأعلى نسبة فوز. حقيقة أنهما الفريقان الوحيدان اللذان كانا جزءًا من الموسم الأول أيضًا، ضمن المراكز الخمسة الأولى، تُظهر هيمنتهما.
  • فاز Mumbai Indians بدوري IPL 4 مرات، وهو العدد الأكبر. يليه Chennai بـ 3 مرات و Kolkata Knight Riders بمرتين. يكمل Sunrisers Hyderabad و Deccan Chargers و Rajasthan Royals قائمة أبطال IPL، حيث فاز كل منهم مرة واحدة.
  • 146 نقطة هو أكبر هامش فوز بالنقاط. هزم Mumbai Indians فريق Delhi Daredevils بهذا الهامش في عام 2017. أكبر هامش فوز بالويكيت هو 10، وقد تحقق عدة مرات.
  • العملاقان، Mumbai و Chennai، لديهما سجل مواجهات مباشرة لصالح Mumbai بنتيجة 17-11. كان لـ Mumbai اليد العليا في موسم 2019 في كل مرة التقيا فيها، بما في ذلك النهائي.

6. الخاتمة

في هذا المقال، أجرينا مجموعة من التحليلات وشاهدنا بعض التصورات المثيرة للاهتمام. ومع ذلك، كان هذا مجرد لمس السطح. يمكنك إجراء تحليل أكثر إثارة للاهتمام على matches.csv كمجموعة بيانات مستقلة. لكن دمج deliveries.csv مع مجموعة البيانات هذه يمكن أن يؤدي إلى تحليل أكثر تعمقًا.

لقد قمت بهذا التحليل والتصور للبيانات كمشروع لدورة “تحليل البيانات باستخدام بايثون: من الصفر إلى Pandas” التي استمرت 6 أسابيع. أُجريت هذه الدورة بواسطة Jovian.ml بالشراكة مع freeCodeCamp.org. يمكنك الاطلاع على المشروع هنا.

الخلاصة التقنية

يُظهر هذا التحليل قوة مكتبات Python مثل Pandas، Matplotlib، و Seaborn في استخلاص رؤى قيمة من البيانات الرياضية المعقدة. من خلال خطوات واضحة بدءًا من الحصول على البيانات وتنظيفها وصولاً إلى التحليل الاستكشافي والإجابة على أسئلة محددة، تمكنا من الكشف عن أنماط واتجاهات تاريخية في دوري IPL. تُبرز النتائج أهمية قرار القرعة، واتساق الفرق على المدى الطويل، وهيمنة فرق معينة مثل Mumbai Indians و Chennai Super Kings. هذا النهج ليس مفيدًا فقط لعشاق الكريكيت، بل يمثل أيضًا نموذجًا ممتازًا لكيفية تطبيق علم البيانات لفهم الظواهر في مجالات متنوعة، مؤكدًا على أن التصور الجيد هو مفتاح توصيل القصص الكامنة في البيانات بفعالية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *