مصورسازی داده های ورزشی با استفاده از پایتون، Matplotlib و Seaborn
در این مطلب قصد داریم به مصورسازی داده های ورزشی با استفاده از پایتون بپردازیم.
رسانههای خبری مشتاقانه فرا رسیدن عصر تحلیل ورزشی را نوید میدهند، تبلیغات بازرگانی ابزارهای کلان داده از جمله ماشین حسابهای جیبی را معرفی میکنند، صاحبنظران به منظور جمعآوری داده در مورد تک تک حرکات بازیکنان از دوربینهای چند بعدی استفاده میکنند و عملکرد خود را تا ده برابر ارتقا میدهند. واژه بازیکنان قهرمانهای دوران کودکیمان – لئونل مسی، لبران جیمز، تام بریدی و غیره – را در ذهن ما تداعی میکند.
هرچند تا فرا رسیدن عصر تحلیل ورزشی فاصله زیادی داریم اما این حوزه شاهد پیشرفتهای چشمگیری بوده و به تیمهای ورزشی در فرایند تصمیمگیری کمکهای شایانی کرده است. تا پیش از روی کار آمدن تحلیل ورزشی، تصمیمگیری در مورد دعوت بازیکنان به تیم، مبادله ، آموزش و تمرین دادن و سیستم بازی بازیکنان بر مبنای احساسات غریزی و سنتهای گذشته صورت میگرفت.
در این مطلب قصد ندارم الگوریتمی برای پیشیبینی احتمال قرار گرفتن تاتنهام در جمع چهار تیم برتر لیگ جزیره توسعه دهم و یا روش تشکیل تیم Jets برای سال 2018 را توضیح دهم. در این مطلب قصد دارم چندین تکنیک مصورسازی داده ها را معرفی کنم که با استفاده از آنها میتوانیم نمودارهای جالب و آموزندهای برای طرفداران ورزشی رسم کنیم.
نکته:
- نمونههایی که ارائه میدهم صرفاً حول موضوع فوتبال و جام جهانی فوتبال است، اما تکنیکهای ارائه شده در این مطلب را میتوانید برای تمامی رشتههای ورزشی و تورنمنتها به کار ببندید.
- با مراجعه به این لینک میتوانید به تمامی کدهای منبع دسترسی پیدا کنید.
![مصورسازی داده](https://hooshio.com/wp-content/uploads/2021/04/2-8.png)
1- جمعآوری داده
خبر بد: فرایند جمعآوری داده معمولاً دشوارترین بخش است.
جمعآوری آمارههای خلاصه یک رویداد ورزشی ( تعداد گلها در یک مسابقه فوتبال، تعداد پرتابها در یک مسابقه بسکتبال و غیره) آسان است، اما پیدا کردن دیتاست گزارش لحظه به لحظه یک بازی فوتبال یا یک مسابقه تنیس کار دشواری است. دنبال کردن بازیکنان در زمین ، به ویژه در ورزشهای پرهیجان نظیر فوتبال و بسکتبال کار دشوار و در عین حال سودآوری است. شرکتهایی از جمله SportVu یا Opta Sports از راه فروش این اطلاعات ارزشمند به تیمها، مشاوران ورزشی و مراکز تحقیقاتی درآمد کسب میکنند.
[irp posts=”19713″]«برای هر مسابقه از سه نفر کمک میگیریم که پخش زنده رویداد ورزشی را از طریق یک ویدئوی گرافیکی از زمین بازی تماشا میکنند : یک نفر تیم میزبان را زیرنظر میگیرد، دیگری تیم حریف را زیر نظر میگیرد و نفر سوم دادهها را بررسی میکند. »
– سیمون بانوب، مدیر بازاریابی Opta
چندی پیش Statsbomb خبر انتشار عمومی دیتاستهای گزارش مستقیم تمامی بازیهای سه لیگ فوتبال را اعلام کرد. این سه لیگ عبارتند از: لیگ ملی فوتبال زنان ایالات متحده، لیگ برتر فوتبال زنان (انگلستان) و جام جهانی 2018 فوتبال. با کلیک بر روی این لینک میتوانید به صورت رایگان به دیتاستهای آنها دسترسی داشته باشید.
تمامی دیتاستها با فرمت json منتشر شدهاندف به همین دلیل باید فرمت دیتاست خام را به تغییر دهید تا به آسانی بتوانید آن بازیابی کنید و یا تغییر دهید.
1 import json 2 from pandas.io.json import json_normalize 3 4 with open('./Germany_Korea.json') as data_file: 5 data = json.load(data_file) 6 df = json_normalize(data, sep = "_")
json_normalize() به صورت خودکار فرمت json را به ساختار رابطهای مسطح Flat relational structure «نرمالسازی» میکند.
در این مقاله آموزشی، منحصراً نمونههایی از جام جهانی 2018 فوتبال ارائه میدهیم. بهتر است پیش از آغاز فرایند تحلیل، سند دیتاست را مطالعه کنید و به یاد داشته باشید که درک دادهها اهمیت زیادی دارد.
2- رسم زمین فوتبال
در مرحله اول زمین فوتبال را با استفاده از Matplotlib رسم میکنیم.
1 def draw_pitch(ax): 2 # focus on only half of the pitch 3 #Pitch Outline & Centre Line 4 Pitch = Rectangle([0,0], width = 120, height = 80, fill = False) 5 #Left, Right Penalty Area and midline 6 LeftPenalty = Rectangle([0,22.3], width = 14.6, height = 35.3, fill = False) 7 RightPenalty = Rectangle([105.4,22.3], width = 14.6, height = 35.3, fill = False) 8 midline = ConnectionPatch([60,0], [60,80], "data", "data") 9 10 #Left, Right 6-yard Box 11 LeftSixYard = Rectangle([0,32], width = 4.9, height = 16, fill = False) 12 RightSixYard = Rectangle([115.1,32], width = 4.9, height = 16, fill = False) 13 14 15 #Prepare Circles 16 centreCircle = plt.Circle((60,40),8.1,color="black", fill = False) 17 centreSpot = plt.Circle((60,40),0.71,color="black") 18 #Penalty spots and Arcs around penalty boxes 19 leftPenSpot = plt.Circle((9.7,40),0.71,color="black") 20 rightPenSpot = plt.Circle((110.3,40),0.71,color="black") 21 leftArc = Arc((9.7,40),height=16.2,width=16.2,angle=0,theta1=310,theta2=50,color="black") 22 rightArc = Arc((110.3,40),height=16.2,width=16.2,angle=0,theta1=130,theta2=230,color="black") 23 24 element = [Pitch, LeftPenalty, RightPenalty, midline, LeftSixYard, RightSixYard, centreCircle, 25 centreSpot, rightPenSpot, leftPenSpot, leftArc, rightArc] 26 for i in element: 27 ax.add_patch(i)
در این قسمت قصد دارم تابع draw_pitch() را خط به خط توضیح دهم. این تابع یک ax argument به عنوان ورودی دریافت میکند؛ ax argument در Matplotlib خروجی تابع add_subplot() است. در مرحله بعد تابع draw_pitch بازآفرینی تصویر زمین فوتبال از جمله دایره مرکزی، محوطه جریمه، محوطه 18 قدم و منطقه کرنر، چندین شی که ابعاد آنها از پیش مشخص شده را اضافه میکند. پس از تعریف این تابع، آن را به همراه تابع استاندارد figure Matplotlib به روش زیر فراخوانی میکنیم:
1 fig=plt.figure() #set up the figures 2 fig.set_size_inches(7, 5) 3 ax=fig.add_subplot(1,1,1) 4 draw_pitch(ax) #overlay our different objects on the pitch 5 plt.ylim(-2, 82) 6 plt.xlim(-2, 122) 7 plt.axis('off') 8 plt.show()
3- ارتقای کیفیت مصورسازی داده ها با استفاده از Pass Map و Heat Map
تکاندهندهترین لحظه در جام جهانی 2018 فوتبال چیست؟
وقایع زیادی وجود دارد که میتوان از آنها به عنوان تکاندهندهترین واقعه جانم جهانی 2018 فوتبال یاد کرد، اما به عقیده من تکاندهندهترین آنها زمانی بود که آلمان، مدافع عنوان قهرمانی، پس از تحمل شکست در برابر کره جنوبی از گردونه رقابتهای جام جهانی کنار رفت. این اتفاق باعث ناراحتی بسیاری شد و افراد زیادی را به گریه انداخت و البته انتقادات زیادی را هم به همراه داشت. پس از این شکست تأمل برانگیز، مسوت اوزیل، بازیکن شماره 10 تیم آلمان، از این تیم کنارهگیری کرد؛ بسیاری مسوت اوزیل را به دلیل بازی ضعیف و همچنین ملاقات با اردوغان، رئیس جمهور ترکیه، پیش از آغاز جام جهانی، مسئول اصلی شکست تیم آلمان میدانند.
[irp posts=”5366″]در ادامه میتوانید سخنان اولی هونس، ریاست باشگاه بایرن مونیخ را راجع به اوزیل بخوانید:
«او سالها بود که مزخرف بازی میکرد. حالا هم خودش و بازی ضعیفش را پشت آن عکس مخفی میکند.»
آیا مسوت اوزیل تا این اندازه بد بود؟
در این قسمت بازی اوزیل در مقابل تیم کره جنوبی، که انتقادات زیادی به آن وارد شد را با یکدیگر بررسی میکنیم. من قصد دارم برای نشاندادن عملکرد او در طول 90 دقیقه بازی یک هیتمپ و یک pass map ترسیم کنم و تأثیراتی ( مثبت و منفی) که بازی او بر عملکرد خط حمله تیم آلمان داشته را ارزیابی کنیم.
Pass map
ابتدا فایل json را بارگذاری میکنیم و برخی دادهها را در کتابخانه Pandas پاک میکنیم تا دیتاستی از پاسکاریهای مسوت اوزیل داشته باشیم.
1 # loading the json file 2 ozil_pass = df[(df['type_name'] == "Pass") & (df['player_name']=='Mesut Özil')] # get passing information of Ozil 3 pass_column = [i for i in df.columns if i.startswith("pass")] 4 ozil_pass = ozil_pass[["id", "period", "timestamp", "location", "pass_end_location", "pass_recipient_name"]]
دیتاست نهایی حاوی اطلاعات مفیدی است، برای مثال این دیتاست نشان میدهد اوزیل 95 بار تلاش کرده توپ را پاس دهد و 7 پاس موفق داشته است که برای یک هافبک هجومی عدد تحسین برانگیزی به شمار میآید. علاوه بر این، این دیتاست نشان میدهد که اوزیل در طول بازی توپ را بیشتر به تونی کروس (19 بار) و مارکو رویس (18 بار) پاس داده است. برای ترسیم pass map فقط به موقعیت شروع و پایان پاس احتیاج داریم.
با استفاده از کد مقابل میتوانیم پاسها را به صورت پیکان بر روی زمین فوتبال نشان دهیم.
1 for i in range(len(ozil_pass)): 2 # annotate draw an arrow from a current position to pass_end_location 3 ax.annotate("", xy = (ozil_pass.iloc[i]['pass_end_location'][0], ozil_pass.iloc[i]['pass_end_location'][1]), xycoords = 'data', 4 xytext = (ozil_pass.iloc[i]['location'][0], ozil_pass.iloc[i]['location'][1]), textcoords = 'data', 5 arrowprops=dict(arrowstyle="->",connectionstyle="arc3", color = "blue"),)
ردیابی منطقه فعالیت با استفاده از نمودار حرارتی
تحلیلگران رسانه و باشگاهی از نمودارهای حرارتی فوتبالی برای نشان دادن منطقهای که بازیکن در ان حضور داشته استفاده میکنند. نمودارهای حرارتی فوتبالی در واقع نمودارهای پراکندگی موقعیت بازیکن هستند و عملکرد بازیکن را در بخشهای مختلف زمین را نشان میدهند. هرچند نظرات مختلفی راجع به میزان سودمندی نمودارهای حرارتی وجود دارد (نمودارهای حرارتی خوب یا بد بودن یک حرکت/ بازی را مشخص نمیکنند!)، اما به لحاظ زیباشناسی بسیار جذاب و سرگرمکننده هستند و علاوه بر این ز محبوبیت زیادی برخوردارند.
یکی از اصلیترین انتقاداتی که به مسوت اوزیل وارد شد این است که در طول بازی کنترل کمی بر زمین داشته است و در طول بازی به ندرت تکل میزند و مالکیت توپ اهمیت کمتری برای او دارد و یا به گفته منتقدان در زمین بازی نرخ همکاری او کم بوده است .
در این قسمت با استفاده از Seaborn و matplotlib یک هیتمپ ترسیم میکنیم و میزان مشارکت اوزیل را طی 90 دقیقه بازی آلمان-کره نشان میدهیم. کد به کار رفته در این قسمت بسیار آسان است. برای رسم این هیتمپ از kdeplot استفاده میکنیم که برآورد از تراکم نقاط پراکندگی موقعیتهای اوزیل رسم میکند.
1 fig, ax = plt.subplots() 2 fig.set_size_inches(7, 5) 3 4 x_coord = [i[0] for i in ozil_action["location"]] 5 y_coord = [i[1] for i in ozil_action["location"]] 6 7 #shades: give us the heat map we desire 8 # n_levels: draw more lines, the larger n, the more blurry it looks 9 sns.kdeplot(x_coord, y_coord, shade = "True", color = "green", n_levels = 30) 10 plt.show()
آیا میتوانیم نمودار بهتری رسم کنیم؟
بله میتوانیم. برای رسم یک نمودار بهتر میتوانیم زمین فوتبال، نقشه پاس ها و نمودارهای حرارتی را در کنار یکدیگر استفاده کنیم و عملکرد اوزیل در طول بازی آلمان – کره را به دقت بیشتری بررسی کنیم.
حالا تصویر جامعتری از عملکرد مسوت اوزیل در طول این بازی داریم. با توجه به تصاویر فوق متوجه میشویم:
- اوزیل تقریباً فقط بر زمین حریف کنترل داشته، در نتیجه انتقاداتی که در خصوص عدم مشارکت در کارهای دفاعی به او وارد شده بیپایه و اساس نیست. سؤالی که در اینجا مطرح میشود این است که آیا اوزیل انتظار داشته بازی را با نتیجه 1-1 برنده شود و توپ را به عنوان هافبک میانی دفاعی در اختیار بگیرد؟
- اوزیل در نیمه دوم بر خلاف نیمه اول، پاسهای مستقیم زیادی داشته است. پاسهای مستقیم اوزیل در نیمه دوم میتواند دو دلیل داشته باشد: 1) نیمه دوم بازی برای تیم آلمان اهمیت و فوریت بیشتری دارد، 2) در نیمه دوم ماریو گومز به عنوان مهاجم مرکزی وارد زمین شد و باعث پاسهای فرار اوزیل شود، همانگونه که در طول بازی نیز دیدیم، در نیمه دوم، شش پاس اوزیل وارد منطقه کرنر شد و این رقم سه برابر نیمه اول است.
در نمودار حرارتی به همراه پاسی که برای تیمو ورنر ترسیم شد یه موردی که توجه مرا به خود جلب کرد این است که ورنر به عنوان مهاجم مرکزی تیم آلمان وارد زمین شد و در نیمه دوم با ماریو گرمز جفت شد:
ورنر مدت زمان زیاد زیادی را در دو جناح بود، اما معمولاً انتظار میرود مهاجم مرکزی بیشتر در محوطه 18 قدم موضع بگیرد. نحوه بازی ورنر تا حدودی میتواند بی ثمر بودن خط دفاعی را در طول بازی توجیه کند، چرا که خط حمله این تیم (ورنر، رویس، گورتزکا و سپس مولر و گومز) جناح سمت راست و چپ را در اختیار کامل داشتند اما نتوانستند کنترلی بر منطقه جریمه داشته باشند، به همین دلیل بازیکنهایی از جمله اوزیل و کروس نمیتوانستند توپ را به سمت محوطه 18 قدم هدایت کنند.
1-مهارتهای خود را محک بزنید: تلاشهای بیوقفه تیم فرانسه
یکی از دوستان من معتقد است دلیل اصلی موفقیت تیم فرانسه در جام جهانی، تلاش بی وقفه این تیم برای شکستن خطهای دفاعی تیم حریف بوده است. برای چند لحظه گلی که بنجامین پاوار در مرحله یک چهارم نهایی در مقابل تیم آرژانتین به ثمر نشاند، فکر کنید.
در این قسمت نیز میتوانیم تمامی شوتهای بازیکنان تیم فرانسه را به تصویر بکشیم و مشخص کنیم آیا شوتها از داخل محوطه جریمهبودهاند یا از خارج محوطه جریمه.
اگر مطابق با روشی که تا به اینجا معرفی کردهام، پیش رویم، به تصویر مقابل دست پیدا میکنیم:
برای اینکه نموداری جامعتر به دست آوریم کارهای زیادی میتوانیم انجام دهیم. برای مثال من:
- از آنجایی که تمرکز ما بر روی شوتهای بازیکنان است و تمامی آنها در سمت راست زمین ثبت شدهاند، فقط سمت راست زمین را ترسیم میکنم.
- و از آنجایی که تمرکز ما بر روی نقطه شروع شوتها است، دیگر نیازی به استفاده از پیکانها نیست و میتوانیم شوتها را در قالب یک نمودار پراکندگی به تصویر بکشیم؛ در این نمودار پراکندگی x و y نشاندهنده نقاطی هستند که بازیکنان توپ را شوت کردهاند.
با توجه به تصویر فوق متوجه میشویم که تعداد شوتهای تیم فرانسه در خارج و داخل محوطه جریمه به یک اندازه بوده است و تا حدودی ادعای تلاش بی وقفه تیم فرانسه برای شکستن خط دفاعی تیم حریف را تأیید میکند، چرا که انتظار داریم تراکم شوتها در خارج از محوطه جریمه کمتر باشد.
با مراجعه به این لینک میتوانید ب کدها به کار رفته در این مطلب دسترسی پیدا کنید.
دادههای به کار رفته برای مصورسازی داده ها در این مطلب توسط Statsbomb منتشر کرده و با مراجعه به این لینک میتوانید به آنها دسترسی پیدا کنید.