Pakiety i ustawienia
library(dplyr)
library(tidyr)
library(cluster)
library(factoextra)
library(mclust)
library(vcd)
library(kableExtra)
library(arules)
library(arulesViz)
library(stringr)
library(DT)
options(
tibble.width = Inf,
width = 2000,
dplyr.width = Inf,
max.print = 1e6
)
Celem analizy jest identyfikacja naturalnych struktur oraz wzorców, a także zależności występujących w danych opisujących zawodników gry EA FC 26, z wykorzystaniem metod analizy nienadzorowanej, w szczególności klastrowania oraz reguł asocjacyjnych.
df_raw <- read.csv("EAFC26_ML.csv") %>%
filter(Position != "GK")
df <- df_raw %>%
mutate(
Preferred.foot = ifelse(Preferred.foot == "Left", 1, 0),
GENDER = ifelse(GENDER == "M", 1, 0)
) %>%
select(-OVR, -PAC, -SHO, -PAS, -DRI, -DEF, -PHY)
df_gold <- df_raw %>%
filter(OVR >= 75) %>% # klasyczne złote
mutate(
Preferred.foot = ifelse(Preferred.foot == "Left", 1, 0),
GENDER = ifelse(GENDER == "M", 1, 0)
) %>%
select(-OVR, -PAC, -SHO, -PAS, -DRI, -DEF, -PHY)
position_groups <- list(
CB = "CB",
FB = c("LB","RB"),
CDM = "CDM",
CM = "CM",
CAM = "CAM",
WM = c("LM","RM"),
WING = c("LW","RW"),
ST = "ST"
)
W pierwszym etapie analizy wczytano zbiór danych zawierający statystyki kart z gry EA FC 26. Z analizy wykluczono bramkarzy (Position = GK), ponieważ ich atrybuty są liczone w odmienny sposób i nie są bezpośrednio porównywalne z zawodnikami z pola.
Preferowana noga zawodnika (Preferred.foot) została zakodowana binarnie, gdzie wartość 1 oznacza lewą nogę, a 0 prawą. Analogicznie płeć (GENDER) została przekształcona do postaci binarnej, gdzie 1 odpowiada zawodnikom płci męskiej, a 0 żeńskiej.
W kolejnym kroku usunięto zmienne zagregowane (OVR, PAC, SHO, PAS, DRI, DEF, PHY), które stanowią syntetyczne wskaźniki oparte na pozostałych atrybutach. Ich obecność mogłaby prowadzić do nadmiernego wpływu tych samych informacji na proces klastrowania oraz zaburzać interpretację wyników klastrowania.
W analizie klastrowania przyjęto zakres liczby klastrów od 2 do 8. Dolna granica wynika z minimalnego sensu interpretacyjnego podziału, natomiast górna ogranicza nadmierną fragmentację danych i ułatwia interpretację uzyskanych wyników.
k_min <- 2
k_max <- 8
Na potrzeby analizy zdefiniowano zestaw funkcji technicznych, odpowiedzialnych za skalowanie danych, wybór liczby klastrów oraz przeprowadzenie klastrowania z walidacją jakości podziału.
minmax_scale <- function(df) {
as.data.frame(lapply(df, function(x) {
if (max(x, na.rm = TRUE) == min(x, na.rm = TRUE)) {
rep(0, length(x))
} else {
(x - min(x, na.rm = TRUE)) /
(max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
}
}))
}
Funkcja minmax_scale() realizuje skalowanie zmiennych
numerycznych do przedziału ⟨0,1⟩, co zapobiega dominacji zmiennych o
większej skali podczas obliczania odległości.
pam_with_validation <- function(data = NULL, dist_mat = NULL) {
# ===== PRZYPADEK 1: DANE NUMERYCZNE =====
if (!is.null(data)) {
data <- as.data.frame(data)
n_hopkins <- min(100, nrow(data) - 1)
hopkins <- get_clust_tendency(
data,
n = n_hopkins,
graph = FALSE,
seed = 123
)$hopkins_stat
sil <- sapply(k_min:k_max, function(k) {
pam_fit <- pam(data, k)
pam_fit$silinfo$avg.width
})
best_k <- which.max(sil) + k_min - 1
pam_model <- pam(data, best_k)
return(list(
best_k = best_k,
hopkins = hopkins,
mean_silhouette = max(sil),
cluster = pam_model$clustering
))
}
# ===== PRZYPADEK 2: MACIERZ ODLEGŁOŚCI (GOWER) =====
if (!is.null(dist_mat)) {
sil <- sapply(k_min:k_max, function(k) {
pam_fit <- pam(dist_mat, k, diss = TRUE)
pam_fit$silinfo$avg.width
})
best_k <- which.max(sil) + k_min - 1
pam_model <- pam(dist_mat, best_k, diss = TRUE)
return(list(
best_k = best_k,
hopkins = NA,
mean_silhouette = max(sil),
cluster = pam_model$clustering
))
}
stop("Musisz podać data albo dist_mat")
}
Funkcja pam_with_validation() przeprowadza klast rowanie
metodą PAM oraz oblicza miary jakości podziału, w tym statystykę
Hopkinsa (określającą klastrowalność danych) oraz średnią wartość
sylwetki, pozwalającą ocenić spójność uzyskanych klastrów.
round_df <- function(x, digits = 4) {
x %>%
dplyr::mutate(
dplyr::across(
where(is.numeric),
~ round(.x, digits)
)
)
}
show_dt <- function(x, n = 25, digits = 4) {
x2 <- x %>%
dplyr::mutate(
dplyr::across(
where(is.numeric),
~ round(.x, digits)
)
)
dt <- DT::datatable(
x2,
rownames = FALSE,
width = "100%",
options = list(
pageLength = n,
scrollX = TRUE,
autoWidth = FALSE,
scrollCollapse = TRUE,
initComplete = DT::JS(
"function(settings, json) {",
" this.api().columns.adjust();",
"}"
)
)
)
num_cols <- names(x2)[vapply(x2, is.numeric, logical(1))]
if (length(num_cols) > 0) {
dt <- DT::formatRound(dt, columns = num_cols, digits = digits)
# 🔹 USUWANIE .0000 / .1200 → .12 / .0000 → 0
dt <- DT::formatStyle(
dt,
columns = num_cols,
`text-align` = "right"
)
dt$x$options$columnDefs <- list(
list(
targets = which(names(x2) %in% num_cols) - 1,
render = DT::JS(
"function(data, type, row) {",
" if (type !== 'display') return data;",
" return parseFloat(data).toString();",
"}"
)
)
)
}
dt
}
Ta część tworzy bardziej czytelne tabele.
W analizie zastosowano dwa podejścia do klastrowania: klastrowanie oparte wyłącznie na zmiennych numerycznych oraz klastrowanie dla danych mieszanych.
W pierwszym przypadku wykorzystano metodę PAM w połączeniu z klasyczną odległością euklidesową. W drugim przypadku zastosowano miarę Gowera, umożliwiającą uwzględnienie jednocześnie zmiennych numerycznych i kategorycznych, a następnie również metodę PAM do wyznaczenia klastrów.
Takie podejście pozwala na porównanie stabilności i jakości podziału w zależności od rodzaju wykorzystanych cech.
cluster_fr <- function(data) {
##################################################
# 1️⃣ KLASTROWANIE NUMERYCZNE
##################################################
df_num <- data %>%
select(where(is.numeric)) %>%
select(-GENDER, -Preferred.foot)
if (ncol(df_num) < 2) {
stop("Za mało zmiennych numerycznych do klastrowania")
}
df_scaled <- minmax_scale(df_num)
res_num <- pam_with_validation(df_scaled)
clusters_numeric <- data %>%
mutate(cluster_numeric = factor(res_num$cluster)) %>%
droplevels()
##################################################
# 2️⃣ KLASTROWANIE MIESZANE (GOWER)
##################################################
df_feat <- data %>%
select(-Name) %>%
mutate(across(where(is.character), as.factor))
gower <- daisy(df_feat, metric = "gower")
res_mix <- pam_with_validation(NULL, gower)
clusters_mixed <- data %>%
mutate(cluster_mixed = factor(res_mix$cluster)) %>%
droplevels()
##################################################
# 3️⃣ ZWROT WYNIKÓW
##################################################
list(
numeric = list(
best_k = res_num$best_k,
hopkins = res_num$hopkins,
mean_silhouette = res_num$mean_silhouette,
data = clusters_numeric
),
mixed = list(
best_k = res_mix$best_k,
hopkins = res_mix$hopkins,
mean_silhouette = res_mix$mean_silhouette,
data = clusters_mixed
)
)
}
res_fr <- cluster_fr(df)
compare_clustering_methods <- function(res) {
ari <- adjustedRandIndex(
res$numeric$data$cluster_numeric,
res$mixed$data$cluster_mixed
)
data.frame(
Method = c("Numeric", "Mixed (Gower)"),
Best_k = c(
res$numeric$best_k,
res$mixed$best_k
),
Silhouette = round(c(
res$numeric$mean_silhouette,
res$mixed$mean_silhouette
), 3),
Hopkins = c(
round(res$numeric$hopkins, 3),
NA
),
ARI = round(c(ari, ari), 3)
)
}
show_dt(compare_clustering_methods(res_fr))
Wyniki przemawiają na korzyść klastrowania opartego na danych numerycznych, które uzyskało wyższą wartość miary Silhouette. Wartość statystyki Hopkinsa, obliczona wyłącznie dla danych numerycznych, potwierdza zasadność przeprowadzenia klastrowania, jednak nie może być bezpośrednio wykorzystana do porównania obu metod.
Umiarkowana wartość współczynnika ARI wskazuje, że obie metody ujawniają częściowo odmienne struktury danych, co sugeruje istotny wpływ uwzględnienia zmiennych kategorycznych na końcowy podział.
cluster_profiles_numeric <- function(clustered_data, cluster_var) {
clustered_data %>%
group_by(.data[[cluster_var]]) %>%
summarise(
n_players = n(),
across(where(is.numeric), mean, na.rm = TRUE),
.groups = "drop"
) %>%
rename(cluster = .data[[cluster_var]])
}
profiles_numeric <- cluster_profiles_numeric(
res_fr$numeric$data,
cluster_var = "cluster_numeric"
)
show_dt(profiles_numeric)
Uzyskane klastry charakteryzują się wyraźnie odmiennymi profilami statystycznymi.
Klaster pierwszy obejmuje zawodników najbardziej wszechstronnych, o umiarkowanie wysokich wartościach zarówno w atrybutach technicznych, jak i fizycznych. Klaster drugi skupia zawodników ofensywnych i kreatywnych, cechujących się wysokimi wartościami tempa, kontroli piłki oraz wykończenia akcji, przy jednocześnie niskich parametrach defensywnych. Klaster trzeci reprezentuje zawodników defensywnych i fizycznych, z wysokimi wartościami w atrybutach obronnych oraz największym wzrostem i masą ciała.
Ze względu na duży rozrzut poziomu umiejętności w pełnym zbiorze danych, w dalszej części pracy przeprowadzone zostanie osobne klastrowanie zawodników „złotych”, zdefiniowanych jako gracze o ocenie ogólnej OVR ≥ 75, w celu uzyskania bardziej jednorodnych i interpretowalnych klastrów.
cluster_profiles_mixed <- function(clustered_data, cluster_var, categorical_vars) {
# część numeryczna
numeric_profile <- clustered_data %>%
group_by(.data[[cluster_var]]) %>%
summarise(
n_players = n(),
across(where(is.numeric), mean, na.rm = TRUE),
.groups = "drop"
) %>%
rename(cluster = .data[[cluster_var]])
# dominujące kategorie
categorical_profiles <- lapply(categorical_vars, function(var) {
clustered_data %>%
count(.data[[cluster_var]], .data[[var]]) %>%
group_by(.data[[cluster_var]]) %>%
mutate(share = n / sum(n)) %>%
slice_max(n, n = 1, with_ties = FALSE) %>%
ungroup() %>%
select(
cluster = .data[[cluster_var]],
value = .data[[var]],
share
) %>%
rename(
!!paste0(var, "_dominant") := value,
!!paste0(var, "_share") := share
)
})
categorical_profile <- Reduce(
function(x, y) left_join(x, y, by = "cluster"),
categorical_profiles
)
left_join(numeric_profile, categorical_profile, by = "cluster")
}
profiles_mixed <- cluster_profiles_mixed(
res_fr$mixed$data,
cluster_var = "cluster_mixed",
categorical_vars = c("Position", "League", "Nation")
)
show_dt(profiles_mixed)
W klastrowaniu mieszanym wyodrębniono dwa wyraźne profile zawodników.
Pierwszy klaster skupia głównie środkowych pomocników, charakteryzujących się wysoką techniką, podaniami oraz kontrolą piłki.
Drugi klaster obejmuje przede wszystkim środkowych obrońców, wyróżniających się wysoką siłą fizyczną oraz statystykami defensywnymi
cluster_position_share <- function(clustered_data, cluster_var) {
clustered_data %>%
group_by(.data[[cluster_var]], Position) %>%
summarise(n_players = n(), .groups = "drop") %>%
group_by(.data[[cluster_var]]) %>%
mutate(share = n_players / sum(n_players)) %>%
arrange(.data[[cluster_var]], desc(share)) %>%
rename(cluster = .data[[cluster_var]])
}
pos_numeric <- cluster_position_share(
res_fr$numeric$data,
cluster_var = "cluster_numeric"
)
show_dt(pos_numeric)
Na podstawie rozkładu pozycji w poszczególnych klastrach można jednoznacznie potwierdzić zasadność wcześniejszej interpretacji profili klastrów.
Pierwszy klaster jest zdominowany przez środkowych pomocników oraz bocznych obrońców, co odpowiada profilowi zawodników wszechstronnych.
Drugi klaster skupia niemal wyłącznie zawodników ofensywnych, w szczególności napastników oraz skrzydłowych.
Trzeci klaster jest silnie zdominowany przez środkowych obrońców i pozostałe pozycje defensywne, co potwierdza jego charakter obronny.
pos_mixed <- cluster_position_share(
res_fr$mixed$data,
cluster_var = "cluster_mixed"
)
show_dt(pos_mixed)
W przypadku klastrowania na danych mieszanych również obserwuje się wyraźny związek pomiędzy przynależnością do klastra a zajmowaną pozycją na boisku.
Pierwszy klaster skupia głównie zawodników ze środka pola oraz ofensywy, takich jak CM, ST, CAM oraz skrzydłowych, co odpowiada profilowi zawodników technicznych i kreatywnych.
Drugi klaster jest zdominowany przez środkowych oraz bocznych obrońców, a także defensywnych pomocników, co potwierdza jego defensywny charakter.
W porównaniu z klastrowaniem numerycznym, podział w modelu mieszanym jest mniej jednoznaczny, co wynika z uwzględnienia zmiennych kategorycznych (pozycja, liga, narodowość), które częściowo rozmywają czysto sportowy profil zawodników.
cramer_test <- function(clustered_data, cluster_var, var) {
tab <- table(
factor(clustered_data[[cluster_var]]),
factor(clustered_data[[var]])
)
chi <- suppressWarnings(chisq.test(tab))
v <- assocstats(tab)$cramer
data.frame(
Variable = var,
Chi_sq_p_value = round(chi$p.value, 5),
Cramers_V = round(v, 3)
)
}
cramer_results_mixed_fr <- rbind(
cramer_test(res_fr$mixed$data, "cluster_mixed", "Position"),
cramer_test(res_fr$mixed$data, "cluster_mixed", "League"),
cramer_test(res_fr$mixed$data, "cluster_mixed", "Nation"),
cramer_test(res_fr$mixed$data, "cluster_mixed", "Team"),
cramer_test(res_fr$mixed$data, "cluster_mixed", "GENDER"),
cramer_test(res_fr$mixed$data, "cluster_mixed", "Preferred.foot")
)
show_dt(cramer_results_mixed_fr)
Test chi-kwadrat oraz współczynnik Craméra zostały zastosowane w celu sprawdzenia istnienia oraz siły zależności pomiędzy przynależnością do klastra a wybranymi zmiennymi kategorycznymi.
W przypadku klastrowania mieszanego istotność statystyczna testu chi-kwadrat dla większości zmiennych jest w dużej mierze oczekiwana, ponieważ zmienne te były bezpośrednio wykorzystywane w procesie klastrowania. Z tego względu kluczową rolę interpretacyjną pełni współczynnik Craméra, informujący o sile zależności.
Najsilniejszą zależność zaobserwowano dla zmiennej Position (V = 0.598), co potwierdza, że pozycja na boisku jest głównym czynnikiem różnicującym klastry. Umiarkowane zależności występują również dla zmiennych Team, League oraz Nation, co wskazuje na istotny, lecz wtórny wpływ kontekstu klubowego i narodowego.
Zmienna Preferred.foot nie wykazuje istotnej zależności z przynależnością do klastra (p-value = 0.52, V = 0.005), co sugeruje, że preferowana noga zawodnika nie ma znaczenia w kontekście wyodrębnionych profili.
cramers_v_numeric <- function(clustered_data, cluster_var, var) {
tab <- table(
factor(clustered_data[[cluster_var]]),
factor(clustered_data[[var]])
)
chi <- suppressWarnings(chisq.test(tab))
n <- sum(tab)
r <- nrow(tab)
c <- ncol(tab)
v <- sqrt(as.numeric(chi$statistic) / (n * (min(r - 1, c - 1))))
data.frame(
Variable = var,
Chi_sq_p_value = round(chi$p.value, 5),
Cramers_V = round(v, 3)
)
}
cramer_numeric_results_fr <- rbind(
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "Position"),
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "League"),
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "Nation"),
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "Team"),
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "GENDER"),
cramers_v_numeric(res_fr$numeric$data, "cluster_numeric", "Preferred.foot")
)
show_dt(cramer_numeric_results_fr)
W przypadku klastrowania numerycznego wszystkie analizowane zmienne kategoryczne wykazują istotną statystycznie zależność z przynależnością do klastrów, mimo że nie wszystkie zostały bezpośrednio wykorzystane w procesie grupowania.
Zmiennymi użytymi w klastrowaniu były wyłącznie atrybuty opisujące umiejętności sportowe oraz cechy fizyczne zawodników, w tym m.in. wiek, wzrost i waga. Natomiast zmienne demograficzne, takie jak płeć (GENDER) oraz preferowana noga (Preferred.foot), zostały celowo wykluczone z macierzy cech i uwzględnione jedynie na etapie interpretacji wyników.
Uzyskane wartości współczynnika Cramér’s V dla tych dwóch zmiennych są niskie, co wskazuje na słabą zależność i potwierdza, że struktura klastrów nie jest determinowana przez cechy demograficzne.
Najsilniejszą zależność z przynależnością do klastrów wykazuje zmienna Position, co uzasadnia przeprowadzenie dalszego klastrowania w obrębie poszczególnych pozycji. Natomiast wpływ ligi, narodowości oraz klubu zostanie zbadany przy pomocy reguł asocjacyjnych.
res_gold <- cluster_fr(df_gold)
show_dt(compare_clustering_methods(res_gold))
W celu sprawdzenia, czy szeroki zakres jakości zawodników nie zaburzał struktury klastrów, przeprowadzono analogiczną analizę wyłącznie dla kart złotych (OVR ≥ 75).
W obu wariantach klastrowania (numerycznym oraz mieszanym) zaobserwowano wzrost wartości współczynnika Silhouette, co świadczy o większej spójności uzyskanych klastrów. Oznacza to, że usunięcie najsłabszych kart ogranicza wpływ skrajnych obserwacji i prowadzi do bardziej jednorodnych grup.
W przypadku klastrowania numerycznego nastąpiło również zmniejszenie optymalnej liczby klastrów z trzech (dla pełnego zbioru) do dwóch dla kart złotych. Wskazuje to, że po odcięciu zawodników o niskich ocenach ogólnych zanikają bardziej subtelne podziały, a struktura danych upraszcza się do dwóch głównych profili zawodników.
Jednocześnie relatywnie wysoka wartość współczynnika ARI (0.534) wskazuje na istotne podobieństwo pomiędzy strukturą klastrów uzyskanych metodą numeryczną i mieszaną. Oznacza to, że uwzględnienie zmiennych kategorycznych nie zmienia zasadniczo charakteru podziału, a oba podejścia prowadzą do zbliżonej segmentacji zawodników.
profiles_gold_numeric <- cluster_profiles_numeric(
res_gold$numeric$data,
cluster_var = "cluster_numeric"
)
show_dt(profiles_gold_numeric)
W przypadku kart złotych optymalna liczba klastrów w klastrowaniu numerycznym zmniejszyła się z trzech do dwóch. Oznacza to, że po ograniczeniu analizy do zawodników o wysokiej jakości (OVR ≥ 75) zanikła trzecia, najsłabsza grupa obecna w pełnym zbiorze danych.
Uzyskane klastry odpowiadają dwóm wyraźnym profilom zawodników:
Klaster 1 charakteryzuje się wysokimi statystykami ofensywnymi i technicznymi (Dribbling, Agility, Finishing, Ball Control, Skill Moves), przy jednocześnie niskich wartościach cech defensywnych. Odpowiada to profilowi zawodników ofensywnych i kreatywnych.
Klaster 2 skupia zawodników o wysokich statystykach defensywnych i fizycznych (Interceptions, Def.Awareness, Tackles, Strength, Aggression), a także większym wzroście i masie ciała. Są to głównie zawodnicy o charakterze defensywnym.
Otrzymany podział jest spójny z intuicyjnym rozumieniem ról boiskowych i potwierdza, że wśród kart złotych dominującym kryterium różnicującym zawodników jest styl gry (ofensywny vs defensywny), a nie ogólny poziom umiejętności.
profiles_gold_mixed <- cluster_profiles_mixed(
res_gold$mixed$data,
cluster_var = "cluster_mixed",
categorical_vars = c("Position", "League", "Nation")
)
show_dt(profiles_gold_mixed)
Na podstawie profili klastrów można stwierdzić, że uzyskany podział ma wyraźne znaczenie merytoryczne. W obu metodach klastrowania (numerycznej oraz mieszanej) wyodrębnione grupy odpowiadają klasycznemu podziałowi zawodników na profile ofensywne i defensywne.
Pierwszy klaster skupia zawodników o wysokich wartościach atrybutów technicznych i ofensywnych, takich jak dribbling, ball control, vision, short passing, finishing oraz positioning. Dominującą pozycją w tym klastrze jest napastnik (ST), co potwierdza ofensywny charakter tej grupy.
Drugi klaster charakteryzuje się podwyższonymi wartościami statystyk defensywnych i fizycznych, w szczególności interceptions, standing tackle, sliding tackle, strength oraz aggression. W tej grupie zdecydowanie dominuje pozycja środkowego obrońcy (CB), co wskazuje na defensywny profil zawodników.
Otrzymany podział jest więc spójny z intuicją piłkarską i potwierdza, że klastrowanie skutecznie rozróżnia zawodników według stylu gry, a nie jedynie losowo dzieli dane.
pos_numeric_gold <- cluster_position_share(
res_gold$numeric$data,
cluster_var = "cluster_numeric"
)
show_dt(pos_numeric_gold)
pos_mixed_gold <- cluster_position_share(
res_gold$mixed$data,
cluster_var = "cluster_mixed"
)
show_dt(pos_mixed_gold)
Klastrowanie oparte na danych numerycznych prowadzi do wyodrębnienia dwóch wyraźnie zróżnicowanych grup zawodników. Pierwszy klaster skupia głównie zawodników o profilu defensywno-środkowym (CB, CDM, CM), natomiast drugi obejmuje przede wszystkim zawodników ofensywnych i bocznych (ST, CAM, LM, RM). Taki rozkład sugeruje, że podział dobrze odzwierciedla różnice profili wynikające z atrybutów sportowych.
W przypadku klastrowania danych mieszanych (Gower + PAM) jeden z klastrów jest silnie zdominowany przez środkowych obrońców (CB), natomiast drugi ma bardziej rozmyty charakter i zawiera zawodników o zróżnicowanych pozycjach. Wskazuje to, że uwzględnienie zmiennych kategorycznych sprzyja separacji opartej o jedną dominującą cechę (pozycję), kosztem uchwycenia bardziej ciągłych różnic profilu widocznych w danych liczbowych.
Pomimo zastosowania odmiennych podejść, uzyskano relatywnie wysoką wartość współczynnika ARI (≈ 0.53), co wskazuje na dość silną zgodność pomiędzy strukturami klastrów w obu wariantach. Oznacza to, że metody w dużej mierze identyfikują ten sam podstawowy podział, choć akcentują różne aspekty danych: wariant mieszany silniej „dociąga” do kategorii (pozycja), a wariant numeryczny lepiej zachowuje ciągłość profili sportowych.
Jednocześnie różnice pomiędzy klastrami sugerują, że klastrowanie mieszane w większym stopniu akcentuje zmienne kategoryczne (takie jak pozycja), natomiast klastrowanie numeryczne lepiej zachowuje ciągłość profili wynikających z atrybutów sportowych. W efekcie obie metody można traktować jako komplementarne, a nie sprzeczne – potwierdzają istnienie stabilnej struktury danych, lecz podkreślają jej różne aspekty.
cramer_gold_numeric <- rbind(
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "Position"),
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "League"),
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "Nation"),
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "Team"),
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "GENDER"),
cramers_v_numeric(res_gold$numeric$data, "cluster_numeric", "Preferred.foot")
)
show_dt(cramer_gold_numeric)
cramer_gold_mixed <- rbind(
cramer_test(res_gold$mixed$data, "cluster_mixed", "Position"),
cramer_test(res_gold$mixed$data, "cluster_mixed", "League"),
cramer_test(res_gold$mixed$data, "cluster_mixed", "Nation"),
cramer_test(res_gold$mixed$data, "cluster_mixed", "Team"),
cramer_test(res_gold$mixed$data, "cluster_mixed", "GENDER"),
cramer_test(res_gold$mixed$data, "cluster_mixed", "Preferred.foot")
)
show_dt(cramer_gold_mixed)
W obu podejściach najsilniejszą zależność z przynależnością do klastra wykazuje pozycja boiskowa (Cramér’s V ≈ 0.82–0.83), co potwierdza, że klastrowanie w największym stopniu odzwierciedla role zawodników. Zależność ta utrzymuje się również w analizie kart złotych, niezależnie od zastosowanej metody.
Pozostałe zmienne kategoryczne wykazują słabe lub umiarkowane zależności, przy czym w wielu przypadkach brak jest istotności statystycznej testu chi-kwadrat. Może to wynikać z dużej liczby kategorii oraz nierównych liczebności w poszczególnych klasach, co ogranicza moc testu. Nawet w sytuacjach, gdy zależność jest istotna statystycznie (np. dla ligi lub narodowości w klastrowaniu numerycznym), jej siła pozostaje wyraźnie niższa niż dla pozycji boiskowej.
Zmienna Preferred.foot oraz płeć zawodnika (GENDER) nie wykazują istotnego związku z przynależnością do klastrów, co sugeruje, że struktura klastrów jest determinowana przede wszystkim przez cechy sportowe, a nie przez zmienne demograficzne.
results_gold_by_position <- lapply(position_groups, function(pos) {
data_pos <- df_gold %>% filter(Position %in% pos)
if (nrow(data_pos) < 100) return(NULL)
cluster_fr(data_pos)
})
gold_position_summary <- do.call(
rbind,
lapply(names(results_gold_by_position), function(name) {
res <- results_gold_by_position[[name]]
if (is.null(res)) return(NULL)
data.frame(
Position_Group = name,
Best_k_numeric = res$numeric$best_k,
Silhouette_numeric = round(res$numeric$mean_silhouette, 3),
Hopkins_numeric = round(res$numeric$hopkins, 3)
)
})
)
show_dt(gold_position_summary)
W celu sprawdzenia, czy w ramach poszczególnych pozycji występują dodatkowe, bardziej subtelne struktury, przeprowadzono klastrowanie kart złotych osobno dla każdej grupy pozycji.
Dla wszystkich analizowanych pozycji optymalna liczba klastrów wynosi 2, co wskazuje na istnienie dwóch dominujących podtypów zawodników w obrębie każdej roli boiskowej. Wartości współczynnika sylwetki są relatywnie niskie (0.09–0.18), co oznacza, że klastry nie są ostro rozdzielone i częściowo nakładają się w przestrzeni cech. Jednocześnie statystyka Hopkinsa dla wszystkich pozycji przekracza 0.6, co potwierdza istnienie struktury klastrowej, choć o umiarkowanej sile.
gold_profiles_by_position <- lapply(names(results_gold_by_position), function(pos) {
res <- results_gold_by_position[[pos]]
if (is.null(res)) return(NULL)
profiles <- cluster_profiles_numeric(
res$numeric$data,
cluster_var = "cluster_numeric"
)
profiles$Position_Group <- pos
profiles
})
gold_profiles_by_position <- do.call(rbind, gold_profiles_by_position)
show_dt(gold_profiles_by_position)
Wyniki klastrowania per pozycja wskazują, że w niemal wszystkich przypadkach algorytm identyfikuje przede wszystkim jeden dominujący wymiar różnicowania – ogólną jakość zawodników. Przy optymalnej liczbie klastrów równej 2 uzyskiwane podziały mają charakter jednoznacznie hierarchiczny i odpowiadają klasycznemu rozróżnieniu na zawodników „lepszych” i „gorszych”.
W obrębie każdej pozycji jeden z klastrów skupia zawodników o systematycznie wyższych wartościach większości atrybutów technicznych, ofensywnych i fizycznych, natomiast drugi charakteryzuje się wyraźnie niższym poziomem tych samych cech. Oznacza to, że nawet po zawężeniu analizy do kart złotych (OVR ≥ 75) jakość karty pozostaje głównym czynnikiem segmentacji, a style gry nie tworzą niezależnych, równorzędnych klas.
Jedynie lokalnie pojawiają się słabsze różnice stylu, takie jak podział napastników na graczy bardziej fizycznych i lepiej grających głową oraz bardziej kompletnych technicznie, czy rozróżnienie pomocników bocznych na profile defensywne i ofensywne. Są to jednak efekty drugorzędne, występujące na tle dominującego podziału jakościowego.
Otrzymane wyniki potwierdzają, że w ramach pojedynczych pozycji struktura danych ma głównie charakter ciągły (kontinuum jakości), a nie dyskretny, co ogranicza możliwość stabilnego wydzielania wielu sensownych stylów gry metodą klastrowania partycyjnego.
W przeciwieństwie do klastrowania partycyjnego, metoda hierarchiczna nie wymaga z góry zadanej liczby klastrów i umożliwia eksploracyjną analizę struktury danych na różnych poziomach szczegółowości. Pozwala to na ocenę, czy wcześniej uzyskane podziały binarne rzeczywiście wyczerpują zróżnicowanie zawodników w ramach danej pozycji, czy też w danych obecne są bardziej subtelne struktury.
Analizę przeprowadzono dla wybranych pozycji (CB,FB,CDM, CM, CAM, WING), dla których klastrowanie metodą PAM wskazywało głównie na prosty podział jakościowy na zawodników „lepszych” i „gorszych”. Dendrogramy umożliwiają wizualną ocenę, czy w obrębie tych grup widoczne są dalsze naturalne rozgałęzienia, które mogą odpowiadać różnym profilom gry.
cluster_hc_position <- function(data) {
df_num <- data %>%
select(where(is.numeric)) %>%
select(-GENDER, -Preferred.foot)
df_scaled <- scale(df_num)
dist_mat <- dist(df_scaled, method = "euclidean")
hc <- hclust(dist_mat, method = "ward.D2")
list(
hc = hc,
data = data
)
}
positions_hc <- list(
CB = "CB",
FB = c("LB","RB"),
CDM = "CDM",
CM = "CM",
CAM = "CAM",
WING = c("LW","RW")
)
results_hc_gold <- lapply(positions_hc, function(pos) {
data_pos <- df_gold %>% filter(Position %in% pos)
if (nrow(data_pos) < 50) return(NULL)
cluster_hc_position(data_pos)
})
names(results_hc_gold) <- names(positions_hc)
for (nm in c("CB","FB","CDM","CM","CAM","WING")) {
plot(results_hc_gold[[nm]]$hc,
labels = FALSE,
sub = "",
main = paste0(nm, " – dendrogram (gold)"))
}
hc_profiles <- function(hc_res, k) {
clusters <- cutree(hc_res$hc, k = k)
hc_res$data %>%
mutate(cluster_hc = factor(clusters)) %>%
group_by(cluster_hc) %>%
summarise(
n_players = n(),
across(where(is.numeric), mean, na.rm = TRUE),
.groups = "drop"
)
}
profiles_CB_2 <- hc_profiles(results_hc_gold$CB, k = 2)
show_dt(profiles_CB_2)
Przy podziale na 2 klastry dla pozycji środkowego obrońcy widoczny jest jednoznaczny podział jakościowy. Pierwszy klaster skupia zawodników o wyższych wartościach niemal wszystkich kluczowych atrybutów, natomiast drugi charakteryzuje się systematycznie niższym poziomem statystyk. Oznacza to, że podział ten ma charakter globalny i odpowiada klasycznemu rozróżnieniu na zawodników „lepszych” i „gorszych”, bez wyraźnych różnic stylu gry.
profiles_CB_4 <- hc_profiles(results_hc_gold$CB, k = 4)
show_dt(profiles_CB_4)
Przy podziale na 4 klastry struktura danych ma wyraźnie charakter hierarchiczny. Klastry 1 i 2 obejmują zawodników o wyższym ogólnym poziomie umiejętności, natomiast klastry 3 i 4 skupiają zawodników wyraźnie słabszych jakościowo – w praktycznie wszystkich atrybutach osiągają oni niższe wartości niż zawodnicy z klastrów 1–2.
W obrębie lepszej grupy widoczny jest czytelny podział stylu gry. Jeden z klastrów reprezentuje obrońców bardziej rozgrywających, z relatywnie wyższymi wartościami cech technicznych i decyzyjnych (Short Passing, Vision, Ball Control), natomiast drugi skupia zawodników o profilu typowo defensywnym, charakteryzujących się wyraźnie lepszymi parametrami fizyczno-obronnymi (Strength, Aggression, Tackles, Heading Accuracy).
Klastry 3 i 4 również pozostają w relacji hierarchicznej – klaster 3 jest ogólnie lepszy jakościowo od klastra 4 w niemal wszystkich atrybutach technicznych i defensywnych. Jednocześnie klaster 4 wyróżnia się wyższymi wartościami cech motorycznych, takich jak Acceleration, Sprint Speed, Agility, Jumping, Stamina oraz Strength. Oznacza to, że w słabszej części struktury obserwowany jest podział na zawodników relatywnie „lepszych technicznie i defensywnie” oraz zawodników „szybszych i bardziej dynamicznych”, przy ogólnie niższym poziomie obu grup względem klastrów 1–2.
profiles_FB_2 <- hc_profiles(results_hc_gold$FB, k = 2)
show_dt(profiles_FB_2)
Dla dwóch klastrów widoczny jest klasyczny podział jakościowy na zawodników lepszych i gorszych.
profiles_FB_3 <- hc_profiles(results_hc_gold$FB, k = 3)
show_dt(profiles_FB_3)
Przy trzech klastrach struktura staje się bardziej zróżnicowana. Klaster 1 obejmuje zawodników ogólnie najlepszych. Klaster 2 skupia zawodników szybszych i bardziej dynamicznych (wyższe Acceleration i Sprint Speed), lecz słabszych w większości pozostałych cech. Klaster 3 jest ogólnie słabszy od klastra 1, ale charakteryzuje się lepszymi parametrami defensywnymi (Interceptions, Defensive Awareness, Tackles, Heading Accuracy, Jumping) w porównaniu do klastra 2. Oznacza to podział na bocznych obrońców bardziej defensywnych oraz bardziej ofensywnych i dynamicznych
profiles_CDM_2 <- hc_profiles(results_hc_gold$CDM, k = 2)
show_dt(profiles_CDM_2)
Dla dwóch klastrów widoczny jest przede wszystkim podział jakościowy – klaster 1 obejmuje zawodników lepszych, natomiast klaster 2 zawodników słabszych, choć nieco bardziej dynamicznych (wyższe Acceleration, Agility i Balance).
profiles_CDM_3 <- hc_profiles(results_hc_gold$CDM, k = 3)
show_dt(profiles_CDM_3)
Przy trzech klastrach pojawia się bardzo mały klaster elitarny (ok. 13 zawodników), który charakteryzuje się wyjątkowo wysokimi wartościami większości cech ofensywnych i motorycznych (m.in. Acceleration, Sprint Speed, Long Shots), przy jednocześnie nieco niższych parametrach siłowych i gry głową. Klaster ten można interpretować jako grupę zawodników odstających pozytywnie od reszty populacji (outlierów).
Pozostałe dwa klastry pozostają w relacji hierarchicznej – klaster 2 ma charakter „średni”, natomiast klaster 3 jest najsłabszy jakościowo, choć wyróżnia się nieco lepszą dynamiką (Acceleration, Sprint Speed, Stamina, Agility) względem klastra 2.
profiles_CM_2 <- hc_profiles(results_hc_gold$CM, k = 2)
show_dt(profiles_CM_2)
Dla dwóch klastrów obserwowany jest jednoznaczny podział na zawodników lepszych i gorszych.
profiles_CM_3 <- hc_profiles(results_hc_gold$CM, k = 3)
show_dt(profiles_CM_3)
Przy trzech klastrach struktura jest bardziej złożona. Klaster 1 skupia zawodników najbardziej kompletnych, osiągających najwyższe wartości w niemal wszystkich atrybutach. Klaster 2 jest ogólnie słabszy, lecz w części cech osiąga wyższe wartości niż klaster 3. Klaster 3 charakteryzuje się relatywnie lepszymi parametrami defensywnymi i fizycznymi (Interceptions, Defensive Awareness, Tackles, Jumping, Stamina, Strength, Aggression), lecz pozostaje wyraźnie słabszy technicznie i ofensywnie od klastra 1.
profiles_CAM_2 <- hc_profiles(results_hc_gold$CAM, k = 2)
show_dt(profiles_CAM_2)
Dla dwóch klastrów widoczny jest głównie podział jakościowy – klaster 1 obejmuje zawodników lepszych, natomiast klaster 2 zawodników słabszych, nieco bardziej defensywnych.
profiles_CAM_3 <- hc_profiles(results_hc_gold$CAM, k = 3)
show_dt(profiles_CAM_3)
Przy trzech klastrach pojawia się czytelny podział stylu gry. Klaster 1 to zawodnicy najbardziej kompletni technicznie i ofensywnie. Klaster 2 wyróżnia się wyraźnie lepszymi parametrami defensywnymi (Interceptions, Defensive Awareness, Tackles, Aggression). Klaster 3 charakteryzuje się najwyższą dynamiką i zwinnością (Acceleration, Agility, Balance), przewyższając w tym zakresie pozostałe klastry, przy słabszych cechach defensywnych.
profiles_WING_2 <- hc_profiles(results_hc_gold$WING, k = 2)
show_dt(profiles_WING_2)
Dla dwóch klastrów występuje klasyczny podział jakościowy – klaster 1 obejmuje zawodników ogólnie lepszych, natomiast klaster 2 zawodników słabszych, choć szybszych (wyższe Acceleration i Sprint Speed).
profiles_WING_3 <- hc_profiles(results_hc_gold$WING, k = 3)
show_dt(profiles_WING_3)
Przy trzech klastrach wyodrębniają się trzy wyraźne profile: Klaster 1 to skrzydłowi najbardziej kompletni – najlepsi w niemal wszystkich cechach ofensywnych i technicznych (finishing, passing, dribbling, ball control, composure) oraz w wybranych cechach defensywnych. Klaster 2 obejmuje zawodników szybkich i silnych fizycznie (Acceleration, Sprint Speed, Heading Accuracy, Jumping, Strength), predysponowanych do gry w powietrzu i kontrataków. Klaster 3 charakteryzuje się najwyższą zwinnością i balansem, a także relatywnie lepszymi cechami kreatywnymi niż klaster 2, lecz pozostaje słabszy od klastra 1.
Klastrowanie hierarchiczne potwierdza, że w ramach większości pozycji dominującym wymiarem różnicowania pozostaje ogólna jakość zawodników. Jednak przy większej liczbie klastrów możliwe jest również wyodrębnienie drugorzędnych stylów gry, takich jak profile bardziej defensywne, bardziej techniczne lub bardziej dynamiczne. Struktura danych ma zatem charakter hierarchiczny: podstawowy podział jakościowy jest najsilniejszy, a różnice stylu gry pojawiają się dopiero na kolejnych poziomach szczegółowości.
W analizie reguł asocjacyjnych wykorzystano dane o zawodnikach z gry EA FC 26. Z całej bazy wybrano wyłącznie zawodników z pola (bez GK), płci męskiej oraz z oceną ogólną co najmniej 65 (karty srebrne i złote). Ograniczenie do OVR ≥ 65 pozwala zmniejszyć wpływ najsłabszych kart, które mogłyby dominować w dyskretyzacji i generować reguły trudne do porównania.
Z analizy wykluczono zawodniczki z dwóch powodów: (1) ich mniejszej liczebności w zbiorze oraz (2) faktu, że przyjęte rankingi (FIFA/UEFA) odnoszą się do piłki męskiej. Dodatkowo uwzględnienie kobiet mogłoby utrudnić interpretację reguł związanych z parametrami fizycznymi (wzrost, waga), ze względu na systematyczne różnice rozkładów tych cech.
W każdym przypadku reguły są wyznaczane wyłącznie na próbie złożonej z zawodników należących do rozpatrywanych lig/narodowości, tzn. np. reguły dla TOP 6–10 lig powstają tylko na danych zawodników z tych pięciu lig (między sobą), a nie na tle całej populacji.
# =========================
# 1) DEFINICJE GRUP
# =========================
# TOP 1–5
top_nations_1_5 <- c("Argentina","Brazil","England","France","Spain")
top_leagues_1_5 <- c(
"Premier League",
"LALIGA EA SPORTS",
"Serie A Enilive",
"Bundesliga",
"Ligue 1 McDonald's"
)
# TOP 6–10
top_nations_6_10 <- c("Portugal","Holland","Morocco","Belgium","Germany")
top_leagues_6_10 <- c(
"Liga Portugal",
"Eredivisie",
"1A Pro League",
"Trendyol Süper Lig",
"Česká Liga"
)
# cechy do reguł
stats_vars <- c(
"Acceleration","Sprint.Speed",
"Positioning","Finishing","Shot.Power","Long.Shots","Volleys","Penalties",
"Vision","Crossing","Free.Kick.Accuracy","Short.Passing","Long.Passing","Curve",
"Dribbling","Agility","Balance","Reactions","Ball.Control","Composure",
"Interceptions","Heading.Accuracy","Def.Awareness","Standing.Tackle","Sliding.Tackle",
"Jumping","Stamina","Strength","Aggression",
"Height","Weight"
)
# =========================
# 2) PRÓBA BAZOWA (bez brązów)
# =========================
df_m_65 <- df_raw %>%
filter(Position != "GK", GENDER == "M", OVR >= 65)
# =========================
# 3) KONSTRUKCJA PODPRÓB (TOP 1–5 i TOP 6–10)
# =========================
# --- ligi 1–5
df_league_1_5 <- df_m_65 %>%
filter(League %in% top_leagues_1_5) %>%
select(League, Nation, Position, all_of(stats_vars)) %>%
droplevels()
# --- nacje 1–5
df_nation_1_5 <- df_m_65 %>%
filter(Nation %in% top_nations_1_5) %>%
select(League, Nation, Position, all_of(stats_vars)) %>%
droplevels()
# --- ligi 6–10
df_league_6_10 <- df_m_65 %>%
filter(League %in% top_leagues_6_10) %>%
select(League, Nation, Position, all_of(stats_vars)) %>%
droplevels()
# --- nacje 6–10
df_nation_6_10 <- df_m_65 %>%
filter(Nation %in% top_nations_6_10) %>%
select(League, Nation, Position, all_of(stats_vars)) %>%
droplevels()
Zanim wygenerowano reguły, sprawdzono liczebności grup. Ma to znaczenie interpretacyjne: przy bardzo małych grupach reguły mogą być niestabilne (wysoki lift może wynikać z przypadku), dlatego wyniki dla najmniej licznych kategorii należy interpretować ostrożniej.
# liczebności: TOP 1–5
league_counts_1_5 <- df_league_1_5 %>%
count(League, name = "n_players") %>%
arrange(desc(n_players))
nation_counts_1_5 <- df_nation_1_5 %>%
count(Nation, name = "n_players") %>%
arrange(desc(n_players))
show_dt(league_counts_1_5)
show_dt(nation_counts_1_5)
# liczebności: TOP 6–10
league_counts_6_10 <- df_league_6_10 %>%
count(League, name = "n_players") %>%
arrange(desc(n_players))
nation_counts_6_10 <- df_nation_6_10 %>%
count(Nation, name = "n_players") %>%
arrange(desc(n_players))
show_dt(league_counts_6_10)
show_dt(nation_counts_6_10)
Algorytm Apriori wymaga danych kategorycznych, dlatego cechy numeryczne zdyskretyzowano metodą kwartylową (IQR). Progi Q1 i Q3 wyznaczano oddzielnie w każdej próbie (np. osobno dla lig TOP 1–5 i osobno dla lig TOP 6–10), aby kategorie LOW/MID/HIGH odnosiły się do rozkładu badanej grupy i były porównywalne w jej obrębie.
iqr_discretize <- function(x) {
q1 <- quantile(x, 0.25, na.rm = TRUE)
q3 <- quantile(x, 0.75, na.rm = TRUE)
cut(
x,
breaks = c(-Inf, q1, q3, Inf),
labels = c("LOW", "MID", "HIGH"),
include.lowest = TRUE
)
}
disc_for_rules <- function(df, stats_vars) {
df %>%
mutate(across(all_of(stats_vars), iqr_discretize)) %>%
mutate(across(everything(), as.factor)) %>%
droplevels()
}
# dyskretyzacja TOP 1–5
df_league_disc_1_5 <- disc_for_rules(df_league_1_5, stats_vars)
df_nation_disc_1_5 <- disc_for_rules(df_nation_1_5, stats_vars)
# dyskretyzacja TOP 6–10
df_league_disc_6_10 <- disc_for_rules(df_league_6_10, stats_vars)
df_nation_disc_6_10 <- disc_for_rules(df_nation_6_10, stats_vars)
# transakcje
trans_league_1_5 <- as(df_league_disc_1_5, "transactions")
trans_nation_1_5 <- as(df_nation_disc_1_5, "transactions")
trans_league_6_10 <- as(df_league_disc_6_10, "transactions")
trans_nation_6_10 <- as(df_nation_disc_6_10, "transactions")
W celu uzyskania reguł interpretowalnych wprost jako „co wyróżnia daną ligę/narodowość”, wymuszono kierunek reguł przez appearance:
po lewej stronie (LHS) dopuszczono wyłącznie przynależność do ligi lub narodowości,
po prawej stronie (RHS) wyłącznie skrajne poziomy cech (HIGH/LOW)
# =========================
# REGUŁY: TOP 1–5 LIGI
# =========================
rules_league_1_5 <- apriori(
trans_league_1_5,
parameter = list(supp = 0.01, conf = 0.25, minlen = 2),
appearance = list(
lhs = paste0("League=", top_leagues_1_5),
rhs = paste0(rep(stats_vars, each = 2), "=", rep(c("HIGH","LOW"), times = length(stats_vars))),
default = "none"
),
control = list(verbose = FALSE)
)
# =========================
# REGUŁY: TOP 1–5 NACJE
# =========================
rules_nation_1_5 <- apriori(
trans_nation_1_5,
parameter = list(supp = 0.02, conf = 0.25, minlen = 2),
appearance = list(
lhs = paste0("Nation=", top_nations_1_5),
rhs = paste0(rep(stats_vars, each = 2), "=", rep(c("HIGH","LOW"), times = length(stats_vars))),
default = "none"
),
control = list(verbose = FALSE)
)
# =========================
# REGUŁY: TOP 6–10 LIGI
# =========================
rules_league_6_10 <- apriori(
trans_league_6_10,
parameter = list(supp = 0.01, conf = 0.25, minlen = 2),
appearance = list(
lhs = paste0("League=", top_leagues_6_10),
rhs = paste0(rep(stats_vars, each = 2), "=", rep(c("HIGH","LOW"), times = length(stats_vars))),
default = "none"
),
control = list(verbose = FALSE)
)
# =========================
# REGUŁY: TOP 6–10 NACJE
# =========================
rules_nation_6_10 <- apriori(
trans_nation_6_10,
parameter = list(supp = 0.02, conf = 0.25, minlen = 2),
appearance = list(
lhs = paste0("Nation=", top_nations_6_10),
rhs = paste0(rep(stats_vars, each = 2), "=", rep(c("HIGH","LOW"), times = length(stats_vars))),
default = "none"
),
control = list(verbose = FALSE)
)
# sortowanie
rules_league_1_5_sorted <- sort(rules_league_1_5, by="lift", decreasing=TRUE)
rules_nation_1_5_sorted <- sort(rules_nation_1_5, by="lift", decreasing=TRUE)
rules_league_6_10_sorted <- sort(rules_league_6_10, by="lift", decreasing=TRUE)
rules_nation_6_10_sorted <- sort(rules_nation_6_10, by="lift", decreasing=TRUE)
rules_to_df_simple <- function(rules, n_trans) {
if (length(rules) == 0) {
return(tibble::tibble(
lhs = character(), rhs = character(),
support = numeric(), confidence = numeric(), lift = numeric(),
count = integer()
))
}
q <- quality(rules)
tibble::tibble(
lhs = labels(lhs(rules)),
rhs = labels(rhs(rules)),
support = as.numeric(q$support),
confidence = as.numeric(q$confidence),
lift = as.numeric(q$lift),
count = as.integer(round(as.numeric(q$support) * n_trans))
) %>%
dplyr::arrange(dplyr::desc(lift), dplyr::desc(confidence), dplyr::desc(support))
}
# ====== TOP 1–5: tabelki per NACJA ======
n_nation_1_5 <- length(trans_nation_1_5)
rules_by_nation_1_5 <- setNames(vector("list", length(top_nations_1_5)), top_nations_1_5)
for (nt in top_nations_1_5) {
r_sub <- subset(rules_nation_1_5_sorted, lhs %pin% paste0("Nation=", nt))
rules_by_nation_1_5[[nt]] <- rules_to_df_simple(r_sub, n_nation_1_5) %>%
dplyr::mutate(Nation = nt, .before = 1)
}
# ====== TOP 1–5: tabelki per LIGA ======
n_league_1_5 <- length(trans_league_1_5)
rules_by_league_1_5 <- setNames(vector("list", length(top_leagues_1_5)), top_leagues_1_5)
for (lg in top_leagues_1_5) {
r_sub <- subset(rules_league_1_5_sorted, lhs %pin% paste0("League=", lg))
rules_by_league_1_5[[lg]] <- rules_to_df_simple(r_sub, n_league_1_5) %>%
dplyr::mutate(League = lg, .before = 1)
}
# ====== TOP 6–10: tabelki per NACJA ======
n_nation_6_10 <- length(trans_nation_6_10)
rules_by_nation_6_10 <- setNames(vector("list", length(top_nations_6_10)), top_nations_6_10)
for (nt in top_nations_6_10) {
r_sub <- subset(rules_nation_6_10_sorted, lhs %pin% paste0("Nation=", nt))
rules_by_nation_6_10[[nt]] <- rules_to_df_simple(r_sub, n_nation_6_10) %>%
dplyr::mutate(Nation = nt, .before = 1)
}
# ====== TOP 6–10: tabelki per LIGA ======
n_league_6_10 <- length(trans_league_6_10)
rules_by_league_6_10 <- setNames(vector("list", length(top_leagues_6_10)), top_leagues_6_10)
for (lg in top_leagues_6_10) {
r_sub <- subset(rules_league_6_10_sorted, lhs %pin% paste0("League=", lg))
rules_by_league_6_10[[lg]] <- rules_to_df_simple(r_sub, n_league_6_10) %>%
dplyr::mutate(League = lg, .before = 1)
}
W analizie zastosowano jednolity próg istotności praktycznej lift ≥ 1.25, aby zachować porównywalność wyników pomiędzy grupami. Jednocześnie należy uwzględnić nierówne liczebności obserwacji w poszczególnych ligach i narodowościach, które mogą wpływać na stabilność reguł (szczególnie dla grup o małej liczbie zawodników). Z tego względu reguły o wysokim lift, ale bardzo niskim wsparciu/liczebności traktowano ostrożnie i interpretowano jako sygnały eksploracyjne, a nie twarde zależności.
rules_by_league_1_5_sig <- lapply(
rules_by_league_1_5,
function(df) df %>% dplyr::filter(lift >= 1.25)
)
show_dt(rules_by_league_1_5_sig[["Premier League"]])
show_dt(rules_by_league_1_5_sig[["LALIGA EA SPORTS"]])
show_dt(rules_by_league_1_5_sig[["Serie A Enilive"]])
show_dt(rules_by_league_1_5_sig[["Bundesliga"]])
show_dt(rules_by_league_1_5_sig[["Ligue 1 McDonald's"]])
Analiza reguł asocjacyjnych dla lig TOP 5 pokazuje, że nie wszystkie ligi wyodrębniają się w danych w jednakowym stopniu ani w postaci równie czytelnych profili.
W przypadku Premier League widoczna jest wyraźna nadreprezentacja reguł wskazujących na wysokie wartości cech zarówno defensywnych, jak i ofensywnych. Reguły o najwyższych wartościach lift dotyczą m.in. wysokich umiejętności odbioru piłki (Sliding Tackle, Standing Tackle, Interceptions, Def. Awareness), ale jednocześnie także cech technicznych i ofensywnych (Dribbling, Ball Control, Vision, Positioning, Finishing). Taki rozkład sugeruje, że Premier League wyróżnia się najbardziej kompletnym profilem zawodników, co jest spójne z intuicyjnym postrzeganiem tej ligi jako najsilniejszej sportowo, a nie tylko wyspecjalizowanej w jednym aspekcie gry.
LaLiga wyraźnie różni się od Premier League profilem zawodników. Reguły wskazują na wysokie wartości cech związanych z rozgrywaniem piłki (Short Passing, Long Passing, Vision), przy jednoczesnym częstszym występowaniu niższej wagi i wzrostu. Otrzymany obraz jest spójny i sugeruje styl oparty na technice, kontroli piłki oraz grze kombinacyjnej, a nie na przewadze fizycznej.
W Serie A oraz Bundeslidze nie wyłania się jednoznaczny, dominujący styl gry. Reguły mają raczej charakter punktowy i dotyczą pojedynczych cech (np. Balance = LOW, Long Shots = HIGH w Serie A czy Weight = HIGH w Bundeslidze), bez tworzenia spójnego wzorca techniczno-taktycznego. W obu ligach można mówić raczej o zróżnicowanych profilach zawodników, bez wyraźnej specjalizacji całej ligi w jednym kierunku.
Ligue 1 McDonald’s wyróżnia się przede wszystkim negatywnie, ponieważ dominują reguły wskazujące na niskie wartości cech technicznych i decyzyjnych (Ball Control, Dribbling, Passing, Reactions). Jednocześnie pojawiają się reguły dotyczące niższej wagi i wzrostu, jednak bez kompensacji w postaci cech technicznych na poziomie LaLigi. W efekcie liga ta prezentuje się jako najsłabsza profilowo w zestawieniu TOP 5
rules_by_nation_1_5_sig <- lapply(
rules_by_nation_1_5,
function(df) df %>% dplyr::filter(lift >= 1.25)
)
show_dt(rules_by_nation_1_5_sig[["Argentina"]])
show_dt(rules_by_nation_1_5_sig[["Brazil"]])
show_dt(rules_by_nation_1_5_sig[["England"]])
show_dt(rules_by_nation_1_5_sig[["France"]])
show_dt(rules_by_nation_1_5_sig[["Spain"]])
Analiza reguł asocjacyjnych dla pięciu najwyżej sklasyfikowanych narodowości ujawnia wyraźne różnice w charakterze i spójności profili zawodników, przy czym siła i czytelność tych wzorców jest silnie uzależniona od liczebności próby oraz struktury atrybutów.
W przypadku Argentyny uzyskano jedynie kilka reguł wskazujących na niższy wzrost oraz obniżone wartości pojedynczych cech technicznych (Curve, Composure). Brak spójnego zestawu reguł sugeruje, że profil argentyńskich zawodników jest relatywnie zrównoważony, a pojedyncze odchylenia nie tworzą jednoznacznego stylu.
Brazylia wyróżnia się bardzo czytelnym profilem: dominują reguły wskazujące na wysoką szybkość, zwinność oraz cechy ofensywne (Sprint Speed, Acceleration, Agility, Finishing, Volleys), przy jednoczesnych niskich wartościach cech defensywnych (Def. Awareness, Tackles, Interceptions). Interpretacja tych wyników jest zgodna z intuicją, jednak wymaga ostrożności ze względu na niższą liczebność próby w porównaniu z innymi narodowościami TOP 5.
Anglia, podobnie jak Argentyna, nie tworzy wyraźnego profilu stylu gry. Pojawiają się pojedyncze reguły wskazujące na niższe wartości Ball Control i Short Passing, jednak nie są one wystarczające, aby mówić o spójnym wzorcu.
Hiszpania wyróżnia się konsekwentnie wysokimi wartościami cech technicznych i decyzyjnych (Short Passing, Vision, Ball Control, Reactions), co wskazuje na styl oparty na kontroli gry i inteligencji boiskowej. Profil ten jest spójny z wynikami dla LaLigi, co dodatkowo wzmacnia wiarygodność interpretacji.
Francja nie generuje wielu jednoznacznych reguł – jedynym wyraźniejszym sygnałem jest wyższy wzrost zawodników. Brak silniejszych zależności sugeruje dużą wewnętrzną heterogeniczność profili.
rules_by_league_6_10_sig <- lapply(
rules_by_league_6_10,
function(df) df %>% dplyr::filter(lift >= 1.25)
)
show_dt(rules_by_league_6_10_sig[["Liga Portugal"]])
show_dt(rules_by_league_6_10_sig[["Eredivisie"]])
show_dt(rules_by_league_6_10_sig[["1A Pro League"]])
show_dt(rules_by_league_6_10_sig[["Trendyol Süper Lig"]])
show_dt(rules_by_league_6_10_sig[["Česká Liga"]])
Dla lig takich jak Liga Portugal czy Eredivisie uzyskane reguły są nieliczne i dotyczą pojedynczych cech, co nie pozwala na jednoznaczne wnioskowanie o stylu gry. W przypadku 1A Pro League pojawiają się sygnały sugerujące słabsze cechy decyzyjne (Reactions, Composure), co może wskazywać na bardziej ofensywną niż zrównoważoną charakterystykę.
Wyniki dla Českiej Ligi generują wiele reguł o wysokich wartościach lift, jednak są one konsekwencją bardzo małej liczebności próby, co czyni je niestabilnymi i eksploracyjnymi. Podobna ostrożność dotyczy interpretacji wyników dla Maroka.
Trendyol Süper Lig wyróżnia się pozytywnie – reguły wskazują na ponadprzeciętne cechy ofensywne (Shot Power, Long Shots, Curve, Volleys), co sugeruje bardziej bezpośredni, siłowy styl gry.
rules_by_nation_6_10_sig <- lapply(
rules_by_nation_6_10,
function(df) df %>% dplyr::filter(lift >= 1.25)
)
show_dt(rules_by_nation_6_10_sig[["Portugal"]])
show_dt(rules_by_nation_6_10_sig[["Holland"]])
show_dt(rules_by_nation_6_10_sig[["Morocco"]])
show_dt(rules_by_nation_6_10_sig[["Belgium"]])
show_dt(rules_by_nation_6_10_sig[["Germany"]])
W przypadku narodowości z miejsc 6–10 rankingu obserwowane reguły asocjacyjne są wyraźnie mniej stabilne i trudniejsze w interpretacji niż dla grupy TOP 5, co w dużej mierze wynika z nierównych liczebności próby oraz większej heterogeniczności zawodników.
Profil Portugalii jest stosunkowo spójny i wskazuje na zawodników lepiej wyszkolonych technicznie niż fizycznie. Dominują reguły sugerujące wysokie wartości cech takich jak Vision, Ball Control, Dribbling, Curve czy Composure, przy jednoczesnym częstszym występowaniu niższego wzrostu, masy ciała oraz siły fizycznej. Taki zestaw cech wskazuje na styl gry oparty na technice, dynamice i inteligencji boiskowej, kosztem przewagi fizycznej.
W przypadku Holandii uzyskane reguły tworzą względnie czytelny obraz zawodników o profilu fizyczno-defensywnym. Wyróżniają się wysokie wartości cech związanych z grą obronną (Interceptions, Standing Tackle, Sliding Tackle, Def. Awareness) oraz gry w powietrzu (Jumping, Heading Accuracy), uzupełnione podwyższoną agresją. Profil ten jest spójny i wyraźnie odmienny od portugalskiego.
Dla Maroka wygenerowano liczne reguły o wysokich wartościach lift, jednak ich interpretacja wymaga dużej ostrożności ze względu na niewielką liczebność próby. Reguły sugerują zawodników lżejszych, niższych, o wysokich umiejętnościach technicznych (Dribbling, Ball Control, Agility, Balance), przy jednocześnie obniżonych cechach defensywnych (Def. Awareness, Tackles, Interceptions). Choć profil ten wydaje się spójny, należy go traktować jako eksploracyjny sygnał, a nie stabilną charakterystykę populacji.
Belgia nie wyróżnia się wyraźnym stylem gry. Uzyskano jedynie pojedyncze reguły wskazujące na niższą masę ciała oraz wyższą wizję gry, co nie pozwala na sformułowanie jednoznacznego profilu. Można to interpretować jako dużą wewnętrzną różnorodność zawodników tej narodowości.
Wyniki dla Niemiec sugerują zawodników cięższych fizycznie, jednak jednocześnie częściej charakteryzujących się niższymi wartościami cech technicznych i decyzyjnych (Passing, Vision, Ball Control, Composure). Interpretacja tych reguł jest jednak utrudniona przez bardzo dużą liczebność próby, która obejmuje również zawodników z lig o niższym poziomie sportowym, co może prowadzić do „rozmycia” profilu i obniżenia średnich wartości cech
Przeprowadzona analiza pokazuje, że klastrowanie oraz reguły asocjacyjne stanowią użyteczne narzędzia eksploracyjne do analizy stylów gry w EA FC 26, jednak ujawniane struktury mają w dużej mierze charakter ogólny i wysokopoziomowy.
W szczególności klastrowanie zawodników prowadzi przede wszystkim do rozróżnienia profili zdominowanych przez cechy ofensywne oraz defensywne, co znajduje odzwierciedlenie w strukturze pozycji przypisanych do poszczególnych klastrów. Oznacza to, że globalne klastrowanie całej populacji zawodników w dużej mierze odzwierciedla role boiskowe, a nie subtelne różnice stylu gry.
Uwzględnienie danych mieszanych dodatkowo wzmocniło wpływ zmiennej „pozycja”, co ograniczyło zdolność algorytmów do identyfikacji bardziej finezyjnych wzorców. Dopiero klastrowanie przeprowadzane osobno dla poszczególnych pozycji, a w szczególności klastrowanie hierarchiczne, pozwoliło na głębszą eksplorację danych i identyfikację bardziej złożonych struktur wewnątrz pozycji.
Reguły asocjacyjne zostały wykorzystane jako narzędzie uzupełniające – zarówno do interpretacji wyników testów zależności (χ² oraz V Craméra), jak i do rozszerzenia eksploracji danych. Wyniki tej analizy wskazują, że wyraźne profile ligowe i narodowościowe pojawiają się stosunkowo rzadko, a w wielu przypadkach brak silnych reguł jest informacją samą w sobie, sugerującą wysoki stopień homogenizacji zawodników lub dużą zmienność wewnątrz analizowanych grup.
Ostatecznie analiza potwierdza, że w EA FC 26 styl gry jest znacznie silniej determinowany przez pozycję boiskową niż przez ligę czy narodowość, a obserwowane różnice pomiędzy tymi grupami mają zazwyczaj charakter subtelny i kontekstowy.
AI użyto do pomocy z kodowaniem i redakcją tekstu Dane z Kaggle