Koľko bodov prinesie minúta navyše?

Lineárna regresia na dátach NBA 2025/26 · Econometrics in R, cvičenie 4

Autor

Richard Vladyka

Publikované

14. 11. 2026

1 O čom je táto analýza

Pomocou R odhadneme a vysvetlíme lineárnu regresiu na výkonoch hráčov NBA v sezóne 2025/26 (súbor nba_dailyleaders_full.csv, denné štatistiky hráčov). Postup je od jednoduchej otázky k modelu:

  1. Súvisí viac odohraných minút s vyšším počtom bodov na zápas?
  2. Graf závislosti.
  3. Regresná priamka a jej interpretácia.
  4. Pridanie ďalších premenných (doskoky, asistencie).
  5. Predikcia.

Diagnostickým testom sa tu nevenujeme. Cieľom je pochopiť myšlienku regresie a prácu s funkciou lm().

2 Príprava údajov

2.1 Načítanie a úprava

Súbor nba_dailyleaders_full.csv je uložený v rovnakom priečinku ako tento dokument. Pôvodné údaje majú jeden riadok na hráča v jednom zápase. Pre prierezovú analýzu ich preto zhrnieme na priemery na zápas a ponecháme hráčov s aspoň 10 zápasmi.

Zobraziť kód
cesta <- "nba_dailyleaders_full.csv"
if (!file.exists(cesta)) stop("Súbor nba_dailyleaders_full.csv sa nenašiel. Uložte ho do rovnakého priečinka ako tento dokument.")

zapasy <- read.csv(cesta, na.strings = c("", "NA"),
                   check.names = FALSE, stringsAsFactors = FALSE)
names(zapasy) <- make.names(trimws(names(zapasy)), unique = TRUE)

# MP je text "40:59" (minúty:sekundy) -> desatinné minúty
zapasy$MP_min <- sapply(strsplit(as.character(zapasy$MP), ":"), function(p) {
  if (length(p) < 2) NA_real_ else as.numeric(p[1]) + as.numeric(p[2]) / 60
})
zapasy <- zapasy[!is.na(zapasy$MP_min) & zapasy$MP_min > 0, ]

# Priemery na hráča + počet zápasov
hraci <- aggregate(cbind(PTS, TRB, AST, MP_min) ~ Player,
                   data = zapasy, FUN = mean)
pocet <- as.data.frame(table(zapasy$Player), stringsAsFactors = FALSE)
names(pocet) <- c("Player", "Zapasy")
hraci <- merge(hraci, pocet, by = "Player")
hraci <- hraci[hraci$Zapasy >= 10, ]
hraci <- hraci[order(-hraci$PTS), ]

V analýze je 507 hráčov z 28712 zápasových záznamov. Jeden riadok predstavuje jedného hráča, takejto organizácii údajov hovoríme prierezové údaje (cross-sectional data).

Zobraziť kód
head(hraci) |>
  tabulka("Šiesti hráči s najvyšším priemerom bodov",
          col.names = c("Hráč", "Body", "Doskoky", "Asistencie",
                        "Minúty", "Zápasy"))
Šiesti hráči s najvyšším priemerom bodov
Hráč Body Doskoky Asistencie Minúty Zápasy
Luka Dončić 33.48 7.73 8.28 35.76 64
Shai Gilgeous-Alexander 30.49 4.05 6.83 33.77 83
Jaylen Brown 28.44 6.82 4.96 34.51 78
Anthony Edwards 27.79 5.06 3.56 34.66 71
Kawhi Leonard 27.79 6.36 3.59 32.20 66
Tyrese Maxey 27.71 4.10 6.49 38.30 82
Varovanie

Ak súbor obsahuje len „denných lídrov“, ide o výber nadpriemerných výkonov, nie o všetkých hráčov ligy. Výsledky preto netreba zovšeobecňovať na celú NBA.

2.2 Prehľad premenných

Použité premenné
Premenná Význam
PTS Body na zápas (vysvetľovaná premenná)
MP_min Minúty na zápas
TRB Doskoky na zápas
AST Asistencie na zápas
Zapasy Počet zápasov hráča

2.3 Chýbajúce hodnoty

Priemery sme počítali z kompletných hodnôt, takže chýbajúce hodnoty by sa tu nemali vyskytnúť. Pre istotu ponechávame kontrolu aj doplnenie mediánom z pôvodného cvičenia (didaktické zjednodušenie; chýbajúcu vysvetľovanú premennú nenahrádzame).

Zobraziť kód
hraci <- hraci[!is.na(hraci$PTS), ]
for (v in c("MP_min", "TRB", "AST")) {
  hraci[[v]][is.na(hraci[[v]])] <- median(hraci[[v]], na.rm = TRUE)
}

