Recommander un voyage de surf là où aucune prévision n'existe
Nalu, produit interne. Code et données publics.
Un surfeur ne pose pas ses congés à la météo du week-end. Il les pose trois mois à l'avance, quand aucune prévision de houle n'existe. Nalu répond autrement : il mesure sur quatre ans d'archive la probabilité qu'un spot fonctionne en octobre, puis croise cette probabilité au prix du billet au départ de Paris.
20spots, chacun avec sa source et son niveau de confiance
240couples spot × mois reclassés à chaque mouvement du curseur
701 280heures de houle dans le cache versionné160 fichiers parquet, 11,8 Mo
0 €de coût total, contrainte posée avant la première ligne
01 / Le problème
Trois mois d'avance, dix jours de prévision
L'horizon de décision d'un voyage est de trois mois. Celui d'une prévision de houle est de dix jours. Entre les deux il n'y a rien, et les guides de surf comblent le vide par des affirmations invérifiables, jamais croisées au prix du billet, qui décide pourtant du voyage autant que la vague.
À cet horizon, la seule chose qu'on puisse écrire honnêtement est une probabilité : « sur quatre ans d'archive, ce spot a fonctionné tant d'heures en octobre » plutôt que « il y aura de la houle ». La première phrase se vérifie.
02 / L'approche
Une probabilité, un prix, un seul curseur
Une seule mesure de qualité : la probabilité qu'une heure de jour soit surfable, évaluée heure par heure sur quatre ans. Pas de note composite, pas de pondération d'expert, parce qu'on n'additionne pas une probabilité et une grandeur non bornée.
Le prix entre comme second axe, et les deux se comparent en rangs centiles sur les 240 couples, jamais par une normalisation min-max. Une seule valeur aberrante suffirait à écraser l'échelle et à rendre le curseur inerte. Une propriété testée le garantit : ajouter un prix absurde ne change pas l'ordre des autres.
Q est une probabilité pure : l'intensité de la houle est calculée et affichée, mais n'entre pas dans
le score, parce qu'on n'additionne pas une probabilité et une grandeur non bornée. Les rangs sont des
centiles calculés sur les 240 couples spot × mois. Le curseur déplace α entre 0 et
1 : à 1, seule la vague compte ; à 0, seul le billet.
03 / Ce qui a été construit
Quatre briques, chacune tenue par un test
Le dépôt est écrit pour être lu, pas pour tourner en production : chaque hypothèse du modèle est isolée, typée, et justifiée en une phrase. Et les règles qui comptent ne vivent pas dans un document, elles vivent dans les tests. En enfreindre une fait échouer la suite.
Schéma du principe. Depuis chaque spot, 180 rayons sont lancés par pas de 2° contre les polygones de
côtes ; l'arc le plus large atteignant 500 km d'océan sans obstacle devient la fenêtre d'exposition.
Quinze spots sur vingt retrouvent ainsi la direction publiée dans les guides, sans intervention. Les cinq
autres portent une correction manuelle assortie d'un motif écrit : sans motif, le modèle refuse de se charger.
Ingestion
Client Open-Meteo officiel, aucun client HTTP ni backoff maison. Le quota est pondéré et non compté en requêtes : 2 296 unités consommées en 8 appels et 196 secondes pour remplir l'archive entière.
Géométrie des spots
Les fenêtres d'exposition à la houle ne sont pas déclarées, elles sont calculées : 180 rayons par spot lancés contre les polygones de côtes, seuil de 500 km d'océan ouvert. Quinze sur vingt retrouvent la direction publiée sans intervention.
Calcul du classement
Expressions vectorisées, aucune boucle ligne à ligne. 1,75 million de lignes traitées en moins de 60 secondes. Un test de performance échouerait sur une implémentation naïve, ce qui rend la règle exécutoire plutôt que déclarative.
Application
Tableau de bord posé sur un cache parquet versionné : zéro appel réseau à l'exécution, vérifié par un test qui coupe la socket. La démo tourne sans internet, ce qui protège une démonstration faite chez un client.
04 / Ce que la mesure a corrigé
Cinq décisions prises contre le plan initial
Le plan de départ a été amendé cinq fois par la mesure. Chaque écart est resté écrit dans le dépôt plutôt que réécrit après coup : ce qu'on décide une fois le résultat connu n'est plus une décision, c'est une justification.
01
Dix ans d'archive abandonnés pour quatre
La réanalyse ERA5-Ocean ne sert aucune houle pure avant décembre 2021 : le champ est vide, année par année, contrairement à ce qu'annonce sa documentation. Restait le choix entre dix ans de mer totale, houle et clapot du vent confondus, et quatre ans de houle seule. La mer totale surestime la houle de 22 cm en médiane, et de plus de 50 cm sur 14 % des heures. J'ai gardé les quatre ans.
02
La validation externe échoue, et le seuil n'a pas bougé
Le modèle retrouve la haute saison publiée sur 10 spots sur 20. Le seuil exigé, 70 %, avait été écrit avant la mesure, et il n'a pas été déplacé après. Le détail est plus instructif que le taux : l'erreur médiane est nulle, donc quand le modèle tombe juste, il tombe pile. Six échecs sur dix se jouent à un mois près. Et un test interdit d'atteindre la métrique en retirant les spots qui la dégradent.
03
Le biais tarifaire, mesuré avant d'être subi
Les prix proviennent du cache de recherche d'une plateforme d'affiliation : la couverture suit mécaniquement la fréquentation touristique. Corrélation de rang mesurée : +0,78. Les cinq destinations sans aucun prix sont exactement les cinq de popularité minimale. Le passage en rang centile aurait rendu ce biais invisible sans le supprimer, et le classement aurait fini par recopier la fréquentation touristique sans que personne le voie. Ces cinq spots restent affichés, et signalés.
04
Un douzième paramètre né d'une confusion de vocabulaire
Les guides de surf publient une période de pic ; la source ne sert qu'une période moyenne. Confondre les deux rendait Uluwatu surfable 14 % du temps et les Maldives 0 %. Un facteur de conversion explicite, appliqué avant toute comparaison, a fait passer le modèle de onze à douze paramètres. Ils vivent tous dans un seul fichier, chacun suivi de la phrase qui le justifie, et un test refuse l'ajout d'un paramètre sans justification.
05
Une condition de vent qui déclarait surfable un vent de 45 nœuds
La règle initiale combinait le secteur du vent et sa vitesse par un OU. Un vent de terre à 45 nœuds passait donc le test. Elle a été corrigée en condition emboîtée (si le vent vient de la terre, alors un plafond, sinon un autre, plus bas) au moment de la revue, avant la première ligne de code. C'est le type d'erreur qu'aucun test ne rattrape après coup, parce que le résultat reste plausible.
05 / Résultats
Pourquoi un spot à 0,7 % gagne le mois de janvier
Curseur au milieu, en janvier, Ponta Preta arrive premier avec 0,7 % d'heures surfables : dix heures sur les 1 364 heures de jour que compte janvier cumulé sur quatre ans. Il gagne parce que le billet est à 301 €.
La Gravière a la meilleure houle du mois de tout le classement et finit troisième, faute de prix disponible. Un spot sans prix reçoit le rang 0, ce qui le pénalise au lieu de le neutraliser. Le choix est assumé, et le tableau le montre au lieu de l'enfouir dans le code.
Rang
Spot
Surfable
Rang qualité
Prix A/R
Rang prix
Score
1
Ponta Preta
0,7 %
0,326
301 €
0,768
0,547
2
Uluwatu
2,2 %
0,494
683 €
0,580
0,537
3
La Gravière
28,3 %
0,992
n.d.
0,000
0,496
Janvier, curseur à mi-course (α = 0,5). Rangs centiles calculés sur les 240 couples spot × mois.
06 / Les limites
Ce que le modèle ne fait pas
Pas de marée. Elle est décisive sur la plupart des récifs, mais aucune source gratuite ne la couvre à l'échelle du monde sur plusieurs années d'archive.
Ni bathymétrie ni réfraction, hors de portée d'une maille de 50 kilomètres.
Des hauteurs au large, pas des tailles de vague. Les seuils portent des noms qui le disent, pour rendre l'abus de langage impossible à commettre par inadvertance.
Quatre ans sous-échantillonnent la variabilité entre années. Un épisode El Niño y pèse plus lourd que sur dix ans.
Vingt spots, pas cinquante. Les seuils sont l'actif central du produit, et vingt spots sourcés valent mieux que cinquante devinés.
Ce que Nalu dit d'une mission chez vous
Ce projet n'a pas de client. Ce qu'il montre est une méthode : mesurer avant d'affirmer, écrire le seuil avant de connaître le résultat, publier l'écart quand il reste. J'applique la même chose à une donnée d'entreprise.