NBA stats
Introduction
Vous allez travailler sur des données de la nba depuis les années 2000.
Les données sont brutes i.e. elles ont été récupérée via l’api stats nba et elles n’ont quasiment pas été retouchées.
Le modèle de données n’est pas fourni ! Ce sera à vous d’explorer vous même la base de données.
Dans votre vie professionnelle, dans 95 % des cas vous allez hériter de missions où la documentation est obsolète voire inexistante.
Il faut donc apprendre à vous débrouiller dès maintenant. Et au passage, vous convaincre que faire et maintenir la doc ça aide et c’est bien !
1 Importer la base de données
🚧
import os
import subprocess
import psycopg2
import s3fs
from dotenv import load_dotenv
load_dotenv()
AWS_ENDPOINT = os.environ["AWS_ENDPOINT_URL"]
DUMP_FILE = "nba.dump"
FILE_PATH = f"s3://ludo2ne/diffusion/ENSAI/SQL-TP/{DUMP_FILE}"
fs = s3fs.S3FileSystem(
client_kwargs={"endpoint_url": AWS_ENDPOINT},
key=os.environ["AWS_ACCESS_KEY_ID"],
secret=os.environ["AWS_SECRET_ACCESS_KEY"],
token=os.environ["AWS_SESSION_TOKEN"],
)
print("📥 Téléchargement du dump depuis S3...")
with fs.open(FILE_PATH, "rb") as f_in:
with open(DUMP_FILE, "wb") as f_out:
f_out.write(f_in.read())
print("✅ Dump téléchargé :")
PG_HOST = os.getenv("POSTGRESQL_HOST")
PG_PORT = os.getenv("POSTGRESQL_PORT", "5432")
PG_DB = os.getenv("POSTGRESQL_DATABASE")
PG_USER = os.getenv("POSTGRESQL_USERNAME")
PG_PASSWORD = os.getenv("POSTGRESQL_PASSWORD")
PG_SCHEMA = os.getenv("POSTGRESQL_SCHEMA")
os.environ["PGPASSWORD"] = PG_PASSWORD
conn = psycopg2.connect(
dbname=PG_DB, user=PG_USER, password=PG_PASSWORD, host=PG_HOST, port=PG_PORT
)
conn.autocommit = True
cur = conn.cursor()
sql_create_schema = f"CREATE SCHEMA IF NOT EXISTS {PG_SCHEMA};"
cur.execute(sql_create_schema)
cmd = [
"pg_restore",
"-d",
PG_DB,
"-h",
PG_HOST,
"-p",
PG_PORT,
"-U",
PG_USER,
"-n",
PG_SCHEMA,
DUMP_FILE,
]
print("⏳ Restauration PostgreSQL...")
subprocess.run(cmd, check=True)
print("🎉 Base NBA restaurée dans votre PostgreSQL")2 Rendez-vous en base inconnue
Je n’ai pas réussi à inviter Laury T pour vous accompagner dans cette aventure mais voici quelques requêtes utiles pour explorer une base de données PostgreSQL.
SELECT table_name
FROM information_schema.tables
WHERE table_schema = '<schema_name>'
AND table_type = 'BASE TABLE'
ORDER BY table_name;SELECT column_name,
data_type,
is_nullable,
column_default
FROM information_schema.columns
WHERE table_schema = '<...>'
AND table_name = '<...>'
ORDER BY ordinal_position;| Colonne | Signification |
|---|---|
| matchup | Description du match : ex. LAL @ BOS (à l’extérieur) ou LAL vs BOS (à domicile). |
| wl | Résultat pour l’équipe : W (win), L (loss). |
| min | Minutes totales jouées par l’équipe (toujours 240 sauf prolongation). |
| pts | Total des points de l’équipe. |
| fgm | Field goals made — tirs réussis (2 pts + 3 pts). |
| fga | Field goals attempted — tirs tentés. |
| fg_pct | Taux de réussite : fgm / fga. |
| fg3m | 3-points réussis. |
| fg3a | 3-points tentés. |
| fg3_pct | Pourcentage à 3 points. |
| ftm | Free throws made — lancers francs réussis. |
| fta | Free throws attempted — lancers francs tentés. |
| ft_pct | Pourcentage aux lancers. |
| oreb | Rebonds offensifs. |
| dreb | Rebonds défensifs. |
| reb | Rebonds totaux (oreb + dreb). |
| ast | Passes décisives. |
| stl | Interceptions. |
| blk | Contres. |
| tov | Ballons perdus (turnovers). |
| pf | Fautes personnelles. |
3 Explorons avec les Spurs
-
- classez ces matchs par date
Ce choix de conception a des avantages et des inconvénients. Nous y reviendrons par la suite lorsque nous afficherons les scores de chaque match.
Regardons maintenant les stats des joueurs.
4 Matchs et classements
Dans toute cette partie nous nous interesserons à la saison 2020-2021.
Interessons-nous la saison régulière.
-
- ajoutez le nombre de matchs joués
-
- classez par nombre de victoires décroissant
Passons maintenant aux Playoffs.
Une dernière question pour la route :
Tout simplement :
WITH last_playoff_games AS (
SELECT season_id, MAX(game_id) AS last_game_id
FROM nba.game
WHERE season_type = 'Playoffs'
GROUP BY season_id
),
final_teams AS (
SELECT g.season_id,
g.team_id AS team_id_win,
g2.team_id AS team_id_los
FROM nba.game g
JOIN last_playoff_games lpg ON g.season_id = lpg.season_id AND g.game_id = lpg.last_game_id
JOIN nba.game g2 ON g2.game_id = g.game_id AND g2.team_id <> g.team_id
WHERE g.wl = 'W'
)
SELECT gwin.season_id,
twin.full_name AS equipe,
COUNT(*) FILTER (WHERE gwin.wl = 'W') || ' - ' || COUNT(*) FILTER (WHERE gwin.wl = 'L') AS score,
tlos.full_name AS adversaire
FROM nba.game gwin
JOIN final_teams ft ON ft.team_id_win = gwin.team_id AND ft.season_id = gwin.season_id
JOIN nba.team twin ON twin.id = gwin.team_id
JOIN nba.game glos ON glos.game_id = gwin.game_id AND glos.team_id = ft.team_id_los
JOIN nba.team tlos ON tlos.id = glos.team_id
WHERE gwin.season_type = 'Playoffs'
GROUP BY gwin.season_id, twin.full_name, tlos.full_name
ORDER BY gwin.season_id DESC;5 Triple zéro
Lors d’un match, les principales statistiques utilisées pour mesurer les performances des joueurs sont : les points, les rebonds, les passes décisives, les interceptions et les contres.
Un joueur réalise alors un :
- double-double : s’il atteint au moins 10 dans deux de ces statistiques
- triple-double : au moins 10 dans trois de ces statistiques (grosse perf)
- quadruple-double : au moins 10 dans quatre de ces statistiques (très rare)
Il existe également d’autres performances très rares :
- double triple-double : s’il atteint au moins 20 dans trois de ces statistiques
- Five-by-five : au moins 5 dans les cinq catégories
Vous aussi en avez assez d’entendre toujours les mêmes noms réaliser ce genre de performance : Wemby, Curry, Gilgeous-Alexander, Doncic, Jokic…
L’heure de la revanche a sonné. Mettons aujourd’hui en lumière les vrais champions, ceux qui réalisent des 0 dans ces stats, font des fautes, perdent des balles.
- Commencez par retirer les lignes ou la colonne minutes est vide
- Puis utilisez
CAST(SPLIT_PART(minutes, ':', 1) AS integer)
-
- quadruple zéro : 0 points et 3 autres stats à 0
- Le
CASE WHENest votre ami ici
Quelques autres titres à décerner (toujours avec au moins 10min de jeu):
-
- i.e. qui n’a marqué aucun point, et a tenté le plus grand nombre de tirs, lancers francs inclus
La colonne plusminuspoints est la différence entre le nombre de points marqués par l’équipe et le nombre de points encaissés lorsque le joueur est sur le terrain.
Cela donne une idée de l’influence et l’impact du joueur lorsqu’il est sur le terrain.
Ils ont essayé de faire perdre leur équipe mais ils n’ont pas réussi :
Arrêtez vos services
C’est la fin du TP, vous pouvez maintenant sauvegarder votre travail et libérer les ressources réservées :