premenne <- c("PTS", "MP_min", "TRB", "AST")
data.frame(
  Premenna = premenne,
  Minimum  = sapply(hraci[premenne], min),
  Median   = sapply(hraci[premenne], median),
  Priemer  = sapply(hraci[premenne], mean),
  Maximum  = sapply(hraci[premenne], max)
) |>
  tabulka("Opisná štatistika (priemery na zápas)",
          col.names = c("Premenná", "Minimum", "Medián", "Priemer", "Maximum"))
Opisná štatistika (priemery na zápas)
Premenná Minimum Medián Priemer Maximum
PTS 0.71 8.38 9.79 33.48
MP_min 3.12 20.83 20.70 38.30
TRB 0.29 3.25 3.76 12.89
AST 0.12 1.73 2.28 10.62

3 Graf

Očakávame, že hráči s viac minútami strelia v priemere viac bodov. Ide zatiaľ o pracovnú hypotézu.

Zobraziť kód
graf_minuty <- ggplot(hraci, aes(x = MP_min, y = PTS)) +
  geom_point(color = hornets[["teal"]], size = 2.2, alpha = 0.75) +
  labs(x = "Priemerný počet minút na zápas",
       y = "Priemerný počet bodov na zápas")
graf_minuty
Obrázok 1: Minúty a body na zápas. Každý bod predstavuje jedného hráča.

Otázky: Vidíte rastúcu alebo klesajúcu závislosť? Majú všetci hráči s podobným počtom minút aj rovnaký priemer bodov?

4 Jednoduchá lineárna regresia

4.1 Model

\[ PTS_i = \beta_0 + \beta_1 MP_i + u_i \]

  • \(i\) je hráč,
  • \(\beta_0\) je konštanta (priesečník s osou Y),
  • \(\beta_1\) je smernica: rozdiel predikovaných bodov pri rozdiele jednej minúty,
  • \(u_i\) je časť bodov, ktorú model nevysvetľuje.

Metóda najmenších štvorcov vyberie priamku s najmenším súčtom štvorcov vertikálnych odchýlok bodov od priamky.

4.2 Odhad

Zobraziť kód
model1 <- lm(PTS ~ MP_min, data = hraci)
tidy(model1)[c("term", "estimate")] |>
  tabulka("Jednoduchá regresia: body a minúty",
          col.names = c("Člen modelu", "Odhad koeficienta"), digits = 3)
Jednoduchá regresia: body a minúty
Člen modelu Odhad koeficienta
(Intercept) -3.852
MP_min 0.659

tidy() z balíka broom urobí z modelu tabuľku s jedným riadkom na koeficient; (Intercept) je konštanta. Znak ~ čítame „vysvetľujeme pomocou“: vľavo je vysvetľovaná, vpravo vysvetľujúca premenná.

Odhadnutá rovnica:

\[ \widehat{PTS}_i = -3.852 + 0.659\,MP_i \]

Smernica: Jedna minúta navyše na zápas sa spája s rozdielom približne 0.66 bodu v predikovanom priemere bodov.

Konštanta: Pri MP = 0 by priamka predikovala približne -3.85 bodu. Nula leží mimo pozorovaného rozsahu (minimum je približne 3.1 minút), preto konštantu chápeme hlavne ako parameter polohy priamky.

Poznámka

Kladná závislosť medzi hráčmi nedokazuje, že ďalšia minúta konkrétnemu hráčovi pridá daný počet bodov. Tréneri dávajú viac minút aj hráčom, ktorí sú už lepší strelci.

4.3 Regresná priamka v grafe

Zobraziť kód
graf_minuty +
  geom_abline(intercept = coef(model1)["(Intercept)"],
              slope = coef(model1)["MP_min"],
              color = hornets[["orange"]], linewidth = 1.2)
Obrázok 2: Odhadnutá regresná priamka pre minúty a body na zápas.

5 Viacnásobná lineárna regresia

Pridáme doskoky (TRB) a asistencie (AST):

\[ PTS_i = \beta_0 + \beta_1 MP_i + \beta_2 TRB_i + \beta_3 AST_i + u_i \]

Zobraziť kód
model <- lm(PTS ~ MP_min + TRB + AST, data = hraci)
tidy(model)[c("term", "estimate")] |>
  tabulka("Viacnásobná regresia: minúty, doskoky a asistencie",
          col.names = c("Člen modelu", "Odhad koeficienta"), digits = 4)
Viacnásobná regresia: minúty, doskoky a asistencie
Člen modelu Odhad koeficienta
(Intercept) -3.2057
MP_min 0.5147
TRB 0.1400
AST 0.7971

\[ \widehat{PTS}_i = -3.206 + 0.515\,MP_i + 0.14\,TRB_i + 0.797\,AST_i \]

