import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
Giriş
2024 Avrupa Futbol Şampiyonası’ndaki son grup maçı öncesinde, Türkiye’nin en iyi performansı sergilemesi için en uygun kadroyu belirlemek hayati önem taşıyor. Bu süreçte, TOPSIS gibi çok kriterli karar verme yöntemleri teknik ekibe en ideal kadroyu oluşturma konusunda önemli bir rehberlik sağlayabilir.
Kullanılacak Kütüphaneler
TOPSIS
Tanım
TOPSIS (Technique for Order Preference by Similarity to Ideal Solution), çok kriterli karar verme (MCDM) problemlerinde kullanılan etkili bir yöntemdir. 1981 yılında Hwang ve Yoon tarafından geliştirilen bu yöntem, alternatiflerin değerlendirilmesinde ideal ve negatif-ideal çözümlerle olan benzerlikleri dikkate alarak sıralama yapar. TOPSIS’in temel amacı, en iyi alternatifi seçmek veya mevcut alternatifleri belirli kriterler doğrultusunda sıralamaktır.
Hesaplama Adımları
TOPSIS’in hesaplaması 7 adımda açıklanabilir.
Karar Matrisi Oluşturma: İlk adımda, alternatifler ve kriterler belirlenir ve bu alternatiflerin her bir kriter için performans değerleri karar matrisi şeklinde düzenlenir.
Normalize Edilmiş Karar Matrisi: Karar matrisi, kriterlerin farklı ölçeklerde olmasından dolayı normalize edilir. Bu adım, tüm kriterlerin ortak bir ölçek üzerinde değerlendirilmesini sağlar.
Ağırlıklı Normalize Edilmiş Matris: Kriterlerin önem derecelerine göre ağırlıklandırılması yapılır ve normalize edilmiş karar matrisi ile bu ağırlıklar çarpılır.
İdeal ve Negatif-İdeal Çözümler: Her bir kriter için en iyi (ideal) ve en kötü (negatif-ideal) değerler belirlenir. İdeal çözüm, kriterlerin maksimum fayda veya minimum maliyet değerlerini içerirken, negatif-ideal çözüm tam tersidir.
İdeal ve Negatif-İdeal Çözümlere Uzaklıkların Hesaplanması: Her bir alternatifin ideal ve negatif-ideal çözümlere olan uzaklıkları hesaplanır. Bu uzaklıklar, Öklidyen mesafe kullanılarak belirlenir.
Göreceli Yakınlık Değerlerinin Hesaplanması: Alternatiflerin ideal çözüme yakınlık dereceleri hesaplanır. Bu değer, negatif-ideal çözüme olan uzaklıkların toplam uzaklığa oranı olarak bulunur.
Alternatiflerin Sıralanması: Son adımda, göreceli yakınlık değerlerine göre alternatifler sıralanır. En yüksek yakınlık değeri, en ideal alternatifi gösterir.
Kullanılacak İstatistikler
Turnuva Aşamalarına Göre Kullanılacak İstatistikler
UEFA Avrupa Şampiyonası eleme aşamaları iki ana bölüme ayrılır: Qualifying (eleme) ve Main tournament (ana turnuva).
- Qualifying (Eleme): Bu aşama, Avrupa’daki milli takımların ana turnuvaya katılmak için yarıştığı süreçtir. Eleme grupları oluşturularak maçlar yapılır ve bu maçlar sonunda en iyi takımlar ana turnuvaya katılma hakkı kazanır.
- Main Tournament (Ana Turnuva): Bu, eleme aşamasını geçen takımların katıldığı nihai turnuvadır.
Her iki aşama da skorlar hesaplanırken ağırlıklandırılacaktır.
Maç İstatistikleri Açıklamaları
Veri setlerinde aşağıdaki istatistikler bulunmaktadır.
- Kategori: Goals (Goller)
Goals - Goller: Futbolcunun maç sırasında attığı gol sayısı.
- Kategori: Attempts (Denemeler)
Attempts on target - Kaleyi bulan şutlar: Futbolcunun kaleyi bulan şut sayısı.
Attempts off target - Kaleyi bulmayan şutlar: Futbolcunun kaleyi bulmayan şut sayısı.
Blocked - Bloke edilen şutlar: Rakip oyuncular tarafından engellenen şut sayısı.
- Kategori: Distribution (Dağıtım)
Passes attempted - Denenen paslar: Futbolcunun pas atma girişimlerinin toplam sayısı.
Passes completed - Tamamlanan paslar: Futbolcunun başarılı bir şekilde takım arkadaşına ulaştırdığı pas sayısı.
Crosses attempted - Denenen ortalar: Futbolcunun ceza sahasına veya ceza sahası yakınına yaptığı orta denemeleri.
Crosses completed - Tamamlanan ortalar: Futbolcunun başarılı bir şekilde takım arkadaşına ulaştırdığı orta sayısı.
- Kategori: Attacking (Hücum)
Assists - Asistler: Futbolcunun takım arkadaşına gol atması için yaptığı son pas sayısı.
Offsides - Ofsaytlar: Futbolcunun ofsayt pozisyonunda yakalandığı durumlar.
Dribbles - Top sürme: Futbolcunun rakip oyuncuları geçmek için topu kontrol ederek yaptığı hareketlerin sayısı.
- Kategori: Defending (Savunma)
Balls recovered - Kazanılan toplar: Futbolcunun rakipten kaptığı veya yeniden kazandığı top sayısı.
Tackles - Müdahaleler: Futbolcunun rakip oyuncudan topu almak için yaptığı müdahale sayısı.
Clearances attempted - Denenen uzaklaştırmalar: Futbolcunun rakip ceza sahasından topu uzaklaştırma girişimleri.
- Kategori: Goalkeeping (Kalecilik)
Saves - Kurtarışlar: Kalecinin rakip oyuncunun şutunu engellediği kurtarış sayısı.
Goals conceded - Yenilen goller: Kalecinin kalesinde gördüğü gol sayısı.
Saves from penalties - Penaltı kurtarışları: Kalecinin penaltı vuruşunu kurtardığı durumlar.
- Kategori: Disciplinary (Disiplin)
Fouls committed - Yapılan fauller: Futbolcunun rakip oyuncuya karşı yaptığı faul sayısı.
Fouls suffered - Uğranılan fauller: Futbolcunun rakip oyuncu tarafından kendisine yapılan faul sayısı.
Yellow cards - Sarı kartlar: Futbolcunun hakem tarafından gördüğü sarı kart sayısı.
Red cards - Kırmızı kartlar: Futbolcunun hakem tarafından gördüğü kırmızı kart sayısı.
Futbolcu Pozisyonları Açıklamaları
Goalkeeper (Kaleci): Kaleci, takımın kalesini koruyan oyuncudur. Rakip takımın şutlarını engellemek, topları yakalamak veya uzaklaştırmak, savunmayı organize etmek gibi görevleri vardır.
Defender (Savunma Oyuncusu): Savunma oyuncuları, rakip takımın gol atmasını engellemeye çalışır. Savunma hattında pozisyon alarak topu kapmak, müdahale etmek, topu uzaklaştırmak gibi görevleri vardır.
Midfielder (Orta Saha Oyuncusu): Orta saha oyuncuları, takımın hem savunma hem de hücum görevlerini üstlenir. Topa sahip olma, pas dağıtma, oyunu yönlendirme, gol fırsatları yaratma ve savunmaya yardımcı olma gibi çeşitli görevleri vardır.
Forward (Forvet Oyuncusu): Forvet oyuncuları, takımın gol atma görevini üstlenen oyunculardır. Rakip kale önünde pozisyon alarak gol fırsatları yaratma ve gol atma gibi görevleri vardır.
Pozisyonlara Göre Kullanılacak İstatistikler, Ağırlıklar ve İşaretler
Hem pozisyonlara göre kullanılacak istatistikler hem de ağırlıklar ile işaretlerin tablosu aşağıdadır.
İdeal Kadronun Belirlenmesi
Skorları Hesaplayacak Fonksiyonun Yazılması
def calculate_topsis_score(position, main_df, weights_df, score_col_name):
# Adım 1
= [col for col in main_df.columns if col not in ['Player', 'Position', 'Matches played', 'Minutes Played']]
norm_main_df = main_df['Minutes Played']
main_minutes_played = main_df[norm_main_df].div(main_minutes_played, axis=0)
main_df[norm_main_df]
# Adım 2
= weights_df[['Stats', f'{position} Weight', f'{position} Sign']].dropna()
position_weights = position_weights['Stats'].tolist()
position_stats = [col for col in position_stats if col in main_df.columns]
position_stats = position_weights[f'{position} Weight'].tolist()
position_weights_values = position_weights[f'{position} Sign'].tolist()
position_signs = [weight for stat, weight in zip(position_weights['Stats'], position_weights_values) if stat in position_stats]
position_weights_values = [sign for stat, sign in zip(position_weights['Stats'], position_signs) if stat in position_stats]
position_signs
# Adım 3
= ['Player', 'Position'] + position_stats
position_required_columns = main_df[position_required_columns]
position_main_df = position_main_df[position_main_df['Position'] == position]
position_main_df
# Adım 4
= position_main_df[position_stats].values
position_matrix = np.zeros_like(position_matrix)
position_matrix_normalized for i in range(position_matrix.shape[1]):
= position_matrix[:, i].min()
col_min = position_matrix[:, i].max()
col_max if col_min == col_max:
= 0
position_matrix_normalized[:, i] else:
= (position_matrix[:, i] - col_min) / (col_max - col_min)
position_matrix_normalized[:, i]
# Adım 5
= position_matrix_normalized * position_weights_values
weighted_matrix
# Adım 6
= np.max(weighted_matrix * (np.array(position_signs) == '+'), axis=0) + np.min(weighted_matrix * (np.array(position_signs) == '-'), axis=0)
ideal_solution = np.min(weighted_matrix * (np.array(position_signs) == '+'), axis=0) + np.max(weighted_matrix * (np.array(position_signs) == '-'), axis=0)
negative_ideal_solution
# Adım 7
= np.sqrt(((weighted_matrix - ideal_solution) ** 2).sum(axis=1))
separation_ideal = np.sqrt(((weighted_matrix - negative_ideal_solution) ** 2).sum(axis=1))
separation_negative_ideal
# Adım 8
= separation_negative_ideal / (separation_ideal + separation_negative_ideal)
topsis_score
# Adım 9
= topsis_score
position_main_df[score_col_name] = position_main_df[['Player', score_col_name]]
position_main_df = position_main_df.sort_values(by=score_col_name, ascending=False).reset_index(drop=True)
position_main_df
return position_main_df
Yukarıdaki fonksiyon aşağıdaki adımları içermektedir.
Adım 1: main_df
içindeki oyuncuların istatistiksel verileri normalize edilir. Minutes Played
sütunundaki değerlere göre diğer istatistiksel sütunlar bölünerek normalleştirilir. Böylece, her oyuncunun performansı, oynadığı dakikaya göre oransal olarak değerlendirilmiş olur.
Adım 2: Belirli bir pozisyon için weights_df
veri çerçevesinden istatistiksel veri sütunlarına ve bu sütunların ağırlıklarına ulaşılır. Aynı zamanda her bir istatistiksel sütunun olumlu (+) veya olumsuz (-) bir etkiye sahip olup olmadığını gösteren işaretler de alınır.
Adım 3: Belirtilen pozisyondaki oyuncuların main_df
içinden ilgili istatistiksel sütunlarını ve pozisyon bilgilerini içeren veri çerçevesi position_main_df
oluşturulur.
Adım 4: Belirtilen istatistiksel veri matrisi min-max normalizasyonu kullanılarak normalize edilir. Bu işlem, her bir istatistiksel ölçütün değerini [0, 1] aralığında ölçeklemeye yarar.
Adım 5: Normalleştirilmiş veri matrisine önceden belirlenmiş ağırlıklar uygulanır. Bu işlem, her bir istatistiksel ölçütün önem derecesine göre performans değerlerini ağırlıklarla çarparak ağırlıklı hale getirir.
Adım 6: Ağırlıklı matris üzerinde ideal ve negatif ideal çözümler belirlenir. İdeal çözüm, her ölçüt için en yüksek ve en düşük değerlerden oluşurken, negatif ideal çözüm ise en düşük ve en yüksek değerlerden oluşur.
Adım 7: Her bir oyuncunun ideal ve negatif ideal çözümlere olan uzaklığını ölçen ayırma ölçütleri hesaplanır.
Adım 8: Ayırma ölçütlerini kullanarak her bir oyuncunun TOPSIS skoru hesaplanır. Bu skor, bir oyuncunun ideal çözüme ne kadar yakın olduğunu gösterir.
Adım 9: Hesaplanan TOPSIS skorları position_main_df
veri çerçevesine eklenir, sadece oyuncu adı ve TOPSIS skoru sütunları alınır, skorlara göre sıralanır ve sonuç olarak döndürülür.
Veri Setinin İçe Aktarılması
Veri setlerine buradan ulaşabilirsiniz.
# Veri seti
= pd.read_excel('turkey_players.xlsx', sheet_name='Qualifying')
qualifying_df = pd.read_excel('turkey_players.xlsx', sheet_name='Main tournament')
main_tournament_df = pd.read_excel('turkey_players.xlsx', sheet_name='Weights')
weights_df
# Fonksiyonu uygulama
= calculate_topsis_score('Goalkeeper', qualifying_df, weights_df, 'TOPSIS Score Qualifying')
goalkeeper_qualifying_df = calculate_topsis_score('Defender', qualifying_df, weights_df, 'TOPSIS Score Qualifying')
defender_qualifying_df = calculate_topsis_score('Midfielder', qualifying_df, weights_df, 'TOPSIS Score Qualifying')
midfielder_qualifying_df = calculate_topsis_score('Forward', qualifying_df, weights_df, 'TOPSIS Score Qualifying')
forward_qualifying_df
= calculate_topsis_score('Goalkeeper', main_tournament_df, weights_df, 'TOPSIS Score Main Tournament')
goalkeeper_main_tournament_df = calculate_topsis_score('Defender', main_tournament_df, weights_df, 'TOPSIS Score Main Tournament')
defender_main_tournament_df = calculate_topsis_score('Midfielder', main_tournament_df, weights_df, 'TOPSIS Score Main Tournament')
midfielder_main_tournament_df = calculate_topsis_score('Forward', main_tournament_df, weights_df, 'TOPSIS Score Main Tournament')
forward_main_tournament_df
# Birleştirme
= pd.merge(goalkeeper_qualifying_df, goalkeeper_main_tournament_df[['Player', 'TOPSIS Score Main Tournament']], on='Player', how='outer')
goalkeeper_combined_df = pd.merge(defender_qualifying_df, defender_main_tournament_df[['Player', 'TOPSIS Score Main Tournament']], on='Player', how='outer')
defender_combined_df = pd.merge(midfielder_qualifying_df, midfielder_main_tournament_df[['Player', 'TOPSIS Score Main Tournament']], on='Player', how='outer')
midfielder_combined_df = pd.merge(forward_qualifying_df, forward_main_tournament_df[['Player', 'TOPSIS Score Main Tournament']], on='Player', how='outer') forward_combined_df
Veri seti: turkey_players.xlsx
dosyasından Qualifying
, Main tournament
ve Weights
adlı Excel sayfalarını okur ve bunları sırasıyla qualifying_df
, main_tournament_df
ve weights_df
değişkenlerine atar.
Fonksiyonu uygulama: calculate_topsis_score
fonksiyonu, her bir pozisyon için hem elemelerde (qualifying_df
) hem de ana turnuvalarda (main_tournament_df
) performans gösteren oyuncuların TOPSIS skorlarını hesaplar. Her pozisyon için ayrı ayrı çağrılar yapılır ve sonuçlar *_qualifying_df
ve *_main_df
değişkenlerine atanır.
Birleştirme: Her pozisyon için elemelerde ve ana turnuvalarda hesaplanan TOPSIS skorları merge
fonksiyonu ile birleştirilir. Her bir pozisyon için birleştirilmiş sonuçlar *_combined_df
değişkenlerine atanır.
İdeal Kadroyu Belirleyecek Fonksiyonun Yazılması
def apply_weights_and_select_players(goalkeeper_combined_df, defender_combined_df, midfielder_combined_df, forward_combined_df, formation, weight_qualifying, weight_main_tournament, unavailable_players=None, plot=False):
# Birleştirme
= [
combined_dfs 'Goalkeeper', goalkeeper_combined_df),
('Defender', defender_combined_df),
('Midfielder', midfielder_combined_df),
('Forward', forward_combined_df)
(
]
# Boş liste
= []
selected_players
# Döngü
for position, df in combined_dfs:
'TOPSIS Score Qualifying'] = df['TOPSIS Score Qualifying'].fillna(0)
df['TOPSIS Score Main Tournament'] = df['TOPSIS Score Main Tournament'].fillna(0)
df[
'Weighted Average TOPSIS Score'] = weight_qualifying * df['TOPSIS Score Qualifying'] + weight_main_tournament * df['TOPSIS Score Main Tournament']
df[
= df.sort_values(by='Weighted Average TOPSIS Score', ascending=False).reset_index(drop=True)
df
= df[['Player', 'Weighted Average TOPSIS Score']]
df
# Görsel
if plot:
=(10, 6))
plt.figure(figsize'Player'], df['Weighted Average TOPSIS Score'], color='#ff9999')
plt.barh(df['Weighted Average TOPSIS Score')
plt.xlabel(f'{position} Players Weighted Average TOPSIS Scores')
plt.title(
plt.gca().invert_yaxis()
plt.show()
# Kadro dışı kontrolü
if unavailable_players and position in unavailable_players:
= df[~df['Player'].isin(unavailable_players[position])]
df
# Formasyon
if position == 'Goalkeeper':
= 1
num_players else:
= formation.split('-')
positions = {
num_players 'Defender': int(positions[0]),
'Midfielder': int(positions[1]),
'Forward': int(positions[2])
}[position]
selected_players.append(df.head(num_players))
# Birleştirme
= pd.concat(selected_players, ignore_index=True)
selected_players_df
return selected_players_df
Fonksiyon: Her bir pozisyon için goalkeeper_combined_df
, defender_combined_df
, midfielder_combined_df
ve forward_combined_df
veri çerçevelerini alır. Ayrıca, oyuncu seçimi için kullanılacak olan formasyonu (formation
) ve elemeler ile ana turnuvaların ağırlıklarını (weight_qualifying
ve weight_main_tournament
) varsayılan değerlerle alır.
Birleştirme: Her bir pozisyon için belirlenen veri çerçeveleri bir listeye (combined_dfs
) eklenir. Her öğe, pozisyon adı ve o pozisyona ait birleştirilmiş veri çerçevesini içerir.
Boş liste: Her bir pozisyon için seçilen oyuncuların veri çerçevelerini içerecektir.
Döngü: Her bir pozisyon için şu işlemler yapılır: TOPSIS Score Qualifying
ve TOPSIS Score Main Tournament
sütunlarında olası eksik değerler (NaN
) sıfırlarla doldurulur. Ağırlıklı TOPSIS skorları hesaplanır ve Weighted Average TOPSIS Score
adlı yeni bir sütun oluşturulur. Skorlara göre veri çerçevesi büyükten küçüğe sıralanır ve sıralı veri çerçevesi Player
ve Weighted Average TOPSIS Score
sütunları kalacak şekilde bırakılır.
Görsel: plot
parametresi True
olarak ayarlanırsa tüm pozisyonlar için oyuncu sıralamaları görsele dökülür.
Kadro dışı kontrolü: Teoride ideal kadro belirlense de uygulamada sakatlıklar, kart cezaları gibi nedenlerle oyuncular kadro dışı kalabilir. Burada, oyuncular dışarıda bırakılır.
Formasyon: Belirtilen formasyona (formation
) göre her bir pozisyon için kaç oyuncu seçileceği belirlenir. num_players
sözlüğü kullanılarak her bir pozisyon için head
fonksiyonu ile ilk num_players
kadar oyuncu seçilir ve seçilen oyuncular selected_players
listesine eklenir.
Birleştirme ve Döndürme: selected_players
listesindeki tüm seçilen oyuncu veri çerçeveleri birleştirilir ve selected_players_df
olarak döndürülür.
Formasyonun 4-4-2 olduğunu varsayarak oyuncuları seçelim. Yani, 4 defans, 4 orta saha ve 2 forvet ile çıkacağımızı varsayıyoruz.
= '4-4-2'
formation = 0.2
weight_qualifying = 0.8
weight_main_tournament
= apply_weights_and_select_players(
selected_players
goalkeeper_combined_df,
defender_combined_df,
midfielder_combined_df,
forward_combined_df,
formation,
weight_qualifying,
weight_main_tournament,=True
plot
)
print(f"Selected Players for {formation}:")
print(selected_players)
Oyuncuların pozisyonlarına göre sıralamalarına bakalım.
Çekya maçı öncesi milli takım oyuncularımızın son durumlarına bakalım ve ideal kadroyu yeniden belirleyelim.
Ayak bileğinde travma yaşayan Orkun Kökçü ve dizinde ağrı şikayeti bulunan Mert Günok’un bu karşılaşmada forma giymesi zor görünüyor. Ayrıca, ilk iki maçta gördüğü sarı kartlar nedeniyle cezalı duruma düşen Abdülkerim Bardakçı bu maçta oynayamayacak.
= '4-4-2'
formation = 0.2
weight_qualifying = 0.8
weight_main_tournament
= {
unavailable_players 'Goalkeeper': ['Mert Günok'],
'Defender': ['Abdülkerim Bardakçı'],
'Midfielder': ['Orkun Kökçü'],
'Forward': []
}
= apply_weights_and_select_players(
selected_players
goalkeeper_combined_df,
defender_combined_df,
midfielder_combined_df,
forward_combined_df,
formation,
weight_qualifying,
weight_main_tournament,
unavailable_players
)
print(f"Selected Players for {formation}:")
print(selected_players)
Tabloya göre;
- İdeal kadronun en iyi kalecisi Mert Günok yerini Altay Bayındır’a bırakmış.
- Abdülkerim Bardakçı ve Orkun Kökçü zaten ilk ideal kadroda da yoktu.
- Arda Güler’in Portekiz maçında sonradan (70’) oyuna girmesi tepki görmüştü. Arda Güler’in hem kendi pozisyon sıralamasında lider olduğunu hem de ideal kadroda bulunduğunu görüyoruz.
Gelecek içeriklerde görüşmek dileğiyle.