¿Cómo aplicamos la desviación estándar y el promedio en un modelo de riesgo-retorno?
En el ámbito de las inversiones, calcular el retorno y el riesgo es esencial para tomar decisiones informadas. Al conocer el promedio y la desviación estándar de una serie de tiempo que representa los retornos de inversión, podemos determinar el valor esperado y el riesgo asociado con dicha inversión. Esto nos proporciona un mejor entendimiento de cuánto podríamos ganar y qué tan volátil es nuestra inversión.
¿Qué herramientas y bibliotecas utilizamos?
Para llevar a cabo estos cálculos y visualizaciones, utilizamos las bibliotecas de Python Numpy y Matplotlib.
Numpy: Nos permite manejar y operar con arreglos y vectores, facilitando el cálculo de promedios y desviaciones estándar.
Matplotlib: Facilita las visualizaciones, lo cual es crucial para interpretar los datos de manera gráfica.
¿Cómo generamos números aleatorios con Numpy?
Para crear muestras aleatorias, utilizamos np.random.uniform. Este método genera números aleatorios dentro de un rango específico, que por defecto es entre 0 y 1. Esto nos es útil cuando queremos simular diferentes escenarios de retorno de inversión.
import numpy as np
# Generamos 6 números aleatorios entre -0.1 y 0.1random_numbers = np.random.uniform(-0.1,0.1,6)print(random_numbers)
¿Cómo interpretamos las series de tiempo de las inversiones?
Las series de tiempo nos muestran cómo varía el retorno de una inversión a lo largo del tiempo. Analizar estas gráficas nos ayuda a identificar patrones y potenciales oportunidades o riesgos en nuestras inversiones. Aquí te doy algunos ejemplos de análisis:
Inversión A: Tiene un retorno constante y bajo. Ideal para quienes buscan estabilidad, aunque no es adecuada si se está buscando un retorno significativo a corto plazo.
Inversión B: Presenta un alto retorno inicial, pero muestra una tendencia a la baja. Es una opción arriesgada, pero con potencial si esperamos una recuperación.
Inversión C: Comienza con retornos negativos que mejoran con el tiempo. Representa una opción de recuperación potencial pero con un historial de pérdidas inicial.
Inversión D: Muestra alta volatilidad y muchos picos positivos hacia el final. Aunque arriesgada, su tendencia final al alza puede considerarse prometedora.
¿Cómo calculamos el riesgo y el retorno esperado?
Para medir el riesgo y el retorno esperado en función de nuestros vectores, calculamos:
Promedio (Retorno esperado): Suma de todos los retornos dividida por el número total de períodos.
Desviación estándar (Riesgo): Indica la variabilidad del retorno comparada con el promedio.
import numpy as np
# Vectores de ejemplo para las inversiones A, B, C, Dinversiones ={'A':[0.1]*50,'B': np.random.uniform(0,1,50),'C': np.random.uniform(-0.5,0.5,50),'D': np.random.uniform(-1,1,50)}# Cálculo de promedios y desviaciones estándarpromedios ={k: np.mean(v)for k, v in inversiones.items()}desviaciones ={k: np.std(v)for k, v in inversiones.items()}print("Promedios:", promedios)print("Desviaciones estándar:", desviaciones)
¿Por qué debemos considerar la estandarización y los z-scores?
La estandarización nos ayuda a comparar diferentes vectores en la misma escala al calcular los z-scores. Un z-score indica cuántas desviaciones estándar está un dato respecto al promedio, permitiendo valorar qué tan extremo es un valor dentro del conjunto de datos.
Ejercicio propuesto: calcular z-scores
Te invito a calcular los z-scores para evaluar mejor las decisiones de inversión en los vectores dados. Esto te proporcionará una perspectiva adicional sobre qué tan arriesgada o segura es una inversión. ¿Continúas pensando que invirtiendo en A o B es lo más adecuado, o cambió tu opinión después del análisis de los z-scores? Deja tus comentarios y sigue aprendiendo, ya tienes las herramientas necesarias para dominar estos modelos de riesgo-retorno.
Existe una función en scipy stats para hacer z-scores. permite estandarizar los valores de xi en zi.
Con esta función, hice la estandarización de las 4 inversiones de la siguiente manera:
from scipy import stats
az = stats.zscore(a)bz = stats.zscore(b)cz = stats.zscore(c)dz = stats.zscore(d)
y obtuve por resultado el siguiente gráfico:
Noté que la fracción de retorno, a pesar de tener las mismas proporciones en todas las inversiones, ahora pasa a tomar valores estandarizados; de igual manera grafiqué el resultado de los riesgos con los valores en z-score y la gráfica resultante es la siguiente:
Dentro del gráfico del mismo sólo me aparecen d y a, justamente las opciones que coincidimos son las "mejores" para invertir.
nota: solo agregué esa línea de código con la función z-score y reemplacé los nombres de las variables en los pasos siguientes 😁
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
a = np.array([0.1]*50)b = np.sin(np.linspace(0,4,50))+ np.random.uniform(-0.1,0.1,50)c = np.cos(np.linspace(2,6,50))+ np.random.uniform(-0.1,0.1,50)d =[i - np.random.uniform(0,0.8)for i in np.linspace(0,1,50)]az = stats.zscore(a)bz = stats.zscore(b)cz = stats.zscore(c)dz = stats.zscore(d)fig,ax = plt.subplots(1,1,figsize=(15,7),dpi=120)ax.plot(np.linspace(0,50,50),az, marker='o', linestyle='dashed',label='Inversión a')ax.plot(np.linspace(0,50,50),bz, marker='o', linestyle='dashed',label='Inversión b')ax.plot(np.linspace(0,50,50),cz, marker='o', linestyle='dashed',label='Inversión c')ax.plot(np.linspace(0,50,50),dz, marker='o', linestyle='dashed',label='Inversión d')ax.set_xlabel('Tiempo')ax.set_ylabel('Fraccion de retorno')ax.legend()plt.show()I = np.array([az,bz,cz,dz])M = np.array([np.mean(x)for x in I])S = np.array([np.std(x)for x in I])fig,ax = plt.subplots(1,1,figsize=(7,7),dpi=120)ax.scatter(S[0],M[0], linestyle='dashed',label='Inversión a')ax.scatter(S[1],M[1], linestyle='dashed',label='Inversión b')ax.scatter(S[2],M[2], linestyle='dashed',label='Inversión c')ax.scatter(S[3],M[3], linestyle='dashed',label='Inversión d')ax.set_xlabel('Riesgo')ax.set_ylabel('Retorno esperado')ax.legend()plt.show()
Los resultados me dieron similar a los de mis compañeros, pero quise graficarlos en histogramas para ver que se podía interpretar, y vi dos cosas.
Primero, creo que por la imprecisión del sistema todos tenemos en la inversión a que el resultado es 1, pero en teoría no podríamos estimar el z para este caso porque la STD es cero y no podemos dividir por cero. Aunque por logica como todos los valores se ubican en la media, su valor deberia ser cero para todos los valores .
Segundo, La inversión que mejor comportamiento tiene es la b porque existen una mayor cantidad de datos que se ubican al lado derecho del gráfico y tienden a llegar hasta una desviación estándar de la media.
z = np.array([(i -M[j])/S[j]for j, i inenumerate(I)])fig, ax = plt.subplots(2,2, figsize=(12,7), dpi=100, facecolor='white')k =[(0,0),(0,1),(1,0),(1,1)]names =['Inversion a','Inversion b','Inversion c','Inversion d']colors =['blue','orange','green','red',]for j, i inenumerate(I): ax[k[j]].hist(i, bins=15, density=True, label=names[j], color=colors[j]) ax[k[j]].set_title(names[j])plt.show()
Si bien usar gráficos de histogramas está bien, en este caso seria mejor el de estimación de densidad de kernel:
sns.kdeplot(z_score_ABCD);
Yo hice algunas modificaciones:
import numpy as np
a = np.array([0.1]*50)b = np.sin(np.linspace(0,4,50))+ np.random.uniform(-0.1,0.1,50)c = np.cos(np.linspace(2,6,50))+ np.random.uniform(-0.1,0.1,50)d =[i - np.random.uniform(0,0.8)for i in np.linspace(0,1,50)]I= np.array([a,b,c,d])ZI=[(i - np.mean(i))/np.std(i)for i inI]# Graficandofig,ax = plt.subplots(1,1,figsize=(15,7),dpi=120)ax.plot(np.linspace(0,50,50),ZI[0], marker='o', linestyle='dashed',label='Inversión a')ax.plot(np.linspace(0,50,50),ZI[1], marker='o', linestyle='dashed',label='Inversión b')ax.plot(np.linspace(0,50,50),ZI[2], marker='o', linestyle='dashed',label='Inversión c')ax.plot(np.linspace(0,50,50),ZI[3], marker='o', linestyle='dashed',label='Inversión d')ax.set_xlabel('Tiempo')ax.set_ylabel('Fraccion de retorno')ax.legend()plt.show()
Me da el siguiente grafico:
for x inZI])S= np.array([np.std(x)for x inZI])fig,ax = plt.subplots(1,1,figsize=(7,7),dpi=120)ax.scatter(S[0],M[0], linestyle='dashed',label='Inversión a')ax.scatter(S[1],M[1], linestyle='dashed',label='Inversión b')ax.scatter(S[2],M[2], linestyle='dashed',label='Inversión c')ax.scatter(S[3],M[3], linestyle='dashed',label='Inversión d')ax.set_xlabel('Riesgo')ax.set_ylabel('Retorno esperado')ax.legend()plt.show()
Me da los siguientes puntos:
Puedo ver que aun estandarizando los vectores la accion a sigue siendo la mejor inversión.
, la inversiones c y a ( media z-scores = 1) deberían ser consideradas para el portafolio.
Por qué las gráficas nos muestran cosas totalmente diferentes al retorno esperado. A qué deberíamos creerle?
Hola, @jaayg23. ¿Cómo te salen distintas? :)
Me refiero a que viendo las gráficas veriamos una tendencia a subir en las inversiones, pero el retorno y el riesgo no se ve de manera tan clara que sea una buena idea invertir ahí. A qué se debe esto? Gracias
Creo que sería mejor invertir en la opción a, a menos que me haya equivocado al graficar ^^'
Hay un "error" en como estás calculando el vector z_a. Debería de darte un vector de puros ceros. Solo que python no lo da (te da algo asi como 1e-18 que es muy pequeño pero no es cero) tanto en la resta de la media como en el std. Por eso da 1 (1e-18/1e-18) en lugar de cero
Hola Platzitueros,
Al correr en mi copia del colab el primer ejemplo del profesor, no me permitía hacer la gráfica. Me indicaba el siguiente error: ++AttributeError: 'module' object has no attribute 'pyplot'++
Buscando, encontré la siguiente explicación: Parece habitual hacerlo: import matplotlib.pyplot as pltmomento en el que puede utilizar las distintas funciones y clases que contiene:
Por lo tanto agregué la importación del anterior y, ¡resultó! Entonces en mi código las importaciones fueron:
import matplotlib as plt
import matplotlib.pyplotas plt
import numpy as np```
Espero ayude a quien le suceda.Saludos
Ejercicio z-scores
Lo primero que realice fue estandarizar todos los valores dados por el profesor en la clase a los valores de z-scores mediante la fórmula dada y después graficar mi resultado con los nuevos valores estandarizados:
import matplotlib.pyplotas plt
import numpy as np
a = np.array([0.1]*50)b = np.sin(np.linspace(0,4,50))+ np.random.uniform(-0.1,0.1,50)c = np.cos(np.linspace(2,6,50))+ np.random.uniform(-0.1,0.1,50)d =[i - np.random.uniform(0,0.8)for i in np.linspace(0,1,50)]def z(x): y=[] a= np.mean(x) b= np.std(x)for i inx: f=(i-a)/b
y.append(f)return y
a1 = np.array(z(a))b1 = np.array(z(b))c1 = np.array(z(c))d1 = np.array(z(d))fig,ax = plt.subplots(1,1,figsize=(15,7),dpi=120)ax.plot(np.linspace(0,50,50),a1, marker='o', linestyle='dashed',label='Inversión a')ax.plot(np.linspace(0,50,50),b1, marker='o', linestyle='dashed',label='Inversión b')ax.plot(np.linspace(0,50,50),c1, marker='o', linestyle='dashed',label='Inversión c')ax.plot(np.linspace(0,50,50),d1, marker='o', linestyle='dashed',label='Inversión d')ax.set_xlabel('Tiempo')ax.set_ylabel('Fraccion de retorno')ax.legend()plt.show()
Después, con los valores ya estandarizados, seguí los pasos vistos en clase para generar la gráfica para comparar el "retorno de inversión" contra el "riesgo" mediante el siguiente código:
I1= np.array([a1,b1,c1,d1])M1= np.array([np.mean(x)for x inI1])S1= np.array([np.std(x)for x inI1])fig,ax = plt.subplots(1,1,sharex=True, sharey=True,figsize=(7,7),dpi=100)ax.scatter(S1[0],M1[0], linestyle='dashed',label='Inversión a')ax.scatter(S1[1],M1[1], linestyle='dashed',label='Inversión b')ax.scatter(S1[2],M1[2], linestyle='dashed',label='Inversión c')ax.scatter(S1[3],M1[3], linestyle='dashed',label='Inversión d')ax.set_xlabel('Riesgo')ax.set_ylabel('Retorno esperado')ax.legend()plt.show()
Y la gráfica resultante fue esta:
Por alguna razón google colab no me dejó graficar los 4 puntos simultáneamente, así que lo máximo que me pudo mostrar al mismo tiempo fueron estos tres. En el caso de la inversión a, esta se encuentra en la coordenada: 0,1, o sea 0 riesgo y 1 en retorno de inversión.
Si alguien sabe a qué se debe agradecería mucho el aporte.
##Conclusiones
En base a los datos generados con la estandarización las inversiones que yo realizaría serían en la inversión a y b por lo siguiente:
Inversión a: Es una inversión segura (el riesgo se encuentra en 0) que está por encima de la media de retorno de inversión.
Inversión b: Es una inversión riesgosa, pero igual de riesgosa que la inversión c y d pero con un retorno de inversión superior.
NOTA: Cualquier feedback es bienvenido para mejorar.
¿Por que los Z-scores de A son 1?
Si al hacerlo de forma manual se obtiene un 0/0 para cada elemento.
Eso es por que los computadores no tiene precision infinita, es decir tienen cierto margen de error al calcular numeros MUY MUY pequeños como tambien lo hará para numeros muy grandes.
Cuando dividimos 1/std(a), eso nos dara un valor extremadamente grande que se acerca al infinito, pero el computador lo representa como un numero muy grande hasta donde sea capaz de calcular.
Y por el otro lado [a - Media de a] = 0. Pero el computador no lo calcula como un 0 total si no como un numero muy muy pequeño que se acerca al 0, pero no es 0.
Entonces si multiplicamos un numero muy grande por un numero ligeramente mayor que 0, vamos a obtener un resultado mayor que cero en este caso nos dio un uno, y como podrás ver esto es un error de precision que seguramente sesgará nuestras predicciones.
y este tipo de errores se conoce como Underfitting.
El termino es Underflow :) lo confundí con otro termino xd
Muy interesante el uso del Mean (siendo el promedio de retorno) y la desviación estándar (el riesgo) en inversiones aunque me genera la duda si en este caso la cantidad de sigmas se utilizan como mayor precisión o certeza.
Aquí se puede ver la variación con respecto a STD de cada inversión. Las agrupé en una misma gráfica para poder deducir cúal nos conciene más
Para realizar la estandarización de un vector x en Python utilizando NumPy, puedes seguir estos pasos:
import numpy as np
def estandarizar_vector(x): promedio = np.mean(x) # Calcular el promedio de x
desviacion_estandar = np.std(x) # Calcular la desviación estándar de x
z_scores =(x - promedio)/ desviacion_estandar # Calcular los z-scores
return z_scores
# Ejemplo de uso
x = np.array([1,2,3,4,5])z_scores =estandarizar_vector(x)print(z_scores)
En este ejemplo, la función estandarizar_vector recibe como entrada el vector x que deseas estandarizar. Luego, se calcula el promedio de x utilizando np.mean(x) y la desviación estándar utilizando np.std(x). Finalmente, se obtienen los z-scores dividiendo la diferencia entre cada entrada de x y el promedio por la desviación estándar.
Al ejecutar el código, obtendrás como resultado los z-scores del vector x. Cada valor en el vector resultante representa cuántas desviaciones estándar se encuentra esa entrada por encima o por debajo del promedio de x.
Aquí la comparación de las graficas retorno/riesgo antes y después de la estandarización.
NOTA: Cuando se calcula el vector a2 (a estandarizado) noten que el resultado debería ser cero. Pero python devuelve un ‘casi cero’ (algo como 1e-18) y cuando divide ‘casi cero’ / ‘casi cero’ da un uno, no un cero como debería, por lo que lo cree con np.zeros(50) .
Así quedan las graficas estandarizadas:
Se nota que la verde es la mera buena, aunque tiene bastante riesgo.
Que buen contenido, gracias
al analizar los datos con una z score lo que creo que se hace es calcular un índice de que tan** buena idea** es invertir en cada punto del índice porque relaciona:
la std que es cuanto fluctúa lo
x - avg que se puede interpretar como si el punto x esta en mejor (arriba del promedio) peor (debajo) en relacion al **promedio **
y los relaciona de forma que x-avg/std por lo que x-avg puede ser alto(ósea que ahora esta mejor) pero si fluctúa (std) mucho su valor se va reducir de forma correspondiente
con lo que obtuve este grafico
del que se podría interpretar que la inversión d y c al final seria buena idea invertir en ellas, específicamente en la d por un posible alto rendimiento pese a su fluctuation además de que tiende a subir al largo plazo, y en la c porque esta subiendo considerablemente comparándola con como empezó
nota: la inversión a no aparece por lo que su std es casi 0 y como se divide sus valores se pierden pero en teoría seria bajo porque la x-avg es casi 0 / std casi 0 por lo que se puede interpretar que no es tan buena idea a pesar de que es constante porque no te da tanto rendimiento
Este video me ayudo a entender mejor el concepto de z-scores:
En realidad no se a que conclusión llegar conlo obtenido con los valores de Z score porque no se como interpretarlos de manera correcta. A la conclusión que llegué es que la mejor inversión sería la A porque no hay riesgos de perdida aunque la ganancia sea minima o casi inexistente y considero que la opción que tenga la media mayor de las inversiones que quedan seria la mejor porque significaría que normalmente estan arriba de lo que se espera.
Dando click aquí puedes ingresar a mi notebook , me serían de mucha utilidad sus aportes.
def z_score(x):return np.mean((x - np.mean(x))/ np.std(x))z_scores = np.array([z_score(x)for x inI])
Y tuve como resultado array([ 1.00000000e+00, -9.76996262e-17, -6.21724894e-17, 1.99840144e-17])
Lo comprobe con la libreria scipy
from scipy import stats
z_scores_sci = np.array([ np.mean(stats.zscore(x))for x inI])
el resultado fue el mismo
import matplotlib.pyplotas plt
import numpy as np
from scipy import stats
if __name__=='__main__': a = np.array([0.1]*50) b = np.sin(np.linspace(0,4,50))+ np.random.uniform(-0.1,0.1,50) c = np.cos(np.linspace(2,6,50))+ np.random.uniform(-0.1,0.1,50) d =[i - np.random.uniform(0,0.8)for i in np.linspace(0,1,50)] fig, ax = plt.subplots(1,1, figsize=(15,7), dpi=120) ax.plot(np.linspace(0,50,50), a, marker ='o', linestyle='dashed', label='Inversion a') ax.plot(np.linspace(0,50,50), b, marker ='o', linestyle='dashed', label='Inversion b') ax.plot(np.linspace(0,50,50), c, marker ='o', linestyle='dashed', label='Inversion c') ax.plot(np.linspace(0,50,50), d, marker ='o', linestyle='dashed', label='Inversion d') ax.set_xlabel('Tiempo') ax.set_ylabel('Fraccion de retorno') ax.legend() #plt.show()I= np.array([a, b, c, d])M= np.array([np.mean(x)for x inI]) # Retorno esperado
S= np.array([np.std(x)for x inI]) # Riesgo #graficar las desviacion estandar y media
fig, ax = plt.subplots(1,1, figsize=(7,7), dpi=120) ax.scatter(S[0],M[0], linestyle='dashed', label='Inversion a') ax.scatter(S[1],M[1], linestyle='dashed', label='Inversion b') ax.scatter(S[2],M[2], linestyle='dashed', label='Inversion c') ax.scatter(S[3],M[3], linestyle='dashed', label='Inversion d') ax.set_xlabel('Riesgo') ax.set_ylabel('Retorno Esperando') ax.legend() #plt.show()Zscore= np.array([(w - np.mean(w)*np.ones(len(w)))/np.std(w)for w inI]) # la hace lo mismo que la funcion Zcores de scipy
#print(Zscore) fig, ax = plt.subplots(1,1, figsize=(15,7), dpi=120) ax.plot(np.linspace(0,50,50),Zscore[0], marker ='o', linestyle='dashed', label='Inversion a') ax.plot(np.linspace(0,50,50),Zscore[1], marker ='o', linestyle='dashed', label='Inversion b') ax.plot(np.linspace(0,50,50),Zscore[2], marker ='o', linestyle='dashed', label='Inversion c') ax.plot(np.linspace(0,50,50),Zscore[3], marker ='o', linestyle='dashed', label='Inversion d') ax.set_xlabel('Tiempo') ax.set_ylabel('Fraccion de retorno') ax.legend() plt.show()