Koeficient pri MP sa teraz interpretuje pri rovnakých hodnotách TRB a AST: jedna minúta navyše sa spája s rozdielom približne 0.51 bodu.

Všimnite si: koeficient pri MP sa zmenil z 0.659 na 0.515. Jednoduchý a viacnásobný model odpovedajú na odlišné otázky: druhý zohľadňuje aj ďalšie charakteristiky hráčov.

Doskoky a asistencie nie sú „príčinou“ bodov. Súvisia s postom hráča (pivot verzus rozohrávač) a jeho rolou v tíme, preto ich znamienka interpretujeme opatrne.

6 Súhrn modelu: glance()

tidy() dáva riadok na koeficient, glance() dáva jeden riadok za celý model.

Zobraziť kód
glance(model)[c("nobs", "r.squared", "sigma")] |>
  tabulka("Základné charakteristiky viacnásobného modelu",
          col.names = c("Počet hráčov", "R²", "Odhad SD chyby (body)"),
          digits = c(0, 3, 3))
Základné charakteristiky viacnásobného modelu
Počet hráčov R² Odhad SD chyby (body)
507 0.799 2.855
  • nobs: počet hráčov použitých pri odhade,
  • r.squared: podiel výberovej variability bodov zachytený modelom,
  • sigma: odhad štandardnej odchýlky chyby v bodoch.

\(R^2\) je približne 0.799, teda model zachytáva asi 79.9 % výberovej variability priemeru bodov. Neznamená to, že toľko percent predikcií je správnych, ani to nedokazuje kauzalitu.

Úplný súhrn dá summary(model). Stĺpce std.error, statistic a p.value z tidy() vysvetlíme na hodine o štatistickom testovaní.

7 Skutočnosť, predikcia a rezíduum

Pre každého hráča porovnáme skutočný priemer bodov s hodnotou na regresnej ploche:

\[ e_i = y_i - \widehat y_i \]

Kladné rezíduum znamená, že hráč strieľa viac, než model predpokladá, záporné menej. Rezíduum vieme vypočítať, skutočnú chybu \(u_i\) nepoznáme.

Zobraziť kód
porovnanie <- data.frame(
  Hrac = hraci$Player,
  Skutocnost = hraci$PTS,
  Predikcia = fitted(model),
  Reziduum = residuals(model)
)
head(porovnanie, 8) |>
  tabulka("Skutočné a predikované priemery bodov na zápas",
          col.names = c("Hráč", "Skutočnosť", "Predikcia", "Rezíduum"))
Skutočné a predikované priemery bodov na zápas
Hráč Skutočnosť Predikcia Rezíduum
Luka Dončić 33.48 22.89 10.60
Shai Gilgeous-Alexander 30.49 20.19 10.30
Jaylen Brown 28.44 19.47 8.97
Anthony Edwards 27.79 18.18 9.60
Kawhi Leonard 27.79 17.12 10.67
Tyrese Maxey 27.71 22.25 5.45
Giannis Antetokounmpo 27.58 17.36 10.22
Nikola Jokić 27.52 25.20 2.32

Úloha: vyberte jeden riadok a ručne overte, že Skutocnost - Predikcia = Reziduum.

8 Predikcia pre zadanú hodnotu

Vrátime sa k jednoduchému modelu a vypočítame, koľko bodov priamka predikuje pre hráča s 20, 25, 30 a 35 minútami na zápas:

Zobraziť kód
nove_udaje <- data.frame(MP_min = c(20, 25, 30, 35))
nove_udaje$Predikcia <- predict(model1, newdata = nove_udaje)
nove_udaje |>
  tabulka("Predikcie jednoduchého regresného modelu",
          col.names = c("Minúty na zápas", "Predikcia bodov na zápas"))
Predikcie jednoduchého regresného modelu
Minúty na zápas Predikcia bodov na zápas
20 9.33
25 12.63
30 15.93
35 19.22

Ide o bodové predikcie, nie o isté výsledky. O predikčných intervaloch sa budeme učiť neskôr.

9 Malé cvičenia

  1. Ktorá premenná je v modeli vysvetľovaná a ktoré sú vysvetľujúce?
  2. Odhadnite model PTS ~ AST. Koeficienty zobrazte pomocou tidy() a tabulka() a nakreslite bodový graf s regresnou priamkou.
  3. Vysvetlite koeficient MP_min v jednoduchom aj viacnásobnom modeli. Prečo dopĺňame „pri rovnakých ostatných premenných“?
  4. Vypočítajte pomocou model1 predikciu pre MP_min = 28 a overte ju ručným dosadením do rovnice.
  5. Nájdite v tabuľke porovnanie hráča s kladným a hráča so záporným rezíduom a vysvetlite význam znamienka.
  6. Vysvetlite, prečo z kladného koeficienta nemožno automaticky vyvodiť kauzálny účinok.