Organización, análisis de datos e inteligencia artificial para la predicción de resultados en Fórmula 1
Abstract
If humans can learn from experience, can computers do the same? Through machine learning we have found out that the answer to this question is yes. In this paper, as will be seen throughout this report, we will use machine learning techniques to try to answer this question. automatic learning techniques to try to answer the question: Who will win the next Formula 1 Grand Prix? In order to do so, we will carry out the steps of exploratory data analysis, feature engineering and modeling, to create models that can answer the question by obtaining predictions that best fit the reality, analyzing the complications that we can obtain and trying to explore different solutions.
Full text
FACULTAD DE MATEM´ ATICAS DEPARTAMENTO DE CIENCIAS DE LA COMPUTACI´ ON E INTELIGENCIA ARTIFICIAL ORGANIZACI´ ON, AN´ ALISIS DE DATOS E INTELIGENCIA ARTIFICIAL PARA LA PREDICCI´ ON DE RESULTADOS EN F´ ORMULA 1. Mar´ıa Jes´us Garc´ıa Villal´on Dirigido por: Luis Valencia Cabrera
A Luis, por su paciencia, dedicaci´on y profesionalidad. A mis padres porque aunque no puedan entender todo lo que hay aqu´ı escrito, se han esforzado para que yo pueda hacerlo y disfrutarlo. A David por ser el mejor compa˜nero. A mis hermanos.
Abstract If humans can learn from experience, can computers do the same? Through machine learning we have found out that the answer to this question is yes. In this paper, as will be seen throughout this report, we will use machine learning techniques to try to answer this question. automatic learning techniques to try to answer the question: Who will win the next Formula 1 Grand Prix? In order to do so, we will carry out the steps of exploratory data analysis, feature engineering and modeling, to create models that can answer the question by obtaining predictions that best fit the reality, analyzing the complications that we can obtain and trying to explore different solutions. Resumen Si los humanos pueden aprender de la experiencia, ¿pueden los ordenadores hacer igual? A trav´es del aprendizaje autom´atico hemos descubierto que la respuesta a ´esta pregunta es s´ı. En este trabajo, como se ver´a a lo largo de ´esta memoria vamos a utilizar las t´ecnicas de aprendizaje autom´atico para intentar responder la cuesti´on: ¿Qui´en ganar´a el siguiente gran premio de F´ormula 1? Para ello, llevaremos a cabo los pasos de an´alisis exploratorio de datos, ingenier´ıa de caracter´ısticas y modelizaci´on, para crear modelos que puedan responder dicha cuesti´on obteniendo predicciones que mejor se ajusten a la realidad, analizando las complicaciones que podamos obtener e intentando explorar diferentes soluciones.
´ Indice general I Introducci´on. 9 1. Introducci´on. 11 II Preliminares. 13 2. Exploraci´on y limpieza de datos. 15 2.1. An´alisis exploratorio de los datos. . . . . . . . . . . . . . . . . . . . . . . . 15 2.2. Ingenier´ıa de caracter´ısticas. . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3. Modelos. 19 3.1. Aprendizaje supervisado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.1.1. Modelos de regresi´on. . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1.2. Modelos de clasificaci´on. . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1.3. Modelos Jer´arquicos. . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2. Aprendizaje no supervisado. . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3. Redes Neuronales Artificiales. . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.4. Validaci´on del ajuste. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.5. M´etricas...................................... 26 4. Herramientas. 29 III Cuerpo. 31 5. Predicciones. 33 5.1. An´alisis exploratorio de los datos. . . . . . . . . . . . . . . . . . . . . . . . 33 5.1.1. Preprocesamiento de datos . . . . . . . . . . . . . . . . . . . . . . . 35 5.2. Estudio de las variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 5.2.1. Primeraprueba.............................. 39 5.2.2. Segundaprueba. ............................ 41 5.3. Modelizaci´on. .................................. 45 5.3.1. Primeraprueba.............................. 47 5.3.2. Segundaprueba. ............................ 53 5.4. Comparaci´on................................... 58 7
8´ INDICE GENERAL IV Conclusiones. 63 6. Conclusiones. 65 7. Bibliograf´ıa 69 V Anexos. 71 A. Primera prueba. 73 B. Segunda prueba. 103 C. Estudio de las variables. 129 D. Comparaci´on modelos y predicciones. 135 E. Predicciones temporada 2022. 145
Parte I Introducci´on. 9
16 CAP´ ITULO 2. EXPLORACI ´ ON Y LIMPIEZA DE DATOS. La ingenier´ıa de caracter´ısticas se basa en la utilizaci´on del conocimiento sobre el contexto o dominio de los datos objeto de estudio para preparar y extraer caracter´ısticas de un conjunto de datos mediante t´ecnicas de miner´ıa de datos. Por lo que en determinadas situaciones necesitaremos transformar las variables, dentro del proceso de ingenier´ıa de caracter´ısticas podemos encontrar pasos o transformaciones que ser´an adecuadas o no seg´un cada modelo concreto, como se aplicar´a despu´es. La selecci´on de caracter´ısticas es un paso importante del preprocesamiento de datos. Si podemos identificar las caracter´ısticas relevantes, entonces el proceso de aprendizaje posterior tratar´a con una dimensionalidad menor. Codificaci´on de variables categ´oricas. Cuando un predictor es categ´orico, es habitual descomponer el predictor en un conjunto de variables m´as espec´ıficas. Para utilizar estos datos en los modelos, las categor´ıas se recodifican en trozos de informaci´on m´as peque˜nos llamados “dummy variables”. Normalmente, cada categor´ıa tiene su propia ‘dummy variable”que es un indicador de cero/uno para ese grupo. Una “dummy variable”podr´ıa decirse que se asemeja a la t´ecnica one-hot-encoding, sin embargo, solo necesitamos n−1 variables ya que la n-´esima puede ser inferida a partir de las restantes. Escalado de caracter´ısticas y normalizaci´on. La transformaci´on de datos m´as com´un es centrar y escalar las variables predictoras. Centrar una variable consiste en restar su media a cada una de sus observaciones. El resultado es que la variable predictora tiene media cero. Escalar una variable consiste en dividir cada observaci´on entre su desviaci´on t´ıpica. El resultado es que la variable predictora tiene varianza uno. Estas transformaciones proporcionan que todas las variables est´en en la misma escala. La ventaja de esta t´ecnica es que todas las variables tengan el mismo peso de cara a la obtenci´on de la salida de los modelos, evitando as´ı el sesgo hacia variables que en un principio presenten mayores ´ordenes de magnitud. El incoveniente que ´esto nos provoca es la p´erdida de interpretabilidad de los valores individuales ya que no estar´ıan en las unidades originales. Limpieza de los datos. Cuando preparamos un conjunto de datos para su posterior an´alisis, es muy com´un encontrarnos con valores desaparecidos. Estos valores afectan al rendimiento de nuestros modelos, ya que no son f´aciles de tratar. Podemos tratarlos de dos formas diferentes: eliminaci´on e imputaci´on. Eliminaci´on Al tener un conjunto de datos que contiene observaciones las cuales poseen valores perdidos, una primera idea que podemos llevar a cabo es eliminar del conjunto las observaciones que contienen estos valores faltantes. Una consecuencia grave de eliminarlos es
2.2. INGENIER´ IA DE CARACTER´ ISTICAS. 17 que estamos perdiendo la informaci´on de la observaci´on que lo contiene. Para conjuntos de datos grandes, eliminar observaciones con valores faltantes no es un problema, asumiendo que los valores perdidos no proporcionan gran informaci´on. En conjuntos de datos peque˜nos, al eliminar los valores perdidos, estar´ıamos perdiendo demasiada informaci´on. Para no perder informaci´on, podemos llevar a cabo la siguiente t´ecnica. Imputaci´on. La imputaci´on es un m´etodo de tratamiento de datos perdidos donde intentamos estimar los valores de las variables predictoras en funci´on de otras variables predictoras, de forma que los valores faltantes se completan mediante esta estimaci´on calculada. Con la imputaci´on no estamos perdiendo toda la informaci´on de dicha observaci´on; sin embargo, tenemos que determinar cu´ales son los valores que imputamos. Una opci´on es basarse en datos existentes y usar un algoritmo de aprendizaje autom´atico. As´ı, una de las t´ecnicas m´as populares de imputaci´on es el modelo de los K vecinos m´as cercanos. Imputaci´on KNN. Con la t´ecnica de imputaci´on KNN una muestra se imputa encontrando las muestras del conjunto de entrenamiento m´as cercanas a ella y promedia estos puntos cercanos para rellenar el valor. Una ventaja de este m´etodo es que es una aproximaci´on de grano m´as fino, atribuimos comportamientos m´as cercanos a los ejemplos m´as similares, en lugar de un valor como el promedio, la mediana o la moda que son de grano m´as grueso y no tienen presente las diferencias entre las observaciones con datos faltantes, atribuyendo el mismo valor a todas. Una desventaja es que cada vez que necesitamos imputar un valor, tendremos que recurrir al conjunto de entrenamiento completo. Reducci´on de la dimensionalidad. Este es un proceso de reducci´on del n´umero de variables de los datos originales para generar un conjunto de datos con una capacidad predictiva similar, pero con un menor n´umero de variables o caracter´ısticas, lo cual significa una disminuci´on del tiempo y complejidad de los c´alculos, consecuentemente facilita su utilizaci´on en modelos al igual que disminuye las posibilidades de sobreajuste. El an´alisis de componentes principales (PCA) permite encontrar un n´umero de variables xinferior al n´umero actual p,x<p, que expresen aproximadamente lo mismo que las pvariables originales. De esta forma, si en un principio requer´ıamos pvariables para interpretar a cada individuo, ahora bastan xvariables. Cada una de estas xnuevas variables recibe el nombre de componente principal. La principal ventaja del PCA, y la raz´on por la que ha conservado su popularidad como m´etodo de reducci´on de la dimensionalidad de los datos, es que crea componentes que est´an no est´an correlacionadas, y prescinde de las variables originales que s´ı puedan estarlo. As´ı, nos quedaremos con el n´umero componentes principales que queramos. En esta situaci´on aparece el t´ermino colinealidad que se usa cuando un par de variables predictoras tienen una correlaci´on significativa entre s´ı. Por este motivo, podr´ıa decirse que la t´ecnica de componentes principales obtiene de la colinealidad de las variables las componentes principales las cuales no est´an correlacionadas.
18 CAP´ ITULO 2. EXPLORACI ´ ON Y LIMPIEZA DE DATOS. Puesto que PCA busca combinaciones lineales de variables predictoras que maximicen variabilidad as´ı esta t´ecnica puede ayudar tambi´en a detectar y eliminar variables que sean pr´oximas a una combinaci´on lineal de otra u otras. Por otro lado, cuando el conjunto de datos consta de demasiadas variables predictoras la t´ecnica PCA puede utilizarse para caracterizar la magnitud del problema.
Cap´ıtulo 3 Modelos. Como ya mencionamos en la introducci´on, en este cap´ıtulo vamos a describir los distintos modelos diferenciando el tipo de aprendizaje autom´atico. Entre otros aspectos, el machine learning utiliza la teor´ıa estad´ıstica para construir modelos matem´aticos porque la tarea principal es hacer inferencia a partir de una muestra. 3.1. Aprendizaje supervisado. Los algoritmos de aprendizaje supervisado hacen uso de los datos, con sus entradas y salidas conocidas y utilizan esa informaci´on para entrenar y validar el modelo. Definici´on 3.1.1 (Aprendizaje supervisado) El lenguaje supervisado significa construir un modelo parametrizado que pueda dividir el dominio de los datos y, a continuaci´on, optimizar los par´ametros mediante algoritmos de entrenamiento, validaci´on y prueba. Asumimos un modelo con un conjunto de par´ametros Y=g(x|θ) donde: g(.) es el modelo funci´on de regresi´on o discriminante dependiento de la naturaleza de la variable respuesta. xvariables predictoras. θpar´ametros. Yvariable respuesta El aprendizaje supervisado tiene dos principales objetivos: objetivos de parametrizaci´on y objetivos de optimizaci´on. Estos objetivos pueden definirse y diferenciarse utilizando la naturaleza continua y discreta de las variables respuesta. El objetivo de la parametrizaci´on se define como la acci´on relacionada con la regre- si´on si el conjunto de respuestas es continuo y se define como acci´on relacionada con la clasificaci´on si el conjunto de respuestas es discreto. El objetivo de la optimizaci´on es seleccionar varios modelos y despu´es seleccionar el mejor modelo utilizando una medida de distancia y los conjuntos de datos etiquetados. Para llevar a cabo estos objetivos requerimos de particionar nuestro conjunto de datos original en conjuntos de entrenamiento y test. 19
20 CAP´ ITULO 3. MODELOS. Entrenamiento Los par´ametros del modelo de machine learning son estimados, aproximados y optimizados utilizando el conjunto de datos etiquetados. Test Sirve para asegurarnos de que el modelo funciona utilizando otro conjunto de datos, es decir, ayudan a determinar la precisi´on del modelo, estimando as´ı c´omo de bien se comportar´a nuestro modelo a la hora de predecir la salida para datos desconocidos para el modelo. 3.1.1. Modelos de regresi´on. La variable respuesta continua Yde un sistema puede modelizarse mediante una relaci´on lineal X Y=βX +(3.1.1) Donde es una variable error. Una minimizaci´on de este error y una regularizaci´on distinta contribuyen a modelos de regresi´on. La continuidad de las variables permite la aplicaci´on de derivadas para la minimizaci´on. Cuando se dispone de un dominio de datos y sus correspondientes valores de respuesta, deber´ıamos ser capaces de estimar el par´ametro a partir de esta minimizaci´on del error. El objetivo de parametrizaci´on es definir un modelo parametrizado utilizando el modelo simple : y=βx. El objetivo de optimizaci´on se consigue mediante la optimizaci´on del siguiente factor de error con respecto al par´ametro β. Para minimizar la funci´on del error con respecto al par´ametro βdefinimos la suma de los cuadrados de los residuos: RSS(β)=(y−Xβ)t(y−Xβ) (3.1.2) Vamos a minimizar (3.1.2). La condici´on necesaria y suficiente de optimalidad es ∇RSS(β) = 0: ∇RSS(β) = 0 ⇔ −2Xt(y−Xβ)=0⇔Xty=XtXβ ˆ β= (XtX)−1Xty Por tanto, ˆ Y=Xˆ β. Observaci´on 3.1.2 1. La condici´on necesaria y suficiente de optimalidad se da si det(XtX)6= 0 2. Si det(XtX) = 0 tendr´ıamos un sistema compatible indeterminado con lo cual obtendr´ıamos infinitos β, luego, no ser´ıamos capaces de hacer predicciones. 3.1.2. Modelos de clasificaci´on. Los modelos de clasificaci´on son adecuados para el sistema que produce respuestas discretas.
3.1. APRENDIZAJE SUPERVISADO. 21 Si la variable respuesta posee solo dos posibles clases, se denomina problema de clasificaci´on binaria, en la cual tenemos una clase como positiva y otra como negativa. Cuando posee m´as de dos clases, se convierte en un problema de clasificaci´on multiclase. Como en el caso anterior, al hallarnos en un tipo de aprendizaje supervisado, asumimos que los datos est´an etiquetados y pueden generar reglas que nos ayuden a etiquetar a un nuevo dato el cual no tiene asignada una etiqueta. Para representar la relaci´on entre los datos del dominio Xy el conjunto de respuesta Yusaremos el mismo modelo del caso continuo (3.1.1) Sin embargo, en esta ecuaci´on Xes continuo e Yes discreto, lo cual dificulta ajustar un modelo lineal o no lineal. Debemos definir un conjunto de respuesta intermedio que es continuo para ajustar el modelo. La nueva variable representa una probabilidad p(x) = P[Y= 1|X=x] p(x) = 1 1 + eβX (3.1.3) Por esta raz´on la regresi´on log´ıstica define la ecuaci´on intermedia, la cual sigue un modelo lineal: log(p 1−p) = βX (3.1.4) El conjunto de datos etiquetados es usado para comparar los resultados producidos por el modelo final en t´erminos de precisi´on de la clasificaci´on y tiempo de c´alculo. Para ello, existen medidas llamadas medidas cualitativas que se utilizan para medir el rendimiento del modelo. Algunas de estas medidas son: exactitud, sensibilidad, especificidad y precisi´on. 3.1.3. Modelos Jer´arquicos. Adopta tanto estrategias de regresi´on como de clasificaci´on con un ´arbol que puede construirse mediante una secuencia de decisiones. Estos conducen a estratificar o segmentar el espacio predictor en un n´umero de regiones simples, de forma que para predecir una observaci´on determinada se utiliza la regi´on a la que pertenece. Los modelos jer´arquicos que vamos a considerar son ´ Arboles de decisi´on. Bosques aleatorios. ´ Arboles de decisi´on Es uno de los m´etodos m´as utilizados y pr´actico para la inferencia infuctiva. Los ´arboles de decisi´on clasifican las instancias orden´andolas en el ´arbol desde la ra´ız hasta alg´un nodo de la hoja. Cada nodo del ´arbol especifica una prueba de alguna variable de la observaci´on, y cada rama que desciende de ese nodo corresponden a uno de los posibles valores de la variable. En general, los ´arboles de decisi´on representan una disyunci´on de conjunciones de restricciones sobre los valores de las variables de las observaciones [4]. Seg´un aplicamos un ´arbol de decisi´on a problemas de regresi´on o clasificaci´on diferenciamos los ´arboles de regresi´on los cuales ayudan a predecir un valor para la variable
22 CAP´ ITULO 3. MODELOS. respuesta, y los ´arboles de clasificaci´on los cuales ayudan a predecir una etiqueta de clase para la variable respuesta. ´ Arboles de regresi´on. El algoritmo debe decidir autom´aticamente las variables de divisi´on y los puntos de divisi´on. Una vez encontrada la mejor divisi´on, dividimos los datos en las dos regiones resultantes de manera que se minimice el error global de la suma de los cuadrados, y repetimos el proceso de divisi´on en cada una de las dos regiones, y luego repetimos el proceso en todas las regiones resultantes. Claramente, un ´arbol muy grande prodr´ıa sobreajustar los datos, mientras que un ´arbol peque˜no podr´ıa no capturar la estructura importante. El tama˜no del ´arbol es un par´ametro de ajuste que rige la complejidad del modelo, y el tama˜no ´optimo del ´arbol debe elegirse de forma adaptativa a partir de los datos. La estrategia m´as usada es hacer crecer un ´arbol grande T0, deteniendo el proceso de divisi´on solo cuando se alcanza alg´un tama˜no m´ınimo de nodo. Entonces el ´arbol grande se poda utilizando la poda de complejidad de costes. Definimos un sub´arbol T⊂T0como cualquier ´arbol que se puede obtener mediante una poda que colapse cualquier n´umero de nodos internos. Indexamos los nodos terminales por m, con el nodo mrepresentando la regi´on Rm.|T|denota el n´umero de nodos terminales de T. Teniendo: Nm= #{xi∈Rm}ˆcm=1 NmPxi∈Rmyi Qm(T) = 1 NmPxi∈Rm(yi −ˆcm)2 Definimos el criterio de coste-complejidad: Cα(T) = |T| X m=1 NmQm(T) + α|T|(3.1.5) La idea es encontrar para cada αel sub´arbol T⊂T0que minimiza Cα(T) en (3.1.5). El par´ametro α≥0 de funcionamiento gobierna el compromiso entre el tama˜no del ´arbol y su bondad de ajuste a los datos. Los valores grandes de αdan lugar a ´arboles m´as peque˜nos, y a la inversa para valores m´as peque˜nos de α. La estimaci´on de αse consigue mediante validaci´on cruzada de 5 o 10 pliegues. Elegimos el valor ˆαpara minimizar la suma de cuadrados de la validaci´on cruzada. El ´arbol final ser´a Tˆα. ´ Arboles de clasificaci´on. El objetivo de los ´arboles de clasificaci´on es particionar el conjunto de datos en peque˜nos y homog´eneos grupos. La homogeneidad significa que cada nodo es m´as puro, es decir, que contiene una proporci´on mayor de una clase en cada nodo. Los ´unicos cambios necesarios en el algoritmo del ´arbol se refieren a los criterios para dividir los nodos y podar el ´arbol.
3.1. APRENDIZAJE SUPERVISADO. 23 Para la regresi´on utilizamos la medida de impureza del nodo de error cuadr´atico Qm(T), pero esto no es adecuado para la clasificaci´on. En un nodo m, que representa una regi´on con mobservaciones sea ˆpmk =1 NmPxi∈RmI(yi=m) La proporci´on de observaciones de la clase ken el nodo m. Clasificamos las observaciones del nodo men la clase k(m) = argmaxkˆpmk Bosques aleatorios Forma estructura de tipo bosque con ´arboles de decisi´on que se generan mediante el muestreo aleatorio con reemplazamiento. Se genera un conjunto de ´arboles los cuales toman diferentes porciones del conjunto de datos, en lugar de uno solo ya que puede ser muy variable y sensible a la muestra tomada, de modo que la predicci´on arrojada por el bosque es proporcionada por el concenso entre los ´arboles. Es decir, al ver los datos cada ´arbol obtenemos un resultado de cada uno luego al combinar sus resultados estamos obteniendo errores que se nivelan de esta forma la predicci´on generaliza mejor. Las predicciones se combinan de diferente forma seg´un nos encontramos en un problema de predicci´on o de clasificaci´on. Problema de regresi´on. Cuando por cada ´arbol obtenemos un resultado, la forma de combinarlos es hacer su media aritm´etica. Problema de clasificaci´on. Cuando por cada ´arbol obtenemos un resultado, para este problema obtenemos las diferentes clases de la variable respuesta. La forma de combinar las predicciones de todos los ´arboles es dandole mayor importancia a la clase que m´as veces se ha predicho. La ventaja es que proporcionan m´ultiples clasificadores de ´arboles de decisi´on entrenados en la fase de prueba. Los par´ametros a optimizar de los bosques aleatorios son el n´umero de predictores seleccionados aleatoriamente, k, para elegir en cada divisi´on, y se conoce com´unmente como mtry. El n´umero de ´arboles del bosque y el n´umero m´ınimo de observaciones en cada nodo. En la figura 3.1, podemos ver un esquema de la estructura de un bosque aleatorio, como cada ´arbol que est´a contenido en ´el genera una predicci´on y c´omo se combinan las predicciones.
24 CAP´ ITULO 3. MODELOS. Figura 3.1: Estructura Random forest. 3.2. Aprendizaje no supervisado. El aprendizaje no supervisado se basa en las correlaciones entre los datos de entrada, y se utiliza para encontrar los patrones o caracter´ısticas significativas en los datos de entrada sin la ayuda de las salidas para los datos observados, ya que en diferencia del aprendizaje supervisado no conocemos estas salidas. Definici´on 3.2.1 (An´alisis cluster) Es una t´ecnica estad´ıstica multivariante que busca agrupar elementos (o variables) tratando de lograr la m´axima homogeneidad en cada grupo y la mayor diferencia entre los grupos, de forma que los que se encuentran dentro de cada cluster est´an m´as relacionados entre s´ı que los elementos asignados a clusters diferentes. B´asicamente, el clustering consiste en agrupar las observaciones seg´un su proximidad, esto es, dos observaciones formaran parte del mismo cluster si est´an pr´oximas y formaran parte de clusters distintos si est´an lejos una de la otra. Podemos diferenciar dos t´ecnicas de clustering: Clustering de partici´on estricta. Dado un conjunto de observaciones, buscamos una partici´on de este conjunto en k clusters siendo el n´umero de clusters k menor que el de observaciones, de manera que se verifique: 1. Ning´un cluster es vac´ıo. 2. La uni´on de todos los clusters es el conjunto total de observaciones. 3. Los clusters son disjuntos dos a dos. Dentro de esta t´ecnica podemos destacar el algoritmo de K-medias. Es uno de los m´etodos de clustering por descenso iterativo m´as populares. Est´a pensado para situaciones en las que todas las variables son de tipo cuantitativo, aunque puede inculir variables cualitativas, empleando distancia Hamming. Clustering de partici´on jer´arquica. Dado un conjunto de observaciones, buscamos construir una partici´on anidada con estructura de ´arbol H={P1, . . . , PQ}tal que
3.3. REDES NEURONALES ARTIFICIALES. 25 si Ci∈PmyCj∈Plcon m > l entonces Ci⊂CjoCi∩Cj= ø ∀i, j, m, l ∈ {1, . . . , Q}, i 6=j. Podemos decir as´ı que cada cluster est´a dividido en clusters lo que proporciona la estructura anidada. 3.3. Redes Neuronales Artificiales. Las redes neuronales son potentes herramientas de t´ecnicas no lineales de regresi´on, las cuales est´an basadas en la teor´ıa del funcionamiento del cerebro. Definici´on 3.3.1 Una red neural artificial es un modelo de procesamiento distribuido y en paralelo, en forma de un grafo dirigido, con la siguiente estructura: elementos de procesamiento, conexiones sin´apticas, conexiones entrantes, conexiones salientes, memoria local, funci´on de activaci´on y continuada o epis´odica. Pueden usarse tanto para aprendizaje supervisado como para no supervisado : predicci´on, clasificaci´on y agrupamiento. Sin embargo, la gran mayor´ıa de variantes de redes neuronales artificiales se enmarcan dentro del aprendizaje supervisado ya que no deja de ser un modelo que trata de ajustar la funci´on subyacente a partir de un conjunto de ejemplos donde conocemos a priori sus entradas y salidas, y entrenamos los pesos de una red que capture las interrelaciones entre los datos de entrada para producir las citadas salidas conocidas. El aprendizaje de una red neuronal puede verse como un problema de optimizaci´on no lineal para encontrar un conjunto de par´ametros de la red que minimicen la funci´on de coste para unos ejemplos dados. La variable objetivo est´a modelada por un conjunto intermedio de variables no observadas, las cuales se denominan variables ocultas. Estas variables ocultas son combinaciones lineales de algunas de las variables predictivas. Sin embargo, estas combinaciones lineales son transformadas por una funci´on no lineal. hk(x) = g(β0k+ P X i=1 xjβjk) Siendo Pel n´umero de variables predictoras. Los coeficientes βson similares a los coeficientes de regresi´on, y, los coeficientes βjk son el efecto de la j-´esima variable predictora en la k-´esima variable oculta. Una red neuronal puede contener m´ultiples capas ocultas para modelizar la variable respuesta. 3.4. Validaci´on del ajuste. Puede que nuestro ajuste haya sido muy bueno con la muestra que hemos estado usando, pero no sabemos si el modelo puede ser ´util para predecir nuevos valores de la variable respuesta. En consecuencia podemos usar varias t´ecnicas de validaci´on, no obstante, en este trabajo destacamos la validaci´on cruzada con k pliegues. La validaci´on cruzada es
Cap´ıtulo 5 Predicciones. Es evidente que la f´ormula 1 ha evolucionado mucho desde sus inicios hasta ahora, sin embargo, desde los inicios nos hemos hecho la misma pregunta: ¿Qui´en ganar´a cada Gran Premio? Para intentar responder a esta cuesti´on resulta interesante aplicar las t´ecnicas de Machine Learning, siguiendo los pasos explicados en la parte de preliminares (II). Haremos en primer lugar un estudio exhaustivo tanto del conjunto de datos como de las variables, para a continuaci´on poder definir e implementar diferentes modelos de regresi´on con los que obtener predicciones. 5.1. An´alisis exploratorio de los datos. Como hemos desarrollado en el cap´ıtulo 2, el primer paso que tenemos que hacer es un estudio exhaustivo de las variables que intervendr´an en nuestros modelos para as´ı comprender qu´e informaci´on contiene cada una de ellas. En primer lugar, vamos a definir cada conjunto de datos que utilizaremos los cuales fueron descargados de [10]. circuits.csv: conjunto de datos en el cual tenemos informaci´on sobre los 79 los circuitos en los que se ha corrido a lo largo de todas las temporadas, como el pa´ıs al que pertenece, la localidad en la que est´a ubicado, su latitud y altitud. constructor results.csv: conjunto que contiene los puntos que cada constructor ha obtenido en cada carrera. constructors.csv: informaci´on de cada escuder´ıa que ha participado en la competici´on de f´ormula 1. drivers.csv: informaci´on de cada piloto que ha disputado alguna carrera en la f´ormula 1. lap times.csv: tiempo por vuelta que llevaba cada piloto en cada carrera, as´ı como la posici´on en la que iba en cada vuelta. pits stops.csv: informaci´on sobre las paradas en boxes de cada piloto en cada carrera, indicando el n´umero de parada y el tiempo empleado en ´esta, tanto en segundos como en milisegundos. 33
34 CAP´ ITULO 5. PREDICCIONES. qualifying.csv: informaci´on correspondiente a la clasificaci´on de cada piloto con cada constructor para cada carrera, es decir, el mejor tiempo en cada Q y posici´on en la que clasific´o en cada carrera. races.csv: informaci´on de cada carrera: circuito en el que se disput´o, fecha en la que se disput´o y ronda dentro de cada temporada en la que se disput´o. results.csv: informaci´on sobre los resultados en cada carrera de cada piloto: constructor con el que estaba corriendo, posici´on desde la que sal´ıa, posici´on en la que acab´o, el estado en el que acab´o, puntos conseguidos, n´umero de vueltas que dio, tiempo de la vuelta r´apida y n´umero de vuelta en la que hizo la vuelta r´apida. status.csv: informaci´on sobre diferentes estados en los que se ha terminado una carrera. Vamos a ver la relaci´on que existe entre las diferentes tablas que vamos a usar en este trabajo, resaltando ´unicamente las columnas m´as importantes. Figura 5.1: Relaci´on entre los conjuntos de datos.
5.1. AN ´ ALISIS EXPLORATORIO DE LOS DATOS. 35 5.1.1. Preprocesamiento de datos Vamos a estudiar c´omo tratar cada tipo de datos que vamos a utilizar. Figura 5.2: Summary races.csv En primer lugar, observamos que en el conjunto de datos de las carreras, tenemos la informaci´on de las carreras que se disputan esta temporada (2022). Sin embargo, como ´esta a´un no ha terminado, es evidente que no tenemos los resultados. Por tanto, en nuestro conjunto de datos vamos a utilizar la informaci´on de las carreras hasta la temporada 2021. Si hacemos ahora un resumen estad´ıstico de todas las carreras sin tener en cuenta la temporada 2022, podemos observar comparando con la figura 5.2 que para ´esta temporada hay un circuito nuevo que ser´a el que tiene el circuitId = 79. Figura 5.3: Summary carreras hasta la temp 21 Esto nos hace pensar que en nuestro conjunto de datos no habr´a informaci´on para este nuevo circuito, por lo que tenemos que pensar alguna forma de crear modelos para poder predecir en circuitos nuevos. Tambi´en podemos destacar que, la f´ormula 1 tuvo sus inicios en el a˜no 1950 y hasta ahora se han disputado m´as de 1000 carreras a lo largo de todos los a˜nos. Figura 5.4: Summary results.csv Realizando ahora un resumen estad´ıstico sobre los resultados en las carreras, observamos que en algunas carreras hay datos alarmantes para pensar que algunas no corresponden a carreras de f´ormula 1. En primer lugar, nos fijamos en el grid, que es la posici´on desde la que salen los pilotos al inicio de la carrera,teniendo esta un m´ınimo de 0, lo cu´al no tiene sentido ya que siempre hay alguien que sale desde primera posici´on (pole) como podemos observar en la posici´on final. Esto nos hace pensar que hay datos perdidos o faltantes, por lo que la informaci´on correspondiente a esos datos, la eliminaremos de nuestro conjunto de datos. A continuaci´on nos fijamos en el n´umero de vueltas, siendo el m´aximo de ´estas 200, n´umero que excede con creces la media de vueltas en una carrera de f´ormula 1, lo que nos hace pensar que en el conjunto de datos hay informaci´on que no pertenece a carreras de f´ormula 1. Si ahora nos fijamos en la posici´on final, correspondiente a la co-
36 CAP´ ITULO 5. PREDICCIONES. lumna position, observamos en primer lugar que el m´aximo que alcanza la posici´on final es 33, n´umero superior al que actualmente estamos acostumbrados a presenciar en estas carreras, ya que lo usual es ver entre 20 y 22 pilotos en las carreras. Adem´as, tambi´en podemos observar que hay muchos valores nulos. Figura 5.5: Summary 500 Millas Podemos pensar que algunos de estos casos se corresponden a los datos perdidos que acompa˜nan a los valores del grid = 0 y muchos otros ser´an los pilotos que no han conseguido terminar la carrera. Observamos datos de carreras de hasta 200 vueltas y vemos que corresponde a las 500 millas de indian´apolis, carrera que no pertenece al campeonato, aunque, en los primeros a˜nos de la f´ormula 1, s´ı que perteneci´o. En 11 temporadas, desde 1950 (inicio de la f´ormula 1) hasta 1960 se consider´o las 500 millas de Indian´apolis como una carrera perteneciente a la competici´on de f´ormula 1, corriendo en ella hasta 55 pilotos como podemos observar en la figura 5.5. Por lo tanto, resulta evidente considerar nuestro conjunto de datos, sin la informaci´on correspondiente a las 500 millas de Indian´apolis, ya que ´esta informaci´on difiere mucho de la proporcionan el resto de carreras de f´ormula 1, lo que no har´ıa m´as que empeorar los resultados de su modelo a consecuencia de datos que no son de inter´es para el caso relevante que nos gustar´ıa resolver a futuro, como es predecir los resultados de las pr´oximas carreras. Vamos ahora a seguir estudiando el conjunto de datos sin la informaci´on de estas carreras. Como vimos en la figura 5.4 existen carreras en las que no hay informaci´on de las posiciones de salida (grid). Figura 5.6: Summary grid=0 Observamos que la mayor´ıa corresponden a las que tampoco tienen informaci´on en la posici´on final, es decir, son datos perdidos. Sin embargo, por los valores perdidos en la posici´on final de la figura 5.4 y los que tenemos en ´esta figura 5.6 la diferencia es muy grande, por lo que ´estos valores perdidos en la posici´on corresponder´an a los pilotos que no han terminado la carrera. Vamos a tomar nuestros datos sin la informaci´on de las observaciones que tienen la variable grid = 0, y vamos a estudiar ahora c´omo vamos a tratar los valores faltantes en la posici´on final. Para tratar los valores faltantes en la posici´on final, tenemos que tener en cuenta el n´umero de pilotos que hay en la posici´on de salida. Nos sigue llamando la atenci´on el n´umero de pilotos que hay en parrilla ya que en algunas
5.1. AN ´ ALISIS EXPLORATORIO DE LOS DATOS. 37 temporadas era muy superior al que estamos acostumbrados a presenciar actualmente, no es muy usual que corran m´as de 24 pilotos. Por este motivo vamos a estudiar el n´umero de pilotos que compitieron en cada temporada. Figura 5.7: Summary resultados Figura 5.8: Numero de pilotos hasta 1972 Observamos que en las primeras temporadas, el n´umero de pilotos que disputa cada carrera var´ıa bastante. Adem´as, el n´umero de carreras en cada temporada es mucho menor que el n´umero de carreras que se disputan actualmente en cada temporada. En la figura 5.9 podemos observar c´omo siguen oscilando el n´umero de pilotos en las diferentes carreras de una temporada. Sin embargo, a partir de 1988 parece estabilizarse este n´umero aunque sigue siendo muy superior al que estamos acostumbrados a presenciar. Tambi´en podemos ver en la figura 5.10 c´omo el n´umero de pilotos parece estabilizarse entre el 1996 y 1997, tomando como m´aximo a partir de esos a˜nos 24 pilotos. Figura 5.9: N´umero de pilotos hasta 1995
38 CAP´ ITULO 5. PREDICCIONES. Figura 5.10: N´umero de pilotos hasta 2021 Para no perder informaci´on de todas las carreras en las que el n´umero de pilotos no est´a estabilizado, vamos a trabajar en lugar de la posici´on absoluta (position), con la posici´on relativa, definiendo ´esta como : posici´on relativa = n´umero de pilotos por delante n´umero de pilotos totales de partida (5.1.1) Donde n´umero de pilotos por delante = posici´on absoluta −1. De ´esta manera, la posici´on relativa var´ıa en el intervalo [0,1). As´ı, vamos a imputar los valores NA de los pilotos que no han acabado la carrera por 1, por tanto, nuestra variable respuesta posici´on relativa (pos relativa) estar´a en el intervalo [0,1]. En consecuencia, en nuestros modelos vamos a utilizar como variable respuesta esta variable posici´on relativa en lugar de la posici´on final original o posici´on absoluta. Vamos a comparar por consiguiente las distribuciones de ambas variables: (a) Distribuci´on position (b) Distribuci´on pos relativa Figura 5.11: Distribuciones
5.2. ESTUDIO DE LAS VARIABLES. 39 Para la variable original, hemos imputado los valores NA de los pilotos que no han acabado la carrera por 25, y por lo que observamos ambas distribuciones tienen la misma forma. En ambas podemos ver c´omo la mayor´ıa de datos se distribuyen entre los primeros puestos, teniendo una bajada brusca pr´acticamente hacia la mitad, y, volviendo a distribuirse una gran cantidad aunque pr´acticamente en los que no terminaron la carrera 5.2. Estudio de las variables. 5.2.1. Primera prueba. En esta primera prueba, vamos a considerar todos los modelos incluyendo como variables cualitativas cada conductor individual (driverId) y cada constructor individual (constructorId), a´un a riesgo de que la cantidad de valores distintos para estas variables sea demasiado grande para arrojar los mejores resultados. Al tener variables factor, vamos a codificarlas como explicamos en (2.2) por lo que tendremos todas las variables a la misma distancia unas de otras. Pasamos a definir las variables que vamos a utilizar en los modelos de la primera prueba: Posici´on de salida (grid). Es una variable cuantitativa que indica la posici´on desde la que sale cada piloto al inicio de la carrera. Posici´on relativa (pos relativa). Definida en (5.1.1), es una variable cuantitativa, la cual ser´a nuestra variable respuesta. Probabilidad de tener un accidente (pAccidentes). Como en nuestro conjunto de datos, tenemos el estado en el que cada piloto acab´o la carrera. Si ha tenido alg´un accidente en ella queda registrado a trav´es de statusId = 3, por lo que hacemos la proporci´on del n´umero de accidentes de cada piloto en cada circuito entre el n´umero total de veces que cada piloto ha corrido en cada circuito. Probabilidad accidente = Nºde accidentes en cada circuito Nºtotal de veces que ha corrido en cada circuito Tiempo medio por vuelta. Dado que el coche depende del constructor con el que est´e el piloto, resulta evidente calcular tiempo medio que cada piloto ha llevado en cada carrera dependiendo del constructor. Sin embargo, si un piloto cambia de escuder´ıa o incluso llega nuevo a la f´ormula 1, no vamos a tener datos correspondientes a este. Por ello, para poder ayudar a predecir estos casos vamos a calcular: •Hist´orico del piloto (tm p). Variable cuantitativa que indica el tiempo medio en vuelta en general del piloto. •Hist´orico del piloto en cada circuito (tm pci). Variable cuantitativa que indica el tiempo medio por vuelta que lleva cada piloto en cada circuito. •Hist´orico de piloto, circuito y constructor (tmean pcc) . Variable cuantitativa que indica el tiempo medio por vuelta que lleva cada piloto en cada circuito seg´un el constructor.
40 CAP´ ITULO 5. PREDICCIONES. El hist´orico del piloto y el del piloto en cada circuito sobre todo son calculadas para el caso en el que un piloto cambie de escuder´ıa. Por otro lado, para un piloto que llegue nuevo a la f´ormula 1 estas variables estar´ıan vac´ıas por lo que en ese caso valdr´ıan 0 para poder ejecutar el modelo. Paradas en Boxes. Como la estrategia es generalmente una decisi´on de equipo, vamos a tener las variables: •Media de paradas. Variable cuantitativa que indica la media de paradas que cada constructor ha hecho en cada circuito. Resulta evidente que el n´umero de paradas que hace un equipo en cada carrera es un n´umero entero, sin embargo, al calcular la media vamos no vamos a utilizar n´umeros enteros ya que se considera m´as realista dejarlo como n´umero real para conocer entre qu´e n´umeros enteros puede estar el n´umero de paradas de cada equipo. •Media de tiempo (en milisegundos) en paradas. Variable cuantitativa que indica la media del tiempo empleado en paradas cada constructor en cada circuito. En el conjunto de datos no tenemos la informaci´on de las paradas en boxes hasta el a˜no 2011. Para subsanar esta carencia de informaci´on, cuando tenemos valores NA, tenemos dos opciones: imputar estos valores o eliminar las observaciones que corresponden a los valores nulos. Como tenemos la informaci´on para cada circuito, en la pr´actica nos hemos encontrado con algunos casos en los que no tenemos ninguna informaci´on para esta variable, es decir, toda la columna correspondiente a esta variable es NA. Por tanto, para no perder la informaci´on correspondiente a estos circuitos, hemos decidido imputar esta variable por la media de las medias de las paradas en el resto de circuitos. Los resultados que vamos a obtener los compararemos seg´un imputamos los valores nulos o los eliminamos. N´umero de ronda (round). Ronda dentro de la temporada en la que est´a siendo la carrera, estableciendo as´ı el orden en que se ha corrido cada circuito cada a˜no. El proceso de creaci´on de estas variables puede verse en el anexo (A). Veamos ahora la correlaci´on entre estas variables en la figura 5.12. Para ello, necesitamos que las variables sean cuantitativas, por lo que vamos a estudiar la correlaci´on sin las variables driverId, constructorId ya que se tratan como factores. Adem´as, no podemos observar la correlaci´on de las variables de paradas en boxes porque no tenemos toda la informaci´on de ellas. Por tanto, al crear el modelo, imputaremos ´estas. Podemos ver c´omo la variable grid es la que m´as est´a correlada con la posici´on relativa, seguida ´esta de la probabilidad de accidentes. Como era de esperar, las variables explicativas que m´as correladas est´an entre s´ı son las que pertenecen al tiempo medio por vuelta. Cabe se˜nalar que el estudio realizado en este apartado se emplear´a como punto de partida para los primeros modelos que crearemos en el apartado (5.3.1).
5.2. ESTUDIO DE LAS VARIABLES. 41 Figura 5.12: Correlaci´on variables primera prueba 5.2.2. Segunda prueba. Teniendo en cuenta como hemos explicado en (5.2.1) al utilizar en la primera prueba las variables driverId, constructorId y tansformarlas a variables dummy, hemos asumido que todos los pilotos est´an a la misma distancia unos de otros, al igual ocurre con los constructores. Sin embargo, es evidente que los pilotos no se diferencian igualmente entre s´ı, sino que se asemejan m´as a unos que otros, y lo mismo ocurre con los constructores. Vamos a introducir en los modelos de esta segunda prueba, nuevas variables que asemejen un poco m´as a los pilotos entre s´ı y a los constructores y sustituyan a las variables factor driverId, constructorId por aspectos que caractericen su rendimiento, y predigan as´ı en funci´on de las variables que caracterizan a los pilotos y escuder´ıas en lugar de sus nombres concretos. Vamos a definir ahora las variables que vamos a utilizar en la segunda prueba: N´umero de carreras disputadas. Para tener informaci´on sobre los pilotos si cambian de escuder´ıa o llegan nuevos a la f´ormula 1, vamos a crear tres variables diferentes: •Hist´orico del piloto (tc). Variable cuantitativa que indica el n´umero total de carreras que un piloto ha disputado. •Hist´orico del piloto en cada circuito (nv pilo c). Variable cuantitativa que indica el n´umero de veces que ha corrido cada piloto en cada circuito. •Hist´orico del piloto en cada circuito y constructor (nv pilo cc). Variable cuantitativa que indica el n´umero de veces que cada piloto ha corrido en cada circuito seg´un el constructor. Posici´on media relativa de cada piloto en cada circuito (posic m). Variable cuantitativa. Posici´on media relativa de cada piloto en cada circuito con cada constructor. (pos mean) Variable cuantitativa.
48 CAP´ ITULO 5. PREDICCIONES. circuitId Modelo lineal ´ Arbol de regresi´on Random Forest Red Neuronal 16 1.1622922 0.3588544 0.3538483 0.3794301 17 0.2827349 0.2138879 0.2145319 0.2751174 18 0.3674427 0.3033192 0.3102486 0.3408274 19 0.4274022 0.2566275 0.2827733 0.3663935 20 0.6202024 0.3438701 0.3454756 0.3664762 ... 41 0.4934907 0.2584718 0.2556948 0.3056952 42 2.0700000 0.3858824 0.3088661 0.3423219 43 0.5632781 0.3449536 0.3755424 0.3729560 44 0.4195789 0.3195727 0.3070025 0.3599789 45 0.4809598 0.3560218 0.3496178 0.3837303 46 0.4570273 0.3685461 0.3536748 0.3602329 47 0.4967642 0.3099282 0.3197434 0.3175050 48 0.4042456 0.3241792 0.3378339 0.3503938 49 0.5195548 0.3200834 0.3615197 0.3828563 50 0.5873269 0.3681792 0.3516247 0.3615218 51 1.2132723 0.3301631 0.3277892 0.3220150 52 0.5145833 0.3482639 0.4186709 0.3839775 53 0.7560060 0.3066842 0.2999487 0.3651805 54 3.8933333 0.4360000 0.4037358 0.4381045 55 0.5681733 0.3886128 0.3723064 0.3786278 56 0.4230059 0.3193410 0.3544286 0.3573350 57 0.4250000 0.4250000 0.4063309 0.4230417 58 0.5346736 0.3786805 0.3619525 0.4107521 59 0.9037037 0.3053498 0.3975144 0.4067873 60 0.2484472 0.3140528 0.3098744 0.3137788 61 1.1600000 0.4306667 0.5268148 0.4062955 62 1.2062500 0.4147727 0.4872222 0.4189497 ... 68 0.4719746 0.1637215 0.1707966 0.2560298 69 0.3625617 0.2223686 0.2349609 0.2904943 70 0.5571014 0.2170468 0.2242700 0.2942830 71 1.1236375 0.2511333 0.2449945 0.2835508 73 0.3877957 0.3347619 0.2628214 0.3057043 75 0.2344988 0.2202381 0.1899421 0.2193441 76 0.2865894 0.4250000 0.1686818 0.4032588 77 1.3845031 0.3583333 0.1979167 0.3444341 78 0.5363466 0.3000000 0.1775000 0.2400476 Tabla 5.1: M´etricas primera prueba imputando.
5.3. MODELIZACI ´ ON. 49 Teniendo presente que nuestra variable objetivo era la posici´on final que tendremos en la carrera, a partir de la informaci´on sobre piloto, circuito, posici´on de partida e informaci´on calculada del rendimiento de a˜nos anteriores. Adem´as, recordar que esta variable se escal´o para encontrarse en el intervalo [0,1], de modo que cuanto m´as nos acerquemos a 0 m´as cerca estaremos de ganar y cuanto m´as nos acerquemos m´as a 1 m´as cerca estaremos de ser ´ultimos, entendiendo que el 1 se corresponder´a con no haber llegado terminado la carrera. En primer lugar, podemos observar como en el modelo lineal aparecen valores de la m´etrica mayores que 1, lo cual no tiene mucho sentido porque nuestra variable objetivo, como hemos mencionado, toma valores en el intervalo [0,1]. Sin embargo, en el resto de modelos, no obtenemos ning´un valor por encima de 1, con lo c´ual obtenemos una mejora. Comparando el resto de modelos con el modelo lineal, obtenemos una mejora considerable de la m´etrica sobre todo en el ´arbol de regresi´on y el random forest. Figura 5.17: Mae prueba 1. Compar´andolo ahora con los resultados de la red neuronal, en la mayor´ıa de circuitos tenemos un mejor mae en la red neuronal, sin embargo existen una serie de circuitos en el que el mae no mejora del modelo lineal a la red neuronal. Centr´andonos ahora en el ´arbol de regresi´on y el bosque aleatorio, tenemos que hay muchos circuitos en los que el mae es mejor en el bosque aleatorio y el resto es mejor en el ´arbol de regresi´on, habiendo entre ´estos poca diferencias. No obstante aunque el ´arbol diera mejor resultado en alg´un caso, cabe resaltar que es mucho menos fiable, ya que nos basamos en una muestra muy concreta, mientras que el bosque se basa en el consenso entre muchos posibles ´arboles, de modo que lo normal es que capture mejor la funci´on subyacente. Podemos a ver gr´aficamente las m´etricas descritas en la tabla 5.1, en la figura 5.17. Para hacer mejor las comparaciones entre ambos modelos, vamos a limitarnos a los que tienen un mae inferior a 1, figura 5.18. Observamos un claro predominio del valor de la m´etrica del modelo lineal superior a 0.5, en cambio, el valor en los dem´as modelos est´a mayoritariamente por debajo de 0.5. Adem´as podemos destacar tambi´en que en la mayor´ıa de los modelos, la m´etrica del random forest es la mejor, es decir, el modelo es el que mejor aproxima los datos.
50 CAP´ ITULO 5. PREDICCIONES. Figura 5.18: Mae prueba 1 con mae ≤1. Eliminando observaciones. Mediante la t´ecnica de eliminaci´on para las variables de la primera prueba, seleccionando el mejor modelo para la m´etrica, en la siguiente tabla 5.2 recogemos los valores de la m´etrica para cada circuito. circuitId Modelo lineal ´ Arbol de regresi´on Random Forest Red Neuronal 1 0.3359059 0.3228616 0.3141560 0.3373982 2 0.4007878 0.2772628 0.2678032 0.3209511 3 0.3270378 0.2368692 0.2140592 0.2707034 42.1151069 0.2496695 0.2437479 0.2974697 ... 32 1.1004042 0.2338613 0.2673860 0.3231953 34 0.4909143 0.3038076 0.3315712 0.3317786 35 0.3734245 0.2357061 0.2425675 0.2814554 39 56.9502208 0.3254790 0.3341160 0.3488061 68 0.4719746 0.1637215 0.1707966 0.2416170 69 0.3625617 0.2223686 0.2349609 0.2827344 70 0.5571014 0.2170468 0.2242700 0.2862820 71 1.1236375 0.2511333 0.2449945 0.2789395 73 0.3877957 0.3347619 0.2628214 0.3214737 75 0.2344988 0.2202381 0.1899421 0.2172106 76 0.2865894 0.4250000 0.1686818 0.4368259 77 1.3845031 0.3583333 0.1979167 0.3476202 78 0.5363466 0.3000000 0.1775000 0.2954655 Tabla 5.2: M´etricas primera prueba eliminando.
5.3. MODELIZACI ´ ON. 51 Por esta t´ecnica podemos ver de nuevo, que existen valores superiores a 1, adem´as se encuentran ´unicamente en el modelo lineal aunque, evidentemente hay menos debido a que al eliminar las observaciones con valores faltantes, estamos eliminando todas las observaciones correspondientes a muchos circuitos. De esta manera, podemos observar que, ´unicamente los valores superiores a 1, coinciden por ambas t´ecnicas en los circuitos 71 y 77. Tambi´en podemos observar c´omo por las dos t´ecnicas obtenemos los mejores valores en cada modelo en los mismos circuitos, se˜nalando adem´as que toman los mismos valores. Veamos gr´aficamente las m´etricas obtenidas en la tabla 5.2, obviando los circuitos en los que encontramos un valor superior a 1. Figura 5.19: Mae prueba 1 eliminando NA. Como vimos en la figura 5.18 eliminando los valores faltantes ocurre pr´acticamente lo mismo: el modelo de random forest es el que mejor ajusta,seguido del ´arbol de regresi´on y la red neuronal, los cuales toman valores por debajo de 0.5, aunque por la t´ecnica de imputaci´on para estos modelos exist´ıan una serie de valores por encima de este n´umero. En cambio el modelo lineal es el que posee los valores m´as altos, siendo por tanto el que produce peores estimaciones. Circuito como variable. A partir de las variables definidas para la primera prueba adem´as de introducir otra variable categ´orica que hace referencia al circuito, vamos a obtener los valores de la m´etrica para cada modelo seg´un las dos t´ecnicas que hemos venido indicando. Comparando los valores seg´un la t´ecnica podemos observar como para el m´etodo de eliminaci´on ´estos valores son m´as bajos, sin embargo, como ya hemos mencionado antes, estamos perdiendo informaci´on de muchos circuitos.
52 CAP´ ITULO 5. PREDICCIONES. Imputando Modelo Lineal ´ Arbol de regresi´on Random forest Red Neuronal Mae 0.338081908 0.3471096 0.3242174 0.349988909 Eliminando Modelo Lineal ´ Arbol de regresi´on Random forest Red Neuronal Mae 0.26125449 0.3161221 0.2747255 0.31368660 Tambi´en podemos se˜nalar que la m´etrica por el modelo de random forest es la mejor. Por el contrario, la m´etrica que obtenemos por el modelo de red neuronal es la mas alta por lo tanto, es la peor. Vamos a comparar ahora los valores de la m´etrica seg´un la t´ecnica aplicada, figura 5.20 en rojo tenemos los valores para la t´ecnica de imputaci´on y en azul para la t´ecnica de eliminaci´on. Teniendo presente que vamos a perder informaci´on de los circuitos en los que todos los valores a imputar son faltantes. En primer lugar, podemos destacar c´omo en los modelos: lineal, ´arbol de regresi´on y random forest, desde el circuito 68 tienen los mismos valores por las diferentes t´ecnicas. Sin embargo, en la red neuronal, figura 5.20d, obtenemos valores diferentes para todos los circuitos seg´un las dos t´ecnicas. Para el modelo lineal, figura 5.20a no existe un predominio claro de unos mejores valores para una de las dos t´ecnicas. Sin embargo para los modelos de ´arbol de regresi´on y random forest, figuras 5.20b y 5.20c respectivamente. (a) Modelo Lineal. (b) ´ Arbol de regresi´on (c) Random forest. (d) Red Neuronal. Figura 5.20: Comparaci´on por ambos m´etodos.
5.3. MODELIZACI ´ ON. 53 5.3.2. Segunda prueba. A partir de las variables definidas en (5.2.2) vamos a crear los modelos ya mencionados seg´un las t´ecnicas de imputaci´on y de eliminaci´on. As´ı obtendremos los valores de la m´etrica para cada circuito seg´un el modelo y podremos comparar los distintos modelos entre s´ı y tambi´en seg´un las dos t´ecnicas que hemos mencionado. Imputando los datos Mediante la t´ecnica de imputaci´on de datos para las variables de la segunda prueba, seleccionando el mejor modelo para la m´etrica, en la siguiente tabla 5.3 recogemos los valores de la m´etrica para cada circuito. circuitId Modelo lineal ´ Arbol de regresi´on Random Forest Red Neuronal 1 2.111359e-01 0.24786524 0.25504102 0.3189394 2 1.732102e-01 0.19466358 0.18908702 0.2790408 3 1.427927e-01 0.15929420 0.15300239 0.2223470 4 1.623603e-01 0.17041106 0.17981365 0.3075616 5 1.313810e-01 0.13632667 0.14460110 0.2396013 ... 51 1.443249e-02 0.05267801 0.04926875 0.2521010 52 1.586450e-01 0.16472222 0.12042063 0.2751404 53 1.248299e-01 0.16854861 0.16187970 0.3771008 54 2.887160e-16 0.43600000 0.12488889 0.2773349 55 1.033160e-01 0.12132974 0.11894035 0.3574291 56 2.724362e-01 0.26342071 0.25488736 0.3039683 57 3.044955e-16 0.42500000 0.05236111 0.3545027 58 1.632074e-01 0.17760066 0.18458043 0.3728122 59 3.249579e-02 0.12888889 0.06629630 0.3031712 60 1.421615e-16 0.31405280 0.08364389 0.3138402 ... 67 4.256709e-02 0.08111888 0.02547931 0.4239934 68 1.319622e-01 0.14568800 0.12203857 0.2032910 69 1.572314e-01 0.18455992 0.18435245 0.2591208 70 1.817868e-01 0.18350039 0.19498415 0.2424766 71 1.822226e-01 0.17460477 0.19204727 0.2226347 73 2.167360e-01 0.20909787 0.22224007 0.2532103 75 1.231703e-01 0.16916667 0.11673611 0.1893824 76 6.111775e-17 0.42500000 0.05222222 0.3127700 77 8.103550e-17 0.35833333 0.09416667 0.5051613 78 5.230510e-16 0.30000000 0.09416667 0.2411461 Tabla 5.3: M´etricas segunda prueba imputando.
54 CAP´ ITULO 5. PREDICCIONES. En primer lugar, es claro que se observa una mejora clara de la m´etrica en los modelos, pero cabe destacar el modelo lineal ya que ha tenido una mejora significativa, adem´as de no tener valores por encima de 1. Veamos los resultados de esta tabla 5.3 gr´aficamente: Figura 5.21: Mae prueba 2 Observando la figura 5.21, vemos que todos los valores est´an distribuidos en el intervalo (0,0.51), lo cu´al refleja la primera mejora con respecto a la primera prueba ya que hab´ıa m´etricas muy superiores a estos datos. Adem´as de una clara diferencia de los valores a partir de 0.25 ya que la mayor´ıa de los valores superiores a este corte corresponden a las obtenidas a trav´es de la red neuronal y algunas del ´arbol de regresi´on. Si nos centramos en el intervalo (0,0.2) tenemos por la figura 5.22 para los primeros 40 circuitos poseen los valores de la m´etrica de los tres modelos bastante similares. Sin embargo a partir del circuito 40 el valor del modelo lineal mejora significativamente con respecto a los valores del resto de modelos.
5.3. MODELIZACI ´ ON. 55 Figura 5.22: Mae prueba 2 intervalo (0,0.2) Eliminando los datos. Mediante la t´ecnica de eliminaci´on para las variables de la segunda prueba, seleccionando el mejor modelo para la m´etrica, en la siguiente tabla 5.4 recogemos los valores de la m´etrica para cada circuito. circuitId Modelo lineal ´ Arbol de regresi´on Random Forest Red Neuronal 1 2.359260e-01 0.2844211 0.2854072 0.3231279 2 1.870156e-01 0.2067417 0.2311247 0.2674320 3 1.613420e-01 0.1827443 0.1968198 0.2425850 ... 22 1.677383e-01 0.2050564 0.2018900 0.2765533 24 1.525113e-01 0.1563143 0.1769219 0.2265117 32 1.956637e-01 0.2174147 0.2247905 0.3033235 34 1.674692e-01 0.2068621 0.2293820 0.2717639 35 1.928107e-01 0.1949914 0.2042374 0.2879746 39 1.647775e-01 0.1647791 0.2117724 0.2925667 68 1.319622e-01 0.1456880 0.1703384 0.2482515 69 1.572314e-01 0.1845599 0.2057780 0.2734587 70 1.817868e-01 0.1835004 0.2165224 0.2552719 71 1.822226e-01 0.1746048 0.2131029 0.2325707 73 2.167360e-01 0.2090979 0.2203900 0.2575063 75 1.231703e-01 0.1691667 0.1712526 0.1835655 76 6.111775e-17 0.4250000 0.1169048 0.3492632 77 8.103550e-17 0.3583333 0.2473194 0.2801743 78 5.230510e-16 0.3000000 0.1054167 0.2876389 Tabla 5.4: M´etricas segunda prueba elimiando.
56 CAP´ ITULO 5. PREDICCIONES. A diferencia de lo que pasaba en la prueba 1, los mejores valores de los modelos en los circuitos no coinciden en los modelos salvo en el modelo lineal y en la red neuronal. Veamos gr´aficamente los valores obtenidos en la tabla 5.4: Figura 5.23: Mae prueba 2 eliminando NA. Como vimos en la figura 5.22 eliminando los valores faltantes sucede b´asicamente lo mismo: los valores obtenidos por la red neuronal son los mayores aunque ahora la mayor´ıa son menor que 0.4 . Si nos restringuimos a los 40 primeros circuitos, teniendo en cuenta que hay bastantes que son suprimidos podemos observar como los valores por los dem´as modelos son en gran medida iguales. Circuito como variable. A partir de las variables definidas para la segunda prueba adem´as de introducir la variable categ´orica que hace referencia al circuito, vamos a obtener los valores de la m´etrica para cada modelo seg´un las dos t´ecnicas que nos hemos venido refiriendo. Imputando Modelo Lineal ´ Arbol de regresi´on Random forest Red Neuronal Mae 0.1455162 0.1432668 0.2060327 0.34026773 Eliminando Modelo Lineal ´ Arbol de regresi´on Random forest Red Neuronal Mae 0.1735876 0.1767279 0.2113356 0.3128904 Podemos observar como en este caso, comparando con lo obtenido para la primera prueba, los valores por la t´ecnica de imputaci´on son menores que por la t´ecnica de eliminaci´on. Aun as´ı, los valores que obtenemos por la red neuronal siguen siendo los m´as altos comparados con los dem´as modelos.
5.3. MODELIZACI ´ ON. 57 Vamos a comparar ahora los valores de la m´etrica seg´un la t´ecnica aplicada. As´ı, en la figura 5.24 podemos observar en rojo los valores para la t´ecnica de imputaci´on y en azul para la t´ecnica de eliminaci´on. Podemos observar c´omo pr´acticamente ambos valores se asemejan bastante, siendo levemente inferior los valores correspondientes a la t´ecnica de imputaci´on, sobre todo en los tres primeros modelos, es decir salvo en la red neuronal, vemos en general predominar el rojo en zonas m´as bajas que el azul. Aunque en los modelos lineal y ´arbol vemos que ocurre como en la prueba 1 que los valores a partir del circuito 68 son iguales para ambas t´ecnicas. B´asicamente hay que destacar que en casi todos los modelos la imputaci´on ha mejorado los resultados. (a) Modelo Lineal. (b) ´ Arbol de regresi´on (c) Random forest. (d) Red Neuronal. Figura 5.24: Comparaci´on por ambos m´etodos.
Cap´ıtulo 6 Conclusiones. Haciendo una breve recopilaci´on de todo lo que se ha hecho a lo largo de este trabajo, destacando que durante del proyecto nos hemos enfrentado a un caso real de calado, se ha buscado una gran cantidad de conjuntos de datos relacionados con la tem´atica, se ha profundizado mucho en labores de limpieza y tratamiento de datos, as´ı como an´alisis exploratorio de los datos, ingenier´ıa de caracter´ısticas y modelizaci´on con distintas t´ecnicas para modelos de regresi´on. Destacar adem´as el empleo de tidyverse y tidymodels, incluyendo el manejo de funciones avanzadas de orden superior con purrr para generar en bloque todos los modelos de cada circuito, y la gran cantidad de agregaciones de datos obtenidas a muchos niveles. Para ambos modelos, sobre todo los de la segunda prueba, hemos calculado muchas variables que acumulan resultados promedio de los pilotos en sus carreras anteriores, generalmente con la misma escuder´ıa, en el mismo circuito, ... No obstante, ser´ıa interesante tener adem´as alguna variable que indique si el piloto en una escuder´ıa, en un circuito est´a mejorando, empeorando o est´a estancado al cabo de los a˜nos y as´ı aunque alg´un piloto que no lleve un buen hist´orico, que es lo que principalmente usamos en el modelo, con esta nueva variable estar´ıamos capturando su tendencia. Por otro lado, al igual que le hemos hecho a la variable respuesta la transformaci´on para trabajar con la posici´on relativa, reflexionando sobre ello, no es lo mismo salir el 20 de 22 que el 20 de 50. De modo que, parece razonable aplicar esta misma transformaci´on a la variable posici´on de salida (grid). Ante todo, cabe notar algo que pod´ıamos intuir, y que lo tenemos a´un m´as presente tras el desarrollo de este trabajo, es que resulta complicado predecir con exactitud las posiciones en las carreras de f´ormula 1, debido a una gran variedad de factores externos los cuales son impredecibles ya sea por la fiabilidad de los coches, los accidentes y colisiones, o simplemente por el estado del tiempo. El factor que m´as puede intervenir en ´esto es la fiabilidad del coche, ya que es imposible saber c´uando va a fallar un coche y menos a´un por qu´e, porque adem´as es algo muy variable de un a˜no para otro, con cambios de normativas y constantes adaptaciones por parte de los equipos de ingenieros de las escuder´ıas. Un fallo inesperado en el coche de alg´un piloto que iba a quedar en alguna posici´on l´ogica para su rendimiento, hace que otros pilotos que podr´ıa predecir bien nuestros modelos, queden en posiciones en las que inicialmente no iban a estar. 65
66 CAP´ ITULO 6. CONCLUSIONES. As´ı mismo, existen otros factores de los cuales no tenemos informaci´on en nuestra base de datos, que pueden influir en la posici´on final y que son sustanciales en las estrategias de los equipos. Por ejemplo, factor que influye mucho en la posici´on final y que profundizan mucho en los equipos en cada carrera, es el n´umero de paradas que deben hacer y sobre todo en qu´e momento de la carrera hacerlas, dado que ´esto influye bastante en la posici´on en la que puedes quedar. Pero para ello, necesitar´ıamos m´as informaci´on de lo que ocurre en las carreras, como el tipo de neum´atico con el que salen, la degradaci´on de la pista, si ha habido un coche de seguridad o no, si empieza a llover, etc. ´ Estos datos no los tenemos por lo que para un trabajo futuro estar´ıa perfecto poder encontrar alguna base de datos con este tipo de informaci´on para estudiar a fondo este factor tan interesante. No obstante, a partir de la informaci´on de la que disponemos hemos podido realizar un gr´afico aproximado, figura 6.1, del intervalo de vueltas en las que se puede realizar cada parada en una serie de circuitos. Figura 6.1: N´umero de parada seg´un la vuelta. De ´estos gr´aficos podemos destacar que en la mayor´ıa de los circuitos la primera parada se realiza entre las 25 primeras vueltas del circuito. Ahora bien, como hemos mencionado, esta informaci´on no es concluyente, ya que no conocemos el tipo de neum´atico con que se corr´ıa, ni las condiciones clim´aticas. Tambi´en podemos notar c´omo los que terminan en las primeras posiciones hacen menos paradas que el resto de pilotos. Otro punto importante a destacar es la predicci´on de pilotos que no van a terminar la carrera, objetivo que parece muy complicado porque resulta inviable saber que le va a pasar al coche, o si en cualquier choque va a provocarle algo que le impida seguir.
67 Est´a claro, como podemos ver en la figura 6.2, que la mayor´ıa de accidentes, en concre- to las colisiones hacen que un piloto no pueda terminar la carrera, en esta figura estamos comparando la posici´on de salida con la posici´on relativa seg´un un piloto ha tenido accidente o una colisi´on. No obstante, ¿es suficiente con eso? Figura 6.2: Importancia colisiones Puede que no, ya que existen una cantidad considerable de acontecimientos que pueden convertirse en fatales para que un piloto no pueda acabar una carrera. Luego, una soluci´on que hemos pensado para un trabajo futuro, es poder realizar un modelo de clasificaci´on (3.1.2) con variables relacionadas con posibles factores que influyan en acabar o no la carrera, de forma que la variable respuesta ser´a acaba o no. Con respecto a los modelos implementados, todo su desarrollo se fue haciendo con tidymodels [11], con gran carga de procesamiento bien estructurado para usar ese ecosistema, y se aprovech´o la estructura para incorporar tambi´en el modelo de red neuronal siguiendo el mismo esquema. Si nos centramos en la red neuronal introducida tenemos que tidymodels solo nos da opci´on a utilizar una red neuronal con una sola capa oculta, lo cu´al nos restringe considerablemente los resultados obtenidos. Por tanto, un trabajo futuro ser´a, cuando las opciones para la red neuronal est´en m´as desarrolladas para tidymodels, para poder hacer m´as pruebas con este tipo de modelos. Otra posibilidad ser´ıa emplear Keras, bien en Python o desde el propio R y as´ı poder implementar una red neuronal m´as compleja para poder contrastar los resultados Profundizando tambi´en en la idea de la creaci´on de modelos en los cuales tomen parte pilotos que no han participado en ninguna carrera de f´ormula 1, la perspectiva que le podemos dar es utilizar la informaci´on que podamos recopilar a trav´es de bases de datos
68 CAP´ ITULO 6. CONCLUSIONES. de las categor´ıas inferiores para conocer mejor sus cualidades y poder aprovecharlas en los modelos. Por otro lado, con respecto a los modelos que contienen circuitos para mejorar las predicciones de ´estos, podr´ıamos crear modelos de aprendizaje no supervisado (3.2) para, a partir de las caracter´ısticas de los circuitos: n´umero de curvas, tipo de curvas, distancia total, tipos de neum´aticos m´as adecuados al circuito, etc. Poder crear diferentes clusters para diferenciar los circuitos en determinados tipos, los cuales posean caracter´ısticas similares como consecuencia en cada tipo diferente de circuitos podr´ıa tener el coche caracter´ısticas diferentes para adecuarse mejor a ´el. De esta manera, tambi´en podremos realizar un estudio de los circuitos en los que son mejores cada piloto o cada escuder´ıa, y pudiendo crear posteriormente nuevas relaciones entre los pilotos los circuitos que tienen en com´un.
Cap´ıtulo 7 Bibliograf´ıa [1] Zhi-Hua Zhou, Machine Learning. [2] Max Kuhn ,Kjell Johnson,Applied Predictive Modeling. [3] Shan Suthaharan,Machine Learning Models and Algorithms for Big Data Classification. [4] Tom M. Mitchell,Machine Learning. [5] ethem alpaydin,Introduction to Machine Learning. [6] Du, K.L. ,Swamy, M.N.S. ,Neural Networks and Statistical Learning. [7] http://www.cs.us.es/cursos/siis-2018/temas/tema-07-siis-2018-19.pdf [8] https://www.iartificial.net [9] https://docplayer.es/210640950-Modelos-predictivos-con-aplicacion-en- el.html [10] https://www.kaggle.com/datasets/rohanrao/formula-1-world- championship-1950-2020?select=lap_times.csv [11] https://www.r-project.org [12] https://www.tidymodels.org [13] https://ggplot2.tidyverse.org/index.html [14] https://rpubs.com [15] https://r4ds.had.co.nz/data-visualisation.html [16] https://fiaresultsandstatistics.motorsportstats.com 69
70 CAP´ ITULO 7. BIBLIOGRAF´ IA
Parte V Anexos. 71
Anexo A Primera prueba. En este anexo vamos a realizar en primer lugar la lectura y el preprocesamiento de los datos. A continuaci´on, crearemos las variables que vamos a utilizar en la primera prueba, definiendo consigo los conjunto de datos para la modelizaci´on. Por ´ultimo, creamos los modelos con las variables definidas anteriormente con las t´ecnicas que ya hemos mencionado, realizando a su vez comparaciones en cada modelo seg´un la t´ecnica. 73
conjunto %>% select(tm_p,tm_pci,tmean_pcc) %>% summary Ahora no obtenemos NA Vamos a tratar las variables driverId y constructorId como factores. conjunto$driverId=as.factor(conjunto$driverId) conjunto$constructorId=as.factor(conjunto$constructorId) Vamos a definir el conjunto de datos sin valores NA, para poder comparar. conjunto2 =conjunto %>% na.omit() Vamos a calcular, la media de las medias de las paradas y del tiempo en paradas, para poder imputar los valores de los circuitos de los que no tenemos información sobre estas variables. med_cir=conjunto2 %>% group_by(circuitId) %>% summarise( med_p=mean(stop_mean), med_tp=mean(timestop_mean)) medTotal=med_cir %>% summarise(med_p=mean(med_p),med_tp=mean(med_tp)) n_circuitos=conjunto%>% group_by(circuitId) %>% nest() ## Comprobamos que toda la columna que queriamos imputar tiene NA ncir2=n_circuitos %>% mutate( tieneNA= data %>% map(~pull(.x,stop_mean) %>% is.na(.) %>% all())) n_circuitos =ncir2 %>% unnest() %>% mutate( stop_mean = ifelse(tieneNA==TRUE,medTotal$med_p,stop_mean), timestop_mean = ifelse(tieneNA==TRUE,medTotal$med_tp,timestop_mean) ) %>% select(-c(tieneNA)) %>% group_by(circuitId) %>% nest() Modelo lineal IMPUTACION KNN Defino la función que realiza un modelo lineal de los datos. lmc_fun <- function (x) { x=x %>% mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId)) ) # 1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) 7
nc_split<- initial_split(as.data.frame(x),prop = 0.70, strata = pos_relativa,breaks = 4) entnc <- nc_split%>% training() testnc <- nc_split %>% testing() #2ºdefinimos el modelo lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") # 3ºCreamos la receta nc_recipe <- recipe(pos_relativa ~.,data=entnc) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos el workflow nc_workflow <- workflow() %>% add_model(lm_model) %>% add_recipe(nc_recipe) # 5ºEstimamos el modelo. nclm_fit <- nc_workflow %>% last_fit(split = nc_split, metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(nc_split),model = list(nclm_fit), metrics= list(nclm_fit %>% collect_metrics()),predictions = list(nclm_fit %>% collect_predictions())) t } Aplico el modelo lineal a los datos de cada circuito. m_nc <- n_circuitos %>% mutate(res=map(data,lmc_fun)) nc2=m_nc %>% unnest(res) mae_mlc=nc2 %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_mlc circuitos como variable ¿Qué ocurre si nos encontramos con un circuito completamente nuevo ? al no tener datos de temporadas anteriores, por el método anterior sería imposible hacer alguna predicción, por lo que vamos a calcular un nuevo modelo en el que le introducimos como variable el circuito en el que se corre. Esta variable la vamos a tratar como factor. 8
conjunto_c =n_circuitos %>% unnest() conjunto_c=conjunto_c %>% mutate(circuitId=as.factor(circuitId)) #1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) c_split<- initial_split(conjunto_c,prop = 0.70,strata = pos_relativa,breaks=4) c_ent <- c_split%>% training() c_test<-c_split %>% testing() # 2ºDefinimos el modelo. lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") # 3ºCreamos la receta. c_recipe <- recipe(pos_relativa ~.,data=c_ent) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(circuitId,driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos un WorkFlow c_workflow <- workflow() %>% add_model(lm_model) %>% add_recipe(c_recipe) #5ºEstimamos el modelo. c_fit <- c_workflow %>% last_fit(split = c_split, metrics=metric_set(mae,rmse,rsq)) met_mlcir=c_fit %>% collect_metrics() met_mlcir ELIMINANDO NA’S Vamos a eliminar todas las observaciones que contienen alguna variable con NA n_circuitos_na=conjunto2%>% group_by(circuitId) %>% nest() lmc_fun_na <- function (x) { x=x %>% 9
mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId)) ) # 1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) nc_split<- initial_split(as.data.frame(x),prop = 0.70,strata = pos_relativa,breaks = 4) entnc <- nc_split%>% training() testnc <- nc_split %>% testing() #2ºdefinimos el modelo lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") #3ºCreamos la receta. nc_recipe<- recipe(pos_relativa ~.,data=entnc) %>% step_novel(driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #4ºCreamos el Workflow. nc_workflow <- workflow() %>% add_model(lm_model) %>% add_recipe(nc_recipe) #5ºEstimamos el modelo. nclm_fit <- nc_workflow %>% last_fit(split = nc_split, metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(nc_split),model = list(nclm_fit), metrics= list(nclm_fit %>% collect_metrics()), predictions = list(nclm_fit %>% collect_predictions())) t } m_nc_na<- n_circuitos_na %>% mutate(res=map(data,lmc_fun_na)) nc2_na=m_nc_na %>% unnest(res) mae_ml_na=nc2_na %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_ml_na 10
circuitos como variable 1ºCreamos una partición entre conjunto test y entrenamiento conjunto2_c=conjunto2 %>% mutate(circuitId=as.factor(circuitId)) #1ºCreamos una división entre entrenamiento y test. set.seed(1234) nc_split_na<- initial_split(conjunto2_c,prop = 0.70, strata = pos_relativa,breaks=4) nc_ent_na <- nc_split_na%>% training() nc_test_na<- nc_split_na%>% testing() # 2ºDefinimos el modelo. lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") # 3ºCreamos la receta. nc_recipe_na <- recipe(pos_relativa ~.,data=nc_ent_na) %>% step_novel(circuitId,driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos un WorkFlow nc_workflow_na<- workflow() %>% add_model(lm_model) %>% add_recipe(nc_recipe_na) # 5ºEstimamos el modelo. nc_fit_na <- nc_workflow_na %>% last_fit(split = nc_split_na, metrics=metric_set(mae,rmse,rsq)) met_mlcir_na =nc_fit_na %>% collect_metrics() met_mlcir_na Observamos que le mae ha mejorado levemente con respecto al que obtenemos con la imputación de los datos, sin embargo, hemos perdido la información de bastantes circuitos. Comparacion Vamos a comparar los valores de la métrica según ambas técnicas. ml_metrics=inner_join(mae_mlc,mae_ml_na,by="circuitId") %>% rename( mae_ml=.estimate.x, mae_ml_na=.estimate.y ) 11
ml_metrics=ml_metrics %>% gather(`mae_ml`,`mae_ml_na`,key = "mae",value = "estimacion")%>% filter(estimacion <= 1) ml_metrics$mae=as.factor(ml_metrics$mae) ml_metrics %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Comparación MAE Modelo Lineal") Árbol de decisión IMPUTACION KNN tree_fun <- function(x){ x=x %>% mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId)) ) set.seed(1234) # 1ºDividimos en entrenamiento y test. tree_split<- initial_split(as.data.frame(x),prop = 0.70,strata = pos_relativa,breaks=4) tree_ent <- tree_split%>% training() tree_test <- tree_split %>% testing() #2ºDefinimos el modelo: Indicamos los parámetros que queremos ajustar. ar_fc <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") # 3ºDefinimos el grid: valores que pueden tomar los parametros. tree_grid <- grid_regular(cost_complexity(), tree_depth(range = c(1,30)), levels = 5) # 4ºValidación cruzada set.seed(1234) cell_folds <- vfold_cv(data = tree_ent, v=5, strata = pos_relativa) # 5ºDefinimos la receta. tree_recipe<- recipe(pos_relativa ~. , data=tree_ent) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(driverId,constructorId) %>% step_dummy(all_nominal(), -all_outcomes()) %>% step_zv(all_numeric(), -all_outcomes()) %>% step_center(all_numeric(), -all_outcomes()) %>% step_scale(all_numeric(), -all_outcomes()) %>% step_corr(all_predictors()) %>% 12
step_lincomb(all_predictors()) # 6ºCreamos el Workflow set.seed(1234) tree_wf <- workflow() %>% add_model(ar_fc) %>% add_recipe(tree_recipe) # 7ºAjustamos el modelo a traves del grid. tree_res <- tune_grid( object = tree_wf, resamples = cell_folds, metrics=metric_set(mae,rmse,rsq), grid = tree_grid ) # 8ºSeleccionamos el mejor modelo best_tree <- tree_res %>% select_best("mae") final_wf <- tree_wf %>% finalize_workflow(best_tree) # 9ºEjecutamos el mejor modelo. final_fit <- final_wf %>% last_fit(tree_split,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(tree_split),model = list(final_fit), metrics= list(final_fit %>% collect_metrics()), predictions = list(final_fit %>% collect_predictions())) t } m_nc_tree<- n_circuitos %>% mutate(res=map(data,tree_fun)) nc2_tree=m_nc_tree %>% unnest(res) mae_tree=nc2_tree %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_tree Se observa una mejora considerable y se ha solucionado el problema de obtener métricas por encima de 1. Circuitos como variables. #1ºCreamos una particion entre entrenamiento y test. #2ºDefinimos el modelo. Indicamos los parámetros que queremos ajustar. 13
ar_fc <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid. tree_grid <- grid_regular(cost_complexity(range=c(0,0.5)), tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada set.seed(1234) cell_cfolds <- vfold_cv(data = c_ent, v=5, strata = pos_relativa) #5ºDefinimos la receta. c_recipe_tree <- recipe(pos_relativa ~.,data=c_ent) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(circuitId,driverId,constructorId) %>% step_dummy(all_nominal(), -all_outcomes()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºDefinimos el workflow set.seed(1234) c_tree_wf <- workflow() %>% add_model(ar_fc) %>% add_recipe(c_recipe_tree) #7ºAjustamos el modelo. c_tree_res <- tune_grid( object = c_tree_wf, resamples = cell_cfolds, metrics=metric_set(mae,rmse,rsq), grid = tree_grid ) #8ºSeleccionamos el mejor modelo. best_treeC<- c_tree_res %>% select_best("mae") 14
final_wfC<- c_tree_wf %>% finalize_workflow(best_treeC) #9ºEjecutamos el mejor modelo. final_fitC<- final_wfC%>% last_fit(c_split,metrics=metric_set(mae,rmse,rsq)) met_treecir=final_fitC%>% collect_metrics() met_treecir ELIMINANDO NA tree_fun_na <- function(x){ x=x %>% mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId))) #1ºCreamos una division entre entrenamiento y test. set.seed(1234) tree_split<- initial_split(as.data.frame(x),prop = 0.70, strata = pos_relativa,breaks=4) tree_ent <- tree_split%>% training() tree_test <- tree_split %>% testing() #2ºDefinimos el modelo, indicando los parámetros a ajustar. ar_fc <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid. tree_grid <- grid_regular(cost_complexity(), tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada. set.seed(1234) cell_folds <- vfold_cv(data = tree_ent, v=5, strata = pos_relativa) #5ºDefinimos la receta recipe_tree_na<- recipe(pos_relativa ~. , data=tree_ent) %>% step_novel(driverId,constructorId) %>% step_dummy(all_predictors()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% 15
step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºCreamos el workflow. set.seed(1234) tree_wf <- workflow() %>% add_model(ar_fc) %>% add_recipe(recipe_tree_na) #7ºAjustamos el modelo. tree_res <- tune_grid( object = tree_wf, resamples = cell_folds, metrics=metric_set(mae,rmse,rsq), grid = tree_grid ) #8ºSeleccionamos el mejor modelo. best_tree <- tree_res %>% select_best("mae") final_wf <- tree_wf %>% finalize_workflow(best_tree) #9ºEstimamos el mejor modelo. final_fit <- final_wf %>% last_fit(tree_split,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(tree_split),model = list(final_fit), metrics= list(final_fit %>% collect_metrics()), predictions = list(final_fit %>% collect_predictions())) t } m_nc_tree_na<- n_circuitos_na %>% mutate(res=map(data,tree_fun_na)) nc2_tree_na=m_nc_tree_na %>% unnest(res) mae_tree_na=nc2_tree_na %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_tree_na Circuitos como variables. #1ºCreamos una dicision entre entrenamiento y test. #2ºDefinimos el modelo.Indicamos los parámetros que queremos ajustar. 16
final_frst_wf <- forest_wf_na %>% finalize_workflow(best_forest_na) #9ºEstimamos el modelo. forest_fit<- final_frst_wf%>% last_fit(split = forest_split,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(forest_split),model = list(forest_fit), metrics= list(forest_fit %>% collect_metrics()), predictions = list(forest_fit %>% collect_predictions())) t } m_nc_forest_na<- n_circuitos_na %>% mutate(res=map(data,forest_fun_na)) nc2_forest_na=m_nc_forest_na %>% unnest(res) mae_forest_na=nc2_forest_na%>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_forest_na Circuitos como variables #1ºCreamos una particion entre entrenamiento y test #2ºDefinimos el modelo. #Indicamos los parámetros que queremos ajustar. forest_fc <- rand_forest( mtry = tune(), trees = tune(), min_n=tune() ) %>% set_engine("ranger") %>% set_mode("regression") #3ºDefinimos el grid. forest_gridc <- expand_grid(mtry=1:5, trees= seq(10,40,5), min_n=seq(1,10,2)) #4ºValidación cruzada set.seed(1234) forest_folds_cna <- vfold_cv(data =nc_ent_na, v=5, strata = pos_relativa) #5ºDefinimos la receta. 23
recipe_forest_cna <- recipe(pos_relativa ~.,data=nc_ent_na) %>% step_novel(driverId,constructorId,circuitId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 6ºDefinimos el workflow set.seed(1234) forest_wf_cna<- workflow() %>% add_model(forest_fc) %>% add_recipe(recipe_forest_cna) #7ºAjustamos el modelo. forest_res_cna <- tune_grid( object = forest_wf_cna, resamples = forest_folds_cna, metrics=metric_set(mae,rmse,rsq), grid = forest_gridc ) #8ºSeleccionamos el mejor modelo. best_forest_cna <- forest_res_cna %>% select_best("mae") final_wf_crfna <- forest_wf_cna %>% finalize_workflow(best_forest_cna) #9ºEstimamos final_fit_cna <- final_wf_crfna %>% last_fit(nc_split_na,metrics=metric_set(mae,rmse,rsq)) mae_for_cna=final_fit_cna%>% collect_metrics() mae_for_cna Comparacion Vamos a comparar los valores de la métrica según ambas técnicas. forest_metrics=inner_join(mae_forest,mae_forest_na,by="circuitId") %>% rename( mae_forest=.estimate.x, mae_forest_na=.estimate.y ) forest_metrics=forest_metrics %>% gather(`mae_forest`,`mae_forest_na`,key = "mae",value = "estimacion") forest_metrics$mae=as.factor(forest_metrics$mae) 24
forest_metrics %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Comparación MAE Random Forest") Red Neuronal. Imputacion KNN rn_function=function(x){ x=x%>% mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId)) ) # 1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) rn_split<- initial_split(as.data.frame(x),prop = 0.70, strata = pos_relativa,breaks = 4) entrn <- rn_split%>% training() testnc <- rn_split %>% testing() # 2ºCreamos la receta rn_recipe <- recipe(pos_relativa ~.,data=entrn) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #3ºdefinimos el modelo rn_model <- mlp( hidden_units=50, epochs =10, ) %>% set_engine("brulee") %>% set_mode("regression") rn_workflow <- workflow() %>% add_model(rn_model) %>% add_recipe(rn_recipe) rn_fit <- rn_workflow %>% last_fit(split = rn_split, metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(rn_split),model = list(rn_fit), metrics= list(rn_fit %>% collect_metrics()), predictions = list(rn_fit %>% collect_predictions())) t 25
} m_nc_rn<- n_circuitos %>% mutate(res=map(data,rn_function)) nc2_rn=m_nc_rn %>% unnest(res) mae_rn=nc2_rn %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_rn Circuito como variable. #1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) c_split<- initial_split(conjunto_c,prop = 0.70, strata = pos_relativa,breaks=4) c_ent <- c_split%>% training() c_test<-c_split %>% testing() # 2ºCreamos la receta. c_recipe <- recipe(pos_relativa ~.,data=c_ent) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(circuitId,driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos un WorkFlow rn_model <- mlp( hidden_units=50, epochs =10, ) %>% set_engine("brulee") %>% set_mode("regression") crn_workflow <- workflow() %>% add_model(rn_model) %>% add_recipe(c_recipe) #5ºEjecutamos crn_fit <- crn_workflow %>% 26
last_fit(split = c_split, metrics=metric_set(mae,rmse,rsq)) met_rncir=crn_fit %>% collect_metrics() met_rncir Eliminando NA rn_function_na=function(x){ x=x%>% mutate( driverId=factor(as.numeric(driverId)), constructorId=factor(as.numeric(constructorId)) ) # 1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) rn_split<- initial_split(as.data.frame(x),prop = 0.70, strata = pos_relativa,breaks = 4) entrn <- rn_split%>% training() testnc <- rn_split %>% testing() # 2ºCreamos la receta rn_recipe <- recipe(pos_relativa ~.,data=entrn) %>% step_novel(driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #3ºdefinimos el modelo rn_model <- mlp( hidden_units=50, epochs =10, ) %>% set_engine("brulee") %>% set_mode("regression") rn_workflow <- workflow() %>% add_model(rn_model) %>% add_recipe(rn_recipe) rn_fit <- rn_workflow %>% last_fit(split = rn_split, metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(rn_split),model = list(rn_fit), metrics= list(rn_fit %>% collect_metrics()), predictions = list(rn_fit %>% collect_predictions())) t } 27
m_nc_rn_na<- n_circuitos_na %>% mutate(res=map(data,rn_function_na)) nc2_rn_na=m_nc_rn_na %>% unnest(res) mae_rn_na=nc2_rn_na %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_rn_na Circuito como variables set.seed(1234) nc_split_na<- initial_split(conjunto2_c,prop = 0.70, strata = pos_relativa,breaks=4) nc_ent_na <- nc_split_na%>% training() nc_test_na<- nc_split_na%>% testing() # 2ºCreamos la receta. nc_recipe_na <- recipe(pos_relativa ~.,data=nc_ent_na) %>% step_novel(circuitId,driverId,constructorId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos un WorkFlow rn_model <- mlp( hidden_units=50, epochs =10, ) %>% set_engine("brulee") %>% set_mode("regression") rn_workflow_na<- workflow() %>% add_model(rn_model) %>% add_recipe(nc_recipe_na) # 5ºEjecutamos rn_fit_na <- rn_workflow_na %>% last_fit(split = nc_split_na, metrics=metric_set(mae,rmse,rsq)) met_rncir_na =rn_fit_na %>% collect_metrics() met_rncir_na 28
Comparacion Vamos a comparar los valores de la métrica según ambas técnicas. rn_metrics=inner_join(mae_rn,mae_rn_na,by="circuitId") %>% rename( mae_rn=.estimate.x, mae_rn_na=.estimate.y ) rn_metrics=rn_metrics %>% gather(`mae_rn`,`mae_rn_na`,key = "mae",value = "estimacion") rn_metrics$mae=as.factor(rn_metrics$mae) rn_metrics %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Comparación MAE Red Neuronal") 29
Anexo B Segunda prueba. En este anexo vamos a crear en primer lugar las variables que vamos a utilizar en la segunda prueba, definiendo consigo los conjunto de datos para la modelizaci´on. Por ´ultimo, creamos los modelos con las variables definidas anteriormente con las t´ecnicas que ya hemos mencionado, realizando a su vez comparaciones en cada modelo seg´un la t´ecnica. 103
Segunda prueba. Vamos a introducir en los modelos, nuevas variables que asemejen un poco más a los pilotos entre sí y a los constructores. Vamos a sustiturir las variables categóricas driverId, constructorId por aspectos que caractericen su rendimiento, y predigan así en función de las variables que caracterizan a los pilotos y escuderías en lugar de sus nombres concretos. PILOTOS Numero total de carreras Numero total de grandes premios que ha disputado cada piloto. nc_pilo =nv_pilo_cc %>% group_by(driverId) %>% summarise(tc= sum(totalc)) Posición media relativa piloto. Posición media relativa piloto circuito constructor. Posicion media en la que quedó en cada circuito según el constructor. posm_piloc =datos_m %>% group_by(circuitId,driverId,constructorId) %>% summarise(pos_mean= mean(pos_relativa)) Posición media relativa piloto circuito. posm_pilo =datos_m %>% group_by(circuitId,driverId) %>% summarise(posic_m= mean(pos_relativa)) Probabilidad de hacer podium piloto. Probabilidad hacer podium piloto circuito constructor Según el constructor. En primer lugar, vamos a calcular el número de podiums que ha hecho cada piloto con cada constructor en cada circuito. pod_prob =datos_m %>% filter(position <= 3) %>% group_by(circuitId,driverId,constructorId) %>% summarise(pod_nc=n()) Ahora vemos el número de veces que ha corrido en cada circuito con cada constructor y hacemos la proporción. pod_prob=left_join(pod_prob,nv_pilo_cc,by=c("circuitId","driverId","constructorId")) pod_prob=pod_prob %>% mutate(prob=pod_nc/totalc) %>% select(-c(pod_nc,totalc)) 1
Probabilidad de hacer podium piloto circuito En primer lugar, calculamos el numero de podiums que ha conseguido cada piloto en cada circuito. pod_prob2 =datos_m %>% filter(position <= 3) %>% group_by(circuitId,driverId) %>% summarise(pod_nc2=n()) Ahora tomo el número de veces que ha corrido en cada circuito y hacemos la proporción. pod_prob2 =left_join(pod_prob2,nv_pilo_c,by=c("circuitId","driverId")) pod_prob2=pod_prob2 %>% mutate(probc= pod_nc2/tveces) %>% select(-c(pod_nc2,tveces)) Probabilidad de hacer podium piloto. Calculamos el número de podiums que ha hecho un piloto en lo que lleva pod_prob3 =datos_m %>% filter(position <= 3) %>% group_by(driverId) %>% summarise(pod_p=n()) Ahora tomo el número de carreras que ha disputado y hago la proporción. pod_prob3 =left_join(pod_prob3,nc_pilo,by=c("driverId")) pod_prob3=pod_prob3 %>% mutate(prob_p= pod_p/tc) %>% select(-c(pod_p,tc)) CONSTRUCTORES. const_c=left_join(resul_construc,carreras,by="raceId") %>% select(circuitId,constructorId,points,year) Numero de veces que ha corrido en el circuito nv_circons=datos_m %>% group_by(circuitId,constructorId) %>% distinct(raceId) %>% count() Puntuación media conseguida en cada circuito. puntm_cc =const_c %>% group_by(circuitId,constructorId) %>% summarise(point_mean = mean(points)) Puntuación media conseguida En primer lugar calculo número de años que lleva cada escudería participando en F1. yeas_const =const_c %>% group_by(constructorId) %>% summarise(n_years= n_distinct(year)) A continuación calculo el total de puntos que ha conseguido en todos esos años, y hago la media. punt_totalm=const_c %>% group_by(constructorId) %>% summarise(punt_mean= sum(points)) punt_totalm =left_join(punt_totalm,yeas_const,by="constructorId") punt_totalm =punt_totalm %>% mutate(points_m = punt_mean/n_years) %>% select(-c(punt_mean,n_years)) 2
set.seed(1234) tree_split3<- initial_split(as.data.frame(x), prop = 0.70,strata = pos_relativa,breaks = 4) tree_ent3<- tree_split3 %>% training() tree_test3<- tree_split3 %>% testing() #2ºDefinimos el modelo. #Indicamos los parámetros que queremos ajustar. ar_fc3 <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid. tree_grid3 <- grid_regular(cost_complexity(), tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada set.seed(1234) cell_folds3 <- vfold_cv(data = tree_ent3, v=5, strata = pos_relativa) #5ºDefinimos la receta. tree_recipe3 <- recipe(pos_relativa ~.,data=tree_ent3) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºCreamos el Workflow set.seed(1234) tree_wf3 <- workflow() %>% add_model(ar_fc3) %>% add_recipe(tree_recipe3) #7ºAjustamos el modelo tree_res3 <- tune_grid( object = tree_wf3, resamples = cell_folds3, metrics=metric_set(mae,rmse,rsq), grid = tree_grid3 ) 9
#8ºSeleccionamos el mejor modelo best_tree3 <- tree_res3 %>% select_best("mae") final_wf3 <- tree_wf3 %>% finalize_workflow(best_tree3) #9ºEjecutamos el mejor modelo. final_fit3 <- final_wf3 %>% last_fit(tree_split3,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(tree_split3),model = list(final_fit3), metrics= list(final_fit3 %>% collect_metrics()), predictions = list(final_fit3 %>% collect_predictions())) t } m_nc_tree3<- n_circuitos3 %>% mutate(res=map(data,tree_fun3)) nc2_tree3=m_nc_tree3 %>% unnest(res) mae_tree3=nc2_tree3 %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_tree3 Circuito como variable. #1ºCreamos una partición entre conjunto test y entrenamiento set.seed(1234) c_split3<- initial_split(conjunto_c3, prop = 0.70, strata = pos_relativa, breaks=4) c_ent3 <- c_split3%>% training() c_test3<-c_split3%>% testing() #2ºDefinimos el modelo ar_fc3 <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid. tree_grid3 <- grid_regular(cost_complexity(), 10
tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada set.seed(1234) cell_folds3 <- vfold_cv(data = c_ent3, v=5, strata = pos_relativa) # 5ºCreamos la receta. c_recipe3 <- recipe(pos_relativa ~.,data=c_ent3) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(circuitId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºCreamos el Workflow set.seed(1234) tree_wf3 <- workflow() %>% add_model(ar_fc3) %>% add_recipe(c_recipe3) #7ºAjustamos el modelo. tree_res3 <- tune_grid( object = tree_wf3, resamples = cell_folds3, metrics=metric_set(mae,rmse,rsq), grid = tree_grid3 ) #8ºSeleccionamos el mejor modelo best_tree3 <- tree_res3 %>% select_best("mae") final_wf3 <- tree_wf3 %>% finalize_workflow(best_tree3) #9ºEjecutamos el mejor modelo. final_fit3 <- final_wf3 %>% last_fit(c_split3,metrics=metric_set(mae,rmse,rsq)) 11
met_treecir3=final_fit3 %>% collect_metrics() met_treecir3 Eliminando NA tree_fun3_na=function(x){ #1ºCreamos una particion entre entrenamiento y test. set.seed(1234) tree_split<- initial_split(as.data.frame(x), prop = 0.70, strata = pos_relativa, breaks = 4) tree_ent <- tree_split%>% training() tree_test <- tree_split %>% testing() #2ºDefinimos el modelo. ar_fc <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid tree_grid <- grid_regular(cost_complexity(), tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada. set.seed(1234) cell_folds <- vfold_cv(data = tree_ent, v=5, strata = pos_relativa) #5ºDefinimos la receta recipe_tree_na<- recipe(pos_relativa ~. , data=tree_ent) %>% step_zv(all_numeric(), -all_outcomes()) %>% step_center(all_numeric(), -all_outcomes()) %>% step_scale(all_numeric(), -all_outcomes()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºCreamos el workflow set.seed(1234) tree_wf <- workflow() %>% add_model(ar_fc) %>% add_recipe(recipe_tree_na) #7ºAjustamos el modelo 12
tree_res <- tune_grid( object = tree_wf, resamples = cell_folds, metrics=metric_set(mae,rmse,rsq), grid = tree_grid ) #8ºSeleccinamos el mejor modelo. best_tree <- tree_res %>% select_best("mae") final_wf <- tree_wf %>% finalize_workflow(best_tree) #9ºEstimamos. final_fit <- final_wf %>% last_fit(tree_split,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(tree_split), model = list(final_fit), metrics= list(final_fit %>% collect_metrics()), predictions = list(final_fit %>% collect_predictions())) t } m_nc_tree_na2<- n_circuitos_na2 %>% mutate(res=map(data,tree_fun3_na)) nc2_tree_na2=m_nc_tree_na2 %>% unnest(res) mae_tree_na2=nc2_tree_na2 %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_tree_na2 Circuitos como variable. #1ºCreamos una partición entre conjunto test y entrenamiento #2ºDefinimos el modelo. ar_fc3 <- decision_tree( cost_complexity = tune(), tree_depth = tune() ) %>% set_engine("rpart") %>% set_mode("regression") #3ºDefinimos el grid. 13
tree_grid3 <- grid_regular(cost_complexity(), tree_depth(range = c(1,30)), levels = 5) #4ºValidacion cruzada set.seed(1234) cell_folds3na <- vfold_cv(data = c_ent3_na, v=5, strata = pos_relativa) # 5ºCreamos la receta. c_recipe3na <- recipe(pos_relativa ~.,data=c_ent3_na) %>% step_novel(circuitId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºCreamos el Workflow set.seed(1234) tree_wf3na <- workflow() %>% add_model(ar_fc3) %>% add_recipe(c_recipe3na) #7ºAjustamos el modelo. tree_res3na <- tune_grid( object = tree_wf3, resamples = cell_folds3na, metrics=metric_set(mae,rmse,rsq), grid = tree_grid3 ) #8ºSeleccionamos el mejor modelo best_tree3na <- tree_res3na %>% select_best("mae") final_wf3na <- tree_wf3na %>% finalize_workflow(best_tree3na) #9ºEjecutamos el mejor modelo. final_fit3na <- final_wf3na %>% last_fit(c_split3_na,metrics=metric_set(mae,rmse,rsq)) 14
met_treecir3_na=final_fit3na %>% collect_metrics() met_treecir3_na Comparacion Vamos a comparar los valores de la métrica según ambas técnicas. tree_metrics3=inner_join(mae_tree3,mae_tree_na2,by="circuitId") %>% rename( mae_tree=.estimate.x, mae_tree_na=.estimate.y ) tree_metrics3=tree_metrics3 %>% gather(`mae_tree`,`mae_tree_na`,key = "mae",value = "estimacion") tree_metrics3$mae=as.factor(tree_metrics3$mae) tree_metrics3 %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Comparación MAE Árbol") Random forest Imputando KNN forest_fun3 =function (x) { #1ºCreamos una particion en entrenamiento y test. set.seed(1234) forest_split3<- initial_split(as.data.frame(x), prop = 0.70,strata = pos_relativa,breaks = 4) forest_ent3 <- forest_split3%>% training() forest_test3 <- forest_split3 %>% testing() #2ºDefinimos el modelo y los parametros a optimizar forest_fc3 <- rand_forest( mtry = tune(), trees = tune(), min_n=tune() ) %>% set_engine("ranger") %>% set_mode("regression") #3ºGrid de parametros forest_grid3 <- expand_grid(mtry=4:10, trees= seq(10,40,5), min_n=seq(1,10,2)) #4ºValidacion cruzada set.seed(1234) 15
forest_folds3 <- vfold_cv(data = forest_ent3, v=5, strata = pos_relativa) #5ºDefinimos la receta forest_recipe3 <- recipe(pos_relativa ~.,data=forest_ent3) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) #6ºWorkflow set.seed(1234) forest_wf3 <- workflow() %>% add_model(forest_fc3) %>% add_recipe(forest_recipe3) #7ºAjustamos el modelo. forest_res3 <- tune_grid( object = forest_wf3, resamples = forest_folds3, metrics=metric_set(mae,rmse,rsq), grid = forest_grid3 ) # 8ºSeleccionamos el mejor best_forest3 <- forest_res3 %>% select_best("mae") final_frst_wf3 <- forest_wf3 %>% finalize_workflow(best_forest3) #9ºEstimamos el modelo. forest_fit3<- final_frst_wf3%>% last_fit(split= forest_split3,metrics=metric_set(mae,rmse,rsq)) t=tibble(split = list(forest_split3), model = list(forest_fit3), metrics= list(forest_fit3 %>% collect_metrics()), predictions = list(forest_fit3 %>% collect_predictions())) t } 16
m_nc_forest3<- n_circuitos3 %>% mutate(res=map(data,forest_fun3)) nc2_forest3=m_nc_forest3 %>% unnest(res) mae_forest3=nc2_forest3 %>% unnest(metrics) %>% filter(.metric == "mae") %>% arrange(.estimate) %>% select(circuitId,.estimate) mae_forest3 Circuitos como variables #1ºCreamos una particion en entrenamiento y test #2ºDefinimos el modelo. #Indicamos los parámetros que queremos ajustar. forest_fc3 <- rand_forest( mtry = tune(), trees = tune(), min_n=tune() ) %>% set_engine("ranger") %>% set_mode("regression") #3ºDefinimos el grid. forest_grid3 <- expand_grid(mtry=1:5, trees= seq(10,40,5), min_n=seq(1,10,2)) #4ºValidación cruzada set.seed(1234) forest_folds_c3 <- vfold_cv(data =c_ent3, v=5, strata = pos_relativa) #5ºDefinimos la receta. recipe_forest_c3 <- recipe(pos_relativa ~.,data=c_ent3) %>% step_impute_knn(stop_mean,timestop_mean) %>% step_novel(circuitId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) 17
# 6ºDefinimos el workflow set.seed(1234) forest_wf_c3<- workflow() %>% add_model(forest_fc3) %>% add_recipe(recipe_forest_c3) #7ºAjustamos el modelo. forest_res_c3 <- tune_grid( object = forest_wf_c3, resamples = forest_folds_c3, metrics=metric_set(mae,rmse,rsq), grid = forest_grid3 ) #8ºSeleccionamos el mejor modelo. best_forest_c3 <- forest_res_c3 %>% select_best("mae") final_wf_c3 <- forest_wf_c3 %>% finalize_workflow(best_forest_c3) #9ºEstimamos el modelo. final_fit_c3 <- final_wf_c3 %>% last_fit(c_split3,metrics=metric_set(mae,rmse,rsq)) mae_for_c3=final_fit_c3%>% collect_metrics() mae_for_c3 Eliminando NA forest_fun_na2 =function (x){ #1ºCreamos una particion en entrenamiento y test. set.seed(1234) forest_split<- initial_split(as.data.frame(x),prop = 0.70, strata = pos_relativa,breaks=4) forest_ent <- forest_split%>% training() forest_test <- forest_split %>% testing() #2ºDefinimos el modelo. forest_fc <- rand_forest( mtry = tune(), trees = tune(), min_n=tune() ) %>% set_engine("ranger") %>% set_mode("regression") #3ºCreamos el grid forest_grid <- expand_grid(mtry=1:5, 18
nc_recipe_na3 <- recipe(pos_relativa ~.,data=c_ent3_na) %>% step_novel(circuitId) %>% step_dummy(all_nominal()) %>% step_zv(all_predictors()) %>% step_center(all_predictors()) %>% step_scale(all_predictors()) %>% step_corr(all_predictors()) %>% step_lincomb(all_predictors()) # 4ºCreamos un WorkFlow rn_model <- mlp( hidden_units=50, epochs =10, ) %>% set_engine("brulee") %>% set_mode("regression") rn_workflow_na3<- workflow() %>% add_model(rn_model) %>% add_recipe(nc_recipe_na3) # 5ºEjecutamos rn_fit_na3 <- rn_workflow_na3 %>% last_fit(split = c_split3_na, metrics=metric_set(mae,rmse,rsq)) met_rncir_na3 =rn_fit_na3 %>% collect_metrics() met_rncir_na3 ``` ## Comparación Vamos a comparar los valores de la métrica según ambas técnicas. rn_metrics3=inner_join(mae_rn3,mae_rn_na2,by="circuitId") %>% rename( mae_rn=.estimate.x, mae_rn_na=.estimate.y ) rn_metrics3=rn_metrics3 %>% gather(`mae_rn`,`mae_rn_na`,key = "mae",value = "estimacion") rn_metrics3$mae=as.factor(rn_metrics3$mae) ``` rn_metrics3 %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Comparación MAE Red Neuronal") 25
Anexo C Estudio de las variables. En este anexo encontramos recogido el c´odigo en R del estudio descriptivo de las variables, tanto las de la primera prueba como las de la segunda. 129
Estudio de las variables Vamos a realizar un estudio descriptivo de los datos. De esta forma, podremos entender mejor la información que nos proporcionan las variables. skim(conjunto) En el primer conjunto de datos tenemos un total de 12 variables de las cuales una (pos_relativa) es la variable respuesta. Dos de las variables explicativas: driverId,constructorId son tipo factor las cuales al hacer los modelos le aplicaremos la creación de variables dummy. También podemos observar como las variables que contienen NA son las que en los modelos vamos a imputar. skim(conjunto3) En el segundo conjunto de datos tenemos un total de 21 variables de las cuales una (pos_relativa) es la variable respuesta, en este conjunto de datos no tenemos ninguna variable tipo factor, todas son cuantitativas. También podemos observar como las variables que contienen NA son las que en los modelos vamos a imputar Distribución de la variable respuesta ggplot(data = datos_m, aes(x=pos_relativa)) + geom_density(fill = "#FFA07A",color="#FF4500",alpha = 0.8) + geom_rug(color="#CD8162",alpha = 0.2) + scale_x_continuous(labels = scales::comma) + labs(title = "Distribución original") + theme_bw() Vamos a comparar la distribución de la posición relativa y la posición absoluta. datos_m %>% mutate_at("position",~replace(.,is.na(.),25)) %>% ggplot(aes(x=position)) + geom_density(fill = "#FFA07A",color="#FF4500",alpha = 0.8) + geom_rug(color="#CD8162",alpha = 0.2) + scale_x_continuous(labels = scales::comma) + labs(title = "Distribución original") + theme_bw() Estadísticos principales de la variable respuesta. summary(conjunto$pos_relativa) Matriz de correlación. Conjunto 1. mp=conjunto %>% select(-c(circuitId,constructorId,driverId,stop_mean,timestop_mean)) a=cor(mp) 1
a_c=melt(a) a_c %>% ggplot(aes(x=Var1,y=Var2,fill=value))+ geom_tile(color = "white")+ scale_fill_gradient2(low = "blue",high = "red",mid = "pink", midpoint = 0,limit = c(-1,1), space = "Lab", name="Coeficiente de \n Correlacion") + theme_minimal()+ theme(axis.text.x = element_text(angle = 90,vjust = 1, size = 10,hjust = 1))+ coord_fixed() En valor absoluto. ab=abs(a) ab_c=melt(ab) ab_c %>% ggplot(aes(x=Var1,y=Var2,fill=value))+ geom_tile(color = "white")+ scale_fill_gradient2(low = "white",high = "red",mid = "pink", midpoint = 0.5,limit = c(0,1), space = "Lab", name="Coeficiente de \n Correlacion") + theme_minimal()+ theme(axis.text.x = element_text(angle = 90,vjust = 1, size = 10,hjust = 1))+ coord_fixed() Conjunto 3. b=cor(conjunto3 %>% select(-c(circuitId,stop_mean,timestop_mean))) b_c=melt(b) b_c %>% ggplot(aes(x=Var1,y=Var2,fill=value))+ geom_tile(color = "white")+ scale_fill_gradient2(low = "blue",high = "red",mid = "pink", midpoint = 0,limit = c(-1,1), space = "Lab", name="Coeficiente de \n correlacion") + theme_minimal()+ theme(axis.text.x = element_text(angle = 90,vjust = 1, size = 10,hjust = 1))+ coord_fixed() En valor absoluto. ba=abs(b) ba_c=melt(ba) ba_c %>% ggplot(aes(x=Var1,y=Var2,fill=value))+ geom_tile(color = "white")+ scale_fill_gradient2(low = "white",high = "red",mid = "pink", midpoint = 0.5,limit = c(0,1), space = "Lab", name="Coeficiente de \n correlacion") + theme_minimal()+ theme(axis.text.x = element_text(angle = 90,vjust = 1, size = 10,hjust = 1))+ coord_fixed() 2
Posiciones ganadas/perdidas con respecto a la posicion de salida. Vamos a coger los datos de la posición de clasificación, la posición final en cada circuito a lo largo de la historia y vamos a ver el número de posiciones ganadas o perdidas con respecto a la posición de salida y a la posición final, dependiendo de cada circuito. Para que no sean demasiados datos veremos las diferencias de posiciones de los que han salido entre los 10 primeros con respecto a su posición final dat_circuit=datos_m %>% select(circuitId,grid,position) clasif10=dat_circuit %>% filter(grid <=10) %>% group_by(circuitId) %>% mutate(diferencia=grid - position, dif= -1*(diferencia < 0)+1*(diferencia >0) ) clasif10$dif=as.integer( clasif10$dif) Si diferencia > 0 entonces ha ganado posiciones. diferencia < 0 entonces ha perdido posiciones. clasif10 %>% filter(circuitId <=33)%>% ggplot(mapping = aes(x=grid,y=diferencia,colour=dif)) + geom_point(alpha=1/5)+ facet_wrap(~circuitId,nrow=8) clasif10 %>% filter(circuitId > 33)%>% ggplot(mapping = aes(x=grid,y=diferencia,colour=dif)) + geom_point(alpha=1/5)+ facet_wrap(~circuitId,nrow=8) Numero de vuelta en la parada Número de vuelta en la que hace cada parada según la posición en la que ha quedado nvueltap =inner_join(boxes,datos_m,by=c("raceId","driverId")) %>% select(circuitId,stop, lap, position) nvueltap=left_join(nvueltap,GP,by="circuitId") %>% select(circuitId,stop,lap,position,Circuito) nvueltap$stop=as.factor(nvueltap$stop) nvueltap %>% ggplot(aes(lap,position))+ geom_point(mapping = aes(x=lap,y=position,colour=stop),alpha=1/2)+ # geom_smooth()+ facet_wrap(~circuitId,nrow=8) #Accidentes y colisiones Según en la posición en la que sale desde el grid, ver en qué posición ha quedado según si ha tenido un accidente o colisión. ac_co =datos_m %>% filter(statusId==3| statusId==4) %>% select(circuitId,grid,pos_relativa,statusId) ac_co =left_join(ac_co,GP,by="circuitId")%>% select(circuitId,Circuito,grid,pos_relativa,statusId) ac_co$statusId=as.factor(ac_co$statusId) 3
ac_co %>% filter(circuitId <=49 & pos_relativa >=0.25) %>% ggplot()+ geom_point(mapping = aes(x=grid,y=pos_relativa,colour=statusId),alpha=1/2)+ facet_wrap(~circuitId,nrow=10)+ scale_y_continuous(breaks = c(0.25,0.5,0.75,1))+ scale_x_discrete(breaks=c(1,10,20,25))+ labs(title = "Relación accidentes con posición relativa",color="Estado") + scale_color_discrete(labels = c("Accidente","Colisión")) 4
134 ANEXO C. ESTUDIO DE LAS VARIABLES.
Anexo D Comparaci´on modelos y predicciones. En este anexo vamos a realizar comparaciones de los valores de las m´etricas de todos los modelos seg´un la prueba y la t´ecnica. Por otro lado, vamos a crear predicciones de varios pilotos en una cierta carrera, seg´un las variables de la segunda prueba a trav´es de la t´ecnica de imputaci´on. 135
Comparaciones Vamos a realizar comparaciones de los valores de las métricas de todos los modelos según la prueba y la técnica. Conjunto 1 (primera prueba) Imputacion KNN Metricas metrics1=inner_join(mae_mlc,mae_tree,by="circuitId") metrics1=inner_join(metrics1,mae_forest, by="circuitId") metrics1=inner_join(metrics1,mae_rn,by="circuitId") %>% rename( mae_ml=.estimate.x, mae_tree=.estimate.y, mae_forest=.estimate.x.x, mae_rn=.estimate.y.y ) metrics1=metrics1 %>% gather(`mae_ml`,`mae_tree`,`mae_forest`,`mae_rn`,key = "mae",value = "estimacion") metrics1$mae=as.factor(metrics1$mae) metrics1 %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ geom_hline(yintercept = 1,col="red") + labs(title = "Metrica MAE prueba 1") Ahora vamos a reducirnos a los datos donde mae <1. metris_less1 =metrics1 %>% filter(estimacion <= 1) metris_less1 %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Metrica MAE prueba 1 mae <=1") Eliminando NA Metricas metrics1_na=inner_join(mae_ml_na,mae_tree_na,by="circuitId") metrics1_na=inner_join(metrics1_na,mae_forest_na, by="circuitId") metrics1_na=inner_join(metrics1_na,mae_rn_na,by="circuitId") %>% rename( mae_ml=.estimate.x, mae_tree=.estimate.y, mae_forest=.estimate.x.x, 1
mae_rn=.estimate.y.y ) metrics1_na=metrics1_na%>% gather(`mae_ml`,`mae_tree`,`mae_forest`,`mae_rn`,key = "mae",value = "estimacion") metrics1_na$mae=as.factor(metrics1_na$mae) metrics1_na %>% filter(estimacion <=1) %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Metrica MAE prueba 1 eliminando Na") Conjunto 3. (segunda prueba) Imputacion KNN Metricas metrics3=inner_join(mae_ml3,mae_tree3,by="circuitId") metrics3=inner_join(metrics3,mae_forest3, by="circuitId") metrics3=inner_join(metrics3,mae_rn3,by="circuitId") %>% rename( mae_ml=.estimate.x, mae_tree=.estimate.y, mae_forest=.estimate.x.x, mae_rn=.estimate.y.y ) metrics3=metrics3 %>% gather(`mae_ml`,`mae_tree`,`mae_forest`,`mae_rn`,key = "mae",value = "estimacion") metrics3$mae=as.factor(metrics3$mae) metrics3 %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Metrica MAE prueba 2") metrics3 %>% filter(estimacion <=0.2) %>% ggplot()+ geom_point(mapping = aes(x=circuitId,y=estimacion,colour=mae),alpha=1/2)+ labs(title = "Metrica MAE prueba 2 \n intervalo (0,0.2)") Eliminando NA Metricas metrics2_na=inner_join(mae_ml_na2,mae_tree_na2,by="circuitId") metrics2_na=inner_join(metrics2_na,mae_forest_na2, by="circuitId") metrics2_na=inner_join(metrics2_na,mae_rn_na2,by="circuitId") %>% rename( mae_ml=.estimate.x, mae_tree=.estimate.y, mae_forest=.estimate.x.x, mae_rn=.estimate.y.y ) 2
144 ANEXO D. COMPARACI ´ ON MODELOS Y PREDICCIONES.
Anexo E Predicciones temporada 2022. En este ´ultimo anexo vamos a predecir la carrera de la temporada 2022, de la cual ya no tenemos informaci´on en la base de datos. Para ello, en primer lugar, modificamos los conjuntos de datos que ten´ıamos para las dos pruebas, a˜nadiendo en estos los datos conocidos de las tres primeras carreras de la temporada 2022. A continuaci´on, creamos los modelos con los conjuntos de datos definidas anteriormente con las t´ecnicas que ya hemos mencionado, realizando a su vez comparaciones en cada modelo seg´un la t´ecnica. Por ´ultimo hacemos las predicciones de la cuarta carrera. 145
Predicciones temporada 2022 Vamos a predecir la carrera, de la cual ya no tenemos información en la base de datos. En primer lugar, leo los datos de las carreras de la temporada 2022 de las que hay información en la base de datos resultados22=read.csv("datosk22/results.csv",header = TRUE,dec = ".") resultados22$position=parse_integer(resultados22$position ) boxes22=read.csv("datosk22/pit_stops.csv",header = TRUE,dec = ".") resul_construc22=read.csv("datosk22/constructor_results.csv",header = TRUE) tiempos_vuelta22=read.csv("datosk22/lap_times.csv",header = TRUE,dec=".") rc22 =left_join(resultados22,carreras,by="raceId") %>% select(-url) datos_m22 =rc22 %>% filter(name !="Indianapolis 500" & grid>0) Num pilotos por carrera npilo_xgp22=datos_m22 %>% group_by(raceId,year) %>% count() %>% arrange(desc(n)) npilo_xgp22 %>% filter(year==2022) Posicion relativa datos_m22 =left_join(datos_m22,npilo_xgp22,by=c("raceId","year")) %>% mutate(pilo_delante=position-1) datos_m22=datos_m22 %>% mutate(pos_relativa=pilo_delante/n) %>% mutate_at("pos_relativa",~replace(., is.na(.), 1)) datos_m22 =datos_m22 %>% filter(pos_relativa <= 1) Número de veces que se ha corrido en cada circuito. Con las carreras ya disputadas en ésta temporada nv_circuitos22=carreras %>% filter(raceId <=1076) %>% group_by(circuitId) %>% summarise(totalv=n()) Número de veces que cada piloto ha corrido en cada circuito. Según el constructor nv_pilo_cc22=datos_m22 %>% group_by(driverId,circuitId,constructorId) %>% summarise(totalc=n_distinct(raceId)) Independientemente del constructor Numero total de veces que un piloto ha corrido en un circuito. 1
nv_pilo_c22=nv_pilo_cc %>% group_by(driverId,circuitId) %>% summarise(tveces=sum(totalc)) Media de paradas. nboxes22 =inner_join(datos_m22,boxes22,by=c("raceId","driverId")) %>% group_by(raceId,driverId,constructorId) %>% summarise(nstop=n()) nboxes22 =left_join(nboxes22,GP2,by=c("raceId")) Ahora vamos a calcular la media de paradas que cada equipo hace en cada circuito, aunque en la realizar el número de paradas es un número entero, en este caso no vamos a redondear la media obtenida para tener una aproximación más exacta del número de paradas que pueden realizar cada equipo en cada circuito. n_stop22 =nboxes22 %>% group_by(circuitId,constructorId) %>% summarise(stop_mean= mean(nstop)) Media tiempo en paradas Voy a hacer la media de tiempo (en milisegundos) que cada equipo ha tardado en parada en cada GP. En primer lugar, vamos a calcular el tiempo medio empleado por cada equipo con cada piloto en una parada en cada carrera time_boxes22 =inner_join(datos_m22,boxes22,by=c("raceId","driverId")) time_boxes22 =time_boxes22 %>% group_by(raceId,driverId,constructorId) %>% summarise(suma_t=sum(milliseconds.y)) t_stop22=inner_join(time_boxes22,nboxes22,by=c("raceId","driverId","constructorId")) t_stop22=t_stop22 %>% mutate(stop_tm=suma_t/nstop) %>% select(circuitId,driverId,constructorId,stop_tm) t_stop22=t_stop22[,-1] Ahora calculo, el tiempo medio por parada en cada circuito de cada equipo. t_stop22 =t_stop22 %>% group_by(constructorId,circuitId) %>% summarise(timestop_mean=mean(stop_tm)) Proporción de accidentes por pilotos y circuitos Filtro por los estados de accidente (statusId=3) y cuento cuántos ha tenido cada piloto en cada circuito. acc22=datos_m22 %>% filter(statusId==3) acc22=acc22 %>% group_by(driverId,circuitId) %>% summarise(totalac=n()) Hago la proporción del número de accidentes de cada piloto en cada circuito entre el número total de veces que cada piloto ha corrido en cada circuito. pacc22=left_join(acc22,nv_pilo_c22,by=c("driverId","circuitId"))%>% mutate(pAccidentes=totalac/tveces) pacc22=pacc22%>% select(driverId,circuitId,pAccidentes) Tiempo medio piloto Tiempo medio piloto Vamos a calcular el histórico del piloto: tiempo medio en vuelta en general del piloto. 2
tiempo_pilo22 =tiempos_vuelta22 %>% group_by(driverId) %>% summarise(tm_p=mean(milliseconds)) Vamos a calcular la media general de todos los pilotos, para poder imputar los valores de los pilotos de los que no tengamos información. tm_pgeneral22 =tiempo_pilo22 %>% summarise(tm_gen=mean(tm_p)) Tiempo medio piloto circuito En primer lugar, voy a calcular, el tiempo medio por vuelta de cada piloto en cada carrera. tiempo_vmean22=tiempos_vuelta22 %>% group_by(raceId,driverId) %>% summarise(t_vm=mean(milliseconds)) tiempo_vmean22 =left_join(tiempo_vmean22,GP2,by="raceId") Vamos a calcular el tiempo medio por vuelta que lleva cada piloto en cada circuito (sin tener en cuenta el constructor) tiempo_pilocir22=tiempo_vmean22 %>% group_by(driverId,circuitId) %>% summarise(tm_pci=mean(t_vm)) Vamos a calcular la media de las medias tm_pcgeneral22 =tiempo_pilocir22 %>% group_by(circuitId) %>% summarise(tm_pcgen= mean(tm_pci)) tm_pcgeneral22 =tm_pcgeneral22 %>% summarise(tm_pcgen= mean(tm_pcgen)) Tiempo medio piloto circuito constructor Calculamos por último, el tiempo medio por vuelta que lleva cada piloto en cada circuito según el constructor. cirpiloeq22=datos_m22 %>% select(raceId,driverId,constructorId) tiempo_pilocc22=left_join(tiempos_vuelta22,cirpiloeq22,by=c("raceId","driverId")) %>% group_by(raceId,driverId,constructorId) %>% summarise(tm_pcc=mean(milliseconds)) tiempo_pilocc22=left_join(tiempo_pilocc22,GP2,by="raceId") %>% group_by(circuitId,driverId,constructorId) %>% summarise(tmean_pcc=mean(tm_pcc)) tm_pccgeneral22 =tiempo_pilocc22 %>% group_by(circuitId,constructorId) %>% summarise(tm_pccgen= mean(tmean_pcc)) tm_pccgeneral22 =tm_pccgeneral22 %>% summarise(tm_pccgen= mean(tm_pccgen)) tm_pccgeneral22 =tm_pccgeneral22 %>% summarise(tm_pccgen= mean(tm_pccgen)) PILOTOS Numero total de carreras Numero total de grandes premios que ha disputado cada piloto. nc_pilo22 =nv_pilo_cc22 %>% group_by(driverId) %>% summarise(tc= sum(totalc)) Posición media relativa piloto. 3
Posición media relativa piloto circuito constructor. Posicion media en la que quedó en cada circuito según el constructor. posm_piloc22 =datos_m22 %>% group_by(circuitId,driverId,constructorId) %>% summarise(pos_mean= mean(pos_relativa)) Posición media relativa piloto circuito. posm_pilo22 =datos_m22 %>% group_by(circuitId,driverId) %>% summarise(posic_m= mean(pos_relativa)) Probabilidad de hacer podium piloto. Probabilidad hacer podium piloto circuito constructor Según el constructor. En primer lugar, vamos a calcular el número de podiums que ha hecho cada piloto con cada constructor en cada circuito. pod_prob22 =datos_m22 %>% filter(position <= 3) %>% group_by(circuitId,driverId,constructorId) %>% summarise(pod_nc=n()) Ahora vemos el número de veces que ha corrido en cada circuito con cada constructor y hacemos la proporción. pod_prob22=left_join(pod_prob22,nv_pilo_cc22,by=c("circuitId","driverId","constructorId")) pod_prob22=pod_prob22 %>% mutate(prob=pod_nc/totalc) %>% select(-c(pod_nc,totalc)) Probabilidad de hacer podium piloto circuito En primer lugar, calculamos el numero de podiums que ha conseguido cada piloto en cada circuito. pod_prob2_22 =datos_m22 %>% filter(position <= 3) %>% group_by(circuitId,driverId) %>% summarise(pod_nc2=n()) Ahora tomo el número de veces que ha corrido en cada circuito y hacemos la proporción. pod_prob2_22 =left_join(pod_prob2_22,nv_pilo_c22,by=c("circuitId","driverId")) pod_prob2_22=pod_prob2_22 %>% mutate(probc= pod_nc2/tveces) %>% select(-c(pod_nc2,tveces)) Probabilidad de hacer podium piloto. Calculamos el número de podiums que ha hecho un piloto en lo que lleva pod_prob3_22 =datos_m22 %>% filter(position <= 3) %>% group_by(driverId) %>% summarise(pod_p=n()) Ahora tomo el número de carreras que ha disputado y hago la proporción. pod_prob3_22 =left_join(pod_prob3_22,nc_pilo22,by=c("driverId")) pod_prob3_22=pod_prob3_22 %>% 4
mutate(prob_p= pod_p/tc) %>% select(-c(pod_p,tc)) CONSTRUCTORES. const_c22=left_join(resul_construc22,carreras,by="raceId") %>% select(circuitId,constructorId,points,year) Numero de veces que ha corrido en el circuito nv_circons22=datos_m22%>% group_by(circuitId,constructorId) %>% distinct(raceId) %>% count() Puntuación media conseguida en cada circuito. puntm_cc22 =const_c22 %>% group_by(circuitId,constructorId) %>% summarise(point_mean = mean(points)) Puntuación media conseguida En primer lugar calculo número de años que lleva cada escudería participando en F1. yeas_const22 =const_c22 %>% group_by(constructorId) %>% summarise(n_years= n_distinct(year)) A continuación calculo el total de puntos que ha conseguido en todos esos años, y hago la media. punt_totalm22=const_c22 %>% group_by(constructorId) %>% summarise(punt_mean= sum(points)) punt_totalm22 =left_join(punt_totalm22,yeas_const22,by="constructorId") punt_totalm22 =punt_totalm22%>% mutate(points_m = punt_mean/n_years) %>% select(-c(punt_mean,n_years)) conjunto22=datos_m22 %>% select(circuitId,driverId,constructorId,grid,pos_relativa,round) conjunto22=left_join(conjunto22,nc_pilo22, by="driverId") conjunto22=left_join(conjunto22,nv_pilo_cc22, by=c("driverId","circuitId","constructorId")) conjunto22=left_join(conjunto22,nv_pilo_c22, by=c("driverId","circuitId")) conjunto22 =left_join(conjunto22,posm_pilo22, by=c("circuitId","driverId")) conjunto22=left_join(conjunto22,posm_piloc22, by=c("circuitId","driverId","constructorId")) conjunto22 =left_join(conjunto22,pod_prob22, by=c("circuitId","driverId","constructorId")) conjunto22=left_join(conjunto22,pod_prob2_22, by=c("circuitId","driverId")) conjunto22=left_join(conjunto22,pod_prob3_22, by="driverId") 5
conjunto22 =left_join(conjunto22,pacc22, by=c("circuitId","driverId")) %>% mutate_at("pAccidentes",~replace(.,is.na(.),0)) conjunto22 =left_join(conjunto22,tiempo_pilo22, by="driverId") conjunto22 =left_join(conjunto22,tiempo_pilocir22, by=c("circuitId","driverId")) conjunto22 =left_join(conjunto22,tiempo_pilocc22, by=c("circuitId","constructorId","driverId")) conjunto22 %>% select(prob,probc,prob_p,tm_p,tm_pci,tmean_pcc) %>% summary conjunto22 =conjunto22 %>% mutate_at(c("prob","probc","prob_p"),~replace(.,is.na(.),0)) Imputamos los NA de los tiempos por la media. conjunto22 =conjunto22 %>% mutate_at("tm_p",~replace(.,is.na(.),tm_pgeneral22$tm_gen)) conjunto22 =conjunto22 %>% mutate_at("tm_pci",~replace(.,is.na(.),tm_pcgeneral22$tm_pcgen)) conjunto22 =conjunto22 %>% mutate_at("tmean_pcc",~replace(.,is.na(.),tm_pccgeneral22$tm_pccgen)) conjunto22 %>% summary conjunto22 =left_join(conjunto22,nv_circons22, by=c("circuitId","constructorId")) conjunto22 =left_join(conjunto22,puntm_cc22, by=c("circuitId","constructorId") ) conjunto22 =left_join(conjunto22,punt_totalm22,by="constructorId") conjunto22 =conjunto22 %>% mutate_at(c("point_mean","points_m"),~replace(.,is.na(.),0)) conjunto22 =left_join(conjunto22,n_stop22, by=c("circuitId","constructorId")) conjunto22=left_join(conjunto22,t_stop22,by=c("circuitId","constructorId")) conjunto22 %>% select (stop_mean,timestop_mean) %>% summary Prediccion circuito 21 Vamos a hacer la predicción de la posición final de los pilotos que salieron los 5 primeros. ## Max Verstappen mv22_em=conjunto22 %>% filter(circuitId==21 & driverId==830 & constructorId==9) %>% select(-c(circuitId,driverId, constructorId,grid,pos_relativa,round)) %>% distinct() %>% mutate(grid=1,round=4) mv22_em=tibble(mv22_em) ML pred_mv22_em=nc3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=mv22_em) pred_mv22_em$.pred 6
posabs_mv22_em=20 * pred_mv22_em$.pred +1 posabs_mv22_em AR apred_mv22_em=nc2_tree3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=mv22_em) apred_mv22_em$.pred aposabs_mv22_em=20 * apred_mv22_em$.pred +1 aposabs_mv22_em Random forest. rfpred_mv22_em=nc2_forest3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=mv22_em) rfpred_mv22_em$.pred rfposabs_mv22_em=20 * rfpred_mv22_em$.pred +1 rfposabs_mv22_em rnpred_mv22_em=nc2_rn3%>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=mv22_em) rnpred_mv22_em$.pred rnposabs_mv22_em=20 * rnpred_mv22_em$.pred +1 rnposabs_mv22_em Charles Leclerc cl22_em=conjunto22 %>% filter(circuitId==21 & driverId==844 & constructorId==6) %>% select(-c(circuitId,driverId, constructorId,grid,pos_relativa,round)) %>% distinct() %>% mutate(grid=2,round=4) cl22_em=tibble(cl22_em) ML pred_cl22_em=nc3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=cl22_em) pred_cl22_em$.pred posabs_cl22_em=20 * pred_cl22_em$.pred +1 posabs_cl22_em AR apred_cl22_em=nc2_tree3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=cl22_em) apred_cl22_em$.pred 7
aposabs_cl22_em=20 * apred_cl22_em$.pred +1 aposabs_cl22_em Random forest. rfpred_cl22_em=nc2_forest3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=cl22_em) rfpred_cl22_em$.pred rfposabs_cl22_em=20 * rfpred_cl22_em$.pred +1 rfposabs_cl22_em RN rnpred_cl22_em=nc2_rn3%>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=cl22_em) rnpred_cl22_em$.pred rnposabs_cl22_em=20 * rnpred_cl22_em$.pred +1 rnposabs_cl22_em Sergio Pérez sp22_em=conjunto22 %>% filter(circuitId==21 & driverId==815 & constructorId==9) %>% select(-c(circuitId,driverId, constructorId,grid,pos_relativa,round)) %>% distinct() %>% mutate(grid=3,round=4) sp22_em=tibble(sp22_em) ML pred_sp22_em=nc3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=sp22_em) pred_sp22_em$.pred posabs_sp22_em=20 * pred_sp22_em$.pred +1 posabs_sp22_em AR apred_sp22_em=nc2_tree3 %>% filter(circuitId==21) %>% unnest(model) %>% pluck(".workflow",1) %>% predict(new_data=sp22_em) apred_sp22_em$.pred aposabs_sp22_em=20 * apred_sp22_em$.pred +1 aposabs_sp22_em RF 8