La distribución loglogística en los retornos de una jugada

 Otra cuestión básica en el análisis de nuestras jugadas de La Quiniela es la distribución de premios que obtendremos con nuestras columnas. Aunque no hace mucho suponía una distribución lognormal para ésta, recientemente, y tras notar que los datos no cuadraban, me percaté de que la distribución que ajustaba a los retornos era la loglogística y no la anterior.

Hay que tener claro que la distribución será un modelo casi perfecto de las posibilidades a las que podemos acceder con una buena selección de apuestas. Como siempre, mi fuente hace referencia a la Wikipedia, aunque insisto que todo debe de ser siempre contrastado, especialmente en la web. Además, en muchas ocasiones veremos que las referencias a esta enciclopedia son más completas y precisas en inglés que en castellano, por lo que conviene siempre echar un vistazo a la entrada en ese idioma.

La función de distribución acumulativa, en este caso será de más utilidad y más sencilla, que la función de densidad de probabilidad. Así la función de distribución será:

11+(x/α)β

Con ésto ya estaría finalizada la entrada, pero voy a detallar cómo obtener los parámetros alfa y beta. En este caso la variable x será el valor en euros de los premios a obtener con nuestro conjunto de columnas, y la función de distribución nos devolverá la probabilidad de que el premios resultante de un intento, sea mayor a esa cifra.

Para empezar podemos descargarnos del siguiente enlace una hoja de Google, en la que se utiliza la función de distribución para ilustrar cómo los valores de los parámetros influyen en los retornos potenciales de una jugada:

https://docs.google.com/spreadsheets/d/1yn2g0sk716ZPMBKJx66aUf7aaJZe8tOOf1n4cVX0RSQ/edit?usp=sharing

En esta hoja he dispuesto los valores que he obtenido para la distribución loglogística en la jugada de la semana pasada en la peña. Jugamos una cantidad más corta de lo habitual, y he empleado los valores con los apostados que le supuse, y no los finales, para que se pueda valorar con qué nos podemos encontrar al calcular la jugada a priori (y dicho sea de paso, no quedar en mal lugar mostrando mis valores con los apostados finales).

En el libro, podemos ir modificando los valores para los parámetros alfa y beta, y ver cómo influyen en la jugada. Sugiero realizar este paso para sopesar cuánto influyen los valores y en qué sentido, si aumentándolos o disminuyéndolos. Aclaro que construyo la distribución tan sólo con los valores mayores a cero, dejando la probabilidad de obtener premio como punto de apoyo secundario a toda la formulación. Sin entrar en muchos detalles para no alargar excesivamente el post, se puede ir entrando en las distintas celdas de ambas páginas, ver las fórmulas empleadas y cómo aplicarlas. 

Dentro del libro, en la hoja "Panel", se muestra un gráfico que a muchos les resultará familiar de alguna herramienta que circula entre la comunidad de quinielistas. Ese gráfico es el resultado de aplicar la distribución a los retornos. Cada porcentaje de recuperación del eje X tiene reflejo en el eje Y, mostrando la probabilidad acumulada inversa de dicha recuperación. Es decir, para obtener una recuperación superior al 100% tendremos una probabilidad de aproximadamente un 13%. Esa será nuestra probabilidad de rentabilizar la jugada.

Hay que recalcar que la rentabilidad real a largo plazo, dependerá sobretodo de la cola de la distribución del gráfico. Cuanto más pesada sea, mayor será nuestra esperanza matemática, la cual también se puede calcular analíticamente a través de los parámetros de la distribución (a esto dedicaré un segundo post, continuación de éste mismo, donde me detendré a comentar más profundamente los detalles).

Como he dicho los retornos de la jugada pueden calcularse "analíticamente" a través de lo anterior. Sin embargo, el método que propongo para encontrar los valores de los parámetros, es puramente empírico a través de simulaciones. En la primera entrada del blog, hablé de la simulación Montecarlo. Pues bien, éste es el método que vamos a aplicar en combinación con la distribución binomial de Poisson (para calcular los premios), y el método de los mínimos cuadrados para la regresión de la recta.

Veamos primero, de refresco, el cálculo de premios con la binomial de Poisson en código. He realizado algunas ligeras modificaciones sin importancia respecto a lo publicado en el primer post, lo básico permanece:


class CalculadoraDeImportes {

