¿Qué decide un partido en cada categoría? — los Four Factors, de Tercera a la ACB¶
bop analytics · informe de análisis de datos
En síntesis. Se mide, con la misma vara, qué peso tiene cada uno de los Four Factors de Dean Oliver en el resultado de un partido en ocho categorías: las siete ligas nacionales (Primera, Segunda y Tercera FEB; LF Endesa, LF2 y LF Challenge; Liga U) y la ACB. Y se da un paso más que Oliver: se separan los ocho factores —los cuatro que hace un equipo y los cuatro que provoca en el rival— para ver cuánto del partido se decide con el balón y cuánto sin él. Sin nombres: es un estudio de equipos y ligas, no de jugadores.
Contenido¶
- Dean Oliver y los Four Factors · 2. Datos y método · 3. Los cuatro factores por categoría (vs Oliver) ·
- Los ocho factores: lo que haces y lo que provocas · 5. Conclusiones y cautelas.
1 · Dean Oliver y los Four Factors¶
En Basketball on Paper (2004), Dean Oliver resumió qué decide un partido de baloncesto en cuatro factores que cualquier equipo puede leer en el acta:
- Tiro — eFG% (acierto de campo con el triple ponderado).
- Pérdidas — TOV% (balones perdidos por posesión).
- Rebote — OREB% (rebotes ofensivos capturados sobre los disponibles).
- Tiros libres — FTM/FGA (puntos desde la línea por tiro de campo).
Y les dio un peso aproximado en la NBA: 40 · 25 · 20 · 15. Cada factor tiene dos caras: la del equipo (lo que hace) y la del rival (lo que le provoca): ocho números por partido. La pregunta de este estudio es si esos pesos son iguales en el baloncesto español, de la Tercera a la ACB, y cuánto pesa cada cara.
2 · Datos y método¶
import warnings; warnings.filterwarnings("ignore")
import sys
from pathlib import Path
import numpy as np, pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.model_selection import cross_val_score
RAIZ = Path.cwd()
while not (RAIZ / "febdata").exists() and RAIZ != RAIZ.parent:
RAIZ = RAIZ.parent
sys.path.insert(0, str(RAIZ))
from analisis.leer import silver
SEED = 42; np.random.seed(SEED)
pd.set_option("display.width", 170)
# --- identidad visual (tema oscuro de marca, acento naranja) ---
BG, PANEL, INK, MUTED, ACCENT = "#0c0c0e", "#151517", "#ededf0", "#8a8a93", "#e8772e"
GRID, POS, NEG, BLUE, TEAL = "#2a2a2e", "#3fb27f", "#d8584f", "#4f93c7", "#2bb7a6"
plt.rcParams.update({
"figure.facecolor": BG, "axes.facecolor": BG, "savefig.facecolor": BG,
"axes.edgecolor": GRID, "axes.labelcolor": INK, "text.color": INK,
"xtick.color": MUTED, "ytick.color": MUTED, "grid.color": GRID,
"axes.grid": True, "grid.alpha": 0.30, "grid.linewidth": 0.6,
"figure.figsize": (9, 5), "figure.dpi": 110, "font.size": 11,
"axes.titlesize": 13, "axes.titleweight": "bold",
"axes.spines.top": False, "axes.spines.right": False,
})
IMG = RAIZ / "negocio" / "img" / "four-factors"; IMG.mkdir(parents=True, exist_ok=True)
def guardar(fig, nombre):
"""Figura para Instagram (1080×1350 px) con la marca al pie."""
fig.text(0.985, 0.012, "bop analytics · análisis independiente", ha="right", va="bottom",
color=MUTED, fontsize=9)
fig.savefig(IMG / f"{nombre}.png", dpi=100, facecolor=BG)
NOMBRE = {"primerafeb": "Primera FEB", "segundafeb": "Segunda FEB", "tercerafeb": "Tercera FEB",
"lfendesa": "LF Endesa", "lf2": "LF2", "lfchallenge": "LF Challenge", "ligau": "Liga U", "acb": "ACB"}
ORDEN = ["acb", "primerafeb", "segundafeb", "tercerafeb", "lfendesa", "lf2", "lfchallenge", "ligau"]
def factores(sp):
"""De la fila equipo×partido a los 8 factores (propios y del rival) + margen, desde el LOCAL."""
s = sp.copy()
for c in ["pts", "pts_rival", "fga", "fgm", "p3m", "fta", "ftm", "oreb", "dreb", "tov", "posesiones",
"op_pts", "op_fga", "op_fgm", "op_p3m", "op_fta", "op_oreb", "op_dreb", "op_tov", "op_pos"]:
s[c] = pd.to_numeric(s[c], errors="coerce")
s["op_ftm"] = s.op_pts - 2 * s.op_fgm - s.op_p3m # TL metidos del rival, desde sus puntos
s["efg"] = (s.fgm + 0.5 * s.p3m) / s.fga * 100
s["tov"] = s.tov / s.posesiones * 100
s["oreb"] = s.oreb / (s.oreb + s.op_dreb) * 100
s["ftm"] = s.ftm / s.fga * 100
s["r_efg"] = (s.op_fgm + 0.5 * s.op_p3m) / s.op_fga * 100 # lo que se le concede al rival
s["r_tov"] = s.op_tov / s.op_pos * 100 # pérdidas que se le fuerzan
s["r_oreb"] = s.op_oreb / (s.op_oreb + s.dreb) * 100 # rebote ofensivo que se le deja
s["r_ftm"] = s.op_ftm / s.op_fga * 100 # tiros libres que se le regalan
s["margen"] = s.pts - s.pts_rival
s["gana"] = (s.margen > 0).astype(int)
h = s[s.condicion == "local"].replace([np.inf, -np.inf], np.nan)
return h.dropna(subset=["efg", "tov", "oreb", "ftm", "r_efg", "r_tov", "r_oreb", "r_ftm", "margen"])
# FEB: capa silver del medallion, temporadas 2024/25 y 2025/26 (posesiones REALES del play-by-play), siete ligas (la Liga U
# solo tiene 2025). ACB: medallion ACB, 10 temporadas (posesiones estimadas de Oliver).
feb = silver("silver_equipo_partido")
feb = feb[feb.temporada.isin([2024, 2025]) & (feb.posesiones > 0)]
acb = pd.read_parquet(RAIZ / "analisis" / "datasets-acb" / "silver" / "silver_equipo_partido")
acb["temporada"] = acb.temporada.astype(int); acb["liga"] = "acb"
df = pd.concat([factores(feb), factores(acb)], ignore_index=True)
print(df.groupby("liga").agg(partidos=("id_partido", "nunique"), temporadas=("temporada", "nunique"),
gana_local=("gana", "mean")).reindex(ORDEN).round(3).rename(index=NOMBRE).to_string())
partidos temporadas gana_local liga ACB 3126 10 0.604 Primera FEB 577 2 0.614 Segunda FEB 728 2 0.596 Tercera FEB 3589 2 0.580 LF Endesa 480 2 0.596 LF2 739 2 0.579 LF Challenge 480 2 0.552 Liga U 199 1 0.603
# Figuras Instagram de texto: portada (01) y los pesos de Oliver (01b)
def lienzo():
fig = plt.figure(figsize=(10.8, 13.5)); ax = fig.add_axes([0, 0, 1, 1]); ax.axis("off")
ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.autoscale(False); return fig, ax
fig, ax = lienzo()
ax.text(0.08, 0.80, "¿QUÉ DECIDE\nUN PARTIDO?", fontsize=56, fontweight="black", color=INK, va="top")
ax.text(0.08, 0.53, "Los Four Factors de Dean Oliver,\nmedidos con la misma vara\nde la Tercera FEB a la ACB.",
fontsize=26, color=INK, va="top")
ax.text(0.08, 0.36, "8 categorías · 9.918 partidos · sin nombres, solo equipos", fontsize=17, color=MUTED, va="top")
ax.plot([0.08, 0.30], [0.31, 0.31], color=ACCENT, lw=5)
guardar(fig, "01_portada"); plt.show()
fig, ax = lienzo()
ax.text(0.08, 0.90, "LO QUE DIJO OLIVER (NBA, 2004)", fontsize=20, color=ACCENT, fontweight="bold", va="top")
ax.text(0.08, 0.84, "Cuatro factores explican casi todo\nel resultado de un partido:", fontsize=26, color=INK, va="top")
for i, (num, nom, exp) in enumerate([("40", "Tiro", "eFG%: acierto con el triple ponderado"),
("25", "Pérdidas", "TOV%: balones perdidos por posesión"),
("20", "Rebote", "OREB%: rebotes ofensivos capturados"),
("15", "Tiros libres", "FTM/FGA: puntos de la línea por tiro")]):
y = 0.64 - i * 0.14
ax.text(0.08, y, num, fontsize=54, fontweight="black", color=ACCENT, va="center")
ax.text(0.27, y + 0.025, nom.upper(), fontsize=22, fontweight="bold", color=INK, va="center")
ax.text(0.27, y - 0.03, exp, fontsize=15, color=MUTED, va="center")
ax.text(0.08, 0.05, "Y cada factor tiene dos caras: lo que haces\ny lo que le provocas al rival. Ocho números.",
fontsize=19, color=INK, va="bottom")
guardar(fig, "01b_oliver"); plt.show()
Método (el mismo para las ocho categorías).
- Un partido = una fila (perspectiva del local). Cada factor en diferencial (local − visitante) para los cuatro de Oliver; para los ocho, el factor propio y el que se provoca al rival por separado.
- z-score por liga y temporada: todo en desviaciones típicas, en la misma escala, sin mezclar ligas ni años. Diana: el margen del partido.
- Regresión lineal del margen sobre los factores estandarizados. El peso de un factor es su coeficiente en valor absoluto, normalizado a 100. Es un modelo explicativo (qué decidió el partido jugado), no predictivo.
- Posesiones reales (contadas del play-by-play) en las categorías nacionales; estimadas (fórmula de Oliver) en la ACB. Dentro de cada liga la definición es la misma, que es lo que importa para el peso.
3 · Los cuatro factores por categoría (vs Oliver)¶
F4 = {"efg": "Tiro (eFG)", "tov": "Pérdidas (TOV)", "oreb": "Rebote (OREB)", "ftm": "Tiros libres (FTM/FGA)"}
OLIVER = {"efg": 40, "tov": 25, "oreb": 20, "ftm": 15}
for k in F4:
df[f"d_{k}"] = df[k] - df[f"r_{k}"]
def z_por(s, cols):
z = s.copy()
for c in cols:
z[c] = z.groupby("temporada")[c].transform(lambda x: (x - x.mean()) / (x.std(ddof=0) + 1e-9))
return z
def pesos(sub, cols, min_n=150):
z = z_por(sub, cols).dropna(subset=cols + ["margen"])
if len(z) < min_n:
return None, len(z), None
lin = LinearRegression().fit(z[cols].values, z.margen)
w = np.abs(lin.coef_); w = w / w.sum() * 100
acc = cross_val_score(LogisticRegression(max_iter=2000), z[cols].values, z.gana, cv=5).mean()
return pd.Series(w, index=cols), len(z), acc
C4 = [f"d_{k}" for k in F4]
filas = []
for lg in ORDEN:
w, n, acc = pesos(df[df.liga == lg], C4)
filas.append([NOMBRE[lg], n, round(acc * 100, 1)] + [round(v) for v in w.values])
w, n, acc = pesos(df, C4)
filas.append(["Todas (pool)", n, round(acc * 100, 1)] + [round(v) for v in w.values])
filas.append(["Oliver (NBA)", np.nan, np.nan] + [OLIVER[k] for k in F4])
T4 = pd.DataFrame(filas, columns=["categoría", "partidos", "acierto victoria %"] + list(F4.values())).set_index("categoría")
T4
| partidos | acierto victoria % | Tiro (eFG) | Pérdidas (TOV) | Rebote (OREB) | Tiros libres (FTM/FGA) | |
|---|---|---|---|---|---|---|
| categoría | ||||||
| ACB | 3126.0 | 95.3 | 47 | 24 | 20 | 9 |
| Primera FEB | 577.0 | 93.4 | 46 | 24 | 20 | 10 |
| Segunda FEB | 728.0 | 92.9 | 45 | 22 | 23 | 10 |
| Tercera FEB | 3589.0 | 94.6 | 45 | 24 | 21 | 10 |
| LF Endesa | 480.0 | 93.8 | 48 | 21 | 21 | 10 |
| LF2 | 739.0 | 93.6 | 43 | 24 | 22 | 11 |
| LF Challenge | 480.0 | 94.4 | 47 | 22 | 21 | 10 |
| Liga U | 199.0 | 93.0 | 45 | 26 | 21 | 9 |
| Todas (pool) | 9918.0 | 94.5 | 46 | 24 | 21 | 10 |
| Oliver (NBA) | NaN | NaN | 40 | 25 | 20 | 15 |
# Figura Instagram: barra apilada por categoría (+ Oliver)
plot = T4[list(F4.values())]
colf = {"Tiro (eFG)": ACCENT, "Pérdidas (TOV)": BLUE, "Rebote (OREB)": POS, "Tiros libres (FTM/FGA)": MUTED}
ys = np.arange(len(plot))[::-1]
fig, ax = plt.subplots(figsize=(10.8, 13.5))
left = np.zeros(len(plot))
for fac, col in colf.items():
v = plot[fac].values.astype(float)
ax.barh(ys, v, left=left, color=col, edgecolor=BG, height=0.72, label=fac)
for y, val, l in zip(ys, v, left):
if val >= 5:
ax.text(l + val / 2, y, f"{val:.0f}", ha="center", va="center",
color=BG if col in (ACCENT, POS) else INK, fontsize=15, fontweight="bold")
left = left + v
ax.set_yticks(ys); ax.set_yticklabels(plot.index, fontsize=15)
ax.set_xlim(0, 100); ax.set_xlabel("peso del factor en el resultado (%)", fontsize=13); ax.grid(axis="y", alpha=0)
ax.set_title("Qué decide un partido, por categoría\nlos Four Factors de Oliver, medidos con la misma vara",
color=INK, fontsize=20, loc="left", pad=18)
ax.legend(ncol=2, facecolor=PANEL, edgecolor=GRID, labelcolor=INK, fontsize=13,
loc="lower center", bbox_to_anchor=(0.5, -0.16))
plt.tight_layout(rect=(0, 0.03, 1, 1)); guardar(fig, "02_four_factors_categorias"); plt.show()
4 · Los ocho factores: lo que haces y lo que provocas¶
F8 = {"efg": "Tiro propio", "tov": "Pérdidas propias", "oreb": "Rebote of. propio", "ftm": "TL propios",
"r_efg": "Tiro del rival", "r_tov": "Pérdidas forzadas", "r_oreb": "Rebote of. concedido", "r_ftm": "TL del rival"}
C8 = list(F8)
filas = []
for lg in ORDEN + ["pool"]:
sub = df if lg == "pool" else df[df.liga == lg]
w, n, acc = pesos(sub, C8)
filas.append([NOMBRE.get(lg, "Todas (pool)"), n] + [round(v, 1) for v in w.values])
T8 = pd.DataFrame(filas, columns=["categoría", "partidos"] + list(F8.values())).set_index("categoría")
T8["Con balón (4 propios)"] = T8[[F8[k] for k in ("efg", "tov", "oreb", "ftm")]].sum(axis=1).round(0)
T8["Sin balón (4 provocados)"] = T8[[F8[k] for k in ("r_efg", "r_tov", "r_oreb", "r_ftm")]].sum(axis=1).round(0)
print("Peso de cada uno de los 8 factores (%), por categoría:")
T8
Peso de cada uno de los 8 factores (%), por categoría:
| partidos | Tiro propio | Pérdidas propias | Rebote of. propio | TL propios | Tiro del rival | Pérdidas forzadas | Rebote of. concedido | TL del rival | Con balón (4 propios) | Sin balón (4 provocados) | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| categoría | |||||||||||
| ACB | 3126 | 23.7 | 11.4 | 10.1 | 4.7 | 22.9 | 12.8 | 9.6 | 4.8 | 50.0 | 50.0 |
| Primera FEB | 577 | 23.6 | 12.8 | 10.6 | 4.9 | 21.2 | 12.1 | 9.8 | 4.9 | 52.0 | 48.0 |
| Segunda FEB | 728 | 23.2 | 11.2 | 11.1 | 4.9 | 22.7 | 11.2 | 10.0 | 5.8 | 50.0 | 50.0 |
| Tercera FEB | 3589 | 22.7 | 12.4 | 10.6 | 4.8 | 22.6 | 12.0 | 9.6 | 5.3 | 50.0 | 50.0 |
| LF Endesa | 480 | 23.4 | 12.2 | 11.1 | 5.5 | 22.6 | 10.9 | 8.9 | 5.4 | 52.0 | 48.0 |
| LF2 | 739 | 22.9 | 12.8 | 10.8 | 5.4 | 21.0 | 12.4 | 9.2 | 5.5 | 52.0 | 48.0 |
| LF Challenge | 480 | 23.8 | 11.1 | 10.2 | 4.9 | 23.3 | 10.5 | 10.1 | 6.1 | 50.0 | 50.0 |
| Liga U | 199 | 21.7 | 13.9 | 10.3 | 4.0 | 22.0 | 13.9 | 9.7 | 4.5 | 50.0 | 50.0 |
| Todas (pool) | 9918 | 23.6 | 12.0 | 10.2 | 4.7 | 22.9 | 12.1 | 9.4 | 5.1 | 50.0 | 50.0 |
# Figura Instagram: los 8 factores del conjunto, por pares (propio vs provocado)
w8 = T8.loc["Todas (pool)", list(F8.values())].astype(float)
pares = [("efg", "r_efg", "Tiro"), ("tov", "r_tov", "Pérdidas"), ("oreb", "r_oreb", "Rebote of."), ("ftm", "r_ftm", "Tiros libres")]
fig, ax = plt.subplots(figsize=(10.8, 13.5))
x = np.arange(len(pares)); bw = 0.36
prop = [w8[F8[a]] for a, _, _ in pares]; prov = [w8[F8[b]] for _, b, _ in pares]
ax.bar(x - bw / 2, prop, bw, color=ACCENT, edgecolor=BG, label="lo que HACES (tu ataque)")
ax.bar(x + bw / 2, prov, bw, color=TEAL, edgecolor=BG, label="lo que PROVOCAS al rival (tu defensa)")
for i, (a, b) in enumerate(zip(prop, prov)):
ax.text(i - bw / 2, a + 0.5, f"{a:.0f}%", ha="center", color=INK, fontsize=15, fontweight="bold")
ax.text(i + bw / 2, b + 0.5, f"{b:.0f}%", ha="center", color=INK, fontsize=15, fontweight="bold")
ax.set_xticks(x); ax.set_xticklabels([p[2] for p in pares], fontsize=15)
ax.set_ylabel("peso en el resultado (%)", fontsize=13); ax.set_ylim(0, max(prop + prov) * 1.25)
ax.set_title("Cada factor tiene dos caras\nel peso de lo que haces y de lo que provocas (8 categorías)",
color=INK, fontsize=20, loc="left", pad=18)
ax.legend(facecolor=PANEL, edgecolor=GRID, labelcolor=INK, fontsize=13, loc="upper right")
plt.tight_layout(rect=(0, 0.03, 1, 1)); guardar(fig, "03_ocho_factores"); plt.show()
# Figura Instagram: con balón vs sin balón, por categoría
sb = T8.loc[[NOMBRE[l] for l in ORDEN], ["Con balón (4 propios)", "Sin balón (4 provocados)"]].astype(float)
ys = np.arange(len(sb))[::-1]
fig, ax = plt.subplots(figsize=(10.8, 13.5))
ax.barh(ys, sb.iloc[:, 0], color=ACCENT, edgecolor=BG, height=0.72, label="con balón (lo que haces)")
ax.barh(ys, sb.iloc[:, 1], left=sb.iloc[:, 0], color=TEAL, edgecolor=BG, height=0.72, label="sin balón (lo que provocas)")
for y, a, b in zip(ys, sb.iloc[:, 0], sb.iloc[:, 1]):
ax.text(a / 2, y, f"{a:.0f}%", ha="center", va="center", color=BG, fontsize=16, fontweight="bold")
ax.text(a + b / 2, y, f"{b:.0f}%", ha="center", va="center", color=BG, fontsize=16, fontweight="bold")
ax.axvline(50, color=INK, lw=1, ls="--", alpha=0.6)
ax.set_yticks(ys); ax.set_yticklabels(sb.index, fontsize=15); ax.set_xlim(0, 100); ax.grid(axis="y", alpha=0)
ax.set_xlabel("peso en el resultado (%)", fontsize=13)
ax.set_title("¿Se gana con el balón o sin él?\nreparto ataque / defensa del peso, por categoría",
color=INK, fontsize=20, loc="left", pad=18)
ax.legend(ncol=2, facecolor=PANEL, edgecolor=GRID, labelcolor=INK, fontsize=13,
loc="lower center", bbox_to_anchor=(0.5, -0.12))
plt.tight_layout(rect=(0, 0.03, 1, 1)); guardar(fig, "04_con_y_sin_balon"); plt.show()
# Mapa de calor: los 8 factores × categoría (para el informe)
H = T8.loc[[NOMBRE[l] for l in ORDEN] + ["Todas (pool)"], list(F8.values())].astype(float)
fig, ax = plt.subplots(figsize=(11, 5.6))
im = ax.imshow(H.values, cmap="YlOrRd", aspect="auto", vmin=0, vmax=H.values.max())
ax.set_xticks(range(len(H.columns))); ax.set_xticklabels(H.columns, rotation=30, ha="right", fontsize=9)
ax.set_yticks(range(len(H))); ax.set_yticklabels(H.index)
for i in range(H.shape[0]):
for j in range(H.shape[1]):
v = H.values[i, j]
ax.text(j, i, f"{v:.0f}", ha="center", va="center", fontsize=9, color="#111" if v > 12 else "#666", fontweight="bold")
ax.set_title("Peso de los ocho factores por categoría (%)", color=INK, loc="left"); ax.grid(False)
plt.tight_layout(); plt.show()
# Figura Instagram de cierre (05)
fig, ax = lienzo()
ax.text(0.08, 0.90, "EN UNA LÍNEA", fontsize=20, color=ACCENT, fontweight="bold", va="top")
ax.text(0.08, 0.83, "En España el tiro pesa más\nque en la NBA y los tiros libres,\nmenos. En las ocho categorías.",
fontsize=30, fontweight="bold", color=INK, va="top")
ax.text(0.08, 0.55, "Y lo que hace tu equipo pesa\nexactamente lo mismo que lo que\nle provoca al rival: mitad y mitad.",
fontsize=30, fontweight="bold", color=INK, va="top")
ax.text(0.08, 0.27, "Método completo, cifras por categoría y cautelas\n(qué NO dicen los datos) en el estudio.",
fontsize=19, color=MUTED, va="top")
ax.plot([0.08, 0.30], [0.19, 0.19], color=ACCENT, lw=5)
ax.text(0.08, 0.12, "bop analytics", fontsize=24, fontweight="black", color=INK, va="top")
guardar(fig, "05_cierre"); plt.show()
5 · Conclusiones y cautelas¶
Lo medido. 9.918 partidos en ocho categorías: la ACB (3.126 partidos, diez temporadas) y las siete ligas nacionales de 2024/25 y 2025/26 (de Tercera FEB, con 3.589, a Liga U, con 199). Misma definición de los factores, mismo modelo, misma escala.
1 · El orden de Oliver se mantiene en todas; los pesos, no. En las ocho categorías manda el tiro (43-48 %), después pérdidas (21-26 %) y rebote (20-23 %), y los tiros libres cierran (9-11 %). Ninguna liga invierte el orden. Pero frente a la NBA de Oliver (40 · 25 · 20 · 15), el baloncesto español carga más en el tiro (46 % en el conjunto) y menos en la línea (10 %): la diferencia de acierto de campo decide casi la mitad del partido, y pisar la línea, una décima parte. Pérdidas y rebote quedan donde Oliver los puso.
2 · Es igual arriba y abajo, en masculino y en femenino. La ACB (47 · 24 · 20 · 9) se parece a Tercera FEB (45 · 24 · 21 · 10) y a LF Endesa (48 · 21 · 21 · 10). Los matices son pequeños: LF2 es la que menos carga en el tiro (43 %) y la Liga U la que más en las pérdidas (26 %), con la muestra más corta (199 partidos). Con esos factores el modelo acierta el ganador en el 93-95 % de los partidos en todas las ligas.
3 · Cada factor tiene dos caras, y pesan lo mismo. Separando lo que un equipo hace de lo que provoca en el rival: el tiro propio pesa 22-24 % y el tiro que se concede, 21-23 %; las pérdidas propias 11-14 % y las forzadas 11-14 %; el rebote ofensivo propio ~10-11 % y el concedido 9-10 %; los tiros libres propios 4-6 % y los del rival 5-6 %. Sumadas las caras, lo que haces con el balón y lo que provocas sin él pesan mitad y mitad en las ocho categorías (48-52 % cada lado). El partido no se gana "en ataque" ni "en defensa": se gana en las dos por igual, y la palanca más grande en cada lado es la misma, el acierto de campo.
Cautelas (honestidad)¶
- Explicativo, no predictivo. Mide qué decidió el partido jugado (los factores de ese partido), no qué va a pasar en el siguiente.
- Posesiones: reales en las ligas nacionales, estimadas (fórmula de Oliver) en la ACB. Dentro de cada liga la definición es la misma, que es lo que importa para el peso.
- El peso no es la palanca. Que el tiro pese un 46 % no dice cómo se mejora el tiro; para eso hay que bajar al estilo (selección de tiro, pase, transición), que es otro estudio.
- Muestras: ACB y Tercera FEB con miles de partidos; Liga U, LF Endesa y LF Challenge con 200-480. Los decimales de las ligas pequeñas se leen como orden de magnitud.
- Sin nombres: es un estudio de ligas y equipos, no de jugadores.