Categories Uncategorized

Analyse de Formule 1 dans R avec f1dataR : temps au tour, arrêts aux stands et performances du pilote

La Formule 1 est l’un des domaines les plus intéressants pour l’analyse de données dans R car elle combine des résultats structurés, un chronométrage tour par tour, une stratégie aux stands et des performances des pilotes dans l’un des ensembles de données les plus riches du sport. Pour quiconque renforce son autorité dans le contenu technique R, il s’agit d’un excellent créneau : il est suffisamment spécifique pour se démarquer, mais suffisamment large pour prendre en charge des didacticiels, des visualisations, des modèles prédictifs et une rédaction analytique longue.

L’un des plus grands avantages de travailler dans cet espace est que f1dataR permet aux utilisateurs de R d’accéder à la fois aux données historiques de Formule 1 et à des flux de travail plus riches au niveau des sessions liés à l’écosystème plus large d’Ergast/Jolpica et de FastF1. Cela permet de passer de simples résultats de course à des questions beaucoup plus intéressantes : qui avait le rythme de course le plus élevé ? Quel pilote a le mieux géré la dégradation des pneus ? Une stratégie d’arrêt au stand a-t-elle réellement fonctionné ? Pouvons-nous construire un modèle de base pour estimer les résultats des courses ?

C’est là que la Formule 1 devient bien plus qu’un sujet sportif. Cela devient une étude de cas pratique sur la gestion des données, la réflexion sur les séries chronologiques, l’ingénierie des fonctionnalités, la visualisation et la prédiction. Et comme l’espace de blog R contient relativement peu de contenu approfondi sur la Formule 1 par rapport aux sujets d’analyse plus généraux, un didacticiel solide ici peut aider à positionner votre site comme une source sérieuse d’expertise.

Pourquoi l’analyse de Formule 1 dans R est une niche si forte

La plupart des didacticiels R sur le Web se concentrent sur des exemples standards : tableaux de bord de ventes, prix des logements ou ensembles de données génériques d’apprentissage automatique. La Formule 1 est différente. Les données ont un contexte, un drame et une audience intégrée. Chaque course vous donne de nouveaux éléments à analyser, et chaque séance contient plusieurs couches d’informations : rythme de qualification, durée du relais, composés de pneus, timing de la voiture de sécurité, performances du secteur, dépassements et stratégie des stands.

Cela fait partie de ce qui rend ce sujet attrayant pour le contenu long. Vous n’enseignez pas seulement le code. Vous montrez comment le code aide à expliquer de véritables décisions concurrentielles. Un temps au tour n’est pas seulement un nombre. C’est une preuve de l’usure des pneus, du trafic, de la charge de carburant, de l’évolution de la piste et de l’exécution du pilote.

Pour les lecteurs qui souhaitent approfondir ce type de flux de travail, des ressources telles que Courir avec des données : Formule 1 et NASCAR Analytics avec R sont utiles car ils renforcent l’idée que l’analyse des courses dans R peut aller bien au-delà des graphiques de base et se transformer en une analyse sérieuse basée sur le code.

Installation des paquets

La première étape consiste à mettre en place un workflow à la fois reproductible et flexible. Pour la plupart des projets d’analyse de Formule 1 dans R, vous souhaiterez f1dataR ainsi qu’un petit ensemble de packages pour le nettoyage, le traçage, la création de rapports et la modélisation des données.

install.packages(c(
  "f1dataR",
  "tidyverse",
  "lubridate",
  "janitor",
  "scales",
  "slider",
  "broom",
  "tidymodels",
  "gt",
  "patchwork"
))

library(f1dataR)
library(tidyverse)
library(lubridate)
library(janitor)
library(scales)
library(slider)
library(broom)
library(tidymodels)
library(gt)
library(patchwork)

Si vous souhaitez travailler avec des données de synchronisation officielles au niveau de la session, c’est également une bonne idée de configurer la prise en charge de FastF1 et de définir un cache local.

setup_fastf1()

options(f1dataR.cache = "f1_cache")
dir.create("f1_cache", showWarnings = FALSE)