    constructor(apostados, apuestasLAE, precioApuesta, repartoPremiosArray) {
        this.apostados = apostados;
        this.recaudacion = apuestasLAE * precioApuesta;
        this.apuestas = apuestasLAE;
        this.reparto = repartoPremiosArray;
    }

    calcular(columna) {
        var probs = this.calcularProbabilidades(columna);
        var importes = [];
        for(var i = 0; i < 6; i++) {
            var cantidad = this.reparto[i] * this.recaudacion / (probs[i] * this.apuestas);
            importes.push(cantidad);
        }
        return importes;
    }

    calcularProbabilidades(columna) {
        var probs = [0.0, 0.0, 0.0, 0.0, 0.0];
        var p14 = 1.0;
        for(var a = 0; a < 14; a++) {
            p14 *= this.apostados[a][columna[a]];
            var p13 = 1.0;
            for(var i = 0; i < 14; i++) {
                if(i != a) p13 *= this.apostados[i][columna[i]];
                else p13 *= (1.0 - this.apostados[i][columna[i]]);
            }
            probs[3] += p13;
            for(var b = a + 1; b < 14; b++) {
                var p12 = 1.0;
                for(var i = 0; i < 14; i++) {
                    if(i != a && i != b) p12 *= this.apostados[i][columna[i]];
                    else p12 *= (1.0 - this.apostados[i][columna[i]]);
                }
                probs[2] += p12;
                for(var c = b + 1; c < 14; c++) {
                    var p11 = 1.0;
                    for(var i = 0; i < 14; i++) {
                        if(i != a && i != b && i != c) p11 *= this.apostados[i][columna[i]];
                        else p11 *= (1.0 - this.apostados[i][columna[i]]);
                    }
                    probs[1] += p11;
                    for(var d = c + 1; d < 14; d++) {
                        var p10 = 1.0;
                        for(var i = 0; i < 14; i++) {
                            if(i != a && i != b && i != c && i != d) p10 *= this.apostados[i][columna[i]];
                            else p10 *= (1.0 - this.apostados[i][columna[i]]);
                        }
                        probs[0] += p10;
                    }
                }
            }
        }
        probs[4] += p14;
        return probs;
    }

}

En este caso he creado una clase para crear una instancia (objeto) que me calcule los premios de una determinada columna ganadora.

Ahora voy a mostrar la clase JS con la que creo el núcleo del simulador:


class Simulador {

    /**
     * Constructor de clase (Simulador).
     * @param {Array} porcentajesMatrix Matriz de 14x3 con los porcentajes reales. 
     */
    constructor(porcentajesMatrix) {
        //Array donde acumularemos las columnas ganadoras
        this.soluciones = [];
        //Matriz de 14x3 con los porcentajes reales
        this.porcentajes = porcentajesMatrix;
    }

    /**
     * Método que simula tantas columnas como le indiquemos en el parámetro.
     * @param {Number} iteraciones Columnas totales que simularemos
     */
    simular(iteraciones) {
        //Simularemos tantas columnas como deseemos (iteraciones)
        for(var i = 0; i < iteraciones; i++) {
            //Generamos una columna aleatoria por porcentajes
            var columna = this.simularColumna();
            //Guardamos la solución en el array de soluciones
            this.soluciones.push(columna);
        }
    }

    /**
     * Método que simula una columna partido a partido.
     */
    simularColumna() {
        var signos = [];
        //Simularemos 14 signos a partir de los porcentajes
        for(var i = 0; i < 14; i++) {
            var s = this.simularSigno(this.porcentajes[i]);
            signos.push(s);
        }
        return signos;
    }

    /**
     * Método que simula el signo de un partido a partir de los porcentajes 1X2.
     * @param {Array} porcentajesArray Porcentajes 1X2 de un partido.
     * @returns {String} Signo que sale de la simulación aleatoria.
     */
    simularSigno(porcentajesArray) {
        //Creamos el pseudaleatorio
        var val = Math.random();
        //Decidimos qué signo toma de valor. 0 para un 1, 1 para una X, y un 2 para un 2
        if(val < porcentajesArray[0]) {
            return 0;
        }
        else if(val > porcentajesArray[0] && val <= (porcentajesArray[0] + porcentajesArray[1])) {
            return 1;
        }
        else if(val > (porcentajesArray[0] + porcentajesArray[1]) && val <= (porcentajesArray[0] + porcentajesArray[1] + porcentajesArray[2])) {
            return 2;
        }
    }
}

Me he molestado en comentar y detallar cada línea de código para que se pueda entender cómo se programa este muestreador. Puede verse que es algo bastante sencillo, y que no requiere de complejidades.

Ahora tan sólo nos queda mostrar el código para el cálculo por mínimos cuadrados. El motivo por el que nos basta con calcular la regresión de una recta, es por la transformación a la que sometemos a la función de distribución acumulada de la loglogística. Para reducir el problema a una simple línea, debemos hacer el logaritmo neperiano a ambos lados de la ecuación, igualándola a la probabilidad acumulada (que puede deducirse de la lista de premios simulada). Despejando y agrupando términos, nos queda la fórmula tradicional de una recta:

y=mx+b

Así, con el logaritmo aplicado y despejando en la fórmula de arriba del todo nos queda (con pA para la probabilidad acumulada):

βlnx+βlnα=1/pA1

Siendo para la recta:

m=β

x=lnx

, y

b=βlnα

No detallo cómo calcular la regresión de la recta por mínimos cuadrados. Me remito al código, o a la búsqueda por parte del lector del método en la web.

De esta manera el código final para calcular los coeficientes alfa y beta por mínimos cuadrados será:


class Loglogistica {

    constructor(listaPremios) {
        this.listaPremios = listaPremios;
    }

	getParametrosLoglogistica() {
		var tamanio = 0;
		for(var i = 0; i < this.listaPremios.length; i++) {
			if(this.listaPremios[i] > 0.0) tamanio++;
		}
		this.listaPremios.sort((a, b) => {return a - b});
		var alfa = 0.0;
		var beta = 0.0;
		var sx = 0.0;
		var sx2 = 0.0;
		var sy = 0.0;
		var sxy = 0.0;
		var n = 0.0;
		var np = 0.0;
		for(var i = 0; i < this.listaPremios.length; i++) {
			if(this.listaPremios[i] > 0.0) {
				np += 1.0;
				var x = Math.log(this.listaPremios[i]);
			    var pA = np / (tamanio + 1.0);
				var y = Math.log(1.0 / pA - 1.0);
				sx += x;
				sx2 += (x * x);
				sy += y;
				sxy += (x * y);
			}
		}
		var m = (np * sxy - sx * sy) / (np * sx2 - sx * sx);
		var b = (sy * sx2 - sx * sxy) / (np * sx2 - sx * sx);
		beta = (-1.0) * m;
		alfa = Math.exp(b / beta);
		return [alfa, beta, np / this.listaPremios.length];
	}

}

Todo el código, y la utilidad programada para cualquier navegador, se puede descargar de:

https://drive.google.com/file/d/1CC7NsZbZivAaYCHtMotjuj40eEQi51ls/view?usp=sharing

Para ejecutarlo, basta con descomprimir, y ejecutar el index.html en el navegador del PC, pudiendo funcionar offline. He procurado dejar la capa de presentación de las CSS al mínimo para no complicar el código, y todo lo que nos interesa estará en el directorio src/js. Lo que no se muestra aquí tan sólo es apoyo de programación para cargar los archivos de datos. Procurad no dejar ningún campo en blanco y emplear para los porcentajes un formato TXT de 14 líneas y separación de tabulador. Al cargar los porcentajes, si son correctos se mostrarán en tanto por uno en las áreas de texto. También la jugada en la correspondiente si logra leer el TXT con las columnas jugadas. Cuando pulsemos calcular, por ejemplo, con 100.000 iteraciones, deberemos ser algo pacientes ya que el peso de cálculo para hallar los premios requiere su tiempo y si pulsamos reiteradamente el botón de cálculo se lanzarán varias instancias que ralentizarán nuestro ordenador. 

Si todo va bien con esas 100.000, en unos segundos tendremos nuestros parámetros personales, que podemos también introducir en la hoja de cálculo para observar la curva si lo deseamos. Como nota adicional, decir que las apuestas LAE se refiere a las apuestas registradas en el juego, y no al tamaño de la jugada.

Dedicaré un segundo post a otros aspectos relacionados con esta distribución de probabilidad. Una vez se entiende todo resulta sencillo, pero soy consciente de que esta entrada ha sido algo espesa y compacta. Como siempre, hay una parte de trabajo por parte de lector de búsqueda de información adicional, pruebas, y sobretodo como comenté el el primer post, algún conocimiento básico de programación, especialmente en JavaScript.

Nos vemos en redes. Un saludo, y suerte.

Comentarios

Entradas populares de este blog

La entropía condicional

Herramienta EM

Herramienta de análisis