Cela peut ressembler à un petit détail, mais la mise en cache est importante lorsque vous créez un contenu analytique sérieux. Cela rend votre flux de travail plus rapide, plus propre et beaucoup plus facile à reproduire lors de la mise à jour ultérieure de blocs-notes, de rapports ou d’articles de blog.

Commencez par les résultats de la course

Avant de plonger dans les tours et la stratégie, commencez par les résultats historiques des courses. Ils constituent la base des résumés de saison, des comparaisons de pilotes, des tendances des constructeurs et des fonctionnalités prédictives.

results_2024 <- load_results(season = 2024)

results_2024 %>%
  clean_names() %>%
  select(round, race_name, driver, constructor, grid, position, points, status) %>%
  glimpse()

Une fois les résultats chargés, vous pouvez créer un tableau récapitulatif de la saison qui donne aux lecteurs un aperçu immédiat de la situation concurrentielle.

season_table <- results_2024 %>%
  clean_names() %>%
  group_by(driver, constructor) %>%
  summarise(
    races = n(),
    wins = sum(position == 1, na.rm = TRUE),
    podiums = sum(position <= 3, na.rm = TRUE),
    avg_finish = mean(position, na.rm = TRUE),
    avg_grid = mean(grid, na.rm = TRUE),
    points = sum(points, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(points), avg_finish)

season_table

Vous pouvez également convertir ce résumé en un tableau de publication plus propre pour un blog ou un rapport.

season_table %>%
  mutate(
    avg_finish = round(avg_finish, 2),
    avg_grid = round(avg_grid, 2)
  ) %>%
  gt() %>%
  tab_header(
    title = "2024 Driver Season Summary",
    subtitle = "Wins, podiums, average finish, and points"
  )

Ce type de résumé est utile, mais en soi, il n’explique pas grand-chose sur la manière dont les résultats ont été obtenus. C’est pourquoi la prochaine étape est importante.

Regarder au-delà de la position finale

L’un des moyens les plus simples d’améliorer une analyse F1 est d’aller au-delà du classement final. Un pilote terminant sixième peut avoir réalisé une excellente performance dans une voiture de milieu de peloton, tandis qu’un podium dans une voiture dominante peut raconter une histoire beaucoup plus simple. Un cadre plus solide compare les résultats à la position de départ, aux performances des coéquipiers et au rythme de course.

Un bon point de départ est le gain de position.

position_gain_table <- results_2024 %>%
  clean_names() %>%
  mutate(
    position_gain = grid - position
  ) %>%
  group_by(driver, constructor) %>%
  summarise(
    mean_gain = mean(position_gain, na.rm = TRUE),
    median_gain = median(position_gain, na.rm = TRUE),
    total_gain = sum(position_gain, na.rm = TRUE),
    races = n(),
    .groups = "drop"
  ) %>%
  arrange(desc(mean_gain))

position_gain_table

Cette métrique est simple, mais elle n’en reste pas moins précieuse car elle donne un premier signal d’exécution de la course. Bien sûr, cela a des limites. Les favoris ont moins de marge de manœuvre pour gagner des places et les courses au milieu de terrain sont souvent influencées par les différences de stratégie, les incidents et la fiabilité. Pourtant, c’est précisément cette nuance qui rend la discussion intéressante.

Ajouter un contexte de course et de circuit

Les performances de la Formule 1 dépendent toujours de la piste. Certaines voitures sont plus fortes sur les circuits à grande vitesse, certains pilotes s’épanouissent sur les circuits urbains et certaines équipes gèrent mieux que d’autres les circuits sensibles aux pneumatiques. Associer les résultats de course aux données de calendrier vous permet de formuler ces questions plus clairement.

schedule_2024 <- load_schedule(season = 2024) %>%
  clean_names()

results_with_schedule <- results_2024 %>%
  clean_names() %>%
  left_join(
    schedule_2024 %>%
      select(round, race_name, circuit_name, locality, country, race_date),
    by = c("round", "race_name")
  )

results_with_schedule %>%
  select(round, race_name, circuit_name, country, driver, constructor, grid, position) %>%
  slice_head(n = 10)

Même à ce stade, vous disposez déjà de suffisamment de structure pour rédiger plusieurs types d’articles : pilotes les plus performants par type de circuit, cohérence des constructeurs tout au long de la saison, écarts entre coéquipiers par site ou surperformance par rapport à la position de départ.

Temps au tour : là où l’analyse devient sérieuse

Les résultats de la course vous disent ce qui s’est passé. Les temps au tour vous disent comment cela s’est passé. C’est là que l’analyse de la Formule 1 devient beaucoup plus précieuse, car vous pouvez commencer à évaluer le rythme de la course, les effets du trafic, la dégradation des pneus et l’évolution des performances d’un pilote sur l’ensemble de l’épreuve.

Il est généralement préférable de se concentrer d’abord sur une séance de course, surtout si votre objectif est d’expliquer clairement le processus.

session_laps <- load_laps(
  season = 2024,
  round = 10,
  session = "R"
) %>%
  clean_names()

session_laps %>%
  select(driver, lap_number, lap_time, compound, tyre_life, stint, pit_out_time, pit_in_time) %>%
  glimpse()

Les champs de temps au tour doivent souvent être nettoyés avant de pouvoir être visualisés ou modélisés. Les convertir en secondes est généralement l’approche la plus pratique.

laps_clean <- session_laps %>%
  mutate(
    lap_time_seconds = as.numeric(lap_time),
    sector1_seconds = as.numeric(sector_1_time),
    sector2_seconds = as.numeric(sector_2_time),
    sector3_seconds = as.numeric(sector_3_time)
  ) %>%
  filter(!is.na(lap_time_seconds)) %>%
  filter(lap_time_seconds > 50, lap_time_seconds < 200)

summary(laps_clean$lap_time_seconds)

Comparaison du rythme de course par pilote

Une fois les données du tour nettoyées, vous pouvez comparer les pilotes sélectionnés et visualiser l’évolution de leur rythme tout au long de la course.

selected_drivers <- c("VER", "NOR", "LEC", "HAM")

laps_clean %>%
  filter(driver %in% selected_drivers) %>%
  ggplot(aes(x = lap_number, y = lap_time_seconds, color = driver)) +
  geom_line(alpha = 0.8, linewidth = 0.8) +
  geom_point(size = 1.2, alpha = 0.7) +
  scale_y_continuous(labels = label_number(accuracy = 0.1)) +
  labs(
    title = "Race pace by lap",
    subtitle = "Raw lap times across the Grand Prix",
    x = "Lap",
    y = "Lap time (seconds)",
    color = "Driver"
  ) +
  theme_minimal(base_size = 13)

Les tracés bruts des temps au tour sont utiles, mais ils sont souvent bruyants car les tours aux stands, les hors-tours et le trafic inhabituel peuvent fausser le schéma. Une analyse plus approfondie filtre une partie de ce bruit et se concentre sur le rythme du drapeau vert.

green_flag_laps <- laps_clean %>%
  filter(driver %in% selected_drivers) %>%
  filter(is.na(pit_in_time), is.na(pit_out_time)) %>%
  group_by(driver) %>%
  mutate(
    median_lap = median(lap_time_seconds, na.rm = TRUE),
    lap_delta = lap_time_seconds - median_lap
  ) %>%
  ungroup() %>%
  filter(abs(lap_delta) < 5)

green_flag_laps %>%
  ggplot(aes(lap_number, lap_time_seconds, color = driver)) +
  geom_line(linewidth = 0.9) +
  geom_smooth(se = FALSE, method = "loess", span = 0.25, linewidth = 1.1) +
  labs(
    title = "Green-flag race pace",
    subtitle = "Smoothed lap-time profile after removing pit laps and large outliers",
    x = "Lap",
    y = "Lap time (seconds)"
  ) +
  theme_minimal(base_size = 13)

Ce type de graphique est l’un des plus utiles dans l’analyse de la F1, car il montre si un pilote était vraiment rapide, s’il profitait simplement de sa position sur la piste ou s’il disparaissait en fin de course.

Dégradation des pneus et analyse des relais

L’un des meilleurs moyens d’ajouter une réelle autorité à un poste en F1 est de quantifier la dégradation. Au lieu de simplement dire qu’un pilote « a bien géré ses pneus », vous pouvez estimer l’évolution du temps au tour à mesure que la durée de vie des pneus augmente au cours d’un relais.

stint_degradation <- laps_clean %>%
  filter(driver %in% selected_drivers) %>%
  filter(!is.na(stint), !is.na(tyre_life), !is.na(compound)) %>%
  filter(is.na(pit_in_time), is.na(pit_out_time)) %>%
  group_by(driver, stint, compound) %>%
  filter(n() >= 8) %>%
  nest() %>%
  mutate(
    model = map(data, ~ lm(lap_time_seconds ~ tyre_life, data = .x)),
    tidied = map(model, broom::tidy)
  ) %>%
  unnest(tidied) %>%
  filter(term == "tyre_life") %>%
  transmute(
    driver,
    stint,
    compound,
    degradation_per_lap = estimate,
    p_value = p.value
  ) %>%
  arrange(degradation_per_lap)

stint_degradation

Une pente positive signifie généralement que le rythme diminue à mesure que le relais vieillit. Une pente plus petite suggère une meilleure préservation des pneus ou un rythme plus stable. L’interprétation n’est pas toujours simple, car le contexte racial est important, mais la méthode est très efficace pour transformer les discussions raciales en preuves.

laps_clean %>%
  filter(driver %in% selected_drivers, !is.na(stint), !is.na(tyre_life)) %>%
  filter(is.na(pit_in_time), is.na(pit_out_time)) %>%
  ggplot(aes(tyre_life, lap_time_seconds, color = driver)) +
  geom_point(alpha = 0.5, size = 1.6) +
  geom_smooth(method = "lm", se = FALSE, linewidth = 1) +
  facet_wrap(~ compound, scales = "free_x") +
  labs(
    title = "Tyre degradation by compound",
    subtitle = "Linear approximation of pace loss as the stint ages",
    x = "Tyre life (laps)",
    y = "Lap time (seconds)"
  ) +
  theme_minimal(base_size = 13)

C’est exactement le genre d’analyse qui rend un article technique mémorable, car il passe de « qui a gagné ? » à « pourquoi le modèle de performance était-il tel qu’il était ? »

Arrêts aux stands et stratégie

La stratégie des stands est l’un des exemples les plus clairs de la manière dont la Formule 1 combine données et prise de décision. Un arrêt n’est pas seulement un événement ; il s’agit d’un compromis entre la position sur la piste, la durée de vie des pneus, le rythme de course et le comportement des concurrents à proximité.

pit_summary <- session_laps %>%
  clean_names() %>%
  mutate(
    had_pit_event = !is.na(pit_out_time) | !is.na(pit_in_time)
  ) %>%
  group_by(driver) %>%
  summarise(
    total_laps = n(),
    pit_events = sum(had_pit_event, na.rm = TRUE),
    stints = n_distinct(stint, na.rm = TRUE),
    first_compound = first(na.omit(compound)),
    last_compound = last(na.omit(compound)),
    .groups = "drop"
  ) %>%
  arrange(desc(pit_events))

pit_summary

Une meilleure façon d’expliquer la stratégie est de reconstruire directement les relais.

strategy_table <- session_laps %>%
  clean_names() %>%
  arrange(driver, lap_number) %>%
  group_by(driver, stint) %>%
  summarise(
    start_lap = min(lap_number, na.rm = TRUE),
    end_lap = max(lap_number, na.rm = TRUE),
    laps_in_stint = n(),
    compound = first(na.omit(compound)),
    avg_lap = mean(as.numeric(lap_time), na.rm = TRUE),
    median_lap = median(as.numeric(lap_time), na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(driver, stint)

strategy_table
strategy_table %>%
  ggplot(aes(x = start_lap, xend = end_lap, y = driver, yend = driver, color = compound)) +
  geom_segment(linewidth = 6, lineend = "round") +
  labs(
    title = "Race strategy by driver",
    subtitle = "Stint map reconstructed from lap-level data",
    x = "Lap window",
    y = "Driver",
    color = "Compound"
  ) +
  theme_minimal(base_size = 13)

Une fois que vous disposez des cartes de relais, votre analyse devient immédiatement plus stratégique. Vous pouvez discuter des contre-dépouilles, des dépassements, des premiers relais longs, des arrêts précoces agressifs et de la question de savoir si une équipe a réellement converti la fraîcheur des pneus en gains significatifs.

Mesurer le rythme après l’arrêt

Une extension utile consiste à examiner si un conducteur a réellement bénéficié de pneus neufs après un arrêt. C’est l’un des moyens les plus simples de passer de l’analyse descriptive des puits à l’interprétation stratégique.

post_stop_pace <- session_laps %>%
  clean_names() %>%
  arrange(driver, lap_number) %>%
  group_by(driver) %>%
  mutate(
    pit_out_lap = !is.na(pit_out_time),
    laps_since_stop = cumsum(lag(pit_out_lap, default = FALSE))
  ) %>%
  ungroup() %>%
  filter(!is.na(lap_time)) %>%
  group_by(driver, laps_since_stop) %>%
  summarise(
    first_laps_avg = mean(as.numeric(lap_time)[1:min(3, n())], na.rm = TRUE),
    stint_avg = mean(as.numeric(lap_time), na.rm = TRUE),
    .groups = "drop"
  )

post_stop_pace

Ce type de tableau permet de répondre à une bien meilleure question que « quand ont-ils eu lieu ? » Il demande : « L’arrêt a-t-il créé un rythme utilisable et ce rythme était-il suffisamment fort pour influencer la course ? »

La comparaison des coéquipiers comme meilleure référence

En Formule 1, la comparaison des coéquipiers est souvent plus informative que la comparaison sur la grille complète, car la voiture est ce qui se rapproche le plus d’un environnement contrôlé. Si un pilote bat systématiquement l’autre en termes de position sur la grille, d’arrivée en course ou de régularité du rythme, cela vous indique quelque chose de beaucoup plus précis que le classement général du championnat.

teammate_table <- results_2024 %>%
  clean_names() %>%
  group_by(constructor, round, race_name) %>%
  mutate(
    teammate_finish_rank = min_rank(position),
    teammate_grid_rank = min_rank(grid)
  ) %>%
  ungroup() %>%
  group_by(driver, constructor) %>%
  summarise(
    avg_finish = mean(position, na.rm = TRUE),
    avg_grid = mean(grid, na.rm = TRUE),
    teammate_beating_rate_finish = mean(teammate_finish_rank == 1, na.rm = TRUE),
    teammate_beating_rate_grid = mean(teammate_grid_rank == 1, na.rm = TRUE),
    points = sum(points, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(teammate_beating_rate_finish), desc(points))

teammate_table

Ce type de comparaison est particulièrement efficace dans un article technique, car il donne aux lecteurs une référence qu’ils comprennent déjà intuitivement, tout en fondant la discussion sur des données.

Analyse sectorielle

Si les temps au tour vous donnent une idée du rythme global, les secteurs peuvent aider à révéler où ce rythme est gagné ou perdu. Même sans plonger dans la télémétrie complète, les divisions sectorielles peuvent révéler si un pilote est fort dans les zones de traction, les sections à grande vitesse ou les parties du circuit où le freinage est important.

sector_summary <- laps_clean %>%
  filter(driver %in% selected_drivers) %>%
  group_by(driver) %>%
  summarise(
    s1 = mean(sector1_seconds, na.rm = TRUE),
    s2 = mean(sector2_seconds, na.rm = TRUE),
    s3 = mean(sector3_seconds, na.rm = TRUE),
    total = mean(lap_time_seconds, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  pivot_longer(cols = c(s1, s2, s3), names_to = "sector", values_to = "seconds")

sector_summary %>%
  ggplot(aes(sector, seconds, fill = driver)) +
  geom_col(position = "dodge") +
  labs(
    title = "Average sector times by driver",
    subtitle = "A simple way to localize pace differences",
    x = "Sector",
    y = "Average time (seconds)",
    fill = "Driver"
  ) +
  theme_minimal(base_size = 13)

Ce type de ventilation est utile car il donne forme à l’analyse. Au lieu de dire qu’un pilote a été globalement plus rapide, vous pouvez montrer d’où vient le temps.

De la description à la prédiction

L’un des angles éditoriaux les plus forts pour un article comme celui-ci est de se terminer par une section sur la modélisation prédictive. Un titre tel que Science des données de Formule 1 dans R : prédire les résultats des courses fonctionne bien car il combine une intention claire, un intérêt technique et un sujet attrayant pour le public.

La clé est d’être réaliste. Le but n’est pas de promettre des prévisions parfaites. Il s’agit de montrer comment les données descriptives de Formule 1 peuvent être converties en fonctionnalités pour un modèle de base.

model_data <- results_2024 %>%
  clean_names() %>%
  arrange(driver, round) %>%
  group_by(driver) %>%
  mutate(
    rolling_avg_finish_3 = slide_dbl(position, mean, .before = 2, .complete = FALSE, na.rm = TRUE),
    rolling_avg_grid_3 = slide_dbl(grid, mean, .before = 2, .complete = FALSE, na.rm = TRUE),
    rolling_points_3 = slide_dbl(points, mean, .before = 2, .complete = FALSE, na.rm = TRUE),
    prev_finish = lag(position),
    prev_grid = lag(grid)
  ) %>%
  ungroup() %>%
  mutate(
    target_top10 = if_else(position <= 10, 1, 0),
    target_podium = if_else(position <= 3, 1, 0)
  ) %>%
  select(
    round, race_name, driver, constructor, grid, points, position,
    rolling_avg_finish_3, rolling_avg_grid_3, rolling_points_3,
    prev_finish, prev_grid, target_top10, target_podium
  ) %>%
  drop_na()

glimpse(model_data)

Cet ensemble de données est volontairement simple, mais c’est un point fort dans un didacticiel. Cela rend la logique visible et donne aux lecteurs quelque chose qu’ils peuvent réellement reproduire et étendre.

Prédire un top 10

set.seed(42)

split_obj <- initial_split(model_data, prop = 0.8, strata = target_top10)
train_data <- training(split_obj)
test_data <- testing(split_obj)

log_recipe <- recipe(
  target_top10 ~ grid + rolling_avg_finish_3 + rolling_avg_grid_3 +
    rolling_points_3 + prev_finish + prev_grid,
  data = train_data
) %>%
  step_impute_median(all_numeric_predictors()) %>%
  step_normalize(all_numeric_predictors())

log_spec <- logistic_reg() %>%
  set_engine("glm")

log_workflow <- workflow() %>%
  add_recipe(log_recipe) %>%
  add_model(log_spec)

log_fit <- fit(log_workflow, data = train_data)

top10_predictions <- predict(log_fit, new_data = test_data, type = "prob") %>%
  bind_cols(predict(log_fit, new_data = test_data)) %>%
  bind_cols(test_data %>% select(target_top10))

top10_predictions
top10_predictions %>%
  roc_auc(truth = factor(target_top10), .pred_1)

top10_predictions %>%
  accuracy(truth = factor(target_top10), estimate = .pred_class)

Prédire la position finale

finish_recipe <- recipe(
  position ~ grid + rolling_avg_finish_3 + rolling_avg_grid_3 +
    rolling_points_3 + prev_finish + prev_grid,
  data = train_data
) %>%
  step_impute_median(all_numeric_predictors()) %>%
  step_normalize(all_numeric_predictors())

lm_spec <- linear_reg() %>%
  set_engine("lm")

lm_workflow <- workflow() %>%
  add_recipe(finish_recipe) %>%
  add_model(lm_spec)

lm_fit <- fit(lm_workflow, data = train_data)

finish_predictions <- predict(lm_fit, new_data = test_data) %>%
  bind_cols(test_data %>% select(position, driver, constructor, race_name, grid))

metrics(finish_predictions, truth = position, estimate = .pred)
finish_predictions %>%
  ggplot(aes(position, .pred)) +
  geom_point(alpha = 0.7, size = 2) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
  labs(
    title = "Predicted vs actual finishing position",
    subtitle = "Baseline linear model",
    x = "Actual finish",
    y = "Predicted finish"
  ) +
  theme_minimal(base_size = 13)

Un modèle de référence comme celui-ci n’est pas censé être un système de prévision parfait. Sa véritable valeur est pédagogique. Il montre comment passer des tableaux de résultats à l’ingénierie des fonctionnalités, puis des fonctionnalités à un flux de travail prédictif reproductible.

Une simple note de conducteur personnalisée

Si vous souhaitez que l’article soit plus original, une option intéressante consiste à créer un score de pilote personnalisé. Les mesures composites fonctionnent bien dans la rédaction de la Formule 1 car elles combinent plusieurs dimensions de performance en un seul classement interprétable.

driver_rating <- results_2024 %>%
  clean_names() %>%
  group_by(driver, constructor) %>%
  summarise(
    avg_finish = mean(position, na.rm = TRUE),
    avg_grid = mean(grid, na.rm = TRUE),
    points = sum(points, na.rm = TRUE),
    wins = sum(position == 1, na.rm = TRUE),
    podiums = sum(position <= 3, na.rm = TRUE),
    gain = mean(grid - position, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    finish_score = rescale(-avg_finish, to = c(0, 100)),
    grid_score = rescale(-avg_grid, to = c(0, 100)),
    points_score = rescale(points, to = c(0, 100)),
    gain_score = rescale(gain, to = c(0, 100)),
    win_score = rescale(wins, to = c(0, 100)),
    rating = 0.30 * finish_score +
             0.20 * grid_score +
             0.25 * points_score +
             0.15 * gain_score +
             0.10 * win_score
  ) %>%
  arrange(desc(rating))

driver_rating

L’important ici est la transparence. Les lecteurs n’ont pas besoin d’être d’accord avec tous les poids de la formule. Ce qui compte, c’est que la méthode soit explicite, interprétable et facile à critiquer ou à améliorer.

Réflexions finales

L’analyse de la Formule 1 dans R est une niche de contenu exceptionnellement forte car elle combine rigueur technique et audience naturellement engagée. Avec f1dataRvous pouvez commencer par les résultats historiques des courses, passer à l’analyse des temps au tour et des relais, explorer la stratégie des stands et l’analyse comparative des pilotes, puis créer des modèles prédictifs de base qui donnent l’impression que le flux de travail est complet.

Cette gamme est exactement ce qui en fait un si bon sujet pour un article de renforcement de l’autorité. C’est pratique, reproductible et ouvre la porte à tout un ensemble d’articles de suivi sur la télémétrie, les qualifications, la dégradation des pneus, les comparaisons de coéquipiers et les prévisions de course.

Si votre objectif est de publier du contenu technique qui démontre une réelle expertise plutôt que de simplement couvrir des exemples superficiels, la science des données de Formule 1 dans R est l’un des meilleurs domaines que vous puissiez choisir.

L’article Analyse de Formule 1 dans R avec f1dataR : temps au tour, arrêts aux stands et performances du pilote apparaît en premier sur R Programming Books.



Berita Terkini

Berita Terbaru

Daftar Terbaru

News

Jasa Impor China

Berita Terbaru

Flash News

RuangJP

Pemilu

Berita Terkini

Prediksi Bola

Technology

Otomotif

Berita Terbaru

Teknologi

Berita terkini

Berita Pemilu

Berita Teknologi

Hiburan

master Slote

Berita Terkini

Pendidikan

Resep

Jasa Backlink

Slot gacor terpercaya

Anime Batch

Leave a Reply

Your email address will not be published. Required fields are